屏幕抓取:正则表达式还是XQuery表达式?

时间:2009-03-14 18:55:14

标签: regex screen-scraping xquery

我正在回答一些面试的测验问题,问题是如何进行屏幕抓取。也就是说,假设您没有更好的结构化方式直接查询信息(例如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>或将标签“Product Dimensions:”更改为“Dimensions:”或许多其他类型变化我的观点是正则表达式无法解决他在我的XQuery解决方案中提到的弱点。

但是,除非为表达式添加足够的上下文,否则正则表达式可以找到误报。它也可能无意中匹配恰好位于注释,属性字符串或CDATA部分内的内容。

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

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

8 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

我使用正则表达式,由于经理给出的原因,提供了一些(更便携,更容易让外部程序员关注等)。

你的反驳论点忽略了他的解决方案在本地更改方面是脆弱的,而你的全局更改是脆弱的。任何违背他意志的行为都可能会打破你的行为,但反之亦然。

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

答案 2 :(得分:2)

尝试JTidy或BeautifulSoup对我来说很好。 肯定// XPATH表达式的报废成本非常高。

答案 3 :(得分:1)

我正在使用BeautifulSoup进行报废。

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

我非常喜欢HTML agility pack:您可以容忍非XHTML兼容网页以及XPath的表现力。

答案 6 :(得分:1)

正则表达式非常快,可以处理非XML文档。这些对XQuery非常有用。但是我认为使用一些转换器来使用XHTML就像整洁而且可能有点简单的XQuery,就像你的最后一部分一样:

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

是一个非常好的选择。

此致

Rafal Rusin

答案 7 :(得分:1)

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

祝你好运!