php实现的网页正文智能提取算法代码实例类

时间:2018-11-07 作者:佚名 编辑:xiaoxin 来源:不甘平庸网

php实现的提取网页正文部分:最近研究百度结果页的资讯采集,其中关键环节就是从采集回的页面中提取出文章。因为难点在于如何去识别并保留网页中的文章部分,而且删除其它无用的信息,并且要做到通用化,不能像火车头那样根据目标站来制定采集规则,因为搜索引擎结果中有各种的网页。这个类是从网上找到的一个php实现的提取网页正文部分的算法。

php智能提取正文图片

php智能提取正文图片

php实现的网页正文智能提取算法代码实例类

<?php
 
class Readability {
    // 保存判定结果的标记位名称
    const ATTR_CONTENT_SCORE = "contentScore";
 
    // DOM 解析类目前只支持 UTF-8 编码
    const DOM_DEFAULT_CHARSET = "utf-8";
 
    // 当判定失败时显示的内容
    const MESSAGE_CAN_NOT_GET = "Readability was unable to parse this page for content.";
 
    // DOM 解析类(PHP5 已内置)
    protected $DOM = null;
 
    // 需要解析的源代码
    protected $source = "";
 
    // 章节的父元素列表
    private $parentNodes = array();
 
    // 需要删除的标签
    // Note: added extra tags from https://github.com/ridcully
    private $junkTags = Array("style", "form", "iframe", "script", "button", "input", "textarea", 
                                "noscript", "select", "option", "object", "applet", "basefont",
                                "bgsound", "blink", "canvas", "command", "menu", "nav", "datalist",
                                "embed", "frame", "frameset", "keygen", "label", "marquee", "link");
 
    // 需要删除的属性
    private $junkAttrs = Array("style", "class", "onclick", "onmouseover", "align", "border", "margin");
 
 
    /**
     * 构造函数
     *      @param $input_char 字符串的编码。默认 utf-8,可以省略
     */
    function __construct($source, $input_char = "utf-8") {
        $this->source = $source;
 
        // DOM 解析类只能处理 UTF-8 格式的字符
        $source = mb_convert_encoding($source, 'HTML-ENTITIES', $input_char);
 
        // 预处理 HTML 标签,剔除冗余的标签等
        $source = $this->preparSource($source);
 
        // 生成 DOM 解析类
        $this->DOM = new DOMDocument('1.0', $input_char);
        try {
            //libxml_use_internal_errors(true);
            // 会有些错误信息,不过不要紧 :^)
            if (!@$this->DOM->loadHTML('<?xml encoding="'.Readability::DOM_DEFAULT_CHARSET.'">'.$source)) {
                throw new Exception("Parse HTML Error!");
            }
 
            foreach ($this->DOM->childNodes as $item) {
                if ($item->nodeType == XML_PI_NODE) {
                    $this->DOM->removeChild($item); // remove hack
                }
            }
 
            // insert proper
            $this->DOM->encoding = Readability::DOM_DEFAULT_CHARSET;
        } catch (Exception $e) {
            // ...
        }
    }
 
 
    /**
     * 预处理 HTML 标签,使其能够准确被 DOM 解析类处理
     *
     * @return String
     */
    private function preparSource($string) {
        // 剔除多余的 HTML 编码标记,避免解析出错
        preg_match("/charset=([\w|\-]+);?/", $string, $match);
        if (isset($match)) {
            $string = preg_replace("/charset=([\w|\-]+);?/", "", $string, 1);
        }
 
        // Replace all doubled-up <BR> tags with <P> tags, and remove fonts.
        $string = preg_replace("/<br\/?>[ \r\n\s]*<br\/?>/i", "</p><p>", $string);
        $string = preg_replace("/<\/?font[^>]*>/i", "", $string);
 
        // @see https://github.com/feelinglucky/php-readability/issues/7
        //   - from http://stackoverflow.com/questions/7130867/remove-script-tag-from-html-content
        $string = preg_replace("#<script(.*?)>(.*?)</script>#is", "", $string);
 
        return trim($string);
    }
 
 
    /**
     * 删除 DOM 元素中所有的 $TagName 标签
     *
     * @return DOMDocument
     */
    private function removeJunkTag($RootNode, $TagName) {
        
        $Tags = $RootNode->getElementsByTagName($TagName);
        
        //Note: always index 0, because removing a tag removes it from the results as well.
        while($Tag = $Tags->item(0)){
            $parentNode = $Tag->parentNode;
            $parentNode->removeChild($Tag);
        }
        
        return $RootNode;
        
    }
 
