python爬虫数据解析总结
1.概述
获取数据中包含了我们需要的数据和不需要的数据,这个时候可以利用一些规则过滤数据,只保留需要的数据。
2.Xpath解析html数据
Xpath解析数据分为解析本地文件数据和解析请求返回的数据,不论解析哪种数据方法都是一样的。
2.1.基本语法
1.查询语法
# 1.路径查询
// 查询所有子孙节点,不考虑层级关系
/ 查询直接子节点
# 2.id查询
//div[@id]:查询所有包含id的div标签
//div[@id="top"]
# 3.属性查询
//@class
# 4.模糊查询
//div[contains(@id, "he")]
//div[starts-with(@id, "he")]
# 5.内容查询,text函数返回查询到的内容
//div/h1/text()
# 6.逻辑运算
//div[@id="head" and @class="s_down"]
//title | //price
2.2.Xpath解析html数据
1.安装lxml库
在使用xpath语法解析html文件前需要先安装lxml库,通过python安装。
# pip方式安装
pip install lxml
# pipenv方式安装 --dev 只安装到开发环境
pipenv install --dev lxml
2.xpath解析本地文件示例
被解析本地html文件代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>path解析HTML文件</title>
</head>
<body>
<ul>
<li>北京</li>
<li>上海</li>
<li>深圳</li>
<li>武汉</li>
</ul>
<ul>
<li>大连</li>
<li>锦州</li>
<li>沈阳</li>
</ul>
</body>
</html>
xpath解析html代码
from lxml import etree
'''
xpath可以解析本地文件和请求接口响应数据
解析本地文件使用 etree.parse
解析服务器响应数据 etree.HTML()
'''
# xpath 解析本地文件
tree = etree.parse('./path.html')
# 路径查询
li_list = tree.xpath('//body/ul/li')
print(li_list)
# id查询,查询有id 的li, text()函数获取标签中的内容
id_list = tree.xpath('//body/ul/li[@id]/text()')
print(id_list)
# 查询指定id值的标签内容
id_l1 = tree.xpath('//body/ul/li[@id="l1"]/text()')
print(id_l1)
# 查找id为l1的li标签的class的属性值
li = tree.xpath('//ul/li[@id="l1"]/@class')
# 查询id值包含l的li标签
li = tree.xpath('//ul/li[contains(@id, "l")]/text()')
print(li)
# 查询id的值以l开头的li标签
li_start = tree.xpath('//ul/li[starts-with(@id, "l")]/text()')
print(li_start)
# 查询id为l1和class为c1的内容
li_list = tree.xpath('//ul/li[@id="l1" and @claa="c1"]/text()')
# 查询id为l1或者id为l2内容
li_list = tree.xpath('//ul/li[@id="l1"]/text() | //ul/li[@id="l2"]/text()')
3.jsonpath解析json数据
当我们需要解析json格式数据时,需要使用jsonpath库提供的方法来解析数据。
3.1.jsonpath语法
1.jsonpath主要语法
- $ 表示文档的根元素
- @ 表示文档的当前元素
- .node_name 或 [‘node_name’] 匹配下级节点
- [index] 检索数组中的元素
- [start🔚step] 支持数组切片语法
- * 作为通配符,匹配所有成员
- … 子递归通配符,匹配成员的所有子元素
- () 使用表达式
- ?()进行数据筛选
2.jsonpath与XPath进行比较
Xpath | JsonPath | 说明 |
---|---|---|
/ | $ | 文档根元素 |
. | @ | 当前元素 |
/ | .或[] | 匹配下级元素 |
.. | N/A | 匹配上级元素,JsonPath不支持 |
// | .. | 递归匹配所有下级元素 |
* | * | 通配符,匹配下级元素 |
@ | N/A | 匹配属性,JsonPath不支持 |
[] | [] | 根据索引获取元素,Xpath索引从1开始,JsonPath索引从0开始 |
| | [ , ] | 连接操作符,将多个结果拼接成数组返回 |
N/A | [start:end:step] | 数据切片操作,Xpath不支持 |
[] | ?() | 过滤表达式 |
N/A | () | 脚本表达式,使用底层脚本引擎,Xpath不支持 |
() | N/A | 分组,JsonPaht不支持 |
3.2.解析json数据示例
1.安装jsonpath库
我们使用jsonpath库提供的方法解析json格式数据,因此需要先安装该库。jsonpath只支持解析本地json文件,不能直接解析请求接口返回的数据,因此需求先将返回的数据保存为本地文件后再解析。
# pip方式安装
pip install jsonpath
# pipenv方式安装 --dev表示安装在开发环境
pipenv install --dev jsonpath
2.解析数据文件
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
3.解析示例
import json
import jsonpath
# 加载本地被解析json文件
obj = json.load(open('./json_parse.json', 'r', encoding='utf-8'))
# 获取所有图书的作者
auth_list = jsonpath.jsonpath(obj, '$.store.book[*].author')
# 获取所有作者
auth_list = jsonpath.jsonpath(obj, '$.store..author')
# 获取store下所有内容
auth_list = jsonpath.jsonpath(obj, '$.store')
# 获取store里面所有东西的price
auth_list = jsonpath.jsonpath(obj, '$.store..price')
# 获取第三本书
auth_list = jsonpath.jsonpath(obj, '$.store.book[2]')
# 获取最后一本书
auth_list = jsonpath.jsonpath(obj, '$.store.book[(@.length - 1)]')
# 获取前面的两本书
auth_list = jsonpath.jsonpath(obj, '$.store.book[0:2]')
# 过滤所有包含isbn的书
# 条件过滤需要在()的前面添加一个? 表示是否存在
auth_list = jsonpath.jsonpath(obj, '$..book[?(@.isbn)]')
# 过滤所有价格低于10的书
auth_list = jsonpath.jsonpath(obj, '$..book[?(@.price < 10)]')
print(auth_list)
3.3.解析淘票票网站json数据示例
从淘票票网站中请求接口获取所有城市数据,通过jsonpath过滤出所有的城市名称。通过这个实例可以掌握JsonPath解析请求接口返回数据完整过程。
import ssl
import urllib.request
from lxml import etree
import json
import jsonpath
# 全局取消ssl证书验证
ssl._create_default_https_context = ssl._create_unverified_context
url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1666688964623_108&jsoncallback=jsonp109&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.8',
'cookie': 'miid=620415126792701199; enc=bXSrNYQszBm0avcGUnaxukmiSNO4zy57XBaokxJX4IEn508D8Y8itOHbJcocQ1ocSh03xlMw%2FAQdiSheaEL5sbdUzhRjT24K8IjspKA6N1zVbhcgMb9o%2FLsj6jmEr7r1; thw=cn; cna=lnbQGjLBgTYCAXt1V9VpygAB; t=3cf604814d241312591de0f1a6c1379e; sgcookie=E100GEcD6f0H1%2BVPzQpFdecSrku8hBUGO83GUY0qL272gAo%2BJ%2FBAm%2FFat%2BWa6GI5sBiJCKa20VU8NDdHPzZrUBfFDBrQQMXCkctCbMudiJZec9WoFZP4eoGh%2FfOizxTtPVFY; tracknick=%5Cu5C0F%5Cu9B3C151; _cc_=Vq8l%2BKCLiw%3D%3D; cookie2=16a86bd0e2f35d6f8dae9a1c2d039eb0; v=0; _tb_token_=5e38e339338b; xlly_s=1; tfstk=cq5PBVVLUBjfxsQLXIAF_pzLtk-RaJfG9m8BEkb1wWm-qR92bs2SvEbAnKJsuDvl.; l=eBLUYtieLsfzGrO6BOfwhurza77tMIRAguPzaNbMiOCPOu5e5yBfW6ykk5TwCnGVhsByR3oUDdSXBeYBcC2sjqj4axom4zDmn; isg=BKWllEEcKvO0gk-3u7Fn5krjtGff4ll08NiERqeKFFzrvsUwbzPcRLpQSCLIvnEs',
'referer': 'https://dianying.taobao.com/'
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# 通过切片去除数据中不需要的括号
content_json = content.split('(')[1].split(')')[0]
# 将请求接口响应数据保存到本地文件
with open('淘票票所有城市.json', 'w', encoding='utf-8') as fp:
fp.write(content_json)
# 读取文件中的数据
obj = json.load(open('淘票票所有城市.json', 'r',encoding='utf-8'))
# 解析json数据,获取所有城市名称
city = jsonpath.jsonpath(obj, '$..regionName')
print(city)
4.Beautiful Soup解析HTML或XML数据
Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库,简单来说,它能将HTML的标签文件解析成树形结构,然后方便地获取到指定标签的对应属性。
BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐使用lxml 解析器。
BeautifulSoup支持解析本地文件和请求响应数据
Beautiful Soup有完整的语法,可以参考中文文档使用:https://beautifulsoup.cn/
在开始之前,确保已经安装好Beautiful Soup和lxml。如果没有安装,请参考下面的安装教程。
# pip方式安装
pip install lxml
# pipenv方式安装 --dev只安装在开发环境
pipenv install --dev lxml
# pip方式安装
pip install bs4
# pipenv方式安装 --dev只安装在开发环境
pipenv install --dev bs4
4.1.Beautiful Soup基础语法使用示例
被解析html文件内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>path解析HTML文件</title>
</head>
<body>
<div>
<ul>
<li id="l1" class="c1">北京</li>
<li id="l2">上海</li>
<li>深圳</li>
<li>武汉</li>
</ul>
<ul>
<li>大连</li>
<li>锦州</li>
<li>沈阳</li>
</ul>
<span>span</span>
<p>p</p>
</div>
</body>
</html>
Beautiful Soup解析html文件代码
from bs4 import BeautifulSoup
# 通过解析本地文件,了解bs4基础语法使用
# 默认打开文件的格式是gbk 所以在打开文件的时候指定编码格式
# BeautifulSoup解析器指定为lxml
soup = BeautifulSoup(open('path.html', encoding='utf-8'), 'lxml')
# 根据标签名称查找节点
# 查找第一个符合条件的数据
print(soup.li)
# 查找标签的属性和值
print(soup.li.attrs)
# bs4常用函数
'''
find函数:查找第一个符合条件的数据
'''
# 返回第一个符合条件的数据
print(soup.find('li'))
# 根据属性值查找标签
print(soup.find('li', id='l2'))
# 根据class属性查找标签要注意class需要添加下划线,不然就和python关键字class冲突
print(soup.find('li', class_='c1'))
'''
find_all: 查找到所有符合条件的数据
'''
# 返回是一个列表,边切返回了所有li标签
print(soup.find_all('li'))
# 如果想获取多个标签的数据,需要放到列表中
print(soup.find_all(['li', 'span']))
# 限制匹配的数量
print(soup.find_all('li', limit=2))
'''
select: 根据CSS选择器得到节点对象
'''
# select返回的是一个列表,并且返回多个数据
print(soup.select('li'))
# 可以通过.代表class类选择器,查找数据
print(soup.select('.c1'))
# #代表id选择器,查找属于
print(soup.select('#l1'))
# 属性选择器
print(soup.select('li[id]'))
# 查找li标签中id为l2的标签
print(soup.select('li[id="l2"]'))
# 层级选择器
# 后代选择器
print(soup.select('div li'))
# 父子选择器
print(soup.select('div > span'))
# 组合选择器,同时找到span和li标签
print(soup.select('span, li'))
'''
节点选择器
'''
# 获取节点内容
# select返回的是列表,列表没有string,因此需要返回列表中某个值
obj = soup.select('#l1')[0]
# 如果标签对象中,只有内容,没有嵌套其他标签,则string和get_text()都可以拿到数据
# 如果标签对象中嵌套了标签,那么string获取不到数据,而get_text()可以获取到数据,因此建议使用get_text()函数获取标签中的数据
print(obj.string)
print(obj.get_text())
# 节点的属性
# 通过属性名称获取标签
obj = soup.select('#l1')[0]
# 输出获取到的标签名称
print(obj.name)
# 输出获取到的标签的属性,返回一个字典
print(obj.attrs)
# 通过属性名称获取属性值
print(obj.attrs.get('id'))
4.2.Beautiful Soup解析星巴克官网数据
下面通过爬取星巴克官网菜单数据,练习Beautiful Soup解析数据使用方法。
import ssl
import urllib.request
from bs4 import BeautifulSoup
# 全局取消ssl证书验证
ssl._create_default_https_context = ssl._create_unverified_context
url = 'https://www.starbucks.com.cn/menu/'
request = urllib.request.Request(url=url)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# print(content)
soup = BeautifulSoup(content, 'lxml')
# 可以通过xpath插件获取内容,然后在转为select选择器
# //ul[@class="grid padded-3 product"]//strong/text()
name_list = soup.select('ul[class="grid padded-3 product"] strong')
for name in name_list:
# 打印产品名称
print(name.get_text())