二、数据提取
在发送请求获取响应之后,可能存在多种不同类型的响应内容;而且很多时候,我们只需要响应内容中的一部分数据。
1 响应分类
- 结构化的响应内容
- json字符串(高频出现)
- 可以使用re、json等模块来提取特定数据
- xml字符串(低频出现)
- 可以使用re、lxml等模块来提取特定数据
- json字符串(高频出现)
- 非结构化的响应内容
- html字符串
- 可以使用re、lxml等模块来提取特定数据
- html字符串
2 XML
xml是一种可扩展标记语言,样子和html很像,功能更专注于对传输和存储数据。
3xml和html的区别
- html
- 超文本标记语言
- 为了更好的显示数据,侧重点是为了显示
- xml
- 可扩展标记语言
- 为了传输和存储数据,侧重点是在于数据内容本身
4 常用数据解析方法
5jsonpath模块
5.1 jsonpath模块的使用场景
如果有一个多层嵌套的复杂字典,想要根据key和下标来批量提取value,这是比较困难的。jsonpath模块就能解决这个问题。
jsonpath可以按照key对python字典进行批量数据提取
5.2 jsonpath语法规则
常用的
- $ 根节点 (最外层的大括号)
- . 子节点
- … 子孙节点 内部任意位置
from jsonpath import jsonpath
import requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
response = requests.get('https://www.logo.com/lbs/getAllCitySearchLabels.json',headers=headers)
dict_data = jsonpath(response.content)
print(jsonpath(dict_data, '$..A..name'))
6 lxml模块
6.1 了解lxml模块和xpath语法
- lxml模块可以利用XPath规则语法,来快速的定位HTML/XML文档中特定元素以及获取节点信息(文本内容、属性值)
- XPath(XML Path Language)是一门在HTML/XML文档中查找信息的语言,可用来在HTML/XML文档中对元素和属性进行遍历。
- 提取xml、html中的数据需要lxml模块和xpath语法配合使用
6.2 xpathhelper
- 作用
- 对当前页面测试xpath语法规则
- 安装
- 1.下载插件
- 2.根据平台判断是否修改扩展名
- 3.浏览器扩展程序界面
- 4.拖动插件安装
6.3 xpath语法
基础节点选择语法
- xpath语法-选取节点以及提取属性或文本内容的语法
- Xpath使用路径表达式来选取XML文档中的节点或者节点集
- 这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似
- 使用chrome插件选择标签时候,选中时,选中的标签会添加属性class=“xh-highlight”
- xpath定位节点以及提取属性或文本内容的语法
- nodename 选中该元素
- / 从根节点选取、或者是元素和元素间的过度
- // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
- . 选取当前节点
- … 选取当前节点的父节点
- @ 选取属性
- text() 选取文本
html
/html/head/title # 绝对路径
/html//titile
//title # 相对路径
//title/../..//title # 绝对路径的上一级的上一级的路径然后再回到title
/ 、//隔开的是标签
. 当前标签
.. 上一级标签
//title/text() # 从开闭标签之间取文本内容
//link/@herf # 从/之前的标签中,也就是从link标签中取属性值
节点修饰语法
- 通过索引修饰节点
- /bookstore/book[1] 选取属性bookstore子元素的第一个book元素
- /bookstore/book[last()] 选取属性bookstore子元素的最后一个book元素
- /bookstore/book[last()-1] 选取属性bookstore子元素的导数第二个book元素
- /bookstore/book[position()>1] 选择bookstore下面的book元素,从第二个开始
/html/body/div[3]/div/div[1]/div[last()] # 选中最后一个
/html/body/div[3]/div/div[1]/div[last()] # 选中倒数第二个
/html/body/div[3]/div/div[1]/div[position()]>=10 # 范围选择
- 通过属性值修饰节点
- //title[@lang=“eng”] 选择lang属性值为eng的所有titile元素
- //book/titile[text()=‘Harry Potter’] 选取所有book下的title元素,仅仅选择文本等于Harry Potter的title元素
//div[@id="content=left"]/div/@id #出现在[]中的@是使用标签属性名和属性值修饰的节点,出现在结尾的/@是取属性值
- 通过子节点的值修饰节点
- /bookstore/book[price>35.00]/titile 选取bookstore元素中的book元素的所有titile元素,且其中的price元素的值大于35.00
//div[span[2]>=9.6] #div标签下的所有span的第二个值大于等于9.6的
//span[i>2000] # span标签下i>2000的
- 通过包含修饰节点
- //span[contains(text(),“下一页”)] 选取span标签的属性text包含"下一页"的 ,可以是属性包含也可以是标签内容包含
//span[contains(text(),"下一页")]
#或者
//span[contains(text(),"一页")]
//span[contains(@id,"quishi_tag_")] #选取span表中的属性id包含"quishi_tag_"的
关于xpath的下标
- 在xpath中,第一个元素的位置索引是1
- 最后一个元素的位置是last()
- 倒数第二个元素的位置级是last()-1
通配语法
- * 匹配任何元素节点
- @* 匹配任何属性节点
- node() 匹配任何类型的节点
- | 复合语法
//*[@id="content-left"] # 用*去通配所有标签,然后找到id=content-left的
//td/a
//h2/a
//td/a|//h2/a # xpath复合使用语法
7.lxml模块
- 安装
- pip3 install lxml=4.2.2
获取百度贴吧关键字中的所有贴吧标题信息并且实现翻页
import requests
from lxml import etree
# 爬取百度贴吧数据
class Tieba(object):
def __init__(self, keyword):
self.url = "http://tieba.baidu.com/f?ie=utf-8&kw={}".format(keyword)
print(self.url)
self.headers = {
# 高端浏览器 有注释
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
# 低端浏览器 没有注释
#"User-Agent":"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; DigExt)"
}
# 获取数据
def get_data(self, url):
response = requests.get(url,headers=self.headers)
return response.content
# 数据提取
def parse_data(self, data):
# 删除源代码中的注释
data = data.decode().replace("<!--","").replace("-->","")
# 创建element对象
html = etree.HTML(data)
# 根据xpath语法提取每页标题对象的贴吧标题xpath对象
el_list = html.xpath("//li[@class=' j_thread_list clearfix']/div/div[2]/div[1]/div[1]/a")
print(len(el_list))
# 遍历每页的贴吧标题xpath对象
data_list = []
for el in el_list:
# 根据每个xpath对象获取每个标题的titile属性和link属性
temp = {}
temp["title"] = el.xpath("./text()")[0]
temp["link"] = "http://tieba.baidu.com" + el.xpath("./@href")[0]
# 将每个标题属性字典添加到列表中
data_list.append(temp)
# 获取下一页url
try:
next_url = "https:" + html.xpath('//a[contains(text(),"下一页>")]/@href')[0]
except:
next_url = None
# 返回每页的标题属性字典和下一页的url
return data_list, next_url
# 保存 伪代码
def save_data(self, data_list):
for data in data_list:
print(data)
# 运行
def run(self):
# 第一次的url是self.url
next_url = self.url
# 实现翻页 直到next_url == None 结束翻页
while True:
# 发送请求,获取响应
data = self.get_data(next_url)
# 从响应中提取数据(数据和翻页用的url)
data_list, next_url = self.parse_data(data)
self.save_data(data_list)
print(next_url)
# 判断是否结束
if next_url == None:
break
if __name__ == '__main__':
tieba = Tieba("***")
tieba.run()
-
etree.HTML()能够自动补全html缺失的标签
-
lxml.etree.tostring 函数可以将转换成为Element对象再转换回html字符串
-
爬虫如果使用lxml提取数据,应该以lxml.etree.tostring的返回结果作为提取数据的依据