    /**
     * 删除元素中所有不需要的属性
     */
    private function removeJunkAttr($RootNode, $Attr) {
        $Tags = $RootNode->getElementsByTagName("*");
 
        $i = 0;
        while($Tag = $Tags->item($i++)) {
            $Tag->removeAttribute($Attr);
        }
 
        return $RootNode;
    }
 
    /**
     * 根据评分获取页面主要内容的盒模型
     *      判定算法来自:http://code.google.com/p/arc90labs-readability/   
     *      这里由郑晓博客转发
     * @return DOMNode
     */
    private function getTopBox() {
        // 获得页面所有的章节
        $allParagraphs = $this->DOM->getElementsByTagName("p");
 
        // Study all the paragraphs and find the chunk that has the best score.
        // A score is determined by things like: Number of <p>'s, commas, special classes, etc.
        $i = 0;
        while($paragraph = $allParagraphs->item($i++)) {
            $parentNode   = $paragraph->parentNode;
            $contentScore = intval($parentNode->getAttribute(Readability::ATTR_CONTENT_SCORE));
            $className    = $parentNode->getAttribute("class");
            $id           = $parentNode->getAttribute("id");
 
            // Look for a special classname
            if (preg_match("/(comment|meta|footer|footnote)/i", $className)) {
                $contentScore -= 50;
            } else if(preg_match(
                "/((^|\\s)(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)(\\s|$))/i",
                $className)) {
                $contentScore += 25;
            }
 
            // Look for a special ID
            if (preg_match("/(comment|meta|footer|footnote)/i", $id)) {
                $contentScore -= 50;
            } else if (preg_match(
                "/^(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)$/i",
                $id)) {
                $contentScore += 25;
            }
 
            // Add a point for the paragraph found
            // Add points for any commas within this paragraph
            if (strlen($paragraph->nodeValue) > 10) {
                $contentScore += strlen($paragraph->nodeValue);
            }
 
            // 保存父元素的判定得分
            $parentNode->setAttribute(Readability::ATTR_CONTENT_SCORE, $contentScore);
 
            // 保存章节的父元素,以便下次快速获取
            array_push($this->parentNodes, $parentNode);
        }
 
        $topBox = null;
        
        // Assignment from index for performance. 
        //     See http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5 
        for ($i = 0, $len = sizeof($this->parentNodes); $i < $len; $i++) {
            $parentNode      = $this->parentNodes[$i];
            $contentScore    = intval($parentNode->getAttribute(Readability::ATTR_CONTENT_SCORE));
            $orgContentScore = intval($topBox ? $topBox->getAttribute(Readability::ATTR_CONTENT_SCORE) : 0);
 
            if ($contentScore && $contentScore > $orgContentScore) {
                $topBox = $parentNode;
            }
        }
        
        // 此时,$topBox 应为已经判定后的页面内容主元素
        return $topBox;
    }
 
 
    /**
     * 获取 HTML 页面标题
     *
     * @return String
     */
    public function getTitle() {
        $split_point = ' - ';
        $titleNodes = $this->DOM->getElementsByTagName("title");
 
        if ($titleNodes->length 
            && $titleNode = $titleNodes->item(0)) {
            // @see http://stackoverflow.com/questions/717328/how-to-explode-string-right-to-left
            $title  = trim($titleNode->nodeValue);
            $result = array_map('strrev', explode($split_point, strrev($title)));
            return sizeof($result) > 1 ? array_pop($result) : $title;
        }
 
        return null;
    }
 
 
    /**
     * Get Leading Image Url
     *
     * @return String
     */
    public function getLeadImageUrl($node) {
        $images = $node->getElementsByTagName("img");
 
        if ($images->length && $leadImage = $images->item(0)) {
            return $leadImage->getAttribute("src");
        }
 
        return null;
    }
 
 
    /**
     * 获取页面的主要内容(Readability 以后的内容)
     *
     * @return Array
     */
    public function getContent() {
        if (!$this->DOM) return false;
 
        // 获取页面标题
        $ContentTitle = $this->getTitle();
 
        // 获取页面主内容
        $ContentBox = $this->getTopBox();
        
        //Check if we found a suitable top-box.
        if($ContentBox === null)
            throw new RuntimeException(Readability::MESSAGE_CAN_NOT_GET);
        
        // 复制内容到新的 DOMDocument
        $Target = new DOMDocument;
        $Target->appendChild($Target->importNode($ContentBox, true));
 
        // 删除不需要的标签
        foreach ($this->junkTags as $tag) {
            $Target = $this->removeJunkTag($Target, $tag);
        }
 
        // 删除不需要的属性
        foreach ($this->junkAttrs as $attr) {
            $Target = $this->removeJunkAttr($Target, $attr);
        }
 
        $content = mb_convert_encoding($Target->saveHTML(), Readability::DOM_DEFAULT_CHARSET, "HTML-ENTITIES");
 
        // 多个数据,以数组的形式返回
        return Array(
            'lead_image_url' => $this->getLeadImageUrl($Target),
            'word_count' => mb_strlen(strip_tags($content), Readability::DOM_DEFAULT_CHARSET),
            'title' => $ContentTitle ? $ContentTitle : null,
            'content' => $content
        );
    }
 
