数据解析
- 正则
- bs4
- xpath(常用)
正则解析
单字符:
. : 除换行以外所有字符
[] :[aoe] [a-w] 匹配集合中任意一个字符
d :数字 [0-9]
D : 非数字
w :数字、字母、下划线、中文
W : 非w
s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ f
v]。
S : 非空白
数量修饰:
* : 任意多次 >=0
+ : 至少1次 >=1
? : 可有可无 0次或者1次
{m} :固定m次 hello{3,}
{m,} :至少m次
{m,n} :m-n次
边界:
$ : 以某某结尾
^ : 以某某开头
分组:
(ab)
贪婪模式: .*
非贪婪(惰性)模式: .*?
如何爬取图片数据?
- 方式1:基于requests
- 方式2:基于urllib
- urllib模块作用和requests模块一样,都是基于网络请求的模块。
- 当requests问世后就迅速的替代了urllib模块
- urllib模块作用和requests模块一样,都是基于网络请求的模块。
import requests
import urllib
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
}
#方式一:用requests模块
img_url = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1312059974,1893880587&fm=11&gp=0.jpg'
response = requests.get(url=img_url,headers=headers)
img_data = response.content #content返回的是二进制形式的响应数据,图片视频都需要二进制数据写入
with open('1.jpg','wb') as fp:
fp.write(img_data)
#方式二:用urllib模块
img_url = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1312059974,1893880587&fm=11&gp=0.jpg'
#可以直接对url发起请求且进行持久化存储
urllib.request.urlretrieve(img_url,'./2.jpg') 路径可选
重点:
上述两种爬取图片操作不同之处在于使用urllib模块无法UA伪装,而requests可以,大部分网站都需要UA伪装,所以常用requests模块
例子1:爬取校花网的图片
需求:爬取校花网中的图片数据
-
操作:需要将每一张图片的地址解析出来,然后对图片地址发起请求即可。
-
分析浏览器开发者工具中Elements和network这两个选项卡对应的页面源码数据有何不同之处?
- Elements中包含的显示的页面源码数据为当前页面所有的数据加载完毕后对应的完整的页面源码数据(包含了动态加载数据)
- network中显示的页面源码数据仅仅为某一个单独的请求对应的响应数据(不包含动态加载数据)
- 结论:如果在进行数据解析的时候,一定是需要对页面布局进行分析,如果当前网站没有动态加载的数据就可以直接使用Elements对页面布局进行分析,否则只可以使用network对页面数据进行分析。
dirName = 'ImgLibs' if not os.path.exists(dirName): os.mkdir(dirName) #1.捕获到当前首页的页面源码数据 url = 'http://www.521609.com/qingchunmeinv/' page_text = requests.get(url=url,headers=headers).text #2.从当前获取的页面源码数据中解析出图片地址 ex = '<li>.*?<img src="(.*?)" width=.*?</li>' img_src_list = re.findall(ex,page_text,re.S) for src in img_src_list: src = 'http://www.521609.com'+src imgPath = dirName+'/'+src.split('/')[-1] urllib.request.urlretrieve(src,imgPath) print(imgPath,'下载成功!!!')
数据解析的作用:
- 用来实现聚焦爬虫,就是获取局部数据
网页中显示的数据都储存在哪里
- 都是储存在html标签中或者标签的属性中
数据解析的通用原理是什么?
- 指定标签的定位
- 获取标签中存储的数据或标签属性中的数据
bs4
- bs4解析原理
- 实例化一个BeautifulSoup的对象,且将待解析的页面源码数据加载到该对象中
- 调用BeautifulSoup对象中相关方法或者属性进行标签定位和文本数据的提取
- 环境安装
- pip install lxml #解析器
- pip install bs4
- BeautifulSoup对象实例化:
- BeautifulSoup(fp,'lxml'):用来将本地存储的html文档中的数据进行解析
- BeautifulSoup(page_text,’lxml‘):用来将互联网上请求到的页面源码数据进行解析
- 标签定位:
- soup.tagName:只可以定位到第一次出现的tagName标签
- soup.find('tagName',attrName='value'):属性定位
- soup.findAll:跟find一样用作属性定位,只不过findAll返回的是列表
- soup.select('选择器'):选择器定位
- 类选择器
- id选择器
- 层级选择
- 大于号:表示一个层级
- 空格:表示多个层级
- 取数据
- .text:返回的是该标签下所有的文本内容
- .string:返回的是该标签直系的文本内容
- 取属性:
- tag['attrName']
from bs4 import BeautifulSoup
#解析本地的html页面数据
fp = open("./test.html","r")
soup = BeautifulSoup(fp,"lxml")
#定位标签
soup.find("div",class="xxxx")
soup.find("a",id="zhou") 这个只能找到一个
soup.findAll("a",id="zhou") findAll返回的是列表
soup.select('.tang')
soup.select('.tang li a')
#取属性
a_tag = soup.find('a',id='feng')
a_tag['href'] #a标签下的属性
#取数据
div_tag = soup.find('div',class_='song')
div_tag.text #div标签下的文本内容
例如获取的数据为:
李清照
王安石
苏轼
柳宗元
this is span
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱
总为浮云能蔽日,长安不见使人愁
'
例子2,爬取三国全篇内容:http://www.shicimingju.com/book/sanguoyanyi.html
import re
from bs4 import BeautifulSoup
url = "http://www.shicimingju.com/book/sanguoyanyi.html"
fp = open('./sanguo.txt','w',encoding='utf-8')
response = requests.get(url=url,headers=headers).text
#数据解析:章节标题,详情页url,章节内容
soup= BeautifulSoup(response,"lxml")
#定位到的所有的符合要求的a标签
a_soup =soup.select(".book-mulu ul li a")
for i in a_soup:
# print(i["href"])
#对详情页发起请求解析出章节内容
response=requests.get(url = "http://www.shicimingju.com"+i["href"],headers=headers).text
soup= BeautifulSoup(response,"lxml")
#这里注意。select查找出来的是bs4对象列表。如果你要text需要【0】.text或者for循环出每一个bs4对象在提取属性或者文本
title = soup.select("#main_left h1")[0].text
#find
content = soup.find('div',class_="chapter_content").text
#或者content = soup.select(".chapter_content")[0].text
fp.write(title+':'+content+'
')
print(title,'保存成功!!!')
xpath
环境安装:
- pip install lxml
解析原理:html标签是以树状的形式进行展示
- 1.实例化一个etree对象,将待解析的页面源码数据加载到该对象中
- 2.调用etree对象的xpath方法结合着不同的xpath表达式实现标签的定位和数据提取
实例化etree对象
- etree.parse("filename") 将本地html文件加载到对象中进行解析
- etree.HTML(page_text)网站获取的页面数据加载到该对象中
标签定位
-
最左侧的:/ 如果xpath表达式最左侧是以/开头则表示该xpath表达式一定要从根标签开始定位指定标签(忽略)
-
非最左侧的:/ 表示一个层级
-
非左侧的:// 表示多个层级
-
最左侧的:// xpath表达式可以从任意位置进行标签定位
-
属性定位 :“tagName[@attrName="value"]”
-
索引定位:tag[index]:索引是从1开始的
-
模糊匹配:
- // div[contains(@class,"ng")] 包含
- //div[starts-with(@class,"ta")] 以什么开头
-
取文本
- /text() :直系文本内容
- //text():所有文本内容
-
取属性
- /@atrrname
根据本地文件接续 from lxml import etree tree = etree.parse('test.html') tree.xpath('/html/head/meta') #定位meta tree.xpath('/html//meta') #定位meta tree.xpath('//meta') #定位meta
定位例子:
tree.xpath('//div[@class="song"]/p')
tree.xpath('//div[@class="song"]/p[1]//text()')
tree.xpath('//a[@id="feng"]/@href')
使用xpath爬取图片名称和图片数据
url = http://pic.netbian.com/4kmeinv/
局部数据解析:
- 我们要将定位到页面中的标签作为待解析的数据,再次使用xpath表达式解析待解析的数据
- 在局部数据解析的时候,xpath表达式中要使用./的操作,./表示的就是当前的局部数据(xpath的调用者)。
#爬取第一页的
dirName = 'GirlsLib'
if not os.path.exists(dirName):
os.mkdir(dirName)
url = 'http://pic.netbian.com/4kmeinv/'
response = requests.get(url=url,headers=headers)
response.encoding = 'gbk'
page_text = response.text
#图片名称+图片数据
tree = etree.HTML(page_text)
#存储的是定位到的指定的li标签
li_list = tree.xpath('//div[@class="slist"]/ul/li')
for li in li_list:
# print(type(li)) #li的数据类型和tree的数据类型一样,li也可以调用xpath方法
title = li.xpath('./a/img/@alt')[0]+'.jpg'#进行局部数据解析
img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
img_data = requests.get(url=img_src,headers=headers).content
imgPath = dirName +'/'+title
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(title,'保存成功!!!')
爬取多页的,观察分析下一页的url跟首页有什么不同。定制通用模板
#爬取多页
dirName = 'GirlsLib'
if not os.path.exists(dirName):
os.mkdir(dirName)
#定义一个通用的url模板:不可变
url = 'http://pic.netbian.com/4kmeinv/index_%d.html'
for page in range(1,6):
if page == 1:
new_url = 'http://pic.netbian.com/4kmeinv/'
else:
new_url = format(url%page)
response = requests.get(url=new_url,headers=headers)
response.encoding = 'gbk'
page_text = response.text
#图片名称+图片数据
tree = etree.HTML(page_text)
#存储的是定位到的指定的li标签
li_list = tree.xpath('//div[@class="slist"]/ul/li')
for li in li_list:
# print(type(li)) #li的数据类型和tree的数据类型一样,li也可以调用xpath方法
title = li.xpath('./a/img/@alt')[0]+'.jpg'#进行局部数据解析
img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
img_data = requests.get(url=img_src,headers=headers).content
imgPath = dirName +'/'+title
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(title,'保存成功!!!')
需求:要求解析出携带html标签的局部数据?(二者不同之处)
-
那只能选择bs4,bs4在实现标签定位的时候返回的直接就是定位到标签对应的字符串数据
-
xpath表达式如何更加具有通用性?
- 在xpath表达式中使用管道符分割的作用,可以表示管道符左右两侧的子xpath表达式同时生效或者一个生效。
#将https://www.aqistudy.cn/historydata/所有的城市名称解析出来 url = 'https://www.aqistudy.cn/historydata/' 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()') tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()') #但是用的很少