摘要
本文内容转自网络,个人学习记录使用,请勿传播
数据解析
何为数据解析
- 概念:就是将爬取到数据中局部的指定的数据进行提取
- 作用:实现聚焦爬虫
- 数据解析通用原理:
- html是用来展示数据,html中展示的数据正是我们要爬取或者采集的数据
- html所展示的数据/想要爬取的数据只会存在于标签之间或者标签的属性中
- 数据解析的通用原理:
- 标签定位
- 提取标签中存储的数据
- 聚焦爬虫编码流程
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
数据解析的主流策略
如何爬取多媒体资源(图片,音频,视频,动图)
方式1:编写麻烦,但是可以实现UA伪装
1
2
3
4
5
6
7
8
9
10
11
12
13import requests
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.36X-Requested-With: XMLHttpRequest'
}
#获取图片地址:通常为img标签的src属性值
img_src = 'http://img.itlun.cn/uploads/allimg/180506/1-1P5061TS6-lp.jpg'
#对图片发起请求
response = requests.get(url=img_src,headers=headers)
#获取图片数据:content返回的是二进制形式的响应数据
img_data = response.content
#持久化存储
with open('./123.jpg','wb') as fp:
fp.write(img_data)
方式2:编写简单,但是无法实现UA伪装
1
2
3
4
5from urllib import request
img_src = 'http://img.itlun.cn/uploads/allimg/180506/1-1P5061TS6-lp.jpg'
#urlretrieve可以直接对图片发起请求,且将请求到的图片数据进行持久化存储
request.urlretrieve(img_src,'./456.jpg')
正则(基本掌握)
案例应用:批量爬取图片
大致思路:
- 将页面中每一张图片的图片地址解析出来
- 对图片地址发起请求获取图片数据,然后进行持久化存储
注意:
- 开发者工具中Elements选项卡中显示的页面源码数据(会包含动态加载数据):
- 是通过所有数据包请求到的数据渲染完毕后的完整数据
- 抓包工具Network中response显示的页面源码数据:(不包含动态加载数据)
- 其实就是通过requests模块对该数据包的url发起请求获取的响应数据
- 单独该数据包请求到的响应数据
- 开发者工具中Elements选项卡中显示的页面源码数据(会包含动态加载数据):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30from urllib import request
import requests
import re
main_url = 'http://md.itlun.cn/a/nhtp/'
response = requests.get(url=main_url)
#处理页面中的中文乱码
response.encoding = 'gbk'
#获取了页面源码数据
page_text = response.text
#数据解析:解析图片的地址
# ex = '<li>.*?<img.*?src="(.*?)" style.*?</li>'
#re.S用来处理回车
# img_src_list = re.findall(ex,page_text,re.S)
#注意:如果确认正则没有写错,则取关注下正则作用到的页面源码是否出现问题
#极有可能页面源码数据出现了动态加载的情况
#在抓包工具中,查看了数据包的响应数据,发现img,li标签都是大写,而我们写的正则匹配的是小写标签,因此匹配失败
# ex = '<LI>.*?<IMG.*?src="(.*?)" style.*?</LI>'
# img_src_list = re.findall(ex,page_text,re.S)
#问题:提取到的图片地址都是一样的。如何解决?继续查看抓包工具的源码
#发现:真正的图片地址是有js动态加载出来的
ex = '<script.*?src = "(.*?)"; </script>'
img_src_list = re.findall(ex,page_text,re.S)
#发现解析出的图片地址,是不完整的,缺少http:
for img_src in img_src_list:
img_src = 'http:'+img_src
# print(img_src)
img_name = img_src.split('/')[-1]
request.urlretrieve(img_src,img_name)
print(img_name,'下载成功!')
bs4(重点)
环境安装:pip install bs4
bs4数据解析的流程
- 1.实例化一个BeautifulSoup的对象,然后把即将被解析的页面源码数据加载到该对象中
- BeautifulSoup(fp,’lxml’):fp表示本地的一个文件,该种方式是将本地存储的html文件进行数据解析
- BeautifulSoup(page_text,’lxml’):page_text是网络请求到的页面源码数据,该种方式是直接将网络请求到的页面源码数据进行数据解析
- 2.调用BeautifulSoup对象中相关的属性和方法实现标签定位和数据提取
- 1.实例化一个BeautifulSoup的对象,然后把即将被解析的页面源码数据加载到该对象中
具体解析的操作:
在当前目录下新建一个test.html文件,然后将下述内容拷贝到该文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34<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>
有了test.html文件后,在练习如下操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39from bs4 import BeautifulSoup
#fp就表示本地存储的一个html文件
fp = open('./test.html','r',encoding='utf-8')
#解析本地存储的html文件中的内容
#实例化BeautifulSoup对象,然后把即将被解析的页面源码数据加载到了该对象中
soup = BeautifulSoup(fp,'lxml') #参数2,lxml是固定形式,表示指定的解析器
#标签定位
#方式1:soup.tagName,只会定位到符合条件的第一个标签
tag1 = soup.title #定位到了title标签
tag2 = soup.div
#方式2:属性定位,find函数,findall函数
#find('tagName',attrName='attrValue'):find只会定位到满足要的第一个标签
tag3 = soup.find('div',class_='song')#定位class属性值为song的div标签
tag4 = soup.find('a',id='feng')#定位id属性值为feng的a标签
#findAll('tagName',attrName='attrValue'):可以定位到满足要求的所有标签
tag5 = soup.findAll('div',class_='song')
#方式3:选择器定位:soup.select('选择器')
#id选择器:#feng ----id为feng
#class选择器:.song ----class为song
#层级选择器:大于号表示一个层级,空格表示多个层级
tag6 = soup.select('#feng') #定位到所有id属性值为feng的标签
tag7 = soup.select('.song')#定位到所有class属性值为song的标签
tag8 = soup.select('.tang > ul > li') #定位到了class为tang下面的ul下面所有的li标签
tag9 = soup.select('.tang li')
#提取标签中的内容
#1.提取标签中间的内容:
#tag.string:只可以提取到标签中直系的文本内容
#tag.text:可以提取到标签中所有的文本内容
# p_tag = soup.p
# print(p_tag.string)
# print(p_tag.text)
# div_tag = soup.find('div',class_='song')
# print(div_tag.text)
#2.提取标签的属性值
#tag['attrName']
img_tag = soup.img
print(img_tag['src']) #提取img标签的src的属性值
案例应用:小说批量爬取
需求:将每一个章节的标题和内容进行爬取然后存储到一个文件中
- 步骤:
- 1.请求主页的页面源码数据
- 2.数据解析:
- 章节标题
- 章节详情页的链接
- 3.解析章节详细内容
- 4.将解析的章节标题和内容进行存储
- 步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29from bs4 import BeautifulSoup
import requests
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'
}
#首页地址
main_url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
#发起请求,获取了主页页面源码
response = requests.get(url=main_url,headers=headers)
response.encoding = 'utf-8'
page_text = response.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:
title = a.string #章节标题
detail_url = 'https://www.shicimingju.com'+a['href'] #详情页地址
#请求详情页的页面源码数据
response = requests.get(url=detail_url,headers=headers)
response.encoding = 'utf-8'
detail_page_text = response.text
#解析:解析章节内容
d_soup = BeautifulSoup(detail_page_text,'lxml')
div_tag = d_soup.find('div',class_='chapter_content')
content = div_tag.text #章节内容
fp.write(title+':'+content+'\n')
print(title,'爬取保存成功!')
fp.close()
案例应用:代理批量爬取
需求:将前5页的所有id和port解析且存储到文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#只爬取了第一页的内容
from bs4 import BeautifulSoup
import requests
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'
}
url = 'https://www.kuaidaili.com/free'
page_text = requests.get(url=url,headers=headers).text
soup = BeautifulSoup(page_text,'lxml')
trs = soup.select('tbody > tr')
for tr in trs:
t1 = tr.findAll('td')[0]
t2 = tr.findAll('td')[1]
ip = t1.string
port = t2.string
print(ip,port)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#爬取多页内容
from bs4 import BeautifulSoup
import requests
import time
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'
}
#爬取多页
#1.创建一个通用的url(可以变换成任意页码的url)
url = 'https://www.kuaidaili.com/free/inha/%d/'
#2.通过循环以此生成不同页码的url
for page in range(1,11):
print('----------正在爬取第%d页的数据!-----------'%page)
#format用来格式化字符串的(不可以修改url这个字符串本身)
new_url = format(url%page)
#循环发送每一页的请求
#注意:get方法是一个阻塞方法!
page_text = requests.get(url=new_url,headers=headers).text
time.sleep(1)
soup = BeautifulSoup(page_text,'lxml')
trs = soup.select('tbody > tr')
for tr in trs:
t1 = tr.findAll('td')[0]
t2 = tr.findAll('td')[1]
ip = t1.string
port = t2.string
print(ip,port)
xpath(重点)
环境安装:pip install lxml
xpath解析的编码流程
- 1.创建一个etree类型的对象,然后把即将被解析的页面源码数据加载到该对象中
- 2.调用etree对象的xpath方法结合着不同形式的xpath表达式,进行标签定位和数据提取
xpath表达式如何理解?
html中的标签是遵从树状结构的。
切记:xpath表达式中不可以出现tbody标签,如果有直接将其删除跨过即可!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32from lxml import etree #如果这种方式报错,使用下面方式导入etree
# from lxml.html import etree
fp = open('test.html','r')
#1.将本地存储好的文件中的数据加载到etree对象中进行数据解析
tree = etree.parse(fp)
#2.调用etree对象的xpath方法结合不同形式的xpath表达式进行标签定位和数据提取
#xpath返回的一定是列表,列表中存储的是定位到的标签对象
# title_tag = tree.xpath('/html/head/title')
# title_tag = tree.xpath('/html//title')
# title_tag = tree.xpath('//head/title')
# title_tag = tree.xpath('//title') #推荐
#最左侧为/:表示必须从树的根标签(html标签)开始进行定位
#最左侧为//:可以从任意位置进行标签的相对位置定位
#非最左侧的/:表示一个层级
#非最左侧的//:表示多个层级
# tag = tree.xpath('//div') #定位所有的div标签
#属性定位:根据标签的属性定位标签
#//tagName[@attrName="attrValue"]
# tag = tree.xpath('//div[@class="song"]')#定位class属性值为song的div标签
# tag = tree.xpath('//a[@id="feng"]')
# tag = tree.xpath('//div[@class="tang"]/ul/li/a[@id="feng"]')
#索引定位:索引是从1开始的
# tag = tree.xpath('//div[@class="tang"]/ul/li[3]')#定位到第三个li标签
#获取定位到标签中的文本内容
# /text()获取标签中直系的文本内容:返回的列表中只会有一个列表元素
# //text()获取标签中所有的文本内容:通常返回列表中存在多个元素
# tag = tree.xpath('//div[@class="song"]/p[3]/text()')
# tag = tree.xpath('//div[@class="song"]//text()')
#获取定位到标签中的属性值://tag/@attrName
tag = tree.xpath('//img/@src')
print(tag)
案例应用:
http://pic.netbian.com/4kmeinv/
将爬取到的图片存储到指定的文件夹中
爬取第一页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33from lxml import etree
import requests
import os
#新建一个文件夹
dirName = 'girls'
if not os.path.exists(dirName):#如果文件夹不存在,则新建,否则不新建
os.mkdir(dirName)
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'
}
url = 'https://pic.netbian.com/4kmeinv/index.html'
response = requests.get(url=url,headers=headers)
response.encoding = 'gbk'
page_text = response.text
#数据解析:图片地址+图片名称
tree = etree.HTML(page_text)#HTML()专门用来解析网络请求到的页面源码数据
#该列表中存储的是每一个li标签
li_list = tree.xpath('//div[@class="slist"]/ul/li')
for li in li_list:
#局部解析:将li标签中指定的内容解析出来
img_title = li.xpath('./a/b/text()')[0]+'.jpg'# 左侧./表示xpath的调用者对应的标签
img_src = 'https://pic.netbian.com'+li.xpath('./a/img/@src')[0]
#对图片发起请求,存储图片数据
img_data = requests.get(url=img_src,headers=headers).content
# girls/123.jpg
img_path = dirName + '/' + img_title
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_title,'下载保存成功!')爬取多页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39from lxml import etree
import requests
import os
#新建一个文件夹
dirName = 'girls'
if not os.path.exists(dirName):#如果文件夹不存在,则新建,否则不新建
os.mkdir(dirName)
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'
}
#创建一个通用的url:除了第一页其他页码的通用url
url = 'https://pic.netbian.com/4kmeinv/index_%d.html'
for page in range(1,6):
if page == 1:
new_url = 'https://pic.netbian.com/4kmeinv/index.html'
else:
new_url = format(url%page)
print('----------正在请求下载第%d页的图片数据----------'%page)
response = requests.get(url=new_url,headers=headers)
response.encoding = 'gbk'
page_text = response.text
#数据解析:图片地址+图片名称
tree = etree.HTML(page_text)#HTML()专门用来解析网络请求到的页面源码数据
#该列表中存储的是每一个li标签
li_list = tree.xpath('//div[@class="slist"]/ul/li')
for li in li_list:
#局部解析:将li标签中指定的内容解析出来
img_title = li.xpath('./a/b/text()')[0]+'.jpg'# 左侧./表示xpath的调用者对应的标签
img_src = 'https://pic.netbian.com'+li.xpath('./a/img/@src')[0]
#对图片发起请求,存储图片数据
img_data = requests.get(url=img_src,headers=headers).content
# girls/123.jpg
img_path = dirName + '/' + img_title
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_title,'下载保存成功!')
https://www.aqistudy.cn/historydata/
爬取热门城市和全部城市的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#第一种写法
from lxml import etree
import requests
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'
}
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
#解析热门城市
hot_li_list = tree.xpath('//div[@class="bottom"]/ul/li')
for li in hot_li_list:
city_name = li.xpath('./a/text()')[0]
print(city_name)
#解析全部城市
all_li_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
for li in all_li_list:
city_name = li.xpath('./a/text()')[0]
print(city_name)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#第二种写法
from lxml import etree
import requests
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'
}
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
#解析热门城市+所有城市
#此处xpath表达式的管道符(|)可以是的xpath表达式更加具有通用性
li_list = tree.xpath('//div[@class="bottom"]/ul/li | //div[@class="bottom"]/ul/div[2]/li')
for li in li_list:
city_name = li.xpath('./a/text()')[0]
print(city_name)
https://sc.chinaz.com/jianli/free.html
下载当前页所有的建立模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27from lxml import etree
import requests
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'
}
url = 'https://sc.chinaz.com/jianli/free.html'
response = requests.get(url=url,headers=headers)
response.encoding = 'utf-8'
page_text = response.text
#数据解析:简历名称+详情页的url
tree = etree.HTML(page_text)
div_list = tree.xpath('//*[@id="container"]/div')
for div in div_list:
title = div.xpath('./p/a/text()')[0]+'.rar'
detail_url = 'https:'+div.xpath('./p/a/@href')[0]
# print(title,detail_url)
#对详情页的url发起请求
detail_page_text = requests.get(url=detail_url,headers=headers).text
#数据解析:下载地址
tree = etree.HTML(detail_page_text)
download_url = tree.xpath('//*[@id="down"]/div[2]/ul/li[1]/a/@href')[0]
#在下载请求建立模板
data = requests.get(url=download_url,headers=headers).content
with open(title,'wb') as fp:
fp.write(data)
print(title,'保存下载成功!')
http://blog.sina.com.cn/lm/pic/
爬取精选图片的缩略图和标题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from lxml import etree
import requests
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'
}
url = 'http://blog.sina.com.cn/lm/pic/'
response = requests.get(url=url,headers=headers)
response.encoding = 'utf-8'
page_text = response.text
#数据解析:图片地址+标题
tree = etree.HTML(page_text)
#直接删除被隐藏的div,将其在xpath表达式中跨过即可
li_list = tree.xpath('//*[@id="pic_wrap0"]//ul[1]/li')
for li in li_list:
img_src = li.xpath('./a/div/img/@src')[0]
title = li.xpath('./a/div/img/@title')[0]
print(img_src,title)
下次直播内容:
- cookie
- 验证码
- 代理
- 模拟登录