【问题标题】:Win32.: How to scrape HTML without regular expressions?Win32.:如何在没有正则表达式的情况下抓取 HTML?
【发布时间】: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的前提:

有什么办法呢?

假设:

  • 原生 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 节点作为子节点。任何不包含 vehicleInfoused_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


【解决方案1】:

Python:

lxml - 更快,或许更擅长解析不良 HTML

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

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

Nokogiri

hpricot

虽然如果您的解析器阻塞,并且您可以大致查明导致阻塞的原因,坦率地说,我认为在将其传递给解析器之前使用正则表达式删除该部分是可以的。

如果您决定使用 lxml,here 是您可能会发现有用的 some XPath 教程。 lxml 教程有点假设您知道 XPath 是什么(我第一次阅读时并不知道。)

编辑:您的帖子自首次发布以来确实增长了...我会尽力回答。

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

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

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

DOM 查询似乎完全适合您的需求。 XPath 查询会返回找到的元素列表——您还想要什么?尽管它的名字,lxml 确实接受“松散的 HTML”。此外,解析器会识别 HTML 中的“路标”并相应地构建整个文档,因此您不必自己动手。

是的,您仍然需要对结构进行搜索,但要在更高的抽象级别上进行搜索。如果网站设计人员决定进行页面大修并完全更改其divs 的名称和结构,那太糟糕了,您必须重写查询,但这应该比重写正则表达式花费更少的时间。除非您想将一些 AI 功能写入您的页面抓取器中,否则没有什么会自动为您完成...

对于没有提供“本机 Win32”库,我深表歉意,起初我以为您只是指“在 Windows 上运行”。但其他人已经回答了这部分。

【讨论】:

  • +1 尤其适用于 lxml——在这个应用程序中取得了巨大的成功。
  • 在 Ruby 方面也有 ScrAPI:blog.labnotes.org/2006/07/11/…
  • 在本机 Win32 应用程序中可用的任何那些?
【解决方案2】:

原生 Win32

您始终可以使用IHtmlDocument2。目前这是 Windows 内置的。通过这个 COM 接口,您可以本机访问强大的 DOM 解析器(IE 的 DOM 解析器!)。

【讨论】:

  • 我过去使用过 IHtmlDocument2。我还有一个可以解析无效 HTML 并将其转换为 DOM 的对象的源代码。那么关于如何遍历 DOM 树和重复结构的任何想法?
  • @Ian,关键是 IHtmlDocument2 将能够处理来自野外的 HTML - 被破坏的原样。遍历 DOM 就像调用 all 和处理元素一样简单(DOM 是分层的)。这不好玩,但如果你想保持原生,这是一个“简单”的解决方案。 msdn.microsoft.com/en-us/library/aa752582(VS.85).aspx
【解决方案3】:

在 .NET 中使用 Html Agility Pack

更新

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

【讨论】:

  • 我写的是一样的——只是我在前面加上了“你没有指定你选择的开发工具......但是你已经指定了窗口,所以如果你使用 .NET 那么:”
  • 我没有指定编译器,但我指定了 native Win32。让我们假设它是 C++。
【解决方案4】:

使用Beautiful Soup

Beautiful Soup 是一个 HTML/XML 解析器 对于 Python 甚至可能变得无效 标记到解析树中。它提供 简单、惯用的导航方式, 搜索和修改解析 树。它通常可以节省程序员 几小时或几天的工作。还有一个 Ruby 端口称为Rubyful Soup

【讨论】:

    【解决方案5】:

    如果你真的在 Win32 下,你可以使用一个又小又快的 COM 对象来做到这一点

    vbs 示例代码:

    Set dom = CreateObject("htmlfile")
    dom.write("<div>Click for <img src='http://www.google.com/images/srpr/logo1w.png'>Google</a></div>")
    WScript.Echo(dom.Images.item(0).src)
    

    您也可以在 JScript 或 Windows 上的 VB/Dephi/C++/C#/Python 等中执行此操作。它直接使用 mshtml.dll dom 布局和解析器。

    【讨论】:

      【解决方案6】:

      另一种方法是使用 html dom 解析器。不幸的是,他们中的大多数似乎都存在格式不正确的 html 的问题,因此您需要先通过 html tidy 或类似的东西来运行它。

      【讨论】:

      • 我认为我们正在寻找特定解析器和整理器的具体建议。
      • 谢谢,他当时没有提供任何细节,但似乎他已经添加了信息。
      【解决方案7】:

      如果无法使用 DOM 解析器 - 无论出于何种原因, 我会选择 PHP 的 explode() 的一些变体,或者你使用的编程语言中可用的任何东西。

      例如,您可以从&lt;div class="vehicleInfo"&gt; 拆分开始,这将为您提供每个结果(请记住忽略第一个位置)。之后,您可以循环结果,将每个结果按&lt;div class="makemodeltrim"&gt; 等分割。

      这绝不是最佳解决方案,而且会非常脆弱(文档布局的任何更改都会破坏代码)。

      另一种选择是使用一些 CSS 选择器库,例如 phpQuery 或您的编程语言的类似库。

      【讨论】:

      • 老派刮。如果你这样做,你不妨使用正则表达式。
      【解决方案8】:

      使用 DOM 解析器

      例如对于 java 检查这个列表

      Open Source HTML Parsers in Java(我喜欢用cobra)

      或者如果你确定,例如您只想解析 html 的某个子集,理想情况下它也是 xml 有效的,您可以使用一些 xml 解析器来解析您传入的片段,然后甚至使用 xpath 来请求您感兴趣的值。

      Open Source XML Parsers in Java(例如 dom4j 好用)

      【讨论】:

      • 问题说本机 Win32,它是 32 位 Windows,并且没有指定语言
      • 问题还说“loose html”,这意味着 XML 解析器不可接受
      【解决方案9】:

      我认为 libxml2,尽管它的名字,也尽其所能解析标签汤 HTML。它是一个 C 库,因此它应该满足您的要求。你可以找到它here

      顺便说一句,另一个答案推荐 lxml,它是一个 Python 库,但实际上是建立在 libxml2 之上的。如果 lxml 对他很有效,那么 libxml2 很可能对你很有效。

      【讨论】:

        【解决方案10】:

        如何将 Internet Explorer 用作 ActiveX 控件?它会在查看页面时为您提供完全呈现的结构。

        【讨论】:

        • 这是IHtmlDocument2 - 除非您不必通过旋转渲染器来浪费资源。
        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

          您对 ihtmldocument2 有何看法, 我认为它应该有所帮助。

          【讨论】:

          • 如果您可以发布一些代码,这将有助于我理解样式表转换如何提供帮助。
          猜你喜欢
          • 1970-01-01
          • 2017-03-09
          • 1970-01-01
          • 2021-04-30
          • 2022-11-14
          • 1970-01-01
          • 2016-09-27
          • 1970-01-01
          • 2018-02-21
          相关资源
          最近更新 更多