
在进行网页抓取的时候,分析定位html节点是获取抓取信息的关键,目前我用的是lxml模块(用来分析XML文档结构的,当然也能分析html结构), 利用其lxmlhtml的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 codecs2 from lxml import etree3 f=codecsopen("ceshihtml","r","utf-8")4 content=fread()5 fclose()6 tree=etreeHTML(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节点。
在python里要是用RE来处理就略麻烦一些:
block_pattern=recompile(u"<h3>档案</h3>()<h3>", reI | reS)
m=block_patternfindall(content)
item_pattern=recompile(u"<li>()</li>", reI | reS)
items=item_patternfindall(m[0])for i in items: print i
那么用xpath要怎么做呢?其实跟jQuery是差不多的:
nodes=treexpath("/descendant::ul[@id='china']")
当然,现在没有id的话也就只能用类似于jQuery的方法了。完整的xpath应该是这样写的(注意,原文件中的TAG有大小写的情况,但是在XPATH里只能用小写):
nodes=treexpath(u"/html/body/form/div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")
更简单的方法就是像jQuery那样直接根据id定位:
nodes=treexpath(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 ntext
对比三种方法应该可以看出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节点。
lxml takes all the pain out of XML
Stephan Richter
lxml是Python语言里和XML以及HTML工作的功能最丰富和最容易使用的库。lxml是为libxml2和libxslt库的一个Python化的绑定。它与众不同的地方是它兼顾了这些库的速度和功能完整性,以及纯Python API的简洁性,大部分与熟知的ElementTree API兼容但比之更优越。
安装lxml:
要求:需要Python23或更后的版本
使用easy_install工具,以超级用户或管理员的角色run下面的命令:
easy_install lxml
在windows下,最好指定版本号:easy_install lxml==226
使用lxml进行开发
lxmletree指南
通常使用lxmletree的方式
>>> from lxml import etree
Element类,一个Element是ElementTree API的主要容器类,大部分的XML tree功能都是通过这个类来访问的。Elements可以非常容易地通过Element工厂方法来创建。
>>> root = etreeElement("root")
元素的XML tag名字是通过tag属性来访问的
>>> print roottag # root
Elements是在XML树状结构中组织的,为创建子元素并将它们加到父元素上,可以使用append()方法。
>>> rootappend( etreeElement("child1") )
我们还有更高效的方法:SubElement工厂方法,它使用和Element工厂方法相同的参数,不过额外需要父节点作第一个参数:
>>> child2 = etreeSubElement(root, "child2")
>>> child3 = etreeSubElement(root, "child3")
可以使用tostring()方法来看得到的XML
>>> print etreetostring(root, pretty_print=True)
<root>
<child1/>
<child2/>
<child3/>
</root>
元素是列表
>>> child = root[0]
>>> print childtag
child1
>>> print len(root)
3
>>> rootindex(root[1]) # lxmletree only!
1
打印所有子节点:
>>> children = list(root)
>>> for child in root:
print(childtag)
child1
child2
child3
可以使用insert()方法插入新的子节点:
>>> rootinsert(0, etreeElement("child0"))
删除子节点:
>>> root[0] = root[-1] # this moves the element!
>>> for child in root:
print(childtag)
child3
child1
child2
如果想把一个元素拷贝到不同的地方,需要创建一个独立的deep copy。
>>> from copy import deepcopy
>>> element = etreeElement("neu")
>>> elementappend( deepcopy(root[1]) )
>>> print(element[0]tag)
child1
>>> print([ ctag for c in root ])
[’child3’, ’child1’, ’child2’]
getparent()返回父节点:
>>> root is root[0]getparent() # lxmletree only!
True
元素的兄弟或邻居节点是通过next和previous属性来访问的
The siblings (or neighbours) of an element are accessed as next and previous elements:
>>> root[0] is root[1]getprevious() # lxmletree only!
True
>>> root[1] is root[0]getnext() # lxmletree only!
True
带属性的元素
XML元素支持属性,可以用Element工厂方法直接创建。
>>> root = etreeElement("root", interesting="totally")
>>> etreetostring(root)
b’<root interesting="totally"/>’
可以使用set和get方法访问这些属性:
>>> print rootget("interesting")
totally
>>> rootset("interesting", "somewhat")
>>> print rootget("interesting")
somewhat
也可以使用attrib性质的字典接口
>>> attributes = rootattrib
>>> print(attributes["interesting"])
somewhat
>>> print(attributesget("hello"))
None
>>> attributes["hello"] = "Guten Tag"
>>> print(attributesget("hello"))
Guten Tag
>>> print(rootget("hello"))
Guten Tag
元素可以包含文字:
>>> root = etreeElement("root")
>>> roottext = "TEXT"
>>> print(roottext)
TEXT
>>> etreetostring(root)
’<root>TEXT</root>’
如果XML用在(X)HTML中,文本也可以在不同的元素中显示:
<html><body>Hello<br/>World</body></html>
元素有tail属性,它包含XML 树中元素直接跟的,直到下个元素的文本。
>>> html = etreeElement("html")
>>> body = etreeSubElement(html, "body")
>>> bodytext = "TEXT"
>>> etreetostring(html)
b’<html><body>TEXT</body></html>’
>>> br = etreeSubElement(body, "br")
>>> etreetostring(html)
b’<html><body>TEXT<br/></body></html>’
>>> brtail = "TAIL"
>>> etreetostring(html)
b’<html><body>TEXT<br/>TAIL</body></html>’
xmlNodeListGetString只取当前节点链表中类型是TEXT或者ENTITY_REF的节点的内容,xmlNodeGetContent获取的是子孙节点中类型是TEXT或者ENTITY_REF的节点的内容拼接的字符串。
以上就是关于python xpath解析返回对象怎么处理全部的内容,包括:python xpath解析返回对象怎么处理、python elementtree 判断节点是否有子节点、请教各位大大一个关于libxml编程的问题,xmlNodeListGetString()函数和xmlNodeGetContent()函数的区别等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)