为什么要做正文提取

一般做舆情分析,都会涉及到网页正文内容提取。对于分析而言,有价值的信息是正文部分,大多数情况下,为了便于分析,需要将网页中和正文不相干的部分给剔除。可以说正文提取的好坏,直接影响了分析结果的好坏。

对于特定的网站,我们可以分析其html结构,根据其结构来获取正文信息。先看一下下面这张图:

正文部分,不同的网站,正文所在的位置不同,并且Html的结构也不同,对于爬虫而言,抓取的页面是各种各样的,不可能针对所有的页面去写抓取规则来提取正文内容,因此需要一种通用的算法将正文提取出来。

现有的网页正文提取算法

  • 基于标签用途的正文提取算法(比如title或h1,h2标签一般用作标题,p一般表示正文段落,根据标签的含义去提取正文)
  • 基于标签密度判定(这个简单,说白了就是字符统计,正文部分html标签的密度比较低,确定一个阈值,按照标签密度提取正文部分)
  • 基于数据挖掘思想的网页正文抽取方法(这里会涉及到统计学和概率论的一些知识,在高深点就成了机器学习了,没有深入研究)
  • 基于视觉网页块分析技术的正文抽取(CV这种高端大气上档次的东西,岂是我等这么容易就能研究明白的。虽然实现上复杂,但就提取效果而言,这种方法提取的精度还是不错的)

前2中方法还是比较容易实现的,主要是处理简单,先前我把标签密度的提取算法实现了,但实际用起来错误率还是蛮高的;后2种方法在实现上就略复杂了,从算法效率上讲应该也高不了哪去。

我们需要的是一种简单易实现的,既能保证处理速度,提取的准确率也不错的算法。于是结合前两种算法,研究网页html页面结构,有了一种比较好的处理思路,权且叫做基于文本密度的正文提取算法吧。后来从网上找了一下类似的算法,发现也有使用类似的处理方法来处理正文提取的,不过还是有些不同。接下来跟大家分享一下这个算法的一些处理思想。

网页分析

我任意取了百度,搜狐,网易的一篇新闻类网页,拿来作分析。

先看一篇百度的文章

任正非为什么主动与我合影,http://liyinghuan.baijia.baidu.com/article/2011

首先请求这个页面,然后过滤到所有的html标签,只保留文本信息,我们可以看到正文信息集中在一下位置:

使用Excel分析行数与每行的字符的关系可以发现:

很明显,正文内容集中在65-100行之间的位置上,而这个区间的字符数也是比较密集的。

再来一篇网易的文章

张小龙神话已破灭 马化腾该接管微信了,http://tech.163.com/13/1230/10/9HB88VE600094NRG.html

还是先看下过滤html标签后的正文部分:

再来一个Excel的分析结果:

正文部分集中在279-282行之间,从图上看,也正是这么几行的文本密度特别高。

最后分析一篇搜狐的新闻

李克强天津调研考察的几个瞬间,http://news.sohu.com/20131229/n392604462.shtml

还是先看下过后标签后的正文:

再看下Excel的分析结果:

而搜狐的这篇文章正文部分主要集中在200-255行之间。其余的文本全部是杂乱的标签文本。

抱歉,漏了很重要的一点说明:为什么分析的时候要把html标签过滤掉呢?过滤html标签是为了降低干扰,因为我们关注的是正文内容,如果带着这样的标签<span style="color: #0000ff;">var</span> chart = <span style="color: #0000ff;">new</span><span style="color: #000000;">去分析,可想而知,对我们的正文分析会有多大的干扰了,也正因如此需要将html标签掉,只对文本做分析,降低干扰。

基于网页分析构思出的正文提取算法

回顾以上的网页分析,如果按照文本密度来找提取正文,那么就是写这么一个算法,能够从过滤html标签后的文本中找到正文文本的起止行号,行号之间的文本就是网页正文部分。

还是从上面三个网页的分析结果看,他们都有这么一个特性:正文部分的文本密度要高出非正文部分很多。我们按照这个特性就可以很容易将算法实现,那就是基于阈(读音:yu)值去分析正文所在的位置。

那么接下来就需要解决一些问题:

  • 如何确定阈值?
  • 如何分析,一行行的分析?还是?

阈值的确定可以通过统计分析得出一个比较好的值,我在实际处理过程中,发现这个值取180是比较合适的,也就是分析文本的时候,如果所分析的文本超过了180,那么就可以认为到达了正文部分。

