爬虫中的数据解析
- 肯德基
#爬取单页的数据
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
data = {
"cname": "",
"pid": "",
"keyword": "天津",
"pageIndex": "1",
"pageSize": "10"
}
#post请求携带请求参数使用data这个参数
response = requests.post(url=url,headers=headers,data=data)
data = response.json()
for dic in data['Table1']:
city = dic['cityName']
address = dic['addressDetail']
print(city,address)
- 下厨房:
import requests
#请求头
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
title = input('请输入菜名:')
params = {
'keyword':title,
'cat':'1001'
}
#1.指定url
url = 'https://www.xiachufang.com/search/'
#2.发起请求
response = requests.get(url=url,headers=headers,params=params)
#处理乱码
response.encoding = 'utf-8' #gbk
# 搜索:charset
#3.获取响应数据
page_text = response.text
#4.持久化存储
fileName = title + '.html'
with open(fileName,'w') as fp:
fp.write(page_text)
数据解析
何为数据解析
-
概念:可以将爬取到的数据中的指定想要的数据进行单独提取。
-
作用:可以实现聚焦爬虫。
-
数据解析通用原理:
- 在一张页面源码中,想要爬取的数据是存在于相关的html的标签中。
- 可以将指定的标签进行定位,然后提取该标签中或者标签属性中存储的数据即可。
-
聚焦爬虫编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
-
python中可以实现数据解析的技术:
- xpath(重要、常用和便捷)
- Bs4(自行了解学习)
- re正则
- pyquery(自行了解学习)
- json
数据解析的主流策略
xpath(重点)
环境安装:pip install lxml
xpath解析的编码流程:
- 1.创建一个etree类型的对象,然后把即将被解析的页面源码数据加载到该对象中。
- 2.调用etree对象中的xpath函数,让其结合着不同形式的xpath表达式进行标签定位和数据提取。
xpath表达式基础操作:
- 搭建一个实验环境:新建一个test.html文件,然后文件中写入下面内容:
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>测试bs4</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www.song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
<img src="http://www.baidu.com/meinv.jpg" alt="" />
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" class="du">杜甫</a></li>
<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
- 学习不同形式的xpath表达式(重点)
from lxml import etree
# 提取本地数据
# 1.创建一个etree类型的工具/对象,把被解析的页面源码数据加载到该对象中
tree = etree.parse('./05-数据解析之本地数据解析.html') # parse是解析本地的html文件里的内容;HTML是解析爬虫爬到的页面源码数据。parse加载的是一个的文件,可以将文件里的数据放到解析对象当中。而HTML可以将字符串形式的页面源码数据放到解析对象当中。所以在爬虫里面用的是HTML。./表示在同一目录下
# 2.调用etree对象的xpath函数,结合不同xpath表达式进行标签定位和数据提取
# 标签定位的xpath表达式:
# 定位meta标签
# ret = tree.xpath('/html/head/meta')[0]
# ret = tree.xpath('//meta')[0] # 从相对位置定位meta标签。<Element meta at 0x1177e613d80>
# 定位所有的div标签
# ret = tree.xpath('//div')
# 索引定位:想要定位到第一个div标签(下标是从1开始)
# ret = tree.xpath('//div[1]')
# 属性定位:定位到class属性值为song的div标签.//tagName[@attrName="attrValue"]。tagName为标签名。attrName为属性名。attrValue为字符串形式的属性值。
# ret = tree.xpath('//div[@class="song"]')
# ret = tree.xpath('//a[@class="du"]')[0]
# 层级标签:定位所有li标签下的a标签
# ret = tree.xpath('//div[@class="tang"]/ul/li/a')
# ret = tree.xpath('//div[@class="tang"]//li/a') # 将div下边的li标签(可以是div的直系子标签,也可以不是直系子标签)全部取出,//指多个层级。
# print(ret)
# 数据提取:
# 提取标签内的文本内容
# ret = tree.xpath('//a[@id="feng"]/text()')[0] # 提取a标签中的文本内容
# ret = tree.xpath('//div[@class="song"]//text()') # /text()提取标签直系的文本内容;//text()提取标签下所有的文本内容。
# print(ret)
# 提取标签的属性值内容:语法://tagName/@attrName。tagName定位到的标签,attrName属性名
# ret = tree.xpath('//img/@src')[0]
# print(ret)
# 可以做标签定位,可以提取直系和非直系内容,可以提取标签的属性值内容。xpath就学会了。
案例应用:碧血剑文本爬取
- url:https://bixuejian.5000yan.com/
- 需求:将每一个章节的标题和内容进行爬取然后存储到文件中
import requests
from lxml import etree
# 1.指定url
url = 'https://bixuejian.5000yan.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
}
# 2.发起请求(携带请求参数)
response = requests.get(url=url, headers=headers)
# 处理乱码
response.encoding = 'utf-8' # 或gbk,试试,一般不需要,出现乱码时在此加上这行代码。
# 搜索:charset
# 3.获取响应数据
page_text = response.text
# 数据解析:解析章节的标题&详情页的url
# 创建一个etree类型的工具/对象,把被解析的页面源码数据加载到该对象中
tree = etree.HTML(page_text) # parse是解析本地的html文件里的内容;HTML是解析爬虫爬到的页面源码数据。parse加载的是一个的文件,可以将文件里的数据放到解析对象当中。而HTML可以将字符串形式的页面源码数据放到解析对象当中。所以在爬虫里面用的是HTML。
# li_list列表中存储的是xpath表达式定位到的每一个li标签
# 调用etree对象的xpath函数,结合不同xpath表达式进行标签定位和数据提取
li_list = tree.xpath('/html/body/div[2]/div[1]/main/ul/li') # xpath地址可以复制出来的:定位到Elements选项卡中->点一下左上角的小箭头(Elements同一行)(Ctrl+Shift+C)->鼠标移到对应页面目标并点击一下->鼠标移到Elements下的框中标出阴影的地方单击右键(这里只是定位到一个目标标签,要定位相同一组的目标标签,要定位在此标签的上一级li标签或上上一级标签ul标签,此处定位在此标签的上上一级ul标签,)->复制->复制xpath->在pycharm中粘贴路径—>手动输入此标签的上一级标签li标签。
# fp = open('xiaoshuo.txt', 'w', encoding='utf-8')
with open('../pydirectory/xiaoshuo.txt', 'w', encoding='utf-8') as fp:
for li in li_list:
# 局部解析:只可以解析li标签内部的局部标签
# 调用etree对象的xpath函数,结合不同xpath表达式进行标签定位和数据提取
title = li.xpath('./a/text()')[0] # 这个.表示的是xpath的调用者,也就是li标签。title是a标签里的文本内容,即小说的标题。text()解析的是文本内容。
detail_url = li.xpath('./a/@href')[0] # detail_url是标题详情页的url。@href解析的是a标签的href属性值。
# print(title, detail_url)
# 对详情页的url发起请求,获取详情页的页面源码数据
detail_response = requests.get(url=detail_url, headers=headers)
detail_response.encoding = 'utf-8'
detail_page_text = detail_response.text # 详情页的页面内容
# 对详情页进行数据解析:章节内容
detail_tree = etree.HTML(detail_page_text)
content = detail_tree.xpath('/html/body/div[2]/div[1]/main/section/div[1]//text()') # 返回一个列表。
# 将content列表转换成一个完整的字符串(小说的内容)
content = ''.join(content).strip() # .join(content)将列表转换成字符串,strip()去除空格。
# 小说的标题有了:title;小说的内容有了:content;下面就是持久化存储:
# 4.持久化存储
fp.write(title + ':' + content + '\n')
print(title, ':章节下载保存成功!')
简历模版下载:https://sc.chinaz.com/jianli/free.html
-
下载当前页所有的建立模板
- 简历名称+简历的下载链接
- 根据简历的下载链接 下载简历文件
- 根据下载地址下载的压缩包,压缩包是二进制的数据
import requests
from lxml import etree
# 1.指定url
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
}
# 2.发起请求(携带请求参数),获取首页的页面源码数据。
for page in range(1, 6):
if page == 1:
url = 'https://sc.chinaz.com/jianli/free.html'
else:
url = f'https://sc.chinaz.com/jianli/free_{page}.html'
print(f'正在爬取{page}页数据.......')
response = requests.get(url=url, headers=headers) # 第一次发请求,相当于进入首页界面。
response.encoding = 'utf-8' # 处理乱码
# 搜索:charset
# 3.获取响应数据
page_text = response.text
# 解析首页的页面源码数据:解析简历的名称&详情页的url
tree = etree.HTML(page_text) # 创建一个etree类型的工具/对象,把被解析的页面源码数据加载到该对象中
# 调用etree对象的xpath函数,结合不同xpath表达式进行标签定位和数据提取
# div_list列表中存储的是xpath表达式定位到的每一个div标签
div_list = tree.xpath(
'//*[@id="container"]/div') # 调用etree对象的xpath函数,结合不同xpath表达式进行标签定位和数据提取,xpath地址可以复制出来的:定位到Elements选项卡中->点一下左上角的小箭头(Elements同一行)(Ctrl+Shift+C)->鼠标移到对应页面目标并点击一下->鼠标移到Elements下的框中标出阴影的地方单击右键(这里只是定位到一个目标标签,要定位相同一组的目标标签,要定位在此标签的上一级div标签或上上一级标签div标签,此处定位在此标签的上上一级div标签,)->复制->复制xpath->在pycharm中粘贴路径—>手动输入此标签的上一级标签div标签。
for div in div_list:
# 局部解析:只可以解析div标签内部的局部标签
title = div.xpath('./p/a/text()')[
0] # 这个.表示的是xpath的调用者,也就是div标签。title是p标签里a标签的文本内容,即简历的标题。text()解析的是文本内容。这不能直接复制xpath地址,那样只能固定获取一个简历,要用./表示遍历到的各个节点(即各个div标签)下的p标签里a标签的内容。
detail_url = div.xpath('./a/@href')[0] # detail_url是标题详情页的url。@href解析的是a标签的href属性值。
# print(title, detail_url)
# 对详情页的url发起请求,获取详情页的页面源码数据
detail_response = requests.get(url=detail_url, headers=headers) # 第二次发请求,相当于进入单独模板界面。
# detail_response.encoding = 'utf-8',看情况,如果无乱码就不写。
detail_page_text = detail_response.text # 详情页的页面内容
# 对详情页进行数据解析:解析出下载地址
detail_tree = etree.HTML(detail_page_text) # 解析详情页
download_url = detail_tree.xpath('//*[@id="down"]/div[2]/ul/li/a/@href')[0] # 用xpath解析出下载地址
# 对下载地址发起请求,下载对应的简历压缩包数据
data = requests.get(download_url, headers=headers).content # 相当于点击下载链接进行下载。# 第三次发请求,相当于进入下载界面进行下载。.content是把下载的源代码转成二进制数据。因为压缩包(.rar)是二进制数据。
file_name = '../pydirectory/简历模板/' + title + '.rar' # '../pydirectory/简历模板/'后面加/表示简历模板是文件夹名,不加/就和title结合成文件名。
with open(file_name, 'wb') as fp:
fp.write(data)
print(title, ":下载保存成功!")
图片数据爬取:
-
将爬取到的图片存储到指定的文件夹中
-
抓取详情页大图
-
爬取多页
-
# 图片数据爬取 # 设置请求参数 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' } # 发起请求,获取首页的页面源码数据。 for page in range(1, 3): if page == 1: url = 'http://pic.netbian.com/4kmeinv/' else: url = f'http://pic.netbian.com/4kmeinv/index_{page}.html' print(f'正在爬取{page}页数据.......') # 对首页进行页面源码数据的获取 response = requests.get(url=url, headers=headers) # 第一次发请求,相当于进入首页界面。 # print(response.apparent_encoding) # 获取网页编码 response.encoding = 'gbk' # 处理乱码 page_text = response.text # 数据解析:图片名字和图片详情页url tree = etree.HTML(page_text) # 注意这不能加引号,否则后面获取不了数据 li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li') for li in li_list: li.xpath('./a/@href') title = li.xpath('./a/b/text()')[0] detail_url = 'http://pic.netbian.com' + li.xpath('./a/@href')[0] # 对图片的详情页发起请求,获取详情页的页面源码数据 detail_response_text = requests.get(url=detail_url, headers=headers).text #第二次发请求,相当于进入大图界面。这是拿详情页面的源码数据。 # 数据解析,获取详情页的大图地址 detail_tree = etree.HTML(detail_response_text) img_src = 'http://pic.netbian.com'+detail_tree.xpath('//*[@id="img"]/img/@src')[0] # 注意是@src不是@href。 # 对大图地址发请求,下载图片。 img_data = requests.get(img_src, headers=headers).content # 第三次发请求,相当于点击下载链接下载图片。content把源码数据转成二进制数据。下载到图片数据。 img_path = '../pydirectory/4k图片/' + title + '.jpg' with open(img_path, 'wb') as fp: fp.write(img_data) print(title, ":下载保存成功!")
防盗链
-
现在很多网站启用了防盗链反爬,防止服务器上的资源被人恶意盗取。什么是防盗链呢?
- 从HTTP协议说起,在HTTP协议中,有一个表头字段:referer,采用URL的格式来表示从哪一个链接跳转到当前的网页或者文件。referer后面所存储的是url,这个url就可以表示当前请求的数据是从哪一个源头来的,referer采用的是用url的格式来表示从哪一个链接跳转到当前的网页或者文件。通俗理解就是:客户端的请求具体从哪里来,服务器可以通过referer进行溯源。一旦检测来源不是网页所规定的,立即进行阻止或者返回指定的页面。
-
案例:抓取微博图片,url:http://blog.sina.com.cn/lm/pic/,将页面中某一组系列详情页的图片进行抓取保存,比如三里屯时尚女郎:http://blog.sina.com.cn/s/blog_01ebcb8a0102zi2o.html?tj=1
-
注意:
-
1.在解析图片地址的时候,定位src的属性值,返回的内容和开发工具Element中看到的不一样,通过network查看网页源码发现需要解析real_src的值。
-
2.直接请求real_src请求到的图片不显示,加上Refere请求头即可
- 哪里找Refere:抓包工具定位到某一张图片数据包,在其requests headers中获取
-
import requests from lxml import etree headers = { 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36', "Referer": "http://blog.sina.com.cn/", } url = 'http://blog.sina.com.cn/s/blog_01ebcb8a0102zi2o.html?tj=1' page_text = requests.get(url,headers=headers).text tree = etree.HTML(page_text) img_src = tree.xpath('//*[@id="sina_keyword_ad_area2"]/div/a/img/@real_src') for src in img_src: data = requests.get(src,headers=headers).content with open('./123.jpg','wb') as fp: fp.write(data) # break
另一种思路写代码:
-
import requests
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Referer': 'https://blog.sina.com.cn/s/blog_01ebcb8a0102zi2o.html?tj=1'
}
url = 'https://blog.sina.com.cn/s/blog_01ebcb8a0102zi2o.html?tj=1'
page_text = requests.get(url=url, headers=headers).text # .text获取源码数据
tree = etree.HTML(page_text)
img_src_list = tree.xpath('//*[@id="sina_keyword_ad_area2"]/div/a/img/@real_src') # 解析出图片地址
# requests.get(url=img_src_list[0], headers=headers) # [0]对第一张图片发起请求。
count = len(img_src_list) # 计算一下要下载的图片的张数。
i = 0
while i < count:
n = 1
data = requests.get(url=img_src_list[i], headers=headers).content # [i]对第i张图片发起请求。
img_path = '../pydirectory/三里屯图片/' + f'{i}' + '.jpg' # f'{i}'下载的图片命名为'i.jgp'。
with open(img_path, 'wb') as fp:
fp.write(data)
print(img_path, ':下载保存成功!')
i += n
图片懒加载
- url:https://sc.chinaz.com/tupian/meinvtupian.html
- 爬取上述链接中所有的图片数据
- 图片懒加载(异步加载):
- 主要是应用在展示图片的网页中的一种技术,该技术是指当网页刷新后,先加载局部的几张图片数据即可,随着用户滑动滚轮,当图片被显示在浏览器的可视化区域范围的话,在动态将其图片请求加载出来即可。(图片数据是动态加载出来)。
- 如何实现图片懒加载/动态加载?
- 使用img标签的伪属性(指的是自定义的一种属性)(real_src,暂时保存图片真正的地址)。在网页中,为了防止图片马上加载出来,则在img标签中可以使用一种伪属性来存储图片的链接,而不是使用真正的src属性值来存储图片链接。(图片链接一旦给了src属性,则图片会被立即加载出来)。只有当图片被滑动到浏览器可视化区域范围的时候,在通过js将img的伪属性修改为真正的src属性(将src的值改为real_src保存的值),则图片就会被加载出来。src保存的不是真正的链接,为属性保存的是真正的链接。伪属性的命名不一定只是real_src,也可以是任意的命名。
- 如何爬取图片懒加载的图片数据?
- 只需要在解析图片的时候,定位伪属性的属性值即可。
# 图片数据爬取
import requests
from lxml import etree
# 设置请求参数
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Referer': 'https://sc.chinaz.com/'
}
# 发起请求,获取首页的页面源码数据。
for page in range(1, 6):
if page == 1:
url = 'https://sc.chinaz.com/tupian/meinvtupian.html'
else:
url = f'https://sc.chinaz.com/tupian/meinvtupian_{page}.html'
print(f'正在爬取{page}页数据.......')
# 对首页进行页面源码数据的获取
response = requests.get(url=url, headers=headers) # 第一次发请求,相当于进入首页界面。
# print(response.apparent_encoding) # 获取网页编码
response.encoding = 'utf-8' # 处理乱码
page_text = response.text
# 数据解析:图片标题和图片详情页url
tree = etree.HTML(page_text) # 注意这不能加引号,否则后面获取不了数据。创建etree对象
li_list = tree.xpath('/html/body/div[3]/div[2]/div') # 调用xpath函数
for li in li_list:
li.xpath('./div/a/@href')
detail_url = 'https://sc.chinaz.com' + li.xpath('./div/a/@href')[0] # 拼接图片详情url
title = li.xpath('./div/a/text()')[0] # 解析图片标题
# 对图片的详情页发起请求,获取详情页的页面源码数据
detail_response_text = requests.get(url=detail_url, headers=headers).text # 第二次发请求,相当于进入大图界面。这是拿详情页面的源码数据。.text把源码数据转为文本数据。
# 数据解析,获取详情页的大图地址
detail_tree = etree.HTML(detail_response_text)
img_src = detail_tree.xpath('/html/body/div[3]/div[1]/div[2]/div[1]/div[1]/p/a/@href')[0]
# 对大图地址发请求,下载图片。
img_data = requests.get(img_src, headers=headers).content # 第三次发请求,相当于点击下载链接下载图片。content把源码数据转成二进制数据。下载到图片数据。.content把源码数据转为二进制数据(图片)
img_path = '../pydirectory/美女图片/' + title + '.jpg'
with open(img_path, 'wb') as fp:
fp.write(img_data)
print(title, ":下载保存成功!")