【发布时间】:2010-12-19 22:05:40
【问题描述】:
最近的blog entry by a Jeff Atwood 说您永远不应该使用正则表达式解析 HTML - 但没有提供替代方案。
我想抓取搜索结果,提取值:
<div class="used_result_container">
...
...
<div class="vehicleInfo">
...
...
<div class="makemodeltrim">
...
<a class="carlink" href="[Url]">[MakeAndModel]</a>
...
</div>
<div class="kilometers">[Kilometers]</div>
<div class="price">[Price]</div>
<div class="location">
<span class='locationText'>Location:</span>[Location]
</div>
...
...
</div>
...
...
</div>
...and it repeats
你可以看到我要提取的值,[括在括号中]:
- 网址
- 品牌和型号
- 公里
- 价格
- 位置
假设我们接受解析HTML的前提:
- 通常是个坏主意
- rapidly devolves into madness
有什么办法呢?
假设:
- 原生 Win32
- 松散的html
假设说明:
原生 Win32
- .NET/CLR 不是原生 Win32
- Java 不是本机 Win32
- perl、python、ruby 不是原生 Win32
- 假设 C++ 在 Visual Studio 2000 中编译成本机 Win32 应用程序
本机 Win32 应用程序可以调用库代码:
- 复制源代码
- 包含函数入口点的 DLL
- 包含 COM 对象的 DLL
- 包含 COM 对象的 DLL,这些 COM 对象是托管 .NET 对象周围的 COM 可调用包装器 (CCW)
松散的 HTML
- xml不是松散的HTML
- xhtml 不是松散的 HTML
- 严格的 HTML 不是松散的 HTML
松散的 HTML 意味着 HTML 不是格式良好的 xml(严格的 HTML 无论如何也不是格式良好的 xml),因此不能使用 XML 解析器。实际上,我假设任何 HTML 解析器都必须在它接受的 HTML 中大方。
澄清#2
假设您喜欢将 HTML 转换为文档对象模型 (DOM) 的想法,那么您如何访问重复的数据结构? 你会如何遍历 DOM 树?我需要一个 used_result_container 类的 DIV 节点,它有一个 vehicleInfo 类的子 DIV。但节点不一定必须是彼此的直接子节点。
听起来我正在用一组正则表达式问题换另一组。如果他们改变了 HTML 的结构,我将不得不重新编写我的代码以匹配 - 就像我使用正则表达式一样。假设我们想避免这些问题,因为这些是正则表达式的问题,我该怎么做呢?
我不会为 DOM 节点编写正则表达式解析器吗?我正在编写一个引擎来解析一串对象,使用内部状态机和正向和反向捕获。不,一定有更好的方法——Jeff 提到的方法。
我故意把原来的问题含糊其辞,以免引人误入歧途。我不想暗示解决方案必然与以下内容有关:
- 遍历 DOM 树
- xpath 查询
澄清#3
我提供的示例 HTML 已精简为重要的元素和属性。我用来修剪 HTML 的机制是基于我使用正则表达式的内部偏见。我自然认为我需要在我寻找的 HTML 中的各种“sign-posts。
所以不要将呈现的 HTML 与整个 HTML 混淆。也许其他一些解决方案取决于 all 原始 HTML 的存在。
更新 4
唯一提出的解决方案似乎涉及使用库将 HTML 转换为文档对象模型 (DOM)。那么问题就变成了:然后呢?
现在我有了 DOM,我该如何处理它?看来我仍然需要使用某种能够进行正向匹配和捕获的常规 DOM 表达式解析器来遍历树。
在这种特殊情况下,我需要所有 used_result_container DIV 节点,其中包含 vehicleInfo DIV 节点作为子节点。任何不包含 vehicleInfo 的 used_result_container DIV 节点都有一个子节点。
是否有具有捕获和正向匹配的 DOM 正则表达式解析器?我不认为 XPath 可以根据较低级别节点的标准选择较高级别的节点:
\\div[@class="used_result_container" && .\div[@class="vehicleInfo"]]\*
注意:我很少使用 XPath,以至于我无法很好地构成假设的 xpath 语法。
【问题讨论】:
-
+1 您已经指定需要接受格式错误的 HTML。您可以指定的其他可能假设。解决方案应尽可能抵抗被抓取页面结构的变化。还要指定可接受的语言以及 .NET/COM 组件是否可接受?
-
解析HTML通常不是一个坏主意,用正则表达式尝试它是个坏主意。
-
COM 组件在 Win32 应用程序中是可接受的,如果它们已经在受支持的 Microsoft Windows 操作系统上注册则更好。 .NET 组件只有在具有 COM 可调用包装器 (CCW) 时才能从本机 Win32 调用,这取决于库。
-
您应该考虑到检索网页的时间几乎总是比使用比 C++ 更高级的语言进行解析的时间长。
-
您非常幸运,该页面的作者非常擅长以反映内容而非演示的方式命名 div。即使有您的限制,这也会使问题数量级变得更容易。
标签: html windows regex winapi screen-scraping