再有就是如何分析的问题,这个其实比较容易确定,一行行的分析效果肯定不好,如果在按行分析的过程中往下在分析几行作为一次分析效果比较好。也就是一次性分析上5行左右,将字符累加起来,看看有没有达到设定的阈值,如果达到了,那么认为已经进入正文部分了。

嗯,主要的处理逻辑就是这样,怎么样,不复杂吧。

我把实现的核心算法也贴出来吧:

int preTextLen = ;         // 记录上一次统计的字符数量(lines就是去除html标签后的文本,_limitCount是阈值,_depth是我们要分析的深度,sb用于记录正文)
int startPos = -; // 记录文章正文的起始位置
for (int i = ; i < lines.Length - _depth; i++)
{
int len = ;
for (int j = ; j < _depth; j++)
{
len += lines[i + j].Length;
} if (startPos == -) // 还没有找到文章起始位置,需要判断起始位置
{
if (preTextLen > _limitCount && len > ) // 如果上次查找的文本数量超过了限定字数,且当前行数字符数不为0,则认为是开始位置
{
// 查找文章起始位置, 如果向上查找,发现2行连续的空行则认为是头部
int emptyCount = ;
for (int j = i - ; j > ; j--)
{
if (String.IsNullOrEmpty(lines[j]))
{
emptyCount++;
}
else
{
emptyCount = ;
}
if (emptyCount == _headEmptyLines)
{
startPos = j + _headEmptyLines;
break;
}
}
// 如果没有定位到文章头,则以当前查找位置作为文章头
if (startPos == -)
{
startPos = i;
}
// 填充发现的文章起始部分
for (int j = startPos; j <= i; j++)
{
sb.Append(lines[j]);
}
}
}
else
{
if (len <= _endLimitCharCount && preTextLen < _endLimitCharCount) // 当前长度为0,且上一个长度也为0,则认为已经结束
{
if (!_appendMode)
{
break;
}
startPos = -;
}
sb.Append(lines[i]);
}
preTextLen = len;
}

核心的提取算法不足60行,经过验证提取的效果还是非常不错的,至少做到了正文提取正确率90%上,效率上做到了平均提取时间30ms左右。

还需解决的一些问题

html标签剔除:这个简单,直接使用正则表达式替换(Regex.Replace(html, "(?is)<.*?>", "")),将所有的html标签剔除即可

html压缩型网页的处理: 压缩后的html代码一般只有一行,对这类的html处理也比较简单(不需要复杂的代码格式化),直接在标签末尾强制添加换行符即可。

正文标题:大多数规范的网址会用h1标签作文正文标题,处理时如果有h1那么从h1标签中提取标题,没有的话,直接从title标签中那吧。

文章发布时间:并不是所有的文章都有发布时间(不过貌似大多数都有哈),直接使用正则从去除标签后的正文中提取时间吧。

保留带标签的正文:我们的算法是和标签无关的,因为算法处理时首先要过滤html标签,去除干扰,那么如果想要带标签的正文怎么办(比如要保留正文中的图片)?这时只能保留2个数组了,一个数组存放过滤标签的文本,便于分析,另一个数组则保留html标签,便于提取原始信息。

Html2Article网页正文提取算法

Html2Article就是我基于以上思想实现的网页正文提取算法。有以下特点:

  • 标签无关,提取正文不依赖标签。
  • 支持从压缩的html文档中提取正文内容。
  • 支持带标签输出原始正文。
  • 核心算法简洁高效,平均提取时间在30ms左右。

算法已开源(也算是为开源做点贡献了吧):

http://blog.zhaishidan.cn/Html2Article/

https://github.com/stanzhai/Html2Article

http://www.oschina.net/p/html2article

使用方法请参考文档介绍说明。

算法是用C#实现的,玩.NET的同学有福了,可以直接使用nuget将html2article添加到你的项目中哦。

另外发现直接从百度搜索“html2article”也能找到很快的找打它,算法实现已经将近半年了,一直比较懒,也没写过文章跟大家分享一下。

