标签归档:正文提取

目前互联网上公布出来的正文提取算法

目前互联网上公布出来的正文提取算法,大家可以综合比较下,一起来测试下哪个更好用。 词网–北京词网科技有限公司http://demo.cikuu.com/cgi-bin/cgi-contex 猎兔网页正文提取 http://www.lietu.com/extract/ PHP版网页正文提取http://www.woniu.us/get_content_demo/ 网页正文提取分析(DEMO) http://61.128.196.27/txt 个人认为http://61.128.196.27/txt 这个提取最牛,基本上无论什么页面都能提取出来,而且能有效的保持原文风格、图片、链接。http://code.google.com/p/joyhtml/
看看这个效果不错
http://www.likeshow.net/article.asp?id=92
我一年前写的玩意 虽然不完善 但尚可用之在新闻和BLOG 论坛提取上 提取的正文对于BLOG和BBS包含评论及回复 具体原理也写很清楚了
如题,想从html源码中提取正文内容,<P></P>之间的内容,但是<P>的写法不规则。除了正则表达式的方法,还有其它的提取方法吗?谢谢!
最新下载
在线演示和最新下载:
http://www.shoula.net/ParseContenthttp://www.pudn.com/downloads152/sourcecode/internet/search_engine/detail668443.html

Google Code开源网页正文提取cx-extractor2010-05-19 12:31基于行块分布函数的通用网页正文抽取:线性时间、不建DOM树、与HTML标签无关
简述:
对于Web信息检索来说,网页正文抽取是后续处理的关键。虽然使用正则表达式可以准确的抽取某一固定格式的页面,但面对形形色色的HTML,使用规则处理难免捉襟见肘。能不能高效、准确的将一个页面的正文抽取出来,并做到在大规模网页范围内通用,这是一个直接关系上层应用的难题。
作者提出了《基于行块分布函数的通用网页正文抽取算法》,首次将网页正文抽取问题转化为求页面的行块分布函数,这种方法不用建立Dom树,不被病态HTML所累(事实上与HTML标签完全无关)。通过在线性时间内建立的行块分布函数图,直接准确定位网页正文。同时采用了统计与规则相结合的方法来处理通用性问题。作者相信简单的事情总应该用最简单的办法来解决这一亘古不变的道理。整个算法实现不足百行代码。但量不在多,在法。
项目网址:http://code.google.com/p/cx-extractor/
算法描述:基于行块分布函数的网页正文抽取算法.pdf
欢迎大家提出意见~

http://www.ngiv.cn/post/204.html
VIPS算法对搜索引擎的意义
http://blog.csdn.net/tingya/archive/2006/02/18/601954.aspx

基于视觉的Web页面分页算法VIPS的实现源代码下载
http://blog.csdn.net/tingya/archive/2006/04/28/694651.aspx
作者信息:飞跃,javascript教程-技术之家博客的博主

http://www.madcn.net/?p=791

我这里有个开源的项目,还不错,你上googlecode搜索joyhtml。
http://gfnpad.blogspot.com/2009/11/blog-post.html
下面几个是一些开源的程序:
1.一个python的基于文本密度的程序:
http://ai-depot.com/articles/the-easy-way-to-extract-useful-text-from-arbitrary-html/
ps:里面有bug,要稍加改动。 另外,对于没有对html注释部分进行处理
2.Java 开源项目: Gate
http://gate.ac.uk/

其实可以利用Dhmtl对象进行编程分析,已获得所要的数据文件,详细请看我的程序
http://www.vbgood.com/thread-94788-1-1.html
http://download.csdn.net/source/568439

一.标题块
l 分块节点:td,div,h,span
l 一般位于Head/Title的位置
l 当前单元含有<h1>-<h3>,<b>,<i>,<strong>等标签
l 样式,一般class包含title,head等字符
l 文字长度,一般大于3个字符,小于35个字符

二.发表时间块
l 分块节点:td,div, span
l 文字长度,一般小于50个字符
l 包含日期格式(2010-08-09)的字符串
l 包含以下关键字:来源,发表