    function __destruct() { }
}
?>

使用方法:

<?php 
$read=new  Readability(file_get_contents('https://www.bgpy.net/qiwenqushi/tansuojiemi_3979.html'));
$rs=$read->getContent();
echo '<pre>';
print_r($rs);
echo '</pre>';
?>

使用起来也非常简单,实例化时传入网页的html源码和相应的编码,然后直接调用其getContent方法即可返回提取到的正文部分,提取出的文章中可能还会含有少部分链接,可以自己后期再修改。

相关阅读
  • PHP解决如抢购并发问题的几种实现方法
    PHP解决如抢购并发问题的几种实现方法
    对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发... [详情]
  • php中session引起的脚本阻塞问题解决办法
    这个问题很多做php开发朋友应该都有遇到过,一个启用了session_start 页面,由于执行时间过长。导致通一个用户访问,另外一个很简单的启用session_start页面一直阻塞着。 直到第一个页面执行完了。第二个页面才可以... [详情]
  • PHP正则替换函数preg_replace和preg_replace_callback使用总结
    在编写PHP模板引擎工具类时,以前常用的一个正则替换函数为 preg_replace(),加上正则修饰符 /e,就能够执行强大的回调函数,实现模板引擎编译(其实就是字符串替换)。... [详情]
  • php上传文件大小限制问题解决方法:post_max_size对大小的影响
    今天在操作php上传的时候发现了一个问题,就是当php脚步上传的文件大小超过php.ini中post_max_size的限制的时候页面不会给出提醒,文件也上传失败,这个问题感觉应该算是一个另类,今天跟大家分享一下。... [详情]
  • php中的filesize读取图片大小代码实例
    filesize() 函数返回指定文件的大小。 若成功,则返回文件大小的字节数。若失败,则返回 false 并生成一条 E_WARNING 级的错误。本文为你详解php如何读取图片详细信息。... [详情]
  • php用Imagick模块完美实现GIF动画缩略图代码实例
    php用Imagick模块完美实现GIF动画缩略图代码实例
    缩略图是个很常用的功能。它的实现并不复杂,但如果原图是GIF动画的话,问题就会变得繁琐一点。下面看看如何用PHP中的Imagick模块来完美实现GIF动画缩略图……... [详情]
  • PHP怎样判断一个gif图片是否为动态图片(动画)?
    如何使用PHP来判断一个gif图片是否为动态图片(动画)?首先想到的是使用getimagesize()函数来看type值,发现都是gif,所以这个办法是不可行的。下面是作者在网上看到的一个函数,经测试,可以用来判断gif是否为动图。... [详情]
  • Linux和windows7下安装php的imagick和imagemagick扩展教程
    最近的PHP项目中,需要用到切图和缩图的效果,在Linux测试服务器上很轻松的就安装好php imagick扩展。但是在本地windows开发环境,安装过程遇到好多问题,在此与大家分享。... [详情]
  • php服务器500错误无输出,快速定位错误代码万能调试方法
    网站线下调试没有问题,放到线上报500错误,无输出,无报错,日志正常,如何才能快速定位错误代码?经过网上搜索,找到一种方法:万能调试代码法,问题得到解决。... [详情]
  • php使用fsockopen读取分段数据出现多余字符的解决方法
    使用fsockopen读取数据时遇到了一个神奇的问题,具体情况如下:请注意上面那些标红的4个字符,它们每隔一段数据就会出现一次,但是用其他的方法如curl,file_get_contents等取回的数据则没有这些玩意。换成其他的网站... [详情]
  • php去掉fsockopen返回结果中的HTTP头信息的方法
    去掉fsockopen返回结果中的HTTP头信息的两种方法:1、【使用split或substr,strpos截断】在返回的内容中HTTP头信息与正文内容是以两个“换行回车”隔开的所以我们可以在此截断,取之后的内容。2、【先取Content-Length... [详情]
  • PHP环境下Memcache的使用方法详解
    Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。它可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在... [详情]
  • php在用file_get_contents抓取网页时出现乱码的2种解决方式
    今天自己在写一个程序,抓取别人的网页,之前公司有些功能也会需要,但是今天在抓取网页的时候发现了一个问题用file_get_contents抓取网页发现乱码情况。于是用转换编码$contents = iconv("gb2312", "utf-8//IGNORE... [详情]
  • php iconv函数在gb232与utf-8之间转码时出错断掉的解决方法
    最近在做一个采集程序,需要用到iconv函数把抓取来过的gb2312编码的页面转成utf-8, 发现只有用iconv函数把抓取过来的数据一转码数据就会无缘无故的少一部分。让我郁闷了好一会儿,去网上一查资料才知道这是iconv函... [详情]
  • php操作excel的类:PHPExcel 1.8.0使用方法
    首先到phpexcel官网上下载最新的phpexcel类,下载解压缩一个classes文件夹,里面包含了PHPExcel.php和PHPExcel的文件夹,这个类文件和文件夹是我们需要的,把classes解压到你项目的一个目录中,重名名为phpexcel,代... [详情]
  • PHP字符串定义的几种方式
    如果遇到需要多行书写的字符串怎么办,比如我们要定义一段JS脚本,当然如果把脚本写在一行里是没问题的,但是如果脚本比较长一些,而且其中会出现很多转义符号的话,脚本最终成型的话,很有可能成了一锅黏粥了。不但... [详情]
  • PHP匿名函数详解
    PHP匿名函数 匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)的参数。 当然,也有其他应用的情况。... [详情]
  • php cli模式传递和获取参数的方法
    php cli模式传递和获取参数的方法
    在命令行里输入程序参数来更改其运行方式是很常见的做法。你也可以对CLI程序这样做。PHP CLI带有两个特殊的变量,专门用来达到这个。目的:一个是$argv变量,它通过命令行... [详情]
  • php通过QRCode类库创建中间带网站LOGO的二维码实例
    我们要生成二维码都需要借助一些类库来实现了,下面我介绍利用PHP QR Code生成二维码吧,生成方法很简单,下面我来介绍一下.利用php类库PHP QR Code来实现,不需要装额外的php扩展,首先下载类库包,有时候地址打不开,地址... [详情]
  • js判断滚动条滚到页面底部并执行事件的代码实例
    js判断滚动条滚到页面底部并执行事件的代码实例:需要了解三个dom元素,分别是:clientHeight、offsetHeight、scrollTop。clientHeight:这个元素的高度,占用整个空间的高度,所以,如果一个div有滚动条,那个这个... [详情]
PHP心得推荐
频道推荐
本周推荐
点击排行