1. 概述
xpath在Python的爬虫学习中,起着举足轻重的地位。
对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但xpath明显比re具有优势,在网页分析上使re退居二线。
xpath 全称为**XML Path Language** 一种小型的**查询语言**
xpath的优点
● 可在XML中查找信息
● 支持HTML的查找
● 通过元素和属性进行导航
2. 使用xpath
python开发使用XPath条件: 由于XPath属于lxml库模块,所以首先要安装库lxml。
● 安装:pip install lxml
● 引用:from lxml import etree
● selector=etree.HTML(源码) #将源码转化为能被XPath匹配的格式
● selector.xpath(表达式) #返回为一列表
xpath的路径表达式
表达式 | 描述 | 实例 | 解析 |
/ | 从根节点选取 | /body/div[1] | 选取根结点下的body下的第一个div标签(子查询) |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 | //a | 选取文档中所有的a标签 (全局子孙查询) |
./ | 当前节点再次进行xpath | ./a | 选取当前节点下的所有a标签 |
@ | 选取属性 | //@calss | 选取所有的class属性 |
代码示例:
from lxml import etree
#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
select = etree.HTML(html_doc) #select基于上面的html字符串文档
ret = select.xpath("//a") #select基于文档中所有的a标签
print("ret:",ret) #f返回的是所有a标签的Element对象
#基于所有a标签的Element对象,拿到具体信息:
for linkE in ret:
name = linkE.xpath("./text()") #基于a标签,取出所有的文本内容
href = linkE.xpath("./@href") #基于a标签,取出所有的href属性值
print("具体信息:",name,href) #打印所有文本、href属性值
#输出:
ret: [<Element a at 0x1dc3e370b00>, <Element a at 0x1dc3e6cc8c0>, <Element a at 0x1dc3e6cd380>]
具体信息: ['Elsie'] ['http://example.com/elsie']
具体信息: ['Lacie'] ['http://example.com/lacie']
具体信息: ['Tillie'] ['http://example.com/tillie']
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 结果 |
/ul/li[1] | 选取属于 ul子元素的第一个 li元素 |
/ul/li[last()] | 选取属于 ul子元素的最后一个 li元素 |
/ul/li[last()-1] | 选取属于 ul子元素的倒数第二个 li元素 |
//ul/li[position()<3] | 选取最前面的两个属于 ul元素的子元素的 li元素 |
//a[@title] | 选取所有拥有名为 title的属性的 a元素 |
//a[@title='xx'] | 选取所有 a元素,且这些元素拥有值为 xx的 title属性 |
//a[@title>10] > < >= <= != | 选取 a元素的所有 title元素,且其中的 title元素的值须大于 10 |
/body/div[@price>35.00] | 选取body下price元素值大于35的div节点 |
以上路径表达式,同样适用于在F12中查找:
例如:打开淘宝网
tips:不知道怎么写xpath,也可以直接让f12帮我们找
只是找出的xapth路径会比较长,它是一个非常完整的xpath
选取未知节点
XPath 通配符可用来选取未知的 XML 元素
通配符 | 描述 |
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型的节点 |
在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
/ul/* | 选取 bookstore 元素的所有子元素 |
//* | 选取文档中的所有元素 |
//title[@*] | 选取所有带有属性的 title 元素 |
//node() | 获取所有节点 |
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径
在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
//book/title \| //book/price | 选取 book 元素的所有 title 和 price 元素 |
//title \| //price | 选取文档中的所有 title 和 price 元素 |
/bookstore/book/title \| //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素 |
逻辑运算
示例:
//div[@id="head" and @class="s_down"] :
查找所有id属性等于head并且class属性等于s_down的div标签
//title | //price :选取文档中的所有 title 和 price 元素,“|”两边必须是完整的xpath路径
属性查询
示例:
//div[@id] :找所有包含id属性的div节点
//div[@id="maincontent"] :查找所有id属性等于maincontent的div标签
//@class
//li[@name="xx"]//text() :获取li标签name为xx的里面的文本内容>
★ @在()里是查询属性,@在[]里是
使用示例如下:
from lxml import etree
#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
select = etree.HTML(html_doc) #select基于上面的html字符串文档
ret = select.xpath("//a/text()") #查询所有a标签的文本属性值
print("ret:",ret)
ret1 = select.xpath("//a[1]/text") #查询第一个a标签的文本属性值
print("ret1:",ret1)
ret2 = select.xpath("//a/@href") #查询所有a标签的链接属性值
print("ret2:",ret2)
ret3 = select.xpath("//a[1]/@href") #查询第一个a标签的链接属性值
print("ret3:",ret3)
ret4 = select.xpath("//a/@id | //a/@class") #查询所有a标签的id属性值和class属性值,不支持用and
print("ret4:",ret4)
#输出:
ret: ['Elsie', 'Lacie', 'Tillie']
ret1: []
ret2: ['http://example.com/elsie', 'http://example.com/lacie', 'http://example.com/tillie']
ret3: ['http://example.com/elsie']
ret4: ['sister', 'link1', 'sister', 'link2', 'sister', 'link3']
获取标签
示例:索引从1开始
tree.xpath('//li[1]/a/text()') : 获取第一个
tree.xpath('//li[last()]/a/text()') : 获取最后一个
tree.xpath('//li[last()-1]/a/text()') :获取倒数第二个
模糊查询
//div[contains(@id, "he")] :查询所有id属性中包含he的div标签
//div[starts-with(@id, "he")] :查询所有id属性中包以he开头的div标签
//div/h1/text() : 查找所有div标签下的直接子节点h1的内容
//div/a/@href:获取a里面的href属性值
//* :获取所有
//*[@class="xx"] :获取所有class为xx的标签
使用示例:
from lxml import etree
#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister c1" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister c2" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
select = etree.HTML(html_doc) #select基于上面的html字符串文档
'''
查询所有class属性值为"sister"的a标签
注意:class属性值必须和html文档一致,是双引号
可以看出,还是使用@class="sister"时,查结果只有一个完全匹配的
所以,需要使用匹配contains、或者starts_with(但必须要以指定的class属性值为开头)
'''
ret = select.xpath('//a[@class="sister"]')
print("ret:",ret)
ret1 = select.xpath('//a[contains(@class,"sister")]')
print("ret1:",ret1)
ret2 = select.xpath('//a[starts-with(@class,"sister")]')
print("ret2:",ret2)
#输出:
ret: [<Element a at 0x1b3e3b7cd00>]
ret1: [<Element a at 0x1b3e3b7cd00>, <Element a at 0x1b3e3b7c840>, <Element a at 0x1b3e3b7cf40>]
ret2: [<Element a at 0x1b3e3b7cd00>, <Element a at 0x1b3e3b7c840>, <Element a at 0x1b3e3b7cf40>]
3. xpath使用案例
以豆瓣电影top250为例,使用xpath进行电影信息提取
>
初级水平,简单处理步骤:
1. 将所有的返回html内容,黏贴在html文件中(后续涉及了直接网页爬取再进行整体优化)
2. 新建一个py文件,进行导入html文件,然后开始电影名提取
from lxml import etree
with open("douban250.html",encoding="utf-8") as f:
data = f.read()
select = etree.HTML(data)
movies = select.xpath('//div[@class="bd doulist-subject"]')
movie_list = []
for movie in movies:
movie_dict = {}
title = movie.xpath('.//div[@class="title"]//a/text()')
movie_dict['电影名'] = title[0].strip() if title else ""
rating = movie.xpath('.//div[@class="rating"]//span[@class="rating_nums"]/text()')
movie_dict['评分'] = rating[0].strip() if rating else ""
director = movie.xpath('.//div[@class="abstract"]/text()[normalize-space() and contains(., "导演:")]')
movie_dict['导演'] = director[0].strip().replace("导演:", "") if director else ""
movie_list.append(movie_dict)
for movie in movie_list:
print("电影名:", movie['电影名'])
print("评分:", movie['评分'])
print("导演:", movie['导演'])
print("-------------")
#输出:
电影名:
评分: 9.7
导演: 弗兰克·德拉邦特
-------------
电影名:
评分: 9.6
导演: 陈凯歌
-------------
电影名:
评分: 9.5
导演: 罗伯特·泽米吉斯
-------------
电影名:
评分: 9.5
导演: 詹姆斯·卡梅隆
-------------
电影名:
评分: 9.4
导演: 吕克·贝松
-------------
电影名:
评分: 9.5
导演: 罗伯托·贝尼尼
-------------
电影名: 千与千寻 千と千尋の神隠し
评分: 9.4
导演: 宫崎骏
-------------
电影名:
评分: 9.5
导演: 史蒂文·斯皮尔伯格
-------------
电影名:
评分: 9.4
导演: 克里斯托弗·诺兰
-------------
电影名: 忠犬八公的故事 Hachi: A Dog's Tale
评分: 9.4
导演: 拉斯·霍尔斯道姆
-------------
……至第25个