Python之xpath语法与lxml模块

Xpath是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历。通常在用Python编写爬虫代码爬取网页的时候用到。

Xpath语法

使用//选取整个页面当中的元素,然后写标签名,然后再写谓词进行提取。
xpath函数返回的是一个列表

选取节点

Xpath使用路径表达式来选取XML文档中的节点或节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

  • /, 如果是在最前面,代表从根节点选取,否则选择某节点下的某个节点,比如/bookstore 表示选取根元素下所有的bookstore节点
  • //,从全局节点中选择节点,与位置层级无关,比如//book,表示从全局节点中找到所有的book节点
  • ./,相对路径,从当前节点开始选取,比如./book,表示当前节点下的book子节点
  • ../,相对路径,从当前节点的上一级选取,比如../magazine,表示当前节点的父节点下的magazine节点
  • @, 选取某个节点的属性,比如//book[@price],表示选择所有拥有price属性的book节点

谓语

谓语用来查找某个特定的节点或包含某个指定的值的节点,被嵌在方括号中。

  • //body/div[1] 选取body下的第一个div元素
  • //body/div[last()] 选取body下的倒数第二个div元素
  • //body/div[position()<3] 选取body下前面两个div元素
  • //body/li[@class] 选取body下拥有class属性的所有li元素
  • //body/li[@class=’lists’] 选取body下所有class属性等于lists的li元素

通配符

  • //div/ul/* 选取div下的ul下的所有子元素
  • //div/ul[@*] 选取div下的所有带有属性的ul元素

选取多个路径

通过在路径表达式中使用“|”运算符,可以选取若干个路径。

  • //div/ul | //div/li 选取所有div元素下的ul元素以及li元素
  • //div[@class=”box” and @id=”box2”] 选取class属性等于box并且id属性等于box2的所有div元素

需要注意的点

  • /和//的区别:/代表只获取直接子节点,//获取子孙节点。
  • contains: 有时候某个属性中包含了多个值,可以使用contains函数,比如//div[contains(@class,’box’)] 选取拥有class属性等于box的div元素,div元素的class属性可以由多个值
  • 谓词中的下标是从1开始的

使用lxml解析HTML代码

lxml是一个HTML/XML的解析器,主要功能是如何解析和提取HTML/XML数据。

  • 调用lxml库
    1
    from lxml import etree
  • 解析HTML字符串,使用lxml.etree.HTML进行解析
    1
    2
    htmlElement = etree.HTML(text)  # 读取文本字符串
    print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))
  • 解析HTML文件,使用lxml.etree.parse进行解析。
    1
    2
    3
    parser = etree.HTMLParser(encoding='utf-8')  # 添加html解析器,以应对处理html文件不规范的情况
    htmlElement = etree.parse("test.html", parser=parser) # 读取html文件
    print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))

lxml与xpath综合使用

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from lxml import etree

parser = etree.HTMLParser(encoding='utf-8')
html = etree.parse("test.html", parser=parser)

trs = html.xpath("//tr") # 获取所有tr标签
for tr in trs:
print(etree.tostring(tr, encoding='utf-8').decoding('utf-8'))

ul = html.xpath("//ul[2]")[0] # 获取第二个ul标签
print(etree.tostring(ul, encoding='utf-8').decoding('utf-8'))

divs = html.xpath("//div[@class='box']") # 获取所有class等于box的div标签
for div in divs:
print(etree.tostring(div, encoding='utf-8').decoding('utf-8'))

aList = html.xpath("//a/@href") # 获取所有a标签的href属性
for a in aList:
print("http://hr.tencent.com/"+a)

# 获取纯文本信息 text()函数
lis = html.xpath("//li[position()>1]") # 获取除第一个li标签外的所有li标签
for li in lis:
href = li.xpath(".//a/@href")[0] # 获取当前li标签下的所有a标签的href属性,注意.符号
title = li.xpath(".//a/text()")[0] # 获取当前li标签下的所有a标签的所有文本
cataletory = li.xpath("./ul[1]//text()")[0]
num = li.xpath("./ul[2]/text()")[0]

lxml结合xpath注意事项:

  • xpath函数返回的永远是一个列表,取列表第一个元素即可
  • 获取属性值
    1
    href = html.xpath("//a/@href")
  • 获取文本,是通过xpath中的text()函数提取
  • 在某个标签下,再执行xpath函数,获取这个标签下的子孙元素,那么应该在//之前加一个点,代表是在当前元素下获取
    1
    title = li.xpath(".//a/text()")[0]