Win32.:如何在没有正则表达式的情况下抓取HTML?

Ian*_*oyd 15 html regex windows winapi screen-scraping

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
Run Code Online (Sandbox Code Playgroud)

你可以看到我想要提取的值,[括在括号中]:

  • 网址
  • MakeAndModel
  • 公里
  • 价钱
  • 地点

假设我们接受解析HTML的前提:

这样做的方法是什么?

假设:

  • 原生Win32
  • 松散的HTML

假设澄清:

原生Win32

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

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

  • 复制了源代码
  • 包含函数入口点的DLL
  • 包含COM对象的DLL
  • 包含COM对象的DLL,这些对象是受管.NET对象周围的COM可调用包装器(CCW)

松散的HTML

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

松散的HTML意味着HTML格式不正确xml(严格的HTML无论如何都不是格式良好的xml),因此不能使用XML解析器.实际上,我假设任何HTML解析器必须在它接受的HTML中慷慨.


澄清#2

假设您喜欢将HTML转换为文档对象模型(DOM)的想法,那么如何访问重复的数据结构?会怎样,你走DOM树?我需要的是一个类的DIV节点 used_result_container,它具有类的子DIV vehicleInfo.但节点不一定必须是彼此的直接子节点.

听起来我正在为另一个交易一组正则表达式问题.如果他们改变HTML的结构,我将不得不重新编写我的代码来匹配 - 就像我对正则表达式一样.假设我们想要避免这些问题,因为那些是正则表达式的问题,我该怎么做呢?

我不会为DOM节点编写正则表达式解析器吗?我正在编写一个引擎来解析一串对象,使用内部状态机和前后捕获.不,必须有更好的方式 - 杰夫提到的方式.

我故意保持原来的问题含糊不清,以免引导人们走错路.我不想暗示解决方案必然与之有关:

  • 走一棵DOM树
  • xpath查询

澄清#3

我提供的示例HTML我修剪了重要的元素和属性.我用来修剪HTML的机制是基于我使用正则表达式的内部偏见.我自然认为我需要HTML中的各种" 签名帖子 " .

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

更新4

唯一提出的解决方案似乎涉及使用库将HTML转换为文档对象模型(DOM).那么问题就必须成为:那又怎样呢?

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

在这种特殊情况下,我需要所有的used_result_container DIV包含节点vehicleInfo DIV节点作为孩子.任何used_result_container不包含DIV节点vehicleInfo有一个孩子是不相关的.

是否存在具有捕获和转发匹配的DOM正则表达式解析器?我不认为XPath可以根据较低级别节点的标准选择更高级别的节点:

\\div[@class="used_result_container" && .\div[@class="vehicleInfo"]]\*
Run Code Online (Sandbox Code Playgroud)

注意:我很少使用XPath,以至于我无法很好地编写假设的xpath语法.

int*_*nt3 8

蟒蛇:

lxml - 更快,也许更好地解析错误的HTML

BeautifulSoup - 如果输入失败lxml试试这个.

Ruby :(听说过以下库,但从未尝试过)

引入nokogiri

角度来说,Hpricot

虽然如果你的解析器窒息,并且你可以粗略地指出导致窒息的原因,我坦率地认为在将它传递给解析器之前使用正则表达式黑客删除该部分是可以的.

如果你决定使用lxml,这里一些你可能会觉得有用的XPath教程.lxml教程假设您知道什么是XPath(我第一次阅读它时没有这样做.)

编辑:你的帖子自第一次出现以来真的有所增长......我会尽力回答我的问题.

我不认为XPath可以根据较低级别节点的标准选择更高级别的节点:

它可以.试试//div[@class='vehicleInfo']/parent::div[@class='used_result_container'].ancestor如果您需要更高级别,请使用.lxml还在getparent()其搜索结果上提供了一种方法,您也可以使用它.真的,你应该看看我链接的XPath站点; 你可以从那里解决你的问题.

那么你如何访问重复的数据结构?

DOM查询似乎完全适合您的需求.XPath查询返回一个找到的元素列表 - 你还想要什么?尽管它的名字,lxml确实接受'松散的HTML'.此外,解析器识别HTML中的"sign-posts"并相应地构造整个文档,因此您不必自己完成.

是的,您仍然需要对结构进行搜索,但需要进行更高级别的抽象.如果网站设计者决定进行页面检查并完全更改其名称和结构div,那么这太糟糕了,您必须重写查询,但它应该比重写正则表达式花费更少的时间.没有什么能自动为你做,除非你想在你的页面刮板中写一些AI功能......

我为没有提供"本机Win32"库而道歉,我首先假设您只是意味着"在Windows上运行".但其他人已经回答了这一部分.


Jos*_*ola 5

使用Html Agility Pack for .NET

更新

既然你需要原生/古董,并且标记可能很糟糕,我建议通过Tidy运行标记然后用Xerces解析它

  • 我写的是相同的 - 只有我以前缀"你没有指定你选择的开发工具...但是你已经指定了windows,所以如果你使用.NET那么:" (2认同)

Fra*_*ger 5

原生Win32

您始终可以使用IHtmlDocument2.此时内置于Windows.使用此COM接口,您可以本机访问功能强大的DOM解析器(IE的DOM解析器!).