在进展网页抓取的时候,分析稳定html节点是获取抓取新闻的第一,近日我用的是lxml模块(用来分析XML文档结构的,当然也能分析html结构),
利用其lxml.html的xpath对html进行辨析,获取抓取信息。

  首先,我们须求安装一个支持xpath的python库。如今在libxml2的网站上被引进的python
binding是lxml,也有beautifulsoup,不嫌麻烦的话仍可以协调用正则表明式去打造,本文以lxml为例讲解。

如果有如下的HTML文档:

 1 <html>
 2   <body>
 3     <form>
 4       <div id='leftmenu'>
 5         <h3>text</h3>
 6         <ul id=’china’><!-- first location -->
 7           <li>...</li>
 8           <li>...</li>
 9              ......
10         </ul>
11         <ul id=’england’><!-- second location-->
12           <li>...</li>
13           <li>...</li>
14              ......
15         </ul>
16       </div>
17     </form>
18   </body>
19 </html>        

从来利用lxml处理:

1 import codecs
2 from lxml import etree
3 f=codecs.open("ceshi.html","r","utf-8")
4 content=f.read()
5 f.close()
6 tree=etree.HTML(content)

etree提供了HTML这些分析函数,现在我们得以一直对HTML使用xpath了,是或不是有点小感动,现在就尝试下吧。

 

在应用xpath以前大家先来看望作为比较的jQuery和RE。

jQuery里要处理那种东西就很简短,尤其是要是这多少个ul节点有id的话(比如是<ul
id=’china’>):

$("#china").each(function(){...});

切实到那边是:

$("#leftmenu").children("h3:contains('text')").next("ul").each(function(){...});

找到id为leftmenu的节点,在其下找到一个内容包括为”text”的h3节点,再取其接下去的一个ul节点。

XML,在python里假设用RE来拍卖就略麻烦一些:

block_pattern=re.compile(u"<h3>档案</h3>(.*?)<h3>", re.I | re.S)
m=block_pattern.findall(content)
item_pattern=re.compile(u"<li>(.*?)</li>", re.I | re.S)
items=item_pattern.findall(m[0])
for i in items:
    print i

那么用xpath要如何是好啊?其实跟jQuery是大概的:

nodes=tree.xpath("/descendant::ul[@id='china']")

理所当然,现在并未id的话也就不得不用接近于jQuery的办法了。完整的xpath应该是这么写的(注意,原文件中的TAG有大小写的情形,不过在XPATH里只可以用小写):

nodes=tree.xpath(u"/html/body/form/div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

更简短的艺术就是像jQuery这样直接根据id定位:

nodes=tree.xpath(u"//div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

那二种办法重返的结果中,nodes[0]就是可怜“text”的h3节点前边紧跟的第二个ul节点,那样就足以列出后边所有的ul节点内容了。

只要ul节点下边还有其余的节点,我们要找到更深节点的始末,如下的大循环就是把这么些节点的公文内容列出:

nodes=nodes[0].xpath("li/a")
for n in nodes:
    print n.text

比较三种办法应该可以看到xpath和jQuery对于页面的剖析都是基于XML的语义进行,而RE则纯粹是基于plain
text。RE对付简单的页面是从未有过难点,假使页面结构复杂度较高的时候(比如一堆的DIV来回嵌套之类),设计一个恰如其分的RE
pattern可能会远比写一个xpath要复杂。越发是当前主流的根据CSS的页面设计格局,其中绝一大半第二节点都会有id――对于使用jQuery的页面来说则更是如此,那时xpath比较RE就有了决定性的优势。

 

附录:主干XPATH语法介绍,详细请参考XPath的官方文档

XPATH基本上是用一体系似目录树的办法来描述在XML文档中的路径。比如用“/”来作为上下层级间的相间。第四个“/”表示文档的根节点(注意,不是指文档最外层的tag节点,而是指文档本身)。比如对于一个HTML文件来说,最外层的节点应该是”/html”。

相同的,“..”和“.”分别被用来表示父节点和本节点。

XPATH重临的不肯定就是唯一的节点,而是符合条件的具备节点。比如在HTML文档里应用“/html/head/scrpt”就会把head里的有所script节点都取出来。

为了缩短定位范围,往往还必要增加过滤条件。过滤的方法就是用“[”“]”把过滤条件足够。比如在HTML文档里应用“/html/body/div[@id=’main’]”,即可取出body里id为main的div节点。

中间@id表示属性id,类似的还能利用如@name,
@value, @href, @src, @class….


函数text()的趣味则是赢得节点包涵的公文。比如:<div>hello<p>world</p><
/div>中,用”div[text()=’hello’]“即可取得这么些div,而world则是p的text()。

函数position()的意思是收获节点的职位。比如“li[position()=2]”表示收获第一个li节点,它也足以被简单为“li[2]”。

可是要留意的是数字定位和过滤
条件的各样。比如“ul/li[5][@name=’hello’]”表示取ul下第五项li,并且其name必须是hello,否则再次回到空。而一旦用
“ul/li[@name=’hello’][5]”的情致就分化,它象征寻找ul下第多少个name为”hello“的li节点。

此外,“*”可以代替所有的节点名,比如用”/html/body/*/span”可以取出body下第二级的有所span,而随便它上顶尖是div如故p或是其他什么东东。


“descendant::”前缀可以代表任意多层的中档节点,它也足以被省略成一个“/”。比如在所有HTML文档中查找id为“leftmenu”的
div,可以用“/descendant::div[@id=’leftmenu’]”,也能够概括地采用“
//div[@id=’leftmenu’]”。

关于“following-sibling::”前缀就好像其名所说,表示一致层的下一个节点。”following-sibling::*”就是随机下一个节点,而“following-sibling::ul”就是下一个ul节点。

相关文章

网站地图xml地图