三.主题块
l 分块节点:td,div
l HTML网页中有一些特殊标签,通常只出现在网页主题块中,如<P><BR>等。因此,主题块中往往包含着特殊标签。
l 主题块内容含有较多的句子,因此具有较多逗号、句号等标点符号(>5)。
l 若从信息量角度考虑,主题块一般是含有较多文字信息。
l 主题块的 标签密度=1000*标签数/文字数 应在小于一个范围。
l 主题块的 文本密度=len(文本)/len(HTML代码) 较大
l 不应该包含 “上一篇”,“下一篇”
l 包含以下字符串的内容块,判定为包含版权信息,需减权:“ICP备04000001号”,“版权所有”,“Copyright”
l 主题块序号在标题块之下
l 主题块序号在发表时间块之下
l 主题块序号在相关链接块之上

四.相关链接块
l 分块节点:td,div
l 文字应为“相关链接”、“相关新闻”、“相关报道”等敏感词,且连接比例很高。
l 链接数小于20

实现:
根据以上信息块特征,采用特征提权算法,C#(3.5)编程实现,命名为QD正文提取组件。经测试,对Html格式规范的以文字为主的内容页,正确提取率在85%以上,各大门户的新闻页面在95%以上。 例子下载(需要安装Microsoft .NET Framework 3.5)

注:QD正文提取组件 不开源,需要源码的朋友可选择付费获取。

这时挑选出的正文一般也就是到位了,但是问题是很可能在头尾残留了一些块广告。我认为这些块广告与正文中广告有很大的不同。这些广告的马脚就是其父节点,它们的父节点要么也包含了正文所在区域,也就是和正文平级,要么本身就是正文所在区域的一个子节点,很难是正文节点本身的。那么对疑似正文节点进行一次扫描,剔除那些父节点文字内容过大(包含了广告以及正文,即和正文平级)的块,也剔除那些父节点文字内容过小的块。
经过这样的处理,得到的内容基本上就是我们需要的正文了。下面就是要提取标题。
在代表整个网页的document中扫描一次,寻找那些有font字体的,strong的,h1的,title的节点,提取他们的信息。然后将得到的文字内容分词,查验分出来的词有多少是被正文包含的,包含最多的一半就是标题。但是这里要注意,有时候找到的节点本身是正文节点的子节点,那么无论怎么分,分出来都是完全包含的,所以要剔除那些本身是正文一部分的疑似标题。这样做对大部分网页也是有效了,但是对仅有的标题就在正文节点里的那些页面,目前为止我还没有特别好的想法。
这些日子也研究了一些别人的论文,有很多思想都非常好,也有很多人想到用马尔科夫,人工神经来训练。也许以后我会考虑用用看吧。现在这样也还可以,呵呵。
?
这个算法我也写了一下,不过是用C++写的。
我不太懂楼上讨论的分页是什么意思,我通过分析dom树然后用文中提到的规则进行dom结点处理以及后续的处理。
我主要是想把网页中的内容按网页框架分开,把正文部分合在一起,然后用贝叶斯决策计算正文特征支持率
提取网页内容。
现在VIPS基本写完。
但是却也发现了些问题,
比如说有些结点的坐标提取出来会有提取不出分隔条,这是因为有少数坐标有些重叠。这里涉及到一个坐标的确定问题。
然后是结点分割规则问题,现在的页面是大部分是通过DIV来组织页面。而VIPS似乎更合适TABLE组织的页面,我试过用TABLE组织的页面,分得相当不错。
另外,TINYA上面的翻译似乎改了些规则,还有部分翻译不是很准确。比如虚拟文本的定义部分与原文有些出入,不知道TINYA有没有注意到。
最后,很感谢TINYA 对这个算法的介绍。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tingya/archive/2006/02/18/601836.aspx

boilerpipe(Boilerplate Removal and Fulltext Extraction from HTML pages) 源码分析

转自:http://blog.csdn.net/fxjtoday/article/details/6320315

开源Java模块boilerpipe(1.1.0), http://code.google.com/p/boilerpipe/

