代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

win32.:如何在不使用正则表达式的情况下擦除HTML?

  •  15
  • Ian Boyd  · 技术社区  · 15 年前

    最近的 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
    • 假设VisualStudio 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节点,它是 已用结果容器 ,其子级为 车辆信息 . 但是节点不一定是彼此的直接子节点。

    听起来我在用一组正则表达式问题换另一组。如果它们改变了HTML的结构,我将不得不重新编写代码来匹配——就像我对正则表达式所做的那样。假设我们想避免这些问题,因为这些是正则表达式的问题,我应该怎么做呢?

    我不会为dom节点编写正则表达式解析器吗?我正在编写一个引擎来解析一系列对象,使用内部状态机和前后捕获。不,一定有更好的方法——杰夫提到的方法。

    我故意把原来的问题弄得模糊不清,以免引导人们走上错误的道路。我不想暗示解决方案必然与以下内容有关:

    • 在DOM树上行走
    • XPath查询

    澄清(3)

    我提供的HTML示例简化到了重要的元素和属性。我用来减少HTML的机制是基于我使用正则表达式的内部偏见。我自然认为我需要各种各样的 招牌 在我寻找的HTML中。

    所以不要混淆整个HTML的呈现HTML。也许其他的解决方案取决于 全部的 原始HTML。

    更新4

    唯一提出的解决方案似乎包括使用库将HTML转换为文档对象模型(DOM)。接下来的问题是: 然后什么 ?

    既然我有了DOM,我该怎么处理它呢?好像我还得带着某种 正则DOM表达式分析器 能够进行正向匹配和捕获。

    在这种情况下,我需要 已用结果容器 div 包含以下内容的节点 车辆信息 将节点划分为子级。任何 已用结果容器 DIV不包含的节点 车辆信息 有孩子是不相关的。

    是否有具有捕获和前向匹配的DOM正则表达式分析器?我认为xpath不能根据低级节点的条件选择高级节点:

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

    注: 我很少使用xpath,所以我不能很好地弥补假设的xpath语法。

    12 回复  |  直到 6 年前
        1
  •  5
  •   Frank Krueger    15 年前

    本地Win32

    你可以一直使用 IHtmlDocument2 . 这是内置到Windows的。通过这个COM接口,您可以 本地的 访问强大的DOM解析器(即DOM解析器!).

        2
  •  8
  •   int3    15 年前

    蟒蛇:

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

    BeautifulSoup -如果LXML输入失败,请尝试此操作。

    红宝石: (听说过以下图书馆,但从未尝试过)

    Nokogiri

    hpricot

    不过,如果您的解析器阻塞了,并且您可以大致确定阻塞的原因,我坦率地认为,在将该部分传递给解析器之前,可以使用regex hack删除该部分。

    如果您决定使用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中的“符号公告”,并相应地构造整个文档,因此您不必自己去做。

    是的,您仍然需要对结构进行搜索,但是在更高的抽象级别上。如果网站设计者决定进行页面翻修并完全更改其名称和结构 div S,那就太糟糕了,你必须重写你的查询,但是这应该比重写你的regex花费更少的时间。没有什么会自动为你做,除非你想写一些人工智能功能到你的页面刮刀…

    很抱歉,我没有提供“本机win32”库,我一开始以为您的意思是“在Windows上运行”。但其他人已经回答了这一部分。

        3
  •  5
  •   Josh Stodola    15 年前

    使用 Html Agility Pack 为.NET

    更新

    由于您需要本机/古董,而且标记可能不好,我建议您通过 Tidy 然后用解析 Xerces

        4
  •  3
  •   Dominic Rodger    15 年前

    使用 Beautiful Soup .

    漂亮的汤是一个HTML/XML解析器 对于可以变为无效的python 标记到分析树中。它提供 简单、惯用的导航方式, 搜索并修改分析 树。它通常可以节省程序员 工作时间或天数。还有一个 Ruby端口已调用 Rubyful Soup .

        5
  •  2
  •   est    15 年前

    如果您真的在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)
    

    您还可以在Windows中使用JScript或VB/DEPHY/C++/C/Y/Python等来实现这一点。它直接使用mshtml.dll DOM布局和解析器。

        6
  •  0
  •   Rob    15 年前

    另一种选择是使用HTML DOM解析器。不幸的是,它们中的大多数似乎都有格式不好的HTML的问题,因此除此之外,您还需要首先通过HTML整洁或类似的方式运行它。

        7
  •  0
  •   phidah    15 年前

    如果一个dom解析器是不可能的-无论什么原因, 我要找一些PHP的变体 explode() 或者你所使用的编程语言中可用的任何东西。

    例如,您可以从分割 <div class="vehicleInfo"> ,这将给您每个结果(记住忽略第一个位置)。之后,您可以循环结果,将每个结果拆分为 <div class="makemodeltrim"> 等。

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

    另一个选择是在一些CSS选择器库之后 phpQuery 或类似于您的编程语言。

        8
  •  0
  •   jitter    15 年前

    使用DOM分析器

    例如,Java检查此列表

    Open Source HTML Parsers in Java (我喜欢用眼镜蛇)

    或者,如果您确定您只想解析HTML的某个子集(理想情况下也是XML有效的),您可以使用一些XML解析器只解析传递给它的片段,甚至使用xpath请求您感兴趣的值。

    Open Source XML Parsers in Java (例如,DOM4J易于使用)

        9
  •  0
  •   LaC    15 年前

    我认为libxml2,尽管它的名字,也尽可能地解析标签soup html。它是一个C库,因此应该满足您的需求。你可以找到它 here .

    顺便说一句,另一个答案建议使用LXML,它是一个python库,但实际上是在libxml2上构建的。如果lxml对他很好,那么libxml2很可能对您很好。

        10
  •  0
  •   Epsilon Prime    15 年前

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

        11
  •  0
  •   Randal Schwartz    15 年前

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

        12
  •  0
  •   user160820    15 年前

    你觉得IHTMLDocument2怎么样? 我认为这会有所帮助。