第四章.数据解析
-
解析 :根据指定的规则对数据进行提取
-
作用 :实现聚焦爬虫
-
聚焦爬虫编码流程:
1.指定url
2.发起请求
3.获取响应数据
4.数据解析
5.持久化存储
4.1数据解析通用原理
-
数据解析作用地点
-
页面源码(一组html标签组成的)
-
-
html标签核心作用
-
用于展示数据
-
-
html是如何展示数据的
-
html所要展示的数据一定是被放置在html标签中,或者是在属性中
-
-
通用原理 : 1.标签定位. 2.取文本或取属性
4.2四种数据解析的方式
4.2.1 正则
-
需求 : 爬取xx百科中糗图数据
链接地址 : https://www.qiushibaike.com
两种爬取方式
#方式一:
import requests
#即将发起请求对应的头信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
img_data = requests.get(url=url,headers=headers).content #content返回的是 byte 类型的数据
with open("./123.jpg",'wb') as f:
f.write(img_data)#方式二:
from urllib import request
url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
request.urlretrieve(url,"./456.jpg")-
方式一 和 方式二 对于图片操作最大的不同之处是什么?
-
方式二不可以使用UA伪装的机制
-
单页数据的爬取
#糗事百科
import re
import os
from urllib import request
dir_name = "./qiutu"
if not os.path.exists(dir_name):
os.mkdir(dir_name)
url = "https://www.qiushibaike.com/pic/"
page_text = requests.get(url=url,headers=headers).text
#数据解析,图片地址
#正则表达式
ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
img_src_list = re.findall(ex,page_text,re.S) # re.S单行匹配
for src in img_src_list:
src = "https:" + src
img_name = src.split("/")[-1]
#图片存储地址
img_path = dir_name + "/" + img_name
#对图片地址单独发起请求获取文件数据
request.urlretrieve(src,img_path)
print(img_name,"下载成功")爬取分页 分析每个页码对应的url是有共性的:https://www.qiushibaike.com/pic/page/3/?s=5222981
#糗事百科
import re
import os
from urllib import request
dir_name = "./qiutumany"
if not os.path.exists(dir_name):
os.mkdir(dir_name)
#指定一个通用模板(不可变)
url = "https://www.qiushibaike.com/pic/page/%d/"
for page in range(1,5):
print("正在打印第{}页的数据".format(page))
#形成某页码完整url
new_url = format(url%page)
page_text = requests.get(url=new_url,headers=headers).text
#数据解析,图片地址
#正则表达式
ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
img_src_list = re.findall(ex,page_text,re.S) # re.S单行匹配
for src in img_src_list:
src = "https:" + src
img_name = src.split("/")[-1]
#图片存储地址
img_path = dir_name + "/" + img_name
#对图片地址单独发起请求获取文件数据
request.urlretrieve(src,img_path) -
模块 :urllib
-
urllib就是一个比较老的网络请求模块,在requests没出现之前,请求发送的都是urllib
4.2.2 bs4
#环境的安装
pip install bs4
pip install lxml
#解析原理
1.实例化一个BeautifulSoup对象,并且将即将被解析的页面源码数据加载到该对象中
2.调用BeautifulSoup对象的相关属性和方法来进行标签定位和数据提取
#如何实例化BeautifulSoup对象
BeautifulSoup(fp,"lxml") #专门用于解析本地存储的html文档中的数据
BeautifulSoup(page_text,"lxml") #专门用于将互联网请求到的页面源码数据进行解析
标签定位
soup.tagName : 定位到第一个tagName标签,返回的是单数
#属性定位:
soup.find(tagName,attrName="value") 返回的是单数
soup.find_all(tagName,attrName="value") 返回的是列表
#选择器定位:
select("选择器") 返回的是列表
标签,类,i, 层级 > :一个层级 空格:多个层级
from bs4 import BeautifulSoup
fp = open("./test.html",'r',encoding='utf-8')
soup = BeautifulSoup(fp,"lxml") #将即将被解析的页面源码加载到对象中
soup.p #<p>百里守约</p>
#find用于属性定位
soup.find("div")
soup.find("div",class_="song")
soup.find_all("div",class_="song")
soup.select(".tang")
soup.select("#feng")
soup.select(".tang > ul > li ")
li_6 = soup.select(".tang > ul > li")[6] #<li><i>度蜜月</i></li>
li_6.i #<i>度蜜月</i>
li_6.i.text #'度蜜月'
li_6.i.string #'度蜜月'
soup.find("a",id="feng")
soup.find("a",id="feng")["href"]
-
soup.find("div")
-
soup.find("div",class_="song")_
-
soup.find_all("div",class_="song")
-
类选择器 :soup.select(".tang")
-
id选择器 :soup.select("#feng")
-
层级选择器 : soup.select(".tang > ul > li ")
-
soup.find("a",id="feng")
-
soup.find("a",id="feng")["href"]
取数据
#取文本
tag.string 标签中直系的文本内容
tag.text 标签中所有文本内容
#取属性
-
需求:爬取三国演义整篇小说内容
-
章节名称
-
章节内容
import requests
#在首页中解析章节名称和每个章节详情页的url
url = "http://www.shicimingju.com/book/sanguoyanyi.html"
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
page_text = requests.get(url=url,headers=headers).text
page_text
soup = BeautifulSoup(page_text,"lxml")
a_list = soup.select(".book-mulu > ul > li > a")
fp = open("sanguo.txt",'w',encoding="utf-8")
for a in a_list:
detail_url = "http://www.shicimingju.com"+ a["href"]
title = a.text
#对章节详情页的url发起请求,解析详情页的章节内容
detail_page_text = requests.get(url=detail_url,headers=headers).text
soup = BeautifulSoup(detail_page_text,"lxml")
chap_content = soup.find("div",class_="chapter_content").text
fp.write(title + ":"+chap_content)
print(title,"爬取成功")
fp.close() -
4.2.3 xpath
#环境的安装: pip install lxml
#xpath解析原理:
1.实例化一个etree类型的对象,且将页面源码加载到该对象中
2.需要调用该对象的xpath方法结合不同形式的xpath表达式进行标签定位和数据提取
#etree对象的实例化
etree.parse(fileName) #用于解析本地存储的html文档中的数据
etree.HTML(page_text) #用于将互联网请求到的页面源码数据进行解析
#xpath返回的永远是一个列表
标签定位
1.xpath表达式中"最"最左边的"/"表示的含义是,当前定位的标签必须从根节点开始进行定位
2.xpath表达式中"最"最左边的"//"表示可以从任意位置进行标签定位
3.xpath表达式中"非"最左边的"/"表示的是一个层级
4.xpath表达式中"非"最左边的"//"表示的是多个层级
#属性定位:
//tageName[@class='value']
#索引定位:
提取数据
#取文本
/text() 取直系的文本
//text() 取所有文本内容
#取属性
tag/@attrName
from lxml import etree
tree = etree.parse("./test.html")
tree.xpath("/html/head/meta") #[<Element meta at 0x84fbb08>] -->绝对路径
tree.xpath("//meta")[0] #[<Element meta at 0x8523748>] -->相对路径,将页面中所有的meta进行定位
tree.xpath("/html//meta")[0]
#属性定位
tree.xpath('//div[@class="song"]')
#索引定位
tree.xpath('//div[@class="tang"]/ul/li[3]') #该索引从 1 开始
#取文本
tree.xpath('//p[1]/text()') #['百里守约', '李清照']
tree.xpath('//div[@class="song"]//text()')
#取属性
tree.xpath('//a[@id="feng"]/@href') #['http://www.haha.com']
-
需求 :爬取xx直聘招聘信息
-
岗位名称
-
公司名称
-
薪资
-
岗位描述
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/76.0.3809.100 Safari/537.36',
'cookie':'lastCity=101010100; __c=1566877560; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1566877561; _uab_collina=156687756118178796315757; __l=l=%2Fwww.zhipin.com%2F&r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DidbSvNzz2fLSl1WXiEmtINauVHUZYSNqejHny725pc5RTwaHqh5uDx1LewpyGmaT%26wd%3D%26eqid%3Dbadf667700040677000000025d64a772&friend_source=0&friend_source=0; __zp_stoken__=91d9QItKEtUk5dMMnDG7lwzq8mBW1g%2FkEsFOHXIi%2FwMd%2FPRRXc%2FPMKjsDYwsfC4b7vAT3FVnTmYBjGp8gW1OeZ5TdA%3D%3D; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1566879753; __a=69160831.1566877560..1566877560.16.1.16.16'
}
url = 'https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&city=101010100&industry=&position='
page_text = requests.get(url,headers=headers).text
#数据解析
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="job-list"]/ul/li')
for li in li_list:
# 需要将li表示的局部页面源码数据中的相关数据进行提取
# 如果xpath表达式被作用在了循环中,表达式要以./或者.//开头
detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/h3/a/@href')[0]
job_title = li.xpath('.//div[@class="info-primary"]/h3/a/div/text()')[0]
salary = li.xpath('.//div[@class="info-primary"]/h3/a/span/text()')[0]
company = li.xpath('.//div[@class="info-company"]/div/h3/a/text()')[0]
#对详情页的url发请求解析出岗位职责
detail_page_text = requests.get(detail_url,headers=headers).text
tree = etree.HTML(detail_page_text)
job_desc = tree.xpath('//div[@class="text"]//text()')
job_desc = ''.join(job_desc)
print(job_title,salary,company,job_desc) -
-
需求 :爬取xx百科的段子内容和作者名称
链接地址 :https://www.qiushibaike.com/text/page/3/
import requests
from lxml import etree
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
url = "https://www.qiushibaike.com/text/page/4/"
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath("//div[@id='content-left']/div")
for div in div_list:
author = div.xpath("./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()")[0]
content = div.xpath("./a[1]/div[@class='content']/span//text()")
content = "".join(content)
print(author,content)#总结:
另一种较为通用的xpath表达式的使用形式: #管道符"|"在xpath表达式中的应用
#用处 : 用于解析不规则布局的页面数据
中文乱码处理问题
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/76.0.3809.100 Safari/537.36',
}
#指定一个通用的url模板
url = 'http://pic.netbian.com/4kmeishi/index_%d.html'
for page in range(1,3):
if page == 1:
new_url = 'http://pic.netbian.com/4kmeishi/'
else:
new_url = format(url%page)
response = requests.get(new_url,headers=headers)
#response.encoding = 'utf-8'
page_text = response.text
tree = etree.HTML(page_text)
li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
for li in li_list:
img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
img_name = li.xpath('./a/b/text()')[0]
img_name = img_name.encode('iso-8859-1').decode('gbk')
ConnectionPool错误
#原因一.在短时间内向网站发起了一个高频的请求
解决方式 :使用代理
#原因二.连接池(http)中的资源被耗尽
立即将请求断开 Connection:close
xpath和bs4最明显的区别
#解决除携带标签的局部内容
bs4相关标签定位的方法或属性 返回值就是携带标签的内容
反爬机制之三 :图片懒加载
在img标签应用了伪属性