使用例子,
URL url = new URL(“http://www.example.com/some-location/index.html “);
// NOTE: Use ArticleExtractor unless DefaultExtractor gives better results for you
String text = ArticleExtractor .INSTANCE.getText(url);
那就从ActicleExtractor开始分析, 这个类用了singleton的design pattern, 使用INSTANCE取得唯一的实例, 实际处理如下步骤

HTML Parser
The HTML Parser is based upon CyberNeko 1.9.13. It is called internally from within the Extractors.
The parser takes an HTML document and transforms it into a TextDocument , consisting of one or moreTextBlocks . It knows about specific HTML elements (SCRIPT, OPTION etc.) that are ignored automatically.
Each TextBlock stores a portion of text from the HTML document. Initially (after parsing) almost every TextBlock represents a text section from the HTML document, except for a few inline elements that do not separate per defintion (for example ‘<A>’anchor tags).
The TextBlock objects also store shallow text statistics for the block’s content such as the number of words and the number of words in anchor text.

Extractors
Extractors consist of one or more pipelined Filters . They are used to get the content of a webpage. Several different Extractors exist, ranging from a generic DefaultExtractor to extractors specific for news article extraction (ArticleExtractor).
ArticleExtractor.process() 就包含了这个pipeline filter, 这个design做的非常具有可扩展性, 把整个处理过程分成若干小的步骤分别实现, 在用的时候象搭积木一样搭成一个处理流. 当想扩展或改变处理过程时, 非常简单, 只需加上或替换其中的一块就可以了.
这样也非常方便于多语言扩展, 比如这儿用的english包里的相应的处理函数,
import de.l3s.boilerpipe.filters.english.IgnoreBlocksAfterContentFilter;
import de.l3s.boilerpipe.filters.english.KeepLargestFulltextBlockFilter;
如果要扩展到其他语言, 如韩文, 只需在filters包里面加上个korean包, 分别实现这些filter处理函数, 然后只需要修改import, 就可以实现对韩语的support.

TerminatingBlocksFinder.INSTANCE.process(doc)
| new DocumentTitleMatchClassifier(doc.getTitle()).process(doc)
| NumWordsRulesClassifier.INSTANCE.process(doc)
| IgnoreBlocksAfterContentFilter.DEFAULT_INSTANCE.process(doc)
| BlockProximityFusion.MAX_DISTANCE_1.process(doc)
| BoilerplateBlockFilter.INSTANCE.process(doc)
| BlockProximityFusion.MAX_DISTANCE_1_CONTENT_ONLY.process(doc)
| KeepLargestFulltextBlockFilter.INSTANCE.process(doc)
| ExpandTitleToContentFilter.INSTANCE.process(doc);
下面具体看一下处理流的每个环节.

TerminatingBlocksFinder
Finds blocks which are potentially indicating the end of an article text and marks them with {@link DefaultLabels#INDICATES_END_OF_TEXT}. This can be used in conjunction with a downstream {@link IgnoreBlocksAfterContentFilter}.(意思是IgnoreBlocksAfterContentFilter必须作为它的downstream)

原理很简单, 就是判断这个block, 在tb.getNumWords() < 20的情况下是否满足下面的条件,
text.startsWith(“Comments”)
|| N_COMMENTS.matcher(text).find() //N_COMMENTS = Pattern.compile(“(?msi)^[0-9]+ (Comments|users responded in)”)
|| text.contains(“What you think…”)
|| text.contains(“add your comment”)
|| text.contains(“Add your comment”)
|| text.contains(“Add Your Comment”)
|| text.contains(“Add Comment”)
|| text.contains(“Reader views”)
|| text.contains(“Have your say”)
|| text.contains(“Have Your Say”)
|| text.contains(“Reader Comments”)
|| text.equals(“Thanks for your comments – this feedback is now closed”)
|| text.startsWith(“© Reuters”)
|| text.startsWith(“Please rate this”)
如果满足就认为这个block为artical的结尾, 并加上标记tb.addLabel(DefaultLabels.INDICATES_END_OF_TEXT);

DocumentTitleMatchClassifier
这个很简单, 就是根据'<title>’的内容去页面中去标注title的位置, 做法就是根据'<title>’的内容产生一个potentialTitles列表, 然后去匹配block, 匹配上就标注成DefaultLabels.TITLE

NumWordsRulesClassifier
Classifies {@link TextBlock}s as content/not-content through rules that have been determined using the C4.8 machine learning algorithm, as described in the paper “Boilerplate Detection using Shallow Text Features” (WSDM 2010), particularly using number of words per block and link density per block.
这个模块实现了个分类器, 用于区分content/not-content , 分类器的构建参见上面这篇文章的4.3节.
分类器使用Decision Trees算法, 用标注过的google news作为训练集, 接着对训练完的Decision Trees经行剪枝, Applying reduced-error pruning we were able to simplify the decision tree to only use 6 dimensions (2 features each for current, previous and next block) without a significant loss in accuracy.
最后用伪码描述出Decision Trees的decision过程, 这就是使用Decision Trees的最大好处, 它的decision rules是可以理解的, 所以可以用各种语言描述出来.
这个模块实现的是Algorithm 2 Classifier based on Number of Words

curr_linkDensity <= 0.333333
| prev_linkDensity <= 0.555556
| | curr_numWords <= 16
| | | next_numWords <= 15
| | | | prev_numWords <= 4: BOILERPLATE
| | | | prev_numWords > 4: CONTENT
| | | next_numWords > 15: CONTENT
| | curr_numWords > 16: CONTENT
| prev_linkDensity > 0.555556
| | curr_numWords <= 40
| | | next_numWords <= 17: BOILERPLATE
| | | next_numWords > 17: CONTENT
| | curr_numWords > 40: CONTENT
curr_linkDensity > 0.333333: BOILERPLATE

有了Classifies, 接下来的事情就是对于所有block进行分类并标注.

IgnoreBlocksAfterContentFilter
Marks all blocks as “non-content” that occur after blocks that have been marked {@link DefaultLabels#INDICATES_END_OF_TEXT}. These marks are ignored unless a minimum number of words in content blocks occur before this mark (default: 60). This can be used in conjunction with an upstream {@link TerminatingBlocksFinder}.

这个模块是TerminatingBlocksFinder模块的downstream, 就是说必须在它后面做, 简单的很, 找到DefaultLabels#INDICATES_END_OF_TEXT, 后面的内容全标为BOILERPLATE.
除了前面正文length不到minimum number of words(default: 60), 还需要继续抓点文字凑数.

BlockProximityFusion
Fuses adjacent blocks if their distance (in blocks) does not exceed a certain limit. This probably makes sense only in cases where an upstream filter already has removed some blocks.
这个模块用来合并block的, 合并的依据主要是根据两个block的offset的差值不大于2, 也就是说中间最多只能隔一个block.
当要求contentOnly时, 会check两个block都标注为content时才会fusion.
int diffBlocks = block.getOffsetBlocksStart() – prevBlock.getOffsetBlocksEnd() – 1;
if (diffBlocks <= maxBlocksDistance)

那么block的offset怎么来的了, 查一下block构造的时候的代码
BoilerpipeHTMLContentHandler .flushBlock()
TextBlock tb = new TextBlock(textBuffer.toString().trim(), currentContainedTextElements, numWords, numLinkedWords, numWordsInWrappedLines, numWrappedLines, offsetBlocks);
offsetBlocks++;

TextBlock构造函数
this.offsetBlocksStart = offsetBlocks;
this.offsetBlocksEnd = offsetBlocks;
可以看出初始情况下, block的offset就是递增的, 并且再没有做过fusion的情况下, offsetBlocksStart和offsetBlocksEnd是相等的.
所以象注释讲的那样, 只有当upstream filter remove了部分blocks以后, 这个模块的合并依据才是有意义的, 不然在没有任何删除的情况下, 所有block都满足fusion条件.

看完这段代码, 我很奇怪, Paper中fusion是根据text density的, 而这儿只是根据block的offset, 有所减弱.
There, adjacent text fragments of similar text density (interpreted as /similar class”) are iteratively fused until the blocks’ densities (and therefore the text classes) are distinctive
enough.
而且我更加不理解的是, 在ArticleExtractor关于这个模块的用法如下,
BlockProximityFusion.MAX_DISTANCE_1.process(doc)
| BoilerplateBlockFilter.INSTANCE.process(doc)
| BlockProximityFusion.MAX_DISTANCE_1_CONTENT_ONLY.process(doc)
调用了BlockProximityFusion两次, 分别在BoilerplateBlockFilter(含义在下节)的down,upstream, 对于BlockProximityFusion.MAX_DISTANCE_1_CONTENT_ONLY.process(doc)的调用我还是能理解的, 再删除完非content的block后, 对剩下的block做一下fusion, 比如原来两个block中间隔了个广告. 不过这儿根据offset, 而不根据text density, 个人觉得功能有所减弱.
可是对于BlockProximityFusion.MAX_DISTANCE_1.process(doc)的调用, 可能是我没看懂, 实在无法理解, 为什么要加这步, 唯一的解释是想将一些没有标注为content的block fusion到content里面去. 奇怪的是这儿fusion是无条件的(在没有删除block的情况下,判断offset无效), 只需要当前的block是content是就和Prev进行fusion. 而且为什么只判断当前block, Prevblock是content是否也应该fusion.个人觉得这边逻辑完全不合理……

BoilerplateBlockFilter
Removes {@link TextBlock}s which have explicitly been marked as “not content”
没啥好说的, 就是遍历每个block, 把没有标注为”content”的都删掉.

KeepLargestFulltextBlockFilter
Keeps the largest {@link TextBlock} only (by the number of words). In case of more than one block with the same number of words, the first block is chosen. All discarded blocks are marked “not content” and flagged as {@link DefaultLabels#MIGHT_BE_CONTENT}
很好理解, 找出最大的文本block作为正文, 其他的标注为DefaultLabels#MIGHT_BE_CONTENT

ExpandTitleToContentFilter
Marks all {@link TextBlock}s “content” which are between the headline and the part that has already been marked content, if they are marked {@link DefaultLabels#MIGHT_BE_CONTENT}. This filter is quite specific to the news domain.
逻辑是找出标注为DefaultLabels.TITLE的block, 和content开始的那个block, 把这两个block之间的标注为MIGHT_BE_CONTENT的都改标注为Content.

TextDocument.getContent()
最后需要做的一步, 是把抽取的内容输出成文本. 遍历每一个标注为content的block, 把内容append并输出.

DefaultExtractor
下面再看看除了ArticleExtractor (针对news)以外, 很常用的DefaultExtractor
SimpleBlockFusionProcessor.INSTANCE.process(doc)
| BlockProximityFusion.MAX_DISTANCE_1.process(doc)
| DensityRulesClassifier.INSTANCE.process(doc);
相对比较简单, 就三步, 第二步很奇怪, 前面没有任何upstream会标注content, 那么这步就什么都不会做

SimpleBlockFusionProcessor
Merges two subsequent blocks if their text densities are equal.
遍历每一个block, 两个block的text densities相同就merge

DensityRulesClassifier
Classifies {@link TextBlock}s as content/not-content through rules that have been determined using the C4.8 machine learning algorithm, as described in the paper “Boilerplate Detection using Shallow Text Features”, particularly using text densities and link densities.
参照NumWordsRulesClassifier , 这儿实现了Paper里面的Algorithm 1 Densitometric Classifier
curr_linkDensity <= 0.333333
| prev_linkDensity <= 0.555556
| | curr_textDensity <= 9
| | | next_textDensity <= 10
| | | | prev_textDensity <= 4: BOILERPLATE
| | | | prev_textDensity > 4: CONTENT
| | | next_textDensity > 10: CONTENT
| | curr_textDensity > 9
| | | next_textDensity = 0: BOILERPLATE
| | | next_textDensity > 0: CONTENT
| prev_linkDensity > 0.555556
| | next_textDensity <= 11: BOILERPLATE
| | next_textDensity > 11: CONTENT
curr_linkDensity > 0.333333: BOILERPLATE

如果有兴趣, 你可以学习其他extractor, 或自己design合适自己的extractor.