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

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

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

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

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

今天先讲第二种。

以国家地理中文网宇宙空间为例(链接),先看代码:(把下列源码存为space.php)

<?php
include "gethtml.php";
$regex_link = '/(?<=<dt><a href=").+?(?=")/s';
$regex_tit = '/(?<=<title>)(.+?)(?= -)/s';
$regex_con = '/<div id="detailMain_box_img".*?(?=<div class="M-L-article-last-p" >)/s';
$header='<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>国家地理中文网宇宙空间</title>';
$footer='</channel></rss>';
$html=gethtml('http://www.nationalgeographic.com.cn/science/space/');
if(preg_match_all($regex_link, $html, $links)){
  $size=count($links[0]);
  for($i=0;$i<$size;$i++){
    $link=preg_replace('/(.+)/','http://www.nationalgeographic.com.cn$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('space.xml',$header.$rss.$footer);
}
?>

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

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

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

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

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

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

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

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

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

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

<?php
error_reporting(E_ERROR);
function gethtml($url,$json){
  $args = json_decode($json,true);
  $useragent = $args["useragent"]?$args["useragent"]:'Mozilla/5.0';
  $timeout = $args["timeout"]?$args["timeout"]:9000;
  $ch = curl_init();
  $options = array(
    CURLOPT_URL => $url,
    CURLOPT_USERAGENT => $useragent,
    CURLOPT_TIMEOUT_MS => $timeout,
    CURLOPT_NOSIGNAL => 1,
    CURLOPT_HEADER => 0,
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_FOLLOWLOCATION => 1
  );
  if($args["ip"]){
    $options[CURLOPT_HTTPHEADER] = array('CLIENT-IP:'.$args["ip"],'X-FORWARDED-FOR:'.$args["ip"]);
  }
  if (preg_match('/^https/',$url)){
    $options[CURLOPT_SSL_VERIFYHOST] = 1;
    $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;
  }
}
?>

 

44 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做的。

发表评论

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