在.Net中推荐的HTML可读性转码库是什么?

3

背景
我正在尝试阅读和分析网页内容,重点是页面的主要内容-而不包括菜单、侧边栏、脚本和其他HTML杂乱无章的内容。

我已经尝试过什么?

  • 我尝试了NReadability,但它会在太多情况下抛出异常并失败。除此之外,它是一个很好的解决方案。
  • HTML Agility Pack在这里不是我需要的,因为我想要摆脱非内容代码。

编辑:我正在寻找一个可以筛选内容并只从页面中提取“相关”文本的库(即对于这个页面,“review”、“chat”、“meta”、“about”和“faq”来自顶部栏目将不会显示,以及“user contributions licensed under”)。

那么,您知道还有哪些稳定的 .Net 库可以从网站中提取内容吗?

2个回答

7

我不知道这是否仍然相关,但这是一个我经常遇到的有趣问题,在网上没有看到很多相关资料。

我自己实现了一个工具来完成这个任务,历时数月。根据合同义务,我不能免费分享此工具。但是,我可以分享一些关于你可以做什么的建议。

悲惨的事实 :(

我可以向你保证,在着手创建可读性工具之前,我们已经尝试了所有选项。目前没有这样的工具能满足我们的需求。

所以,你想提取内容?

太好了!你需要几个东西:

  1. 处理页面HTML的工具。我使用CsQuery,这是Jamie在上面的答案中建议的。它非常适合选择元素。
  2. 编程语言(在这个例子中是C#,任何.NET语言都可以!)
  3. 让你下载页面本身的工具。CsQuery可以通过createFromUrl单独使用。如果您想对其进行预处理并更精细地控制标头,则可以创建自己的帮助程序类。(尝试使用用户代理、查找移动版本等)

好的,我已经准备好了,接下来呢?

令人惊讶的是,在内容提取领域中研究非常少。一篇脱颖而出的文章是使用浅层文本特征检测锅炉板。您还可以阅读这里在StackOverflow上的答案,了解可读性如何工作以及一些方法。

以下是我喜欢的一些论文:

我读完了,实践中做了什么

根据我的经验,以下是提取内容的好策略:

  • 简单启发式算法:过滤 <header><nav> 标签,移除仅包含链接的列表。移除整个 <head> 部分。根据元素名称给予正负分数,并删除得分最低的元素(例如,带有包含名为“navigation”的类的 div 可能会得到较低的分数)。这就是可读性的工作原理。

  • 元内容。分析链接到文本的密度,这本身就是一个强大的工具,您可以比较链接文本和 html 文本的数量,并在此基础上进行操作,最密集的文本通常是内容所在的地方。 CsQuery 让您轻松比较嵌套链接标记的文本量和文本量。

  • 模板。在同一网站上爬取多个页面并分析它们之间的差异,常量通常是页面布局、导航和广告。您通常可以根据相似之处进行过滤。这种基于“模板”的方法非常有效。诀窍在于想出一种高效的算法来跟踪模板并检测模板本身。

  • 自然语言处理。这可能是这里最先进的方法,使用自然语言处理工具相对简单,可以检测段落、文本结构以及实际内容的开始和结束位置。

  • 学习,学习是这种任务的一个非常强大的概念。在最基本的形式中,这涉及创建一个程序,在网站的预定义结果集上“猜测”要删除的 HTML 元素,并学习哪些模式是可以删除的。从我的经验来看,这种方法在每个站点上都效果最佳。

  • 选择器固定列表。出人意料的是,这非常有效,而人们往往会忘记它。如果您正在从几个特定的站点抓取数据并手动提取内容,则使用选择器可能是最快捷的方法。如果可以,请保持简单 :)

实践中的应用

混合匹配,好的解决方案通常涉及多种策略的组合。由于我们将其用于复杂任务,因此最终得到了一个相当复杂的东西。实际上,内容提取是一项非常复杂的任务。不要尝试创建非常通用的东西,坚持提取 需要的内容。进行大量测试,单元测试和回归对于这种程序非常重要,始终比较并阅读 readability 的代码,它非常简单,可能会让您入门。

祝好运,请告诉我这个项目进展如何。


1

CsQuery: https://github.com/jamietre/csquery

这是一个.NET 4 jQuery端口。消除非内容节点可以通过多种方式完成:使用.Text方法将所有内容作为字符串获取;或者筛选文本节点,例如:

var dom = CQ.CreateFromUrl(someUrl); 
// or var dom = CQ.Create(htmlText);

IEnumerable<string> allTextStrings = dom.Select("*")
            .Contents()
            .Where(el => el.NodeType == NodeType.TEXT_NODE)
            .Select(el => el.NodeValue);

它的工作方式与jQuery相同,除了当然,您还有.NET框架和LINQ可以使您的生活更轻松。 Select选择DOM中的所有节点,然后Contents选择每个节点的所有子节点(包括文本节点)。这就是CsQuery的全部内容;然后使用LINQ,Where仅过滤文本节点,Select获取每个节点的实际文本。

这将包括大量空格,返回所有内容。如果您只想要整个页面的一大块文本,只需

string text = dom.Select("body").Text();

好的,将会处理。 Text 方法合并空格,因此实际文本之间只会有一个空格。


这似乎是另一种HtmlAgilityPack的形式。看起来不错,但不是我所需要的 - 请参见我的编辑。 - seldary
哦 - 我以为你在处理HAP时遇到的问题是难以提取文本与结构节点之间的区别。我不确定你如何确定什么符合“相关”,这似乎是一个人工智能问题,但我认为你只需忽略任何字符数低于某个任意值的文本节点就可以做得相当好。试图决定什么是“主要内容”和什么是“侧边栏”,除了简单的文本大小之外,几乎是不可能的,除非你真正知道你在内容中寻找什么。 - Jamie Treworgy
NReadability、Instapaper、readability.com 是一些可以做到这一点的产品示例(或多或少)。虽然完美是不可能的,但我相信有可能找到一个适合的解决方案。 - seldary
如果你不需要那些工具提供的很多启发式分析的好处(比如说不需要完美),我认为你可以用一种基本的算法,用csquery实现起来非常容易。例如:去掉内联标签(例如span,i,b);保留所有标题;然后丢弃包含少于80个字符的其余块元素。我敢打赌这样就能消除大多数网页上的布局内容了。无论如何,我现在理解你的问题了——太糟糕了,NReadability效果不佳——但你可以尝试实现一些基本的东西。 - Jamie Treworgy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接