我为开源做贡献,网页正文提取——Html2Article的更多相关文章

  1. 利用Readability解决网页正文提取问题

    分享: 利用Readability解决网页正文提取问题   做数据抓取和分析的各位亲们, 有没有遇到下面的难题呢? - 如何从各式各样的网页中提取正文!? 虽然可以用SS为各种网站写脚本做解析, 但是 ...

  2. 分享: 利用Readability解决网页正文提取问题

    原文:http://www.cnblogs.com/iamzyf/p/3529740.html 做数据抓取和分析的各位亲们, 有没有遇到下面的难题呢? - 如何从各式各样的网页中提取正文!? 虽然可以 ...

  3. 网页正文提取,降噪的实现(readability/Document)

    安装: pip install readability-lxml 使用: # encoding:utf-8import html2textimport requestsimport refrom re ...

  4. Python网页正文转换语音文件的操作方法

    天气真的是越来越冷啦,有时候我们想翻看网页新闻,但是又冷的不想把手拿出来,移动鼠标翻看.这时候,是不是特别想电脑像讲故事一样,给我们念出来呢?人生苦短,我有python啊,试试用 Python 来朗读 ...

  5. 从 SDWebImage 谈如何为开源软件做贡献

    来源:伯乐在线 - 酷酷的哀殿 链接:http://ios.jobbole.com/89483/ 点击 → 申请加入伯乐在线专栏作者 从 SDWebImage 谈如何为开源软件做贡献 相识 – 知我者 ...

  6. 使用GitHub进行协同项目开发和开源项目贡献

    本教程致力于摆脱git命令行快速的学习使用GitHub. 此次是GitHub课程的第三次课程,也是最后一次课程.推荐进行按照次序查看本次教程.上篇文章:程序员,一起玩转GitHub版本控制,超简单入门 ...

  7. 算法工程师想进一步提高竞争力?向TensorFlow开源社区贡献你的代码吧

    算法工程师为什么也要向社区贡献代码? [作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] “做算法的人要熟悉算法框架源码吗?算法工程师难 ...

  8. 按示例学python:使用python抓取网页正文

    平时打开一个网页,除了文章的正文内容,通常会有一大堆的导航,广告和其他方面的信息.本博客的目的,在于说明如何从一个网页中提取出文章的正文内容,而过渡掉其他无关的的信息. 这里先看看 demo : ht ...

  9. 如何往Spark社区做贡献,贡献代码

    随着社区正在努力准备Apache Spark的下一版本3.0,您可能会问自己“我如何参与其中?”.现在的Spark代码已经很庞大,因此很难知道如何开始自己做出贡献.Spark PMC & Co ...

随机推荐

  1. 可爱的Python_课后习题_CDay−2 完成核心功能

    1. 在前文的grep 实现例子中,没有考虑子目录的处理方式,因为如果直接open 目录进行读grep 是古老实用且高效的模式文本匹配工具,在所有的Unix/Linux 系统中都会默认安装,它最常做的 ...

  2. AngularJS Best Practices: ng-include vs directive

    For building an HTML template with reusable widgets like header, sidebar, footer, etc. Basically the ...

  3. iPhone CSS media query(媒体查询)

    iPhone5  iPhone6  iPhone6Plus iPad设备 media query(媒体查询)代码. iPhone < 5: @media screen and (device-a ...

  4. Design and Analysis of Algorithms_Fundamentals of the Analysis of Algorithm Efficiency

    I collect and make up this pseudocode from the book: <<Introduction to the Design and Analysis ...

  5. NOI 1.5 42:画矩形

    描述 根据参数,画出矩形. 输入 输入一行,包括四个参数:前两个参数为整数,依次代表矩形的高和宽(高不少于3行不多于10行,宽不少于5列不多于10列):第三个参数是一个字符,表示用来画图的矩形符号:第 ...

  6. 利用Python实现从百度下载图片到本地磁盘

    import urllib.request import os import re url=r'http://image.baidu.com/search/index?tn=baiduimage&am ...

  7. Excel 导入 导出 Microsoft

    导出: private void exportExcel() { if (saveFileDialog1.ShowDialog() == DialogResult.OK) { Application. ...

  8. 第一次写这么长的js

    是公司一个项目,要求显示不同的sku,然后根据sku组合显示不同的价格区间,根据填写的数量落在哪个价格区间,然后进行计算.实际截图如下: 前端JS如下: <script type="t ...

  9. 构建简单的Maven工程,使用测试驱动的方式开发项目

    构建简单的Maven工程很简单,这里写这篇随笔的原因是希望自己能记住几个小点. 一.安装Maven 1.下载maven:https://maven.apache.org/download.cgi 2. ...

  10. HttpClient——Get,Post

    package com.ch.day5_httpclient; import java.util.List; import com.ch.myutils.NetWorkUtil; import com ...