数据解析
1 聚焦爬虫
爬取页面中指定内容数据
2 数据解析
1.正则表达式
2.bs4
3.xpath(重点)
3 数据解析的原理
解析的数据内容都会在标签之间或者标签对应的属性之间进行存储,那如果定位到指定的标签,再对标签或者标签的属性进行提取(解析)
4 流程
1.指定url
2.UA伪装
3.获得响应数据
4.数据解析
5.持久化存储
5 实战:爬取图片数据
在糗事百科上照一张图,获取地址url
import requests
import os
if __name__ == "__main__":
url = "https://pic.qiushibaike.com/system/pictures/12407/124078483/medium/N32DL358XZ7FRTCT.jpg"
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
img_data = requests.get(url = url).content
# content为获取二进制,text为获取文本形式,json()为获取json格式
with open("./pic.jpg","wb") as f: # 要二进制写入
f.write(img_data)
但是这样只能爬取一张一张的爬取图片,需要找到一种批量获取图片网址的方法
在百度上搜索jpg格式的图片
随着鼠标滚动,可以看出有很多的局部数据刷新包,得到响应信息:
url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=9658182917810117021&ipn=rj&ct=201326592&is=&fp=result&queryWord=jpg&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=©right=&word=jpg&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1614221263049="
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
'''
从AJAX请求包里提取响应到的信息,从信息中抓取图片网址
'''
response = requests.post(url = url, headers = headers)
text = response.text
# print(response)
re.match(),re.findall(),re.search()的关系
这里用findall
'''
提取图片网址
'''
r = re.findall(r'thumbURL":"https://(.*?).jpg',text) # .*贪婪模式 .*?懒惰模式
找到的是http://之后.jpg之前的东西,前后补全,可以构成图片的url
然后一张张存储则可以实现
import requests
import os
import re
if __name__ == "__main__":
url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=9658182917810117021&ipn=rj&ct=201326592&is=&fp=result&queryWord=jpg&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=©right=&word=jpg&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1614221263049="
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
'''
从AJAX请求包里提取响应到的信息,从信息中抓取图片网址
'''
response = requests.post(url = url, headers = headers)
text = response.text
# print(response)
'''
提取图片网址
'''
r = re.findall(r'thumbURL":"https://(.*?).jpg',text) # .*贪婪模式 .*?懒惰模式
'''
将找到的图片网址一个个存储下来
'''
count = 0
for i in r:
pic_url = "https://" + i + ".jpg"
response = requests.get(url = pic_url, headers = headers)
response_b = response.content # 图片以二进制存储
save_path = './pics/' + str(count + 1) + ".jpg"
with open(save_path,"wb") as f:
f.write(response_b)
count += 1
print(".\pics\%d.jpg saved!" %count)
可以看到存储了一个包里面的三十张图片
6 bs4实现数据解析
bs4解析数据的原理:对标签进行定位,解析标签或者标签属性中的数据
bs4数据解析流程:
1.实例化一个BeautifulSoup对象,并将页面源码数据加载到该对象当中
2.通过调用BeautifulSoup对象中的相关方法进行标签定位或者数据解析
要首先下载bs4模块,要下载lxml解析器:
pip install bs4
pip install lxml (xpath也会用到)
(有可能会下载失败,下载失败换channel到国内的源就好了)
实例化BeautifulSoup的方法有两个:
1.将本地html文档加载到BeatifulSoup中
fp = open('./test.html','r',encoding = 'utf-8')
soup = BeautifulSoup(fp,'lxml')
2.将互联网上获取的网页源码加载到BeautifulSoup中
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
通常使用第二种,第一种还要单独存储html文件
soup提供的用于数据解析的方法和属性
1.soup.tagName
返回的是文档中第一次出现的tagName对应的标签
2.soup.find(’tagName‘)
同soup.div,同时也可以实现属性定位soup.find('div',class_ = 'song')
3.soup.find_all('tagName')
返回的是符合要求的所有标签,返回的是一个列表
4.soup.select('某种选择器(可以是id,class,标签等选择器)')
另外还可以作为层级选择器使用soup.select('.tang > ul > li > a')
5.获取标签之间的文本数据soup.a.text/string/get_text()
string只能获取该标签下的直系文本内容,text可以获取标签下所有的文本内容
6.获取标签中的属性值soup.a['href']
7 bs4解析应用到实战中
需求:爬取三国演义小说中所有的章节标题和章节内容
网址:https://www.zggdwx.com/sanguo.html#.E4.BD.9C.E8.80.85
找到目录在什么地方,在mdui-list类下面,用select定位到mdui-list下面的a标签这里
from bs4 import BeautifulSoup
import requests
if __name__ == '__main__':
# 首先对首页中的页面数据进行爬取
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
url= 'https://www.zggdwx.com/sanguo.html#.E4.BD.9C.E8.80.85'
page_text = requests.get(url = url,headers = headers).text
# 在首页中解析出章节的标题和详情页的url
# 使用bs4解析数据
# 1.实例化一个BeautifulSoup对象
soup = BeautifulSoup(page_text,'lxml')
# 2.解析章节标题和详情页的url
# 这里最好选select选择器定位到所有的a标签
mulu_list = soup.select('.mdui-list > a')
得到的muli_list是这样的
从若干a标签里面找到网址和标题数据
url_list = []
title_list = []
for a in mulu_list:
detail_url = a['href'] # 详情页的url
title = a.string # 获取每一章的标题
print(detail_url)
print(title)
url_list.append(detail_url)
title_list.append(title)
接下来就是对详情页进行分析,找到内容的部分
那可以看到详情在”mdui-card mdui-card-shadow mdui-typo“这个class下面,这里直接取出这个下面的所有文本,不用select查找p标签了,不然还要一段一段给拼接起来
# 取其中一个url进行目录的提取,如果要提取所有的可以加一个循环
detail_text = requests.get(url = url_list[5],headers = headers).text
detail_soup = BeautifulSoup(detail_text,'lxml')
content = detail_soup.find('div', class_ = 'mdui-card mdui-card-shadow mdui-typo')
print(content.text)
#存储
fp = open('./sanguo.txt',"w",encoding = 'utf-8')
fp.write(content.text)
8 xpath解析
xpath的原理:
1.实例化一个etree对象,将页面源码加载到该对象中
2.调用etree中的xpath方法结合xpath表达式实现标签的定位和内容的捕获
xpath环境需要安装:
lxml
如何实例化etree对象,两种方法:
1.本地html加载到etree对象中etree.parse(filepath)
2.互联网上获取的源码数据加载到对象中etree.HTML('page_text')
xpath表达式的编写:
r = tree.xpath('/html/head/title') #第一个/表示从根节点开始
r = tree.xpath('html//title') #//表示多个层级,相当于bs4的空格
r = tree.xpath('//title') #//是一个意思,可以表示从任何位置开始定位
实现属性定位
tree.path('//div[@class = 'song']')
实现索引定位
tree.xpath('//div[@class = "song"]/p[3]') #索引是从1开始的
获取标签的文本内容和属性的数值
r = tree.path('//div[@class = "tang"]//li[5]/p/text()')[0]
/text()获取标签下直系内容
//text()获取标签下所有内容
r = tree.xpath('//div[@class = "song"]/img/@src')
/@属性名字 取得属性的值
10 xpath实战:爬取58二手房的房源信息
网址:https://bj.58.com/ershoufang/
找到每一个二手房源信息所在的位置
from lxml import etree
import requests
if __name__ == "__main__":
# 先爬取源码数据
url = 'https://bj.58.com/ershoufang/'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批div对象的列表
div_list = tree.xpath('//section[@class = "list"]/div')
对每个对象定位到标题和房价信息:
from lxml import etree
import requests
if __name__ == "__main__":
# 先爬取源码数据
url = 'https://bj.58.com/ershoufang/'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批div对象的列表
div_list = tree.xpath('//section[@class = "list"]/div')
# 3.对每个div对象进行xpath解析,得到每个二手房信息的title
house_dic = {}
for div in div_list:
title = div.xpath('./a/div[2]/div[1]/div[1]/h3/text()')[0] #表示当前解析的标签位置开始,即从div开始
price = div.xpath('./a/div[2]/div[2]/p[1]/span[1]/text()')[0]
price_text = div.xpath('./a/div[2]/div[2]/p[1]/span[2]/text()')[0]
house_dic.update({title:str(price) + price_text})
print(house_dic)
11 xpath爬取图片
网址:http://pic.netbian.com/4kmeinv/
from lxml import etree
import requests
if __name__ == "__main__":
# 先爬取源码数据
url = 'http://pic.netbian.com/4kfengjing/'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "slist"]/ul/li')
# 3.对每个div对象进行xpath解析,得到每个二手房信息的title
for li in li_list:
pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
print(pic_url)
print(pic_name)
如片名称出现乱码现象,对乱码的中文部分进行解码
from lxml import etree
import requests
if __name__ == "__main__":
# 先爬取源码数据
url = 'http://pic.netbian.com/4kfengjing/'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "slist"]/ul/li')
# 3.对每个div对象进行xpath解析,得到每个二手房信息的title
for li in li_list:
pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
# 指定编码格式,通用处理中文乱码的解决方法
pic_name = pic_name.encode('iso-8859-1').decode('gbk')
print(pic_url)
print(pic_name)
存储图片:
from lxml import etree
import requests
import os
if __name__ == "__main__":
# 先爬取源码数据
url = 'http://pic.netbian.com/4kfengjing/'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "slist"]/ul/li')
# 3.对每个div对象进行xpath解析
if not os.path.exists('./picLibs'):
os.mkdir('./picLibs')
for li in li_list:
pic_url = 'http://pic.netbian.com/' + li.xpath('./a/img/@src')[0] # 局部数据解析./
pic_name = li.xpath('./a/img/@alt')[0] + '.jpg' # 获取图片名称
# 指定编码格式,通用处理中文乱码的解决方法
pic_name = pic_name.encode('iso-8859-1').decode('gbk')
pic_data = requests.get(url = pic_url,headers = headers).content
pic_path = 'picLibs/' + pic_name
with open(pic_path,"wb") as f:
f.write(pic_data)
print(pic_name + 'download finish!')
注意:xpath表达式可以用或者连接:
tree.xpath(’ … | … ')使得xpath表达式具备更强的通用性
12 爬取免费简历模板
网址:http://www.gerenjianli.com/moban/index.html
从网站下批量下载简历
1.先得到单个ppt的下载页面:
from lxml import etree
import requests
import os
if __name__ == "__main__":
# 先爬取源码数据
url = 'http://www.gerenjianli.com/moban/index.html'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
# 3.对每一个li对象解析
for li in li_list:
ppt_url = li.xpath('./div/span/a/@href')[0]
ppt_name = li.xpath('./div/span/a/text()')[0]
# 中文乱码了
# 指定编码格式,通用处理中文乱码的解决方法
ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
print(ppt_url)
print(ppt_name)
然后通过观察找到了点下载按钮触发的是哪个网址
这个网址一点就触发下载操作,用代码去伪装浏览器触发这个网址的访问:
from lxml import etree
import requests
import os
if __name__ == "__main__":
# 先爬取源码数据
url = 'http://www.gerenjianli.com/moban/index.html'
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
# 3.对每一个li对象解析
if not os.path.exists('./pptLib'):
os.mkdir('./pptLib')
for li in li_list:
ppt_url = li.xpath('./div/span/a/@href')[0]
ppt_name = li.xpath('./div/span/a/text()')[0]
# 中文乱码了
# 指定编码格式,通用处理中文乱码的解决方法
ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
print(ppt_url)
print(ppt_name)
ppt_data = requests.get(url = ppt_url, headers = headers).content # 二进制存储
save_path = './pptLib/' + ppt_name + ".doc"
with open(save_path, "wb") as f:
f.write(ppt_data)
print("save_path was download!!!")
这样就下载好一页里面的所有ppt了,一共36个,那如果想要批量爬取很多页的ppt,需要观察每一个的网址特点,发现只是在index_后面加上一个页数就可以了,第一页没有页码,需要分别处理:
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 1 14:13:12 2021
@author: zhongxi
"""
from lxml import etree
import requests
import os
if __name__ == "__main__":
if not os.path.exists('./pptLib'):
os.mkdir('./pptLib')
url_list = ['http://www.gerenjianli.com/moban/index.html'] # 第一页的网址没有index后面的数字,单独列出来
for page in range(10):
url_list.append('http://www.gerenjianli.com/moban/index_' + str(page + 2) + '.html')
for url in url_list:
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74"}
page_text = requests.get(url = url, headers = headers).text
# 1.实例化一个etree对象
tree = etree.HTML(page_text)
# 2.得到一批li对象的列表
li_list = tree.xpath('//div[@class = "list_boby"]/ul/li')
for li in li_list:
try:
ppt_url = li.xpath('./div/span/a/@href')[0]
ppt_name = li.xpath('./div/span/a/text()')[0]
# 中文乱码了
# 指定编码格式,通用处理中文乱码的解决方法
ppt_name = ppt_name.encode('iso-8859-1').decode('gbk')
# print(ppt_url) # 得到单个ppt的详情页
# print(ppt_name)
# 从单个ppt详情页上获取下载链接
ppt_page_data = requests.get(url = ppt_url, headers = headers).text
tree = etree.HTML(ppt_page_data)
download_url = tree.xpath('//div[@class = "donwurl2"]/a/@href')[0]
print(download_url)
# 获得ppt数据
ppt_data = requests.get(url = download_url, headers = headers).content # 二进制存储
save_path = './pptLib/' + ppt_name + ".doc"
with open(save_path, "wb") as f:
f.write(ppt_data)
print(save_path,"was download!!!")
except:
pass
continue
一共321个,9页数据一共应该是324个,但是可能存在三个ppt名称出现/错误,导致不能下载
这里一定要注意,最后获取的ppt数据一定是从下载链接得到的响应数据,我先开始写成了ppt详情页数据,然后将整张网页存储了下来,ppt是一张图片不能编辑,这是不符合需求的。