Python - 爬虫(数据解析)
-
爬虫的分类
-
通用
-
聚焦
-
增量式
-
-
requests模块的作用
- 模拟浏览器发起请求
-
参数动态化
- 字典,键值就是请求携带的请求参数,需要作用到data/params
-
请求头伪装
- User-Agent 请求载体的身份标识
- UA监测(反爬机制)
-
动态加载数据
- ajax,js(另外的一个请求帮我们请求到的一个数据,并不是当前url地址栏帮我们请求到的数据)
- 基于抓包工具做做局部搜索,能搜索到不是动态加载,如果是基于抓包工具做全局搜索,目的是定位动态加载数据包,提取url和请求所携带的请求参数
数据解析
-
re
-
bs4
-
xpath
-
什么是数据解析,数据解析可以干什么?
- 概念:就是将一组数据中的局部数据进行提取
- 作用:是用来实现聚焦爬虫
- 概念:就是将一组数据中的局部数据进行提取
正则数据解析
数据解析的通用原理
问题:html(展示数据可以存储在哪里?) xml(存贮数据)
- 标签之中
- 属性中
-1.标签定位
-2.取文办或者取属性
单个图片(二进制)数据爬取
content #返回的是二进制类型的响应数据
#方法一
import requests
url = 'http://pic.netbian.com/uploads/allimg/190824/212516-15666531161ade.jpg'
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
response = requests.get(url=url,headers=headers).content #返回的是二进制类型的响应数据
with open('1.jpg','wb')as fp:
fp.write(response)
urllib.request.urlretrieve
方法二:urllib就是一个低版本的requests
import urllib
url = 'http://pic.netbian.com/uploads/allimg/190824/212516-15666531161ade.jpg'
response = urllib.request.urlretrieve(url=url,filename = './2.jpg')
print(response)
方法一与方法二区别
请求头的问题,方法一可以进行UA伪装,方法二不行
多个图片(二进制)数据爬取
- 抓包工具中response中显示的野蛮源码和开发者工具的ELement选项卡显示的页面源码的区别是什么?
- ELement(显示的页面源码内容是当前网页加载完毕后对应的所有数据包含动态加载数据)
- response(显示的内容仅仅是当前一个请求请求到的数据(不包含动态加载数据)
re(正则)
段子网爬取
bs4
- 环境的安装
- pip install bs4
- pip install lxml
- 解析原理
- 实例化一个BeautifuSoup的一个对象.把即将被解析的页面源码内容加载到该对象中
- 调用BeautifuSoup对象中相关的方法和属性进行标签定位和本文数据的提取
- BeautifuSoup对象是实例化的方式
- BeautifuSoup(fp,'lxml'):将本地的文件内容加载到该对象中进行数据解析
- BeautifuSoup(page_text,'lxml'):讲互联网请求到的数据加载到该对象中进行数据解析
bs4相关解析操作
测试页面数据
<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>
-
标签定位:返回的值一定是定位到的标签
- soup.tagName:定位到第一个出现的tagName标签
- 属性定位:soup.find('tagName',attrName='value'),返回的是单数
- find_all('tagName',attrName='value')返回的是复数(列表)
- 选择器定位:select('选择器'),返回的也是一个列表
- 层级选择器:
- 大于号保湿一个层级
- 空格表示多个层级
- 层级选择器:
-
取文本,取属性
- string:只可以将标签 中的直系的文本取出
- text:可以将标签中所有的内容取出
-
取属性
- tag['attrName']
- 案例-小说网站爬取及解析bs4
#需求:http://www.shicimingju.com/book/sanguoyanyi.html进行全篇小说内容的爬取
#分析:
#首页:解析出章节的名称+详情页的url
#详情页:解析章节内容
#爬取到首页的页面数据
url = "http://www.shicimingju.com/book/sanguoyanyi.html"
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
fp = open('./sanguo.txt','a',encoding='utf-8')
page_text = requests.get(url=url,headers=headers).text
# print(page_text) 解析章节名称 + 详情页的url
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('.book-mulu > ul > li > a')
for a in a_list:
title = a.string #章节标题
detail_url = 'http://www.shicimingju.com'+a['href']
#爬取详情页的页面源码内容
detail_page_text = requests.get(url=detail_url,headers=headers).text
#解析章节内容
detail_soup = BeautifulSoup(detail_page_text,'lxml')
div_tag = detail_soup.find('div',class_="chapter_content")
content = div_tag.text #章节内容
#写入
fp.write(title+':'+content+'\n')
print(title,'下载完成!!!')
fp.close()
xpth 解析
测试页面数据
<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>
-
环境的安装
- pip install lxml
-
解析原理(流程)
- 实例化一个etree的对象,将解析的数据加载到该对象中
- 需要调用etree对象中的xpth方法结合着不同的xpth表达式进行标签定位和文本数据提取
-
etree对象的实例化
- etree.parse('filePath'):将本地数据加载到etree中
- etree.HTMl(page_text):将互联网上的数据加载到该对象中
-
标签定位
-
最左侧的/:xpath表达式一定要从根标签开始进行定位
-
非最左侧的/:表示一个层级
-
最左侧的//:从任意位置进行标签定位(常用)
-
非最左侧//:表示多个层级
-
//tagName:定位到所有的tagName标签
-
-
属性定位
- //tagName[@attrName='vlaue']
- 索引定位://tagName[index],index索引是从1开始
from lxml import etree
tree = etree.parse('./test.html')
tree.xpath('/html/head/meta')
tree.xpath('/html//meta')
tree.xpath('//meta')
tree.xpath('//div')
tree.xpath('//div[@class='tang']')
tree.xpath('//li[3]')
tree.xpath('//a[@id=feng]/text()')[0]
tree.xpath('//div[2]//text()')
tree.xpath('//a[@id="feng"]/@herf')
- 取文本
- /text():取直系的文本内容.返回列表只有一个元素
- //text():取所有的文本内容.返回列表会有多个列表元素
- 取属性
- /@attrName
案例
from lxml import etree
import requests
###案例
#需求:爬取解析虎牙直播的房间名称,热度,详情页的url
url = 'https://www.huya.com/g/lol'
headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
page_text = requests.get(url=url,headers=headers).text
#数据解析
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="box-bd"]/ul/li')
for li in li_list:
#实现局部解析 :将局部标签下制定的内容进行解析
#局部解析xpath表达式中的最左侧的./表示的就是xpath方法调用者对应的标签
title = li.xpath('./a[2]/text()')[0]
hot = li.xpath('./span/span[2]/i[2]/text()')[0]
detail_url = li.xpath('./a[1]/@href')[0]
print(title,hot,detail_url)
案例
案例 - 爬取图片 图片地址 ,名字
url = 'http://pic.netbian.com/4kmeinv/index_%d.html'
for page in range(10,15):
new_url = format(url%page) #只可以表示非第一页的页面链接
if page == 1:
new_url = 'http://pic.netbian.com/4kmeinv/'
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
#url模板
page_text = requests.get(new_url,headers=headers).text
tree = etree.HTML(page_text)
li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
for li in li_list:
img_name = li.xpath("./a/img/@alt")[0]+'.jpg'
img_name = img_name.encode('iso-8859-1').decode('gbk') #乱码处理
img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
print(img_name,img_src)
案例 - xpath表达式另一种写法
- xpath表达式中管道符的引用
- 目的:使得xpath具有更强的通用性
#解析出所有城市的名称
url = "https://www.aqistudy.cn/historydata/"
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
# hot_cities = tree.xpath('//div[@class="bottom"]/ul/li/a/text()')
# all_cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()')
#整合成一个表达式
all_cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text() | //div[@class="bottom"]/ul/li/a/text()')
print(all_cities)