教程:RSS全文输出,自己动手做。(一)

这里以PHP版为例,尽量说得通俗点吧,水平实在有限,见谅。虽然并没有多高深,但请转载者注意起码的礼貌。

目前我这里所有的获取全文输出的网站大概是三种情况:

  1. 要输出的内容集中在一页上,也就是看似列表页的页面里集中了你想要的所有内容,并不需要点击“更多”或“继续阅读”才能看到文章整体。比如糗事百科、wiki。
  2. 有列表页,要查看相应文章必须点击链接进入。
  3. json方式写入。特征就是查看源文件并不能找到你在浏览器中看到的内容。比如腾讯新闻图片(链接),它的真正内容在这(链接)。

第一种最省事,第二种最常见,第三种稍麻烦。

今天先讲第二种。

以知乎日报为例 (链接),先看代码:(把下列源码存为zhihu.php)

<?php
include "gethtml.php";
$regex_link = '/(?<=<a href="\/story\/).+?(?=")/';
$regex_tit = '/(?<=<title>)(.+?)(?= -)/s';
$regex_con = '/<span class="img-source">.*?(?=<div class="qr">)/s';
$header='<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>知乎日报</title>';
$footer='</channel></rss>';
$html=gethtml('http://daily.zhihu.com/');
if(preg_match_all($regex_link, $html, $links)){
 $size=count($links[0]);
 for($i=0;$i<$size;$i++){
 $link=preg_replace('/(.+)/','http://daily.zhihu.com/story/$1',$links[0][$i]);
 $content=gethtml($link);
 preg_match($regex_con,$content,$article);
 preg_match($regex_tit,$content,$title);
 $rss.='<item><title>'.$title[0].'</title><link><![CDATA['.$link.']]></link><description><![CDATA['.$article[0].']]></description></item>';
 }
 file_put_contents('zhihu.xml',$header.$rss.$footer);
}
?>

重点是第8行往后。只解释我认为重要的,不明白的可以讨论。

第2行,引入gethtml方法,来自下面的代码。

