6

我正在回答一些面试问题,问题是关于我将如何进行屏幕抓取。也就是说,从网页中挑选内容,假设您没有更好的结构化方式来直接查询信息(例如 Web 服务)。

我的解决方案是使用XQuery表达式。表达式相当长,因为我需要的内容在 HTML 层次结构中很深。在找到具有id属性的元素之前,我必须以公平的方式搜索祖先。例如,为 Product Dimensions 抓取 Amazon.com 页面如下所示:

//a[@id="productDetails"]
/following-sibling::table
//h2[contains(child::text(), "Product Details")]
/following-sibling::div
//li
/b[contains(child::text(), "Product Dimensions:")]
/following-sibling::text()

这是一个非常讨厌的表达方式,但这就是亚马逊提供 Web 服务 API 的原因。无论如何,这只是一个例子。问题不是关于亚马逊,而是关于屏幕抓取。

面试官不喜欢我的解决方案。他认为它很脆弱,因为亚马逊对页面设计的更改可能需要重写 XQuery 表达式。调试与应用它的页面中的任何内容都不匹配的 XQuery 表达式是很困难的。

我并不反对他的说法,但我认为他的解决方案没有任何改进:他认为最好使用正则表达式,并在运输重量附近搜索内容和标记。例如,使用 Perl:

$html =~ m{<li>\s*<b>\s*Product Dimensions:\s*</b>\s*(.*?)</li>}s;

我的反驳是,这也容易受到亚马逊更改其 HTML 代码的影响。他们可以用大写字母 ( ) 拼写 HTML 标签<LI>,或添加 CSS 属性或将标签“产品尺寸:”更改<b><span>更改为“尺寸:”或许多其他类型的更改。我的观点是,正则表达式并不能解决他在我的 XQuery 解决方案中指出的弱点。

但除此之外,正则表达式可能会发现误报,除非您为表达式添加足够的上下文。它还可能无意中匹配恰好位于注释、属性字符串或 CDATA 部分中的内容。

我的问题是,您使用什么技术来进行屏幕抓取?你为什么选择那个解决方案?有什么令人信服的理由使用一个吗?或者从不使用另一个?除了我上面展示的那些之外,还有第三种选择吗?

PS:为了论证起见,假设没有 Web 服务 API 或其他更直接的方式来获取所需的内容。

4

8 回答 8

4

我会使用正则表达式,但这只是因为大多数 HTML 页面不是有效的 XML,所以你永远不会让 XQUERY 工作。

我不知道 XQuery,但这对我来说就像一个 XPATH 表达式。如果是这样,其中有这么多“//”运算符看起来有点贵。

于 2009-03-14T19:06:02.033 回答
3

由于经理给出的原因,我会使用正则表达式,再加上一些(更便携,更容易让外部程序员遵循等)。

您的反驳论点忽略了一点,即他的解决方案在局部变化方面很脆弱,而您的解决方案在全球变化方面很脆弱。任何破坏他的东西都可能破坏你的,但反之亦然。

最后,在他的解决方案中构建 slop / flex 要容易得多(例如,如果您必须处理输入中的多个细微变化)。

于 2009-03-14T19:01:19.567 回答
2

试试 JTidy 或 BeautifulSoup 对我来说效果很好。当然 // XPATH experssion 报废成本很高。

于 2009-04-22T16:06:00.720 回答
1

我正在使用BeautifulSoup进行报废。

于 2009-03-14T19:08:50.763 回答
1

实际上,我发现 CSS 搜索表达式比任何一个都更容易阅读。您选择的语言中可能至少存在一个库,可以解析页面并允许您编写 CSS 指令来定位特定元素。如果附近有适当的类或 ID 挂钩,则表达式非常简单。否则,抓住看起来合适的元素并遍历它们以找到您需要的元素。

至于脆弱,嗯,它们都是脆弱的。根据定义,屏幕抓取取决于该页面的作者,不会大幅改变其布局。使用可读且以后可以轻松更改的解决方案。

于 2009-03-14T19:11:07.827 回答
1

屏幕抓取的非脆性解决方案?祝面试官好运:正则表达式抛弃了很多上下文并不意味着它们不那么脆弱:只是它们在其他方面很脆弱。脆弱性甚至可能不是一个缺点:如果源网页发生变化,如果您的解决方案发出警报,您通常会更好,而不是试图以一种聪明(且不可预测)的方式进行补偿。正如你所指出的。这些事情总是取决于你的假设:在这种情况下,取决于什么构成了可能的变化。

我更喜欢HTML 敏捷包:您可以容忍不符合 XHTML 的网页以及 XPath 的表达能力。

于 2009-03-14T19:53:43.917 回答
1

正则表达式非常快,并且适用于非 XML 文档。这些都是针对 XQuery 的真正优点。但是我认为使用一些转换器到 XHTML 就像整洁并且可能更简单的 XQuery,就像你的最后一部分一样:

//b[contains(child::text(), "Product Dimensions:")]/following-sibling::text()

是一个很好的选择。

问候,

拉法尔·鲁辛

于 2010-01-23T19:50:39.393 回答
1

要处理 html 页面,最好使用 HTMLAgilityPack(以及一些 Linq 代码)。这是解析所有元素和/或使用 XPath 进行直接搜索的好方法。在我看来,它比 RegEx 更准确,更容易编程。我以前有点不愿意使用它,但它很容易添加到您的项目中,我认为这是使用 html 的事实标准。http://htmlagilitypack.codeplex.com/

祝你好运!

于 2012-12-30T05:17:04.107 回答