Win32:如何在不使用正则表达式的情况下抓取HTML?

15
最近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的前提:

那么应该怎么做呢?

假设条件:

  • 本地Win32
  • 松散的html

澄清假设:

本地Win32

  • .NET/CLR不是本地Win32
  • Java不是本地Win32
  • perl、python、ruby不是本地Win32
  • 假设C++在Visual Studio 2000中编译为本地Win32应用程序

本地Win32应用程序可以调用库代码:

  • 复制的源代码
  • 包含函数入口点的DLL
  • 包含COM对象的DLL
  • 包含COM对象的DLL是COM-callable包装器(CCW),围绕托管.NET对象

宽松HTML

  • xml不是宽松HTML
  • xhtml不是宽松HTML
  • 严格的HTML不是宽松HTML

宽松HTML意味着HTML不是良好格式化的xml(严格的HTML无论如何也不是良好格式化的xml),因此不能使用XML解析器。实际上,我认为任何HTML解析器都必须在接受的HTML方面很慷慨。


澄清#2

假设你喜欢将HTML转换为文档对象模型(DOM)的想法,那么如何访问重复的数据结构?会如何遍历DOM树?我需要一个DIV节点,它是used_result_container类,它有一个子DIV,该子DIV是vehicleInfo类。但是节点不一定要直接位于彼此的子级中。

听起来我正在用另一组正则表达式问题来交换问题。如果它们更改HTML的结构,则必须重新编写匹配代码-就像使用正则表达式一样。并且假设我们想避免这些问题,因为这些是正则表达式的问题,那么我该怎么办?

我不会为DOM节点编写正则表达式解析器吧?我正在编写一个引擎来解析对象字符串,使用内部状态机和前向和后向捕获。不,一定有更好的方法-就像Jeff所暗示的那样。

我故意保持原始问题含糊不清,以免误导人们走错路。我不想暗示解决方案与以下内容有任何关系:

  • 遍历DOM树
  • xpath查询

澄清#3

我提供的示例HTML已经删减到重要的元素和属性。我用于删除HTML的机制是基于我使用正则表达式的内在偏见。我自然而然地认为我需要在HTML中寻找各种标志

因此,不要将呈现的HTML与整个HTML混淆。也许其他解决方案取决于所有原始HTML的存在。

更新4

唯一提出的解决方案似乎涉及使用库将HTML转换为文档对象模型(DOM)。然后问题就变成了:那么怎么办

既然我有了DOM,我该怎么办呢?似乎我仍然需要使用某种常规DOM表达式解析器遍历树,能够进行前向匹配和捕获。

在这种特定情况下,我需要所有包含vehicleInfo DIV节点作为子节点的used_result_container DIV节点。不包含vehicleInfo作为子节点的任何used_result_container DIV节点都不相关。

是否有带有捕获和前向匹配的DOM正则表达式解析器?我认为XPath不能根据较低级别节点的标准选择更高级别节点:

\\div[@class="used_result_container" && .\div[@class="vehicleInfo"]]\*

注意: 我很少使用XPath,因此我不能很好地编写假设的xpath语法。


+1 你已经指定需要接受格式不良的HTML。您可以指定其他可能的假设。解决方案应尽可能抵抗被爬取页面结构的更改。还要指定哪些语言是可接受的,以及.NET / COM组件是否可接受? - MarkJ
1
解析HTML通常不是一个坏主意,但用正则表达式尝试解析它是一个坏主意。 - Svante
COM组件可以从Win32应用程序中调用,最好是它们已经在支持的Microsoft Windows操作系统上注册过了。.NET组件只有在具有COM可调用包装器(CCW)的情况下才能从本机Win32中调用,这取决于库。 - Ian Boyd
“重复的数据结构”是什么意思?您是指页面上有一个vehicleInfo divs列表,并且您想提取每个div的carlink吗? - int3
1
你很幸运,页面的作者非常擅长以反映内容而非展示方式来命名 div。即使有了你的限制,这也使问题变得容易了数个数量级。 - Stephen Harmon
显示剩余7条评论
12个回答

0
Perl中的HTML::Parser和HTML::Tree模块非常擅长解析Web上的大多数所谓的HTML。从那里,您可以使用类似XPath的查询定位元素。

0

你对ihtmldocument2有什么看法?我认为它应该会有所帮助。


如果您能发布一些代码,将有助于我理解样式表转换如何帮助解决问题。 - Ian Boyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接