ToB企服应用市场:ToB评测及商务社交产业平台

标题: Python爬虫之xpath语法及案例使用 [打印本页]

作者: 我爱普洱茶    时间: 2022-9-16 17:17
标题: Python爬虫之xpath语法及案例使用
Python爬虫之xpath语法及案例使用

---- 钢铁侠的知识库 2022.08.15
我们在写Python爬虫时,经常需要对网页提取信息,如果用传统正则表达去写会增加很多工作量,此时需要一种对数据解析的方法,也就是本章要介绍的Xpath表达式。
Xpath是什么

XPath,全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言。最初是用来搜寻 XML 文档的,但同样适用于 HTML 文档的搜索。所以在做爬虫时完全可以使用 XPath 做相应的信息抽取。
XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供超过 100 个内置函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有想要定位的节点都可以用 XPath 来选取。
下面介绍实战中常用的几个知识点,详细也可以看W3C介绍:https://www.w3school.com.cn/xpath/index.asp
Xpath语法介绍

路径常用规则

表达式描述实例nodename选取此节点的所有子节点xpath('//div')选取了div节点的所有子节点/从根节点选取xpath('/div')从根节点上选取div节点//选取所有当前节点,不考虑位置xpath('//div')选取所有的div节点.选取当前节点xpath('./div')选取当前节点下的div节点..选取当前节点的父节点xpath('..')回到上一个节点@选取属性xpath('//@calss')选取所有的class属性谓语规则

谓语被嵌在方括号内,用来查找某个特定的节点或包含某个制定的值的节点
表达式结果xpath('/body/div[1]')选取body下的第一个div节点xpath('/body/div[last()]')选取body下最后一个div节点xpath('/body/div[last()-1]')选取body下倒数第二个div节点xpath('/body/div[positon()❤️]')选取body下前两个div节点xpath('/body/div[@class]')选取body下带有class属性的div节点xpath('/body/div[@]')选取body下class属性为main的div节点xpath('/body/div[price>35.00]')选取body下price元素值大于35的div节点通配符

通配符来选取未知的XML元素
表达式结果xpath('/div/*')选取div下的所有子节点xpath('/div[@*]')选取所有带属性的div节点取多个路径

使用“|”运算符可以选取多个路径
表达式结果xpath('//div|//table')选取所有的div和table节点功能函数

使用功能函数能够更好的进行模糊搜索
函数用法解释starts-withxpath('//div[starts-with(@id,"ma")]')选取id值以ma开头的div节点containsxpath('//div[contains(@id,"ma")]')选取id值包含ma的div节点andxpath('//div[contains(@id,"ma") and contains(@id,"in")]')选取id值包含ma和in的div节点text()xpath('//div[contains(text(),"ma")]')选取节点文本包含ma的div节点语法熟悉

下面举一段HTML文本进行语法热身,代码如下
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # time: 2022/8/8 0:05
  4. # author: gangtie
  5. # email: 648403020@qq.com
  6. from lxml import etree
  7. text = '''
  8.             <ul id='ultest'>
  9.                  <li ><a target="_blank" href="https://www.cnblogs.com/link1.html">first item</a></li>
  10.                  <li ><a target="_blank" href="https://www.cnblogs.com/link2.html">second item</a></li>
  11.                  <li ><a target="_blank" href="https://www.cnblogs.com/link3.html">third item</a></li>
  12.                  <li ><a target="_blank" href="https://www.cnblogs.com/link4.html">fourth item</a></li>
  13.                  <li ><a target="_blank" href="https://www.cnblogs.com/link5.html">fifth item</a>
  14.              </ul>
  15.          
  16. '''
  17. # 调用HTML类进行初始化,这样就成功构造了一个XPath解析对象。
  18. # 利用etree.HTML解析字符串
  19. page = etree.HTML(text)   
  20. print(type(page))
复制代码
可以看到打印结果已经变成XML元素:
  1. [/code][size=3]字符串转换HTML[/size]
  2. 字符串利用etree.HTML解析成html格式:
  3. [code]print(etree.tostring(page,encoding='utf-8').decode('utf-8'))
  4. ```
  5. <html><body>
  6.             <ul id="ultest">
  7.                  <li ><a target="_blank" href="https://www.cnblogs.com/link1.html">first item</a></li>
  8.                  <li ><a target="_blank" href="https://www.cnblogs.com/link2.html">second item</a></li>
  9.                  <li ><a target="_blank" href="https://www.cnblogs.com/link3.html">third item</a></li>
  10.                  <li ><a target="_blank" href="https://www.cnblogs.com/link4.html">fourth item</a></li>
  11.                  <li ><a target="_blank" href="https://www.cnblogs.com/link5.html">fifth item</a>
  12.              </li></ul>
  13.          
  14. </body></html>
  15. Process finished with exit code 0
  16. ```
