我正在回答一些面试问题,问题是关于我将如何进行屏幕抓取。也就是说,从网页中挑选内容,假设您没有更好的结构化方式来直接查询信息(例如 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 或其他更直接的方式来获取所需的内容。