最近的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
原生Win32应用程序可以调用库代码:
HTML
松散的HTML意味着HTML格式不正确xml(严格的HTML无论如何都不是格式良好的xml),因此无法使用XML解析器。实际上,我假设任何HTML解析器必须在它接受的HTML中慷慨。
假设 您喜欢将HTML转换为文档对象模型(DOM)的想法,那么如何访问重复的数据结构? 你如何走DOM树?我需要一个DIV节点,它是一个 used_result_container 的类,它有一个类 vehicleInfo 的子DIV。但节点不一定必须是彼此的直接子节点。
听起来我正在为另一组交换一组正则表达式问题。如果他们改变HTML的结构,我将不得不重新编写我的代码来匹配 - 就像我对正则表达式一样。假设我们想要避免这些问题,因为那些是正则表达式的问题,我该怎么做呢?
我不会为DOM节点编写正则表达式解析器吗?我正在编写一个引擎来解析一串对象,使用内部状态机和前后捕获。不,必须有更好的方式 - 杰夫提到的方式。
我故意将原来的问题模糊不清,以免引导人们走错路。我不想暗示解决方案必然与之有关:
我提供的示例HTML我修剪了重要的元素和属性。我用来修剪HTML的机制是基于我使用正则表达式的内部偏见。我自然认为我需要HTML中的各种“签名帖子。”
所以不要将呈现的HTML与整个HTML混淆。也许其他一些解决方案取决于所有原始HTML的存在。
唯一提出的解决方案似乎涉及使用库将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语法。
答案 0 :(得分:8)
的Python:
lxml - 更快,也许更好地解析错误的HTML
BeautifulSoup - 如果lxml输入失败,请尝试此操作。
Ruby:(听说过以下几个库,但从未尝试过它们)
虽然如果你的解析器窒息,并且你可以粗略地指出导致窒息的原因,我坦率地认为在将它传递给解析器之前使用正则表达式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中的“sign-posts”并相应地构造整个文档,因此您不必自己完成。
是的,您仍然需要对结构进行搜索,但需要进行更高级别的抽象。如果网站设计者决定进行页面检查并完全更改其div
的名称和结构,那么这太糟糕了,您必须重写查询,但它应该比重写正则表达式花费更少的时间。没有什么能自动为你做,除非你想在你的页面刮板中写一些AI功能......
我为没有提供“原生Win32”库而道歉,我首先假设您只是意味着“在Windows上运行”。但其他人已经回答了这一部分。
答案 1 :(得分:5)
答案 2 :(得分:5)
原生Win32
您始终可以使用IHtmlDocument2。此时内置于Windows。使用此COM接口,您可以 native 访问功能强大的DOM解析器(IE的DOM解析器!)。
答案 3 :(得分:3)
Beautiful Soup是一个HTML / XML解析器 对于Python甚至可以变得无效 标记到解析树中。它提供 简单,惯用的导航方式, 搜索和修改解析 树。它通常可以节省程序员 几小时或几天的工作。还有一个 Ruby端口名为Rubyful Soup。
答案 4 :(得分:2)
如果你真的在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 / Dephi / C ++ / C#/ Python等执行此操作。它直接使用mshtml.dll dom布局和解析器。
答案 5 :(得分:0)
使用DOM解析器
e.g。对于java检查此列表
Open Source HTML Parsers in Java(我喜欢使用眼镜蛇)
或者,如果您确定,例如你只想解析你的html的某个子集,理想情况下也是xml有效你可以使用一些xml解析器来解析你传入它的片段,然后甚至使用xpath来请求你感兴趣的值。
Open Source XML Parsers in Java(例如dom4j易于使用)
答案 6 :(得分:0)
另一种方法是使用html dom解析器。不幸的是,似乎他们中的大多数都有糟糕的html问题,所以另外你需要先通过html整理或类似的东西来运行它。
答案 7 :(得分:0)
如果DOM解析器不可能 - 无论出于何种原因,
我会选择PHP explode()
的某些变体或您使用的编程语言中提供的任何变体。
例如,您可以通过<div class="vehicleInfo">
拆分开始,这将为您提供每个结果(请记住忽略第一个位置)。之后,您可以循环结果,将每个结果拆分为<div class="makemodeltrim">
等。
这绝不是一个最佳解决方案,它将非常脆弱(几乎任何文档布局的变化都会破坏代码)。
另一个选择是为你的编程语言提供一些CSS选择器库,如phpQuery或类似的。
答案 8 :(得分:0)
我认为libxml2尽管有其名称,但也尽力解析标签汤HTML。它是一个C库,所以它应该满足您的要求。你可以找到它here。
BTW,另一个答案推荐lxml,这是一个Python库,但实际上是建立在libxml2上的。如果lxml对他有效,那么libxml2很可能适合你。答案 9 :(得分:0)
如何将Internet Explorer用作ActiveX控件?它会在查看页面时为您提供完全呈现的结构。
答案 10 :(得分:0)
Perl中的HTML :: Parser和HTML :: Tree模块非常适合在Web上解析大多数典型的所谓HTML。从那里,您可以使用类似XPath的查询来定位元素。
答案 11 :(得分:0)
您如何看待ihtmldocument2, 我认为它应该有所帮助。