前言
XPath是一种XML路径语言,适合于对HTML中的标签进行搜索。虽然学习过正则表达式,但是XPath的功能以及效率要比正则表达式方便的多,在python中要使用XPath,需要安装lxml库,lxml库是Python的一个解析库,支持HTML和XML的解析,支持XPath。
pip install lxml
一、XPath基础使用:
例如针对如下的一段html文本:
<div> <ul> <li class="item-0"><a href="www.baidu.com">baidu</a> <li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a> <li class="item-2"><a href="https://www.csdn.net/">csdn</a> <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
首先用etree.HTML()方法构造出相应的解析对象:
from lxml import etree
html = etree.HTML(text)
显然可以看出这段文本代码是不完整的,可以使用etree模块中的etree.tostring()方法对文本进行补全:
res = etree.tostring(html)
print(res.decode('utf-8')) # 不进行编码则输出二进制格式
输出:
<html><body><div>
<ul>
<li class="item-0"><a href="www.baidu.com">baidu</a>
</li><li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
</li><li class="item-2"><a href="https://www.csdn.net/">csdn</a>
</li><li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
</li></ul></div></body></html>
除了对转换后的文本进行解析,也可以直接对文件进行解析,使用etree.parse()方法:
# 在工程路径下存放test.html文件
html = etree.parse('./test.html', etree.HTMLParser())
res = etree.tostring(html)
print(res.decode('utf-8'))
二、XPath基础语法:
在XPath语法中,每一对标签可视为一个结点,对使用etree解析的对象使用xpath()方法获取指定标签中的内容。
- //:定位根路径,从文档中匹配当前节点之后的所有节点,而不考虑它们的位置。
- /:定位当前路径的下一路径,当前结点为根节点。
- .:当前结点
- ..:当前节点的父节点
- *:匹配任何元素
- [ ]:谓语,可视作选择条件。
- @:属性值
- |:若干路径
- contains():包含,contains(@class, 'class属性中的包含内容')
- text():文本内容
获取所有结点:
res = html.xpath('//*')
[<Element html at 0x133e6862f88>, <Element body at 0x133e6862f08>, <Element div at 0x133e6862ec8>, <Element ul at 0x133e6862fc8>, <Element li at 0x133e686d048>, <Element a at 0x133e686d0c8>, <Element li at 0x133e686d108>, <Element a at 0x133e686d148>, <Element li at 0x133e686d188>, <Element a at 0x133e686d088>, <Element li at 0x133e686d1c8>, <Element a at 0x133e686d208>]
获取指定结点下的子节点:
res = html.xpath('//li/a')
[<Element li at 0x1a38de6d0c8>, <Element li at 0x1a38de6d048>, <Element li at 0x1a38de6d108>, <Element li at 0x1a38de6d148>]
根据属性值获取结点:
res = html.xpath('//li[@class="item-3"]')
根据顺序选择结点:
res = html.xpath('//li[4]/a/text()') # 与上例得到结果相同
res = html.xpath('//li[last()]/a/text()') # 获取最后一个<li>标签的<a>标签文本信息
获取标签中的属性:
res = html.xpath('//li/@class')
['item-0', 'item-1', 'item-2', 'item-3']
获取标签下的文本信息:
res = html.xpath('//li/a/text()')
['baidu', 'myblog', 'csdn', 'hao123']
根据包含的属性值获取结点:
text = """
<div>
<ul>
<li class="item-0" name="one"><a href="www.baidu.com">baidu</a>
<li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
<li class="item-2"><a href="https://www.csdn.net/">csdn</a>
<li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
"""
html = etree.HTML(text)
res = html.xpath('//li[contains(@class, "item-0") and @name="one"]/a/text()|//li[@class="item-3"]/a/text()')
print(res)
三、XPath实战:
了解了XPath的用法后,对获取豆瓣电影TOP250的信息用XPath进行爬取。
import requests
from requests.exceptions import RequestException
from lxml import etree
def get_one_page(url):
headers = {
"User - Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
}
try:
response = requests.get(url, headers = headers)
if response.status_code == 200:
return response.text
else:
print("获取数据失败,状态码:%d" %response.status_code)
return None
except RequestException:
print("请求失败")
return None
def write_to_file(content):
with open("Douban250.txt", 'a', encoding="utf-8") as f:
f.write(str(content) + '\n')
f.close()
def parse_page(html):
content = etree.HTML(html)
all_data = content.xpath('//div[@id="content"]/div/div[1]/ol/li')
for item in all_data:
num = item.xpath('.//em/text()')[0]
name = item.xpath('.//span[@class="title"][1]/text()')[0]
pic = item.xpath('./div/div[1]/a/img/@src')[0]
actors = item.xpath('./div/div[2]/div[2]/p/text()')[0]
time = item.xpath('./div/div[2]/div[2]/p/text()')[1]
score = item.xpath('.//div[@class="star"]/span[2]/text()')[0]
peoples = item.xpath('.//div[@class="star"]/span[4]/text()')[0]
info_dict = {
"num": num,
"name": name,
"pic": pic,
"actors": actors.strip(),
"time": time.strip(),
"score": score,
"peoples": peoples
}
write_to_file(info_dict)
def main():
for page in range(10):
url = 'https://movie.douban.com/top250?start=' + str(25 * page)
html = get_one_page(url)
parse_page(html)
if __name__ == '__main__':
main()
参考: