二、解析
Xpath
1.初识Xpth
1.1什么是Xpath?
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。
XPath定位在爬虫和自动化测试中都比较常用,通过使用路径表达式来选取 XML 文档中的节点或者节点集,熟练掌握XPath可以极大提高提取数据的效率。
1.2Xpath语法
选取节点
表达式 | 描述 |
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取(取子节点)。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
路径表达式 | 结果 |
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性。 |
谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 结果 |
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position() < 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]//title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
@* | 匹配任何属性节点。 |
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//* | 选取文档中的所有元素。 |
//title[@*] | 选取所有带有属性的 title 元素。 |
选取若干路径
通过在路径表达式中使用"|"运算符,可以选取若干个路径。
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
//book/title | //book/price | 选取 book 元素的所有 title 和 price 元素。 |
//title | //price | 选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
1.3HTML的基本标签
标题:`<h1>、<h2>、<h3>、<h4>、<h5>、<h6>、<title>`
段落:<p>
链接:<a>
图像:<img>
样式:<style>
列表:`无序列表<ul>、有序列表<ol>、列表项<li>`
块:`<div>、<span>`
脚本:<script>
注释:<!--注释-->
2.Xpath的基本使用
2.1安装lxml库
在pycharm的设置–>项目:Python爬虫–>Python解释器 中进行安装。
2.2Xpath解析类型
(1)本地文件 etree.parse
(2)服务器响应的数据 etree.HTML(content)
2.2.1Xpath解析本地文件
本地文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<ul>
<li id="l1" class="c1">北京</li>
<li id="l2">上海</li>
<li id="c3">深圳</li>
<li id="c4">上海</li>
</ul>
<ul>
<li>大连</li>
<li>锦州</li>
<li>沈阳</li>
</ul>
</body>
</html>
解析的基本使用
from lxml import etree
# xpath解析本地文件
tree = etree.parse('17_解析_xpath的基本使用.html')
# tree.xpath('xpath路径')
# // 查找所有的子孙结点,不考虑层级关系
#// 找直接子节点
# 查找ul下面的li
# li_list = tree.xpath('//body/ul/li')
# 查找所有有id的属性的li标签
# text()获取标签中的内容
# li_list = tree.xpath('//ul/li[@id]/text()')
# 找到id为l1的li标签
# li_list = tree.xpath('//ul/li[@id="l1"]/text()')
# 查找到id为l1的li标签的class的属性值
# []中写标签的属性值,属性名前要有@,值用""括起来
# li = tree.xpath('//ul/li[@id="l1"]/@class')
# 查询id中包含l的li标签
# li_list = tree.xpath('//ul/li[contains(@id, "l")]/text()')
# 查询id的值以l开头的li标签
# li_list = tree.xpath('//ul/li[starts-with(@id, "l")]/text()')
# 查询id为l1和class为c1的标签
# li_list = tree.xpath('//ul/li[@id="l1" and @class="c1"]/text()')
# | (或)不能再标签之间用
li_list = tree.xpath('//ul/li[@id="l1"]/text() | //ul/li[@id="l2"]/text()')
print(li_list)
print(len(li_list))
2.2.2Xpath解析服务器响应数据
例一:以获取百度网站的“百度一下”为例
import urllib.request
url = 'https://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
}
reqeust = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
from lxml import etree
# 解析服务器响应的数据
tree = etree.HTML(content)
# 获取想要的数据 xpath的返回值是一个列表类型的数据
result = tree.xpath('//input[@id="su"]/@value')[0]
print(result)
例二:利用解析下载站长素材的前十页图片
import urllib.request
form lxml import etree
# 需求 下载前十页的图片
# https://sc.chinaz.com/tupian/jianzhutupian.html
# https://sc.chinaz.com/tupian/jianzhutupian_2.html
# https://sc.chinaz.com/tupian/jianzhutupian_3.html
# 封装请求对象定制
def create_request(page):
if (page == 1): # 因为第一页的url和其他页的不同需要单独判断
url = 'https://sc.chinaz.com/tupian/jianzhutupian.html'
else:
url = 'https://sc.chinaz.com/tupian/jianzhutupian_' + str(page) + '.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
}
request = urllib.request.Request(url=url, headers=headers)
return request
# 封装获取网页内容
def get_content(request):
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
return content
def down_load(content):
# 下载图片 利用xpath找出图片名称以及图片的路径
tree = etree.HTML(content)
name_list = tree.xpath('/html/body/div[3]/div[2]/div//img/@alt')
src_list = tree.xpath('/html/body/div[3]/div[2]/div/img/@data-original')
for i in range(len(name_list)):
name = name_list[i]
src = src_list[i]
url = 'https:' + src
urllib.request.urlretrieve(url=url, filename='./loveImg/' + name + '.jpg')
if __name__ == '__main__':
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
for page in range(start_page, end_page+1):
# (1)请求对象的定制
request = create_request(page)
# (2)模拟向服务器发送数据
content = get_content(request)
# (3)下载
down_load(content)
JsonPath
1.初识JsonPath
1.1关于JsonPath
JsonPath和JSON文档有关系,正如XPath之于XML文档一样,JsonPath为Json文档提供了解析能力,通过使用JsonPath,你可以方便的查找节点、获取想要的数据,JsonPath是Json版的XPath。
1.2JsonPath语法
(1)JsonPath语法要点:
$
表示文档的根元素@
表示文档的当前元素.node_name
或['node_name']
匹配下级节点[index]
检索数组中的元素[start:end:step]
支持数组切片语法*
作为通配符,匹配所有成员..
子递归通配符,匹配成员的所有子元素(<expr>)
使用表达式?(<boolean expr>)
进行数据筛选
(2)JsonPath与Xpath的比较
XPath | JsonPath | 说明 |
|
| 文档根元素 |
|
| 当前元素 |
|
| 匹配下级元素 |
|
| 匹配上级元素,JsonPath不支持此操作符 |
|
| 递归匹配所有子元素 |
|
| 通配符,匹配下级元素 |
|
| 匹配属性,JsonPath不支持此操作符 |
|
| 下标运算符,根据索引获取元素,XPath索引从1开始,JsonPath索引从0开始 |
` | ` |
|
|
| 数据切片操作,XPath不支持 |
|
| 过滤表达式 |
|
| 脚本表达式,使用底层脚本引擎,XPath不支持 |
|
| 分组,JsonPath不支持 |
注意:
- JsonPath的索引从0开始计数
- JsonPath中字符串使用单引号表示,例如:
$.store.book[?(@.category=='reference')]
中的'reference'
2.JsonPath的基本使用
2.1JsonPath解析本地文件
本地文件
{ "store": {
"book": [
{ "category": "修真",
"author": "六道",
"title": "坏蛋是怎样练成的",
"price": 8.95
},
{ "category": "修真",
"author": "天蚕土豆",
"title": "斗破苍穹",
"price": 12.99
},
{ "category": "修真",
"author": "唐家三少",
"title": "斗罗大陆",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "修真",
"author": "南派三叔",
"title": "星辰变",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "黑色",
"price": 19.95
}
}
}
代码示例
import urllib.request
import json
import jsonpath
#加载本地文件
obj = json.load(open('filename'))
# 获取书店下的所有作者
author_list = jsonpath.jsonpath(obj, '$.store.book[*].author')
print(author_list)
# 直接利用绝对路径获取所有作者
authors_list = jsonpath.jsonpath(obj, '$..author')
print(authors_list)
# 获取store下所有的元素
tag_list = jsonpath.jsonpath(obj, '$.store.*')
print(tag_list)
# 获取store下所有东西的价格
price_list = jsonpath.jsonpath(obj, '$.store..price')
print(price_list)
注意:
- json.load()参数是对象
- json.loads()参数是字符串
2.2JaonPath解析服务器响应文件
以解析淘票票为例
import urllib.request
import json
url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1681105103837_108&jsoncallback=jsonp109&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'
# 经测试只需要referer即可
headers = {
'referer': 'https://dianying.taobao.com/?spm=a1z21.3046609.city.1.7db2112axDN5Jc&n_s=new&city=110100',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(reqeust)
content = response.read().decode('utf-8')
#利用分隔符分割,返回的是数组,取第二个元素,即下标为1
content = content.split('(')[1].split(')')[0]
#写入本地文件
with open('解析_jsonpath解析淘票票.json', 'w', encoding='utf-8') as fp:
fp.write(content)
obj = json.load(open('解析_jsonpath解析淘票票.json', 'r', encoding='utf-8'))
city_list = jsonpath.jsonpath(obj, '$..regionName')
print(city_list)
关于本例url如何获取的问题
f12,刷新主页,清空全部nerwork栏内容,然后鼠标点击地名会出现接口,接口中有url,为json数据内容。
注意:
-
content.split('(')
以'('
为分割符进行分割,并以列表 形式返回
Bs4
1.初识Bs4
1.1什么是Bs4
BS4全称是Beatiful Soup,它提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为tiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。
1.2Bs4语法
在 BS4 中,通过标签名和标签属性可以提取出想要的内容
eg:
获取整个p标签的html代码:soup.p
获取p标签:soup.p.b
获取p标签内容:soup.p.text
2.Bs4的基本使用
2.1Bs4解析本地文件
本地文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<ul>
<li id="l1">张三</li>
<li id="l2">李四</li>
<li>王五</li>
<a href="" id="" class="a1">尚硅谷</a>
</ul>
</div>
<a href="" title="a2">百度</a>
<div id="d1">
<span>
哈哈哈
</span>
</div>
<p id="p1" class="p1">呵呵呵</p>
</body>
</html>
解析本地文件
from bs4 import BeautifulSoup
# 默认打开文件的格式是gbk
soup = BeautifulSoup(open('解析_bs4的基本使用.html', encoding='utf-8'), 'lxml')
# 根据标签名查找节点
# 找到的是第一个符合条件的数据
print(soup.a)
print(soup.a.attrs) # attrs 获取标签的属性和属性值
# bs4的一些函数
# (1)find
# 返回第一个符合条件的数据
print(soup.find('a'))
# 根据title的值找到对应的标签对象
print(soup.find('a', title="a2"))
# 根据class的值找到对应的标签对象,class需要加下划线
print(soup.find('a', class_="a1"))
# (2)find_all 返回列表,并返回所有的a标签
print(soup.find_all('a'))
# 如果想获取的是多个标签的数据,那么需要在参数中添加列表的数据(参数的数据类型是列表)
print(soup.find_all('['a', 'span']'))
# limit的作用,查找前几个数据
print(soup.find_all('li', limit=2))
# (3) select 推荐使用
# select方法返回的是一个列表,并且返回多个数据
print(soup.select('a'))
# 可以通过 . 代表class,我们把这种操作叫做类选择器
print(soup.select('.a1'))
# 可以通过 # 代表id
print(soup.select('#l1'))
# 属性选择器--通过属性来寻找对应的标签
# 查找li标签中有id的标签
print(soup.select('li[id]'))
# 查找li标签中id为l2的标签
print(soup.select('li[id="l2"]'))
# 层级选择器
# 后代选择器
# 找到的是div下面的li
print(soup.select('div li'))
# 子代选择器
# 某标签的第一级子标签
# 注意:很多的计算机编程语言中,如果不加空格不会输出内容,但在bs4中,不会报错,会显示内容
print(soup.select('div > ul > li'))
# 找到a标签和li标签的所有对象
print(soup.select('a, li'))
# 节点信息
# 获取节点信息
obj = soup.select('#d1')[0]
# 如果标签对象中只有内容,那么string和get_text()都可以使用
# 如果标签对象中除了内容还有标签,那么string就获取不到数据,而get_text()可以获取数据
# 推荐使用 get_text()
print(obj.string)
print(obj.get_text())
# 节点的属性
obj = soup.select('#p1')[0]
# name是标签的名字
print(obj.name)
# 将属性值作为一个字典返回
print(obj.attrs)
# 获取节点的属性
obj = soup.select('#p1')[0]
print(obj.attrs.get('class')) # 通过字典中的get()方法获取class的值
print(obj.get('class'))
print(obj['class'])
2.1Bs4解析服务器响应文件
以爬取星巴克数据为例
import urllib.request
from bs4 import BeautifulSoup
url = ''
response = urllib.request.urlopen(url)
content = response.read().decode('utf-8')
soup = BeautifulSoup(content, 'lxml')
# //ul[@class="grid padded-3 product"]//strong/text()
# name_list = soup.select('.grid padded-3 product') 错误的
name_list = soup.select('ul[class="grid padded-3 product"] strong')
# print(name_list)
for name in name_list:
# print(name.string)
print(name.get_text())