看一下第8行gethtml(‘http://daily.zhihu.com/’)得到了什么(链接),虽然有点乱,但是目的达到了,http://daily.zhihu.com/ 我已经抓到本地服务器上。

第9行是要挑出需要的链接 $links(链接

第13行利用这些链接继续抓取页面 $content=gethtml($link)

第14、15行从$content里查找需要的$title(文章标题)和$article(文章内容)

后面就是按RSS要求的格式输出,并最终生成xml文件。

最后,想办法打开https://feedx.net/rss/tutorial/zhihuu.php,只要程序不出错,就会同目录生成zhihu.xml,订阅地址即为https://feedx.net/rss/tutorial/zhihu.xml

怎么自动打开那个网址呢,crontab、计划任务、cPanel、wordpress插件都可以,如果都没有的话,网上也是有人提供这个服务的,http://cron-job.org,自己去看吧…

上面我说的查找、挑出都是用正则来实现的,此外最好对html特别熟悉,操作起来才能得心应手。

想要自己完成还是要有一定基础的,完全靠伸手是不现实的,还不如去买那些能可视化操作的产品。如果正则不熟或不想学,可以试试PHP Simple HTML DOM Parser,类似jQuery。

============================================

参数的用法:

默认可以不加参数,基本写法是这样的:

$headers=['User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36','Cookie: BAIDUID=4C2D58EAE60F2ACDB32DCAAAA9795A4D:FG=1;H_WISE_SIDS=106370_122302; BDSVRTM=13'];
$args=['headers'=>$headers,'header'=>1];
$url='http://feedx.net';
$html=gethtml($url,$args);

上面的意思是我去抓取的时候让自己更像个浏览器,模拟的是chrome 65.0.3325.162版,还加上了cookie;因为一个网址可能会跳转很多次才到目标网址,通常我们是不用去管这些的,比如我好奇心很重,就可以把header信息也打印出来。还有更多的一些用法可以参考curl自己添加。

============================================

下面是在别人的基础上自己总结的利用curl抓取页面的方法,把下面源码存为gethtml.php,与zhihu.php放于同一目录下。我所有的抓取都是用的这个方法,当然,省事的话可以用 file_get_contents,那就是真正的20行代码完成全文RSS输出了,不过可选的参数就没有了,有的页面会抓取不到。

<?php
function gethtml($url,$args=null){
 $proxy = $args["proxy"]?$args["proxy"]:'';
 $headers = $args["headers"]?$args["headers"]:[];
 $nobody = $args["nobody"]?$args["nobody"]:0;
 $header = $args["header"]?$args["header"]:0;
 $ch = curl_init();
 $options = array(
 CURLOPT_URL => @$url,
 CURLOPT_PROXY => @$proxy,
 CURLOPT_HTTPHEADER => @$headers,
 CURLOPT_NOSIGNAL => 1,
 CURLOPT_HEADER => @$header,
 CURLOPT_NOBODY => @$nobody,
 CURLOPT_RETURNTRANSFER => 1,
 CURLOPT_FOLLOWLOCATION => 1
 );
 if (preg_match('/^https/',$url)){
 $options[CURLOPT_SSL_VERIFYHOST] = 2;
 $options[CURLOPT_SSL_VERIFYPEER] = 0;
 }
 curl_setopt_array($ch, $options);
 $data = curl_exec($ch);
 $curl_errno = curl_errno($ch); 
 curl_close($ch);
 if($curl_errno>0){
 return 'error';
 }else{
 return $data;
 }
}
?>

 

62 Comments

  1. kobe

    大概看完了,再次謝謝站長的分享。
    簡單來說就是對多層頁面使用函數gethtml獲得主網頁及下級網頁內容後,使用正則分析生成輸出的xml文件。 對不同網站有可能需要通過修改正則來處理不同頁面內容。看完後我在自己的apache上试验通过,下一步是试试不同的网站。自己雖然是多年程序員,但都是数据库、cs架构的,從來沒接觸過web這部分。下面的问题还请站长解答:
    1. 如何自动执行php?是使用crontab吗?还是通过一个主php?
    2. 访问权限的问题,php不能让所有人访问,生成的xml的访问权限也因应不同进行设置,这部分如何解决?

    再次感谢!

    • rssfull

      1. 我用的是空间商后台管理面板cPanel里模拟crontab的一个可视化排程工具,cron jobs(https://confluence2.cpanel.net/display/ALD/Cron+Jobs)。确实也可以通过PHP自己来完成(搜索:PHP 排程,有很多结果。我没试过,总觉得不保险)
      2.没太看懂。我只是把PHP起了个别人想不到的名字,或者你也可以把xml生成到其它目录。这样好像并不涉及权限问题。

      • kobe

        谢谢,我研究研究。

        下面是我学写的蓝影网的php。测试运行通过了,阅读器里能使用。但有两个问题,能帮我看看吗?自己试了很多次都解决不了。
        1. 在获得内容时,总是不能把 tag 去掉。
        2. 不知道为什么在阅读器中总是不能显示下载链接。

        再次感谢!


        <?php
        include "gethtml.php";

        $regex_link = '/(?<=<h2 class=\"entry-title\"><a href=").+?(?=")/s';
        $regex_tit = '/(?<=<title>)(.+?)(?=<\/title>)/s';
        $regex_con = '/(<article.*?>)(.*?)(<\/article>)/s';

        $header='<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>蓝影网</title>';
        $footer='</channel></rss>';
        $html=gethtml('http://www.lanyingwang.com/');

        if(preg_match_all($regex_link, $html, $links)){
        $size=count($links[0]);
        for($i=0;$i<$size;$i++){
        $link=$links[0][$i];
        $content=gethtml($link);

        preg_match($regex_con,$content,$article);
        preg_match($regex_tit,$content,$title);

        $rss.='<item><title>'.$title[0].'</title><link><![CDATA['.$link.']]></link><description><![CDATA['.$article[0].']]></des$
        }
        file_put_contents('lanyingwang.xml',$header.$rss.$footer);
        }
        ?>

  2. rssfull

    已回复邮箱。贴在这里可能帮助到别人

    /(<article.*?>)(.*?)(<\/article>)/

    按你的要求,需要明白这两个用法(零宽断言):
    (?=exp)     匹配exp前面的位置不包括exp本身
    (?<=exp)     匹配exp后面的位置不包括exp本身

    而上面的正则其实是这样的:
    /(exp)(.*?)(exp)/
    只是进行了分组,它是包括exp本身的。所以消不掉。(当然,你已经把它分组了,也可以用一个替换得到想要的内容)

    但是这个又不能这么写
    /(?<=<article.*?>)(.*?)(?=<\/article>)/
    因为零宽断言不能含有通配符

    可以这样匹配:
    /<header class=”entry-header”>.*?(?=<\/article>)/
    也就是紧贴着article标签的header开始(包括header)到</article>为止(不包括</article>)
    其实也不必为这些标签烦恼。我试过,标签不配对问题都不太大。多一个少一个的不用纠结。

    我试了试你的代码,下载链接好像没问题,抱歉了,帮不到。
  3. kobe

    最近發現用Simple HTML DOM Parser(http://simplehtmldom.sourceforge.net/)比正則來得簡單好用,站長可以試試。

    PS: 站長能否教下如何弄微博的RSS輸出,微博需要登錄,自己試了些教程,總是有問題,先謝謝了。

    • rssfull

      这个用过,总觉得不如正则来得直接,弃用了。
      我对微博没啥兴趣,刚看了看,确实页面是抓不到的,要模拟登录什么的,curl可以办到吧,这里不讨论这个。
      如果只是想要抓到页面,其实有别的办法。教程随后更新。

  4. Pingback: 幻璧 - 碧宇星辰

  5. yezi

    我有一个国外的服务器,新建一个站点,然后我将你这上面的两个文件放在里面。初步能得到XML的文件。现在咨询一下,如何能让这两个文件自动更新。能读到最新的文件呢。设置自动刷新吗?我是VPS。再就是关于这个墙的问题,在国外存到服务器上是不经过墙的所以这个没问题。我们这边的电脑通过RSS订阅软件拉取数据时。拉过来的是XML格式的国内外的文档。这个墙是如何辨别的?HTTPS能保护服务器和域名吗?我是担心我服务器IP别被封了。感谢有空答复。谢谢。

    • feedx

      只需要将space.php自动执行就可以,你是vps,那有很多办法执行它。排程、计划任务之类的。
      自己用不必考虑墙的问题。

  6. yezi

    还有一个咨询问题,有些国外网站上面的图片在国内是看不到的,我看你 个站点上说存到你的服务器上面,复杂不。我服务性能高些,我想试试能不能放我服务器上。看看效果。谢谢

    • feedx

      图片本地化也是写在space.php里,每个网站不同,具体写法也有些不同。大概流程是取得xml文件里的图片地址,把图片存到本地服务器,替换xml里的图片地址。

  7. yezi

    这样一个网站:
    原网址:http://www.qhrb.com.cn/organ/organsee/ 我做下令规则space.php如下:<?php
    include "gethtml.php";
    $regex_link = '/(?<=<a href=").+?(?=")/s';
    $regex_tit = '/(?<=)(.+?)(?=)/s’;
    $regex_con = ‘/.*?(?=)/s’;
    $header=’期货日报’;
    $footer=”;
    $html=gethtml(‘http://www.qhrb.com.cn/organ/organsee/’);
    if(preg_match_all($regex_link, $html, $links)){
    $size=count($links[0]);
    for($i=0;$i<$size;$i++){
    $link=preg_replace('.+','$1',$links[0][$i]);
    $content=gethtml($link);
    preg_match($regex_con,$content,$article);
    preg_match($regex_tit,$content,$title);
    $rss.='’.$title[0].”;
    }
    file_put_contents(‘qhrb.xml’,$header.$rss.$footer);
    }
    ?>

    不知道问题出在哪里,麻烦指点一下,万分感谢!

    • feedx

      这个错误太多了。
      弄这个要对三个东西有所熟悉,html、正则表达式和php,虽然是用php写的,其实前两个更重要一些。
      对教程里的每一步都要清楚在干什么,其实,不用php环境也可以知道这步做得对不对,比如我要抓取一篇文章,先打开那篇文章,查看源文件,复制到一些稍高级的编辑器里(支持正则查找,比如notepad++),查找,查找模式选择正则和匹配新行。只要能查到的是你想要的那块代码,这就成功了,这就是$regex_con的作用。那块代码就是html的知识,查找的内容就是正则表达式。
      以此类推,$regex_tit是需要能查什么也就清楚了。

  8. yezi

    链接,标题,内容分别是这样的正规则:
    $regex_link = ‘/(?<=<a href=").+?(?=")/s';
    $regex_tit = '/(?<=)(.+?)(?=)/s’;
    $regex_con = ‘/.*?(?=)/s’;
    在NOTEPAD++里面我能定位到他们。
    就是后面的那个:
    $html=gethtml(‘http://www.qhrb.com.cn/organ/organsee/’);
    if(preg_match_all($regex_link, $html, $links)){
    $size=count($links[0]);
    for($i=0;$i<$size;$i++){
    $link=$links[0][$i];
    $content=gethtml($link);
    preg_match($regex_con,$content,$article);
    preg_match($regex_tit,$content,$title);
    $rss.='’.$title[0].'<![CDATA['.$article[0].']]
    链接不知道如何弄出来,您上面的是相对地址,这个是绝对地址。我测试了几个方法也找不出。再次感谢。

  9. yezi

    我发现问题了,您这个网站把我一些代码过滤掉了。我把我写的源码放这里:http://qhlt.cn/thread-4791-1-1.html 麻烦您帮我看看是哪里的问题,十分感谢。

    • feedx

      问题不太大,稍改下就可以了。
      php的正则规则的格式是这样的’/这里是规则/’,所以,规则里是不能出现“/”的,如果有需要转一下,改为“\/”。或者把正则格式改为’#这里是规则#’,这样就可以直接用“/”了。
      有三处错误
      1. 取得链接那个规则错了,有”/”;
      2. 标题的正则也错了,同上;
      3. $link=preg_replace(‘.+’,’$1′,$links[0][$i]);确实是有问题的,其实这个不用替换,直接用$link=$links[0][$i];之所以有时需要替换是因为得到的链接是不带它本身的域名的,比如得到的链接是这样的$link=’/contactus/index6.html’;这样要抓取的话gethtml($link)是得不到内容的,此时需要把$link加上域名,$link=preg_replace(‘/(.+)/’,’http://xxx.com/$1′,$link);

  10. yezi

    万分感谢,搞定了。现在还有一个小技术问题,就是如何让这个space.php的文件定时运行一下,好查有没有新的资讯更新。我看你在有些文章中说用CURL类的,具体怎么个弄法?

  11. yezi

    今天下午费了老长的时间弄这个批量执行计划,我是WIN 的VPS 弄了好半天也没有成功,希望这个网站不要有什么限制。

  12. yezi

    我晚上也做了几个放上面了,有些能执行,有些执行错误,我看说明是超时,我想是否可以在提取数据时设一限制。比方说一次只取最新的20个。一次执行时提取的数据少一些。是不是就不会出现这种超时的问题了。

  13. yezi

    今天做新浪网的RSS时出现了一点问题,这个是我问题的链接:http://qhlt.cn/thread-4791-1-1.html 您的网站过滤源码,所以我就发到我论坛上面了。有空麻烦您看一下,看看有没有处理方法。 就是链接提取时出现了一些不想要的链接。十分感谢。

    • feedx

      通常是这么解决的,再精确定位一下,你已经取得了本页的$html,preg_match($regex_ul,$html,$ul);这样就能得到$ul[0];这时要改preg_match_all($regex_link, $html, $links为preg_match_all($regex_link, $ul[0], $links。大概结构是这样的:
      $html=gethtml(‘http://www.nationalgeographic.com.cn/science/space/’);
      preg_match($regex_ul,$html,$ul);//把需要的代码块再做一次筛选匹配
      if(preg_match_all($regex_link, $ul[0], $links)){
      //其它代码
      }
      只需要你自己写出$regex_ul的正则,应该没问题吧。
      ————————-另一种————————-
      当然,不止一种解决办地,具体到这个,仔细观察,你需要的链接都是http://finance.sina.com.cn/开头的,这样其实是可以排除那些干扰链接的
      $regex_link = ‘#(?< =<li><a href="http://finance.sina.com.cn/).+?(?=")#s';//用域名做限定,排除干扰项
      ……
      $link=’http://finance.sina.com.cn/’.$links[0][$i];//再把域名加回来

  14. 十分感谢,新浪的至少能提取出新闻了。现在我就考虑有没有好的项目呢,这个RSS资讯自己或小满园看还行,一多就容易出现侵权的问题,同时墙的问题也一直存在。我是做投资的。设想是做一个类似的新闻信息聚合,看看有没有市场。现在先自己弄着玩吧。感谢这几天的指导,我也弄了一个小站,已经加你友情链接,有空多交流:)

  15. yezi

    做这个的人好少啊。最近遇到一个问题,咨询一下,我抓的经常出现只有标题没有内容,或连标题也没有,当然内容也没有了。我用的是quiterss订阅,双击能看到原网址确实有内容,同时通过正规则我发现并没有什么问题。我用的是https://cron-job.org 这个定期刷新的。您经验多,有没有解决方法,十分感谢。

  16. yezi

    这是我做的一个论坛的RSS输出。现在的问题是一对一对的输出,在列表截链接时出现了一点问题,麻烦有空帮我看看能解决不。 http://qhlt.cn/thread-6447-1-1.html 这个是问题的详细,这里回复会过滤一些源码,所以我放到一个贴子里面了。这个论坛是用DISCUZ 7.2做的。

  17. 十分感谢楼主分享教程,我在vultr新建了个网站,php版本5.2,上传了这两个文件,运行没有反应哪?也不能生成xml文件。能是什么原因哪?

  18. yezi

    这几天我做金融街的RSS出现了些问题,麻烦帮我看一下我错在哪里了:http://www.qhlt.cn/thread-12656-1-1.html 按之前的注意事项检查了好多遍也没有看出问题所在。因为你这个网站过滤一些代码。所以我贴到一个论坛上面了。方便时加我一下QQ:95527664

  19. yezi

    刚才想咨询一下我做的这个RSS出现了些问题。想给你发个地址,帮着看一下,发现你这里不能贴地址了。我怎么加你呢。要不您加我QQ:95527664 或您向我邮箱发一下您的微信,刚才我给您转了9块。那个微信不是我的,不过也可以联系到我。

    • feedx

      是可以贴地址的,超过两个或因为一些其它规则才会去垃圾箱,我是能看到的。我会抽空看一下。
      另外,小事一桩,无需付费……

    • feedx

      你用的是不是海外服务器,好像它给禁了,我用了代理是可以抓到的,去掉代理就不成,会超时,基本抓不到东西,不过好解决,它的手机端是没禁用墙外浏览的。
      对比一下PC端的链接和手机端的链接:
      http://futures.jrj.com.cn/2018/04/16112124394219.shtml
      http://m.jrj.com.cn/madapter/futures/2018/04/16112124394219.shtml
      要把得到$link稍做修改:
      $link=preg_replace(‘#http://futures.jrj.com.cn(.+)#’,’http://m.jrj.com.cn/madapter/futures$1′,$link);
      所以后面的抓取是要依据http://m.jrj.com.cn/madapter/futures/2018/04/16112124394219.shtml来进行,相信到这儿应该明白了吧。

      还有个好处就是页面更轻盈了,抓取负担会小很多,所以基本上只要有手机端页面的我肯定会去抓手机端的。

kobe进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注