复制代码
经过处理可以看到缺失的也自动补全了,还自动添加html、body节点。
查找绝对路径

通过绝对路径获取a标签的所有内容
  1. a = page.xpath("/html/body/div/ul/li/a")
  2. for i in a:
  3.     print(i.text)
  4. ```
  5. first item
  6. second item
  7. third item
  8. None
  9. fifth item
  10. ```
复制代码
查找相对路径(常用)

查找所有li标签下的a标签内容
  1. html = etree.HTML(text)
  2. a = html.xpath("//a/text()")
  3. print(a)
  4. ```
  5. ['first item', 'second item', 'third item', 'fifth item']
  6. ```
复制代码
当前标签节点

. 表示选取当前标签的节点。
我们先定位 ul 元素节点得到一个列表,打印当前节点列表得到第一个 ul,
接着打印 ul 节点的子节点 li,text()输出。
  1. page = etree.HTML(text)
  2. ul = page.xpath("//ul")
  3. print(ul)
  4. print(ul[0].xpath("."))
  5. print(ul[0].xpath("./li"))
  6. print(ul[0].xpath("./li/a/text()"))
  7. ```
  8. [<Element ul at 0x234d16186c0>]
  9. [<Element ul at 0x234d16186c0>]
  10. [<Element li at 0x234d1618ac0>, <Element li at 0x234d1618b00>, <Element li at 0x234d1618b40>, <Element li at 0x234d1618b80>, <Element li at 0x234d1618bc0>]
  11. ['first item', 'second item', 'third item', 'fifth item']
  12. ```
复制代码
父节点

.. 表示选取当前标签的父节点。
可以看到得到ul的上一级div
  1. page = etree.HTML(text)
  2. ul = page.xpath("//ul")
  3. print(ul[0].xpath("."))
  4. print(ul[0].xpath(".."))
  5. ```
  6. [<Element ul at 0x1d6d5cd8540>]
  7. [<Element div at 0x1d6d5cd8940>]
  8. ```
复制代码
属性匹配

匹配时可以用@符号进行属性过滤
查找a标签下属性href值为link2.html的内容
  1. html = etree.HTML(text)
  2. a = html.xpath("//a[@href='link2.html']/text()")
  3. print(a)
  4. ```
  5. ['second item']
  6. ```
复制代码
函数

last():查找最后一个li标签里的a标签的href属性
  1. html = etree.HTML(text)
  2. a = html.xpath("//li[last()]/a/text()")
  3. print(a)
  4. ```
  5. ['fifth item']
  6. ```
复制代码
contains:查找a标签中属性href包含link的节点,并文本输出
  1. html = etree.HTML(text)
  2. a = html.xpath("//a[contains(@href, 'link')]/text()")
  3. print(a)
  4. ```
  5. ['first item', 'second item', 'third item', 'fifth item']
  6. ```
复制代码
实战案例

上面说完基本用法,接下来做几个实战案例练练手。
案例一:豆瓣读书
  1. #  -*-coding:utf8 -*-
  2. # 1.请求并提取需要的字段
  3. # 2.保存需要的数据
  4. import requests
  5. from lxml import etree
  6. class DoubanBook():
  7.     def __init__(self):
  8.         self.base_url = 'https://book.douban.com/chart?subcat=all&icn=index-topchart-popular'
  9.         self.headers = {
  10.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  11.                           'Chrome/104.0.0.0 Safari/537.36'
  12.         }
  13.     # 请求并提取需要的字段
  14.     def crawl(self):
  15.         res = requests.get(self.base_url, headers=self.headers)
  16.         lis = etree.HTML(res.text).xpath('//*[@id="content"]/div/div[1]/ul/li')
  17.         # print(type(lis))
  18.         books = []
  19.         for li in lis:
  20.             # print(etree.tostring(li,encoding='utf-8').decode('utf-8'))
  21.             # print("==================================================")
  22.             title = "".join(li.xpath(".//a[@class='fleft']/text()"))
  23.             score = "".join(li.xpath(".//p[@class='clearfix w250']/span[2]/text()"))
  24.             # list输出带有['\n                    刘瑜 / 2022-4 / 广西师范大学出版社 / 82.00元 / 精装\n                ']
  25.             publishing = "".join(li.xpath(".//p[@class='subject-abstract color-gray']/text()")).strip()
  26.             book = {
  27.                 'title': title,
  28.                 'score': score,
  29.                 'publishing': publishing,
  30.             }
  31.             books.append(book)
  32.         self.save_data(books)
  33.     def save_data(self, datas):
  34.             with open('books.txt', 'w', encoding='utf-8') as f:
  35.                 f.write(str(datas))
  36.     def run(self):
  37.         self.crawl()
  38. if __name__ == '__main__':
  39.     DoubanBook().run()
复制代码
案例二:彼岸图片下载
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # author: 钢铁知识库
  4. # email: 648403020@qq.com
  5. import os
  6. import requests
  7. from lxml import etree
  8. # 彼岸图片下载
  9. class BiAn():
  10.     def __init__(self):
  11.         self.url = 'https://pic.netbian.com'
  12.         self.headers = {
  13.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  14.                           'Chrome/104.0.0.0 Safari/537.36',
  15.             'cookie': '__yjs_duid=1_cb922eedbda97280755010e53b2caca41659183144320; Hm_lvt_c59f2e992a863c2744e1ba985abaea6c=1649863747,1660203266; zkhanecookieclassrecord=%2C23%2C54%2C55%2C66%2C60%2C; Hm_lpvt_c59f2e992a863c2744e1ba985abaea6c=1660207771; yjs_js_security_passport=1225f36e8501b4d95592e5e7d5202f4081149e51_1630209607_js'
  16.         }
  17.         # 如果目录不存在会报错
  18.         if not os.path.exists('BianPicture'):
  19.             os.mkdir('BianPicture')
  20.     # 请求拿到ul列表
  21.     def crawl(self):
  22.         res = requests.get(self.url, headers=self.headers)
  23.         res.encoding = 'gbk'
  24.         uls = etree.HTML(res.text).xpath('//div[@]/ul[@]/li')
  25.         # print(etree.tostring(uls,encoding='gbk').decode('gbk'))
  26.         # 循环拿到图片名、图片地址,拼接请求再次下载到图片
  27.         for ul in uls:
  28.             img_name = ul.xpath('.//a/b/text()')[0]
  29.             img_src = ul.xpath('.//a/span/img/@src')[0]
  30.             # print(img_name + img_src)
  31.             img_url = self.url + img_src
  32.             # 拼接后下载图片,转义Bytes
  33.             img_res = requests.get(img_url, headers=self.headers).content
  34.             img_path = "BianPicture/" + img_name + ".jpg"
  35.             data = {
  36.                 'img_res': img_res,
  37.                 'img_path': img_path
  38.             }
  39.             self.save_data(data)
  40.     # 数据保存逻辑
  41.     def save_data(self, data):
  42.         with open(data['img_path'], 'wb') as f:
  43.             f.write(data['img_res'])
  44.         # print(data)
  45.     def run(self):
  46.         self.crawl()
  47. if __name__ == '__main__':
  48.     BiAn().run()
复制代码

案例三:全国城市名称爬取
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # author: 钢铁知识库
  4. # email: 648403020@qq.com
  5. import os
  6. import requests
  7. from lxml import etree
  8. class CityName():
  9.     def __init__(self):
  10.         self.url = 'https://www.aqistudy.cn/historydata/'
  11.         self.headers = {
  12.             'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
  13.         }
  14.         # 判断目录是否存在
  15.         if not os.path.exists('city_project'):
  16.             os.mkdir('city_project')
  17.     def crawl(self):
  18.         res = requests.get(url=self.url, headers=self.headers).text
  19.         uls = etree.HTML(res).xpath('//div[@]/div[2]/ul/div[2]/li')
  20.         all_city_name = list()
  21.         for ul in uls:
  22.             city_name = ul.xpath('.//a/text()')[0]
  23.             # print(type(city_name))
  24.             all_city_name.append(city_name)
  25.             self.save_data(all_city_name)
  26.     def save_data(self, data):
  27.         with open('./city_project/city.txt', 'w') as f:
  28.             f.write(str(data))
  29.     def run(self):
  30.         self.crawl()
  31. if __name__ == '__main__':
  32.     CityName().run()
复制代码
xpath使用工具

chrome生成XPath表达式

经常使用chome的朋友都应该知道这功能,在 审查 状态下(快捷键ctrl+shift+i,F12),定位到元素(快捷键ctrl+shift+c) ,在Elements选项卡中,右键元素 Copy->Copy xpath,就能得到该元素的xpath了

Xpath Helper插件

为chome装上XPath Helper就可以很轻松的检验自己的xpath是否正确了。安装插件需要特别上网,安装好插件后,在chrome右上角点插件的图标,调出插件的黑色界面,编辑好xpath表达式,表达式选中的元素被标记为黄色

---- 钢铁侠的知识库 2022.08.15
结语:

以上就是利用XPath的所有用法,从常用语法,到案例练习都走了一遍。下一章 钢铁知识库 会继续介绍另一种好用的解析框架,Beautiful Soup,觉得有用点赞加关注,就当你送了红包666

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4