3

背景
我正在尝试阅读和分析网页内容,重点关注页面的主要内容——没有菜单、侧边栏、脚本和其他 HTML 混乱。

我尝试了什么?

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

编辑:我正在寻找一个真正筛选内容的库,并且只给我页面中的“相关”文本(即对于这个页面,单词“review”、“chat”、“meta”、“about” ,并且顶部栏中的“常见问题解答”以及“许可下的用户贡献”将不会显示。

那么,您知道其他任何用于从网站中提取内容的稳定 .Net 库吗?

4

2 回答 2

7

我不知道这是否仍然相关,但这是一个我经常遇到的有趣问题,而且我在网络上没有看到太多涵盖它的材料。

我自己在几个月的时间里实现了一个工具,可以做到这一点。出于合同义务,我不能随意分享这个工具。但是,我可以自由地分享一些关于您可以做什么的建议。

悲哀的事实 :(

我可以向您保证,在自己承担创建可读性工具的任务之前,我们已经尝试了所有选项。目前还没有这样的工具可以满足我们的需求。

那么,您要提取内容吗?

伟大的!你需要一些东西

  1. 用于处理页面 HTML 的工具。我使用CsQuery,这是 Jamie 在上面的答案中建议的。它非常适合选择元素。
  2. 一种编程语言(在本例中为 C#,任何 .NET 语言都可以!)
  3. 一种工具,可让您自行下载页面。CsQuery它独立于createFromUrl. 如果您想对页面进行预处理并获得对标题的更细粒度的控制,您可以创建自己的帮助程序类来下载页面。(尝试使用用户代理,寻找移动版本等)

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

令人惊讶的是,在内容提取领域的研究很少。一个突出的部分是Boilerplate Detection using Shallow Text Features。您还可以在 StackOverflow 中从论文作者那里阅读此答案,以了解 Readability 是如何工作的以及一些方法是什么。

这里还有一些我喜欢的论文:

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

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

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

  • 元内容。分析文本链接的密度,这本身就是一个强大的工具,您可以将链接文本的数量与 html 文本进行比较并使用它,最密集的文本通常是内容所在的位置。CsQuery让您可以轻松地将文本数量与嵌套链接标签中的文本数量进行比较。

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

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

  • 学习,学习对于这类任务来说是一个非常强大的概念。在最基本的形式中,这涉及创建一个程序,该程序“猜测”要从网站上删除的一组预定义结果中的 HTML 元素,并了解哪些模式可以删除。根据我的经验,这种方法在每个站点的机器上效果最好。

  • 修复了选择器列表。令人惊讶的是,这是非常有效的,人们往往会忘记它。如果您使用选择器从特定的几个站点抓取并手动提取内容可能是最快的事情。如果可以的话,保持简单:)

在实践中

混搭,一个好的解决方案通常涉及多个策略,结合几个。我们最终得到了一些非常复杂的东西,因为我们将它用于复杂的任务。在实践中,内容提取是一项非常复杂的任务。不要尝试创建非常笼统的东西,坚持需要抓取的内容。大量测试,单元测试和回归对于这类程序非常重要,总是比较和阅读可读性的代码,它非常简单,它可能会让你开始。

祝你好运,让我知道这是怎么回事。

于 2013-03-26T22:49:10.790 回答
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 就是这样;然后使用 LINQWhere过滤器仅用于文本节点,并Select从每个节点中获取实际文本。

这将包括很多空格,它会返回所有内容。如果您只是想要整个页面的文本块,只需

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

会做的。该Text方法合并空白,因此每条实际文本之间将有一个空格。

于 2012-06-07T18:11:22.293 回答