3. 数据解析
数据解析
- 正则
- bs4
- xpath
- pyquery(自学)
正则解析
单字符:
.:除换行以外所有字符
[]:[aoe] [a-w]匹配集合中任意一个字符
\d:数字 [0-9]
\D:非数字
\w:数字、字母、下划线、中文
\W:非\w
\s:所有的空白字符包,括空格、制表符、换页符等等。等价于[\f\n\r\t\v]
\S:非空白符
- 数量修饰符
*:任意多次 >=0
+:至少1次 >=1
?:可有可无 0次或者1次
{m}:固定m次 hello{3,0}
{m,}:至少m次
{m,n}:m-n次
边界:
$:以某某结尾
^:以某某开头
分组:
(ab)
贪婪模式:.*
非贪婪(惰性)模式:.*?
-
使用正则进行图片爬取的批量解析爬取
-
如何爬取图片数据
- 方式一:基于requests
- 方式二:基于urllib
- urllib模块作用和requests模块一样,都是基于网络请求的模块
- 当requests问世之后就迅速替代了urllib模块
- urllib模块作用和requests模块一样,都是基于网络请求的模块
import requests
from bs4 import BeautifulSoup
import urllib
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
}
# 方式一
img_url = 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=151472226,3497652000&fm=26&gp=0.jpg'
response = requests.get(url=img_url,headers=headers)
# .comtent返回的是二进制行形式的响应数据
img_data = response.content
with open('1.jpg','wb') as fp:
fp.write(img_data)
# 方式二:
img_url = 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=151472226,3497652000&fm=26&gp=0.jpg'
# 可以直接对url发起请求且持久化存储
urllib.request.urlretrieve(img_url,'./2.jpg')
('./2.jpg', <http.client.HTTPMessage at 0x1dbb0bfa400>)
-
上述两种爬取图片的操作不同之处是什么?
- 使用urllib的方式爬取图片无法进行UA伪装,而requests的方式可以进行UA伪装
-
需求:爬取校花网中的图片数据
- www.521609.com
- 操作:需要将一张图片呢的地址解析出来,然后对图片地址发起请求即可。
-
分析浏览器开发者工具中Elements和network这两个选项卡对应得页面源码数据有何不同之处?
- Elements中包含的显示的页面源码数据为当前页所有的数据加载完毕后对应得完整页面源码数据(包含了动态加载数据)
- 结论:如果在进行数据解析的时候。一定是需要对页面布局进行解析,如果分析当前网站没有动态加载的数据就可以直接使用Elements对页面布局进行分析,否则只可以使用network对页面进行分析
深圳山木培训吴玲
import re
import os
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 instatll bs4
- BeautifulSoup对象的实例化:
- BeautifulSoup(fp,'lxml):用来将本地存储的html文档中的数据进行解析
- BeautifulSoup(page_text,'lxml'):用来将互联网上请求到的页面源码数据进行解析
- 标签定位:
- soup.tagName:只可以定位到第一次出现tagName的标签
- soup.find('tagName',attrName="value"):属性定位
- soup.findAll('tagName',attrName="value"):跟find一样作用于属性定位,只不过findAll返回的是列表
- soup.select('选择器'):选择器定位
- 类选择器
- id选择器
- 层及选择器
- 大于号:表示一个层级
- 空格:表示多个层级
- 取数据
- .text:返回的就是该标签下所有的文本内容
- .string:返回的就是该标签直系的文本内容
- 取属性:
- tag['attrName']
from bs4 import BeautifulSoup
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
soup.p
soup.find('div',classmethodss_='song')
soup.find('a',id='feng')
soup.findAll('a',id='feng')
soup.select('.tang')
soup.select('.tang li')
data = soup.select('li a')for content in data: # 取文本 print(content.text) # 取属性 print(content['href'])
main_url = 'https://www.shicimingju.com/book/sanguoyanyi.html'page_text = requests.get(url=main_url,headers=headers).textfp = open('./sanguo.txt','w',encoding='utf-8')soup = BeautifulSoup(page_text,'lxml')# 数据解析:章节标题、详情页url、章节内容# 定位到的所欲符合要求的a标签a_list = soup.select('div.book-mulu > ul > li > a')for a in a_list: title = a.string detail_url = 'https://www.shicimingju.com' + a['href'] # 对详情页发起请求解析出文章内容 page_text_detail = requests.get(url=detail_url,headers=headers).text soup = BeautifulSoup(page_text_detail,'lxml') div_tag = soup.find('div',class_='chapter_content') content = div_tag.text fp.write(title + ':' + content + '\n') print(title,'保存成功!!!')fp.close()
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")]:找到带有class属性的,并且其属性值中含有ng的div标签
- //div[start-with(@class,'ta')]:找到带有class属性,并且其属性值是以ta开头的div标签
- 取文本
- /text():直系文本内容
- //text():所有的文本内容
- 取属性
- /@attrName
from lxml import etreetree = etree.parse('test.html')tree.xpath('/html/head/meta') # 定位metatree.xpath('/html//meta') # 定位metatree.xpath('//meta') # 定位meta
# 定位class为song的div下面所有的p
tree.xpath('//div[@class="song"]/p')
# 定位class为song的div下面的第2个p
tree.xpath('//div[class="song"]/p[2]')
-
使用哪xpath爬取图片名称和图片数据
-
局部数据解析:
- 我们要将定位到的页面中的标签作为带解析的数据。再次使用xpath表达式将带解析数据
- 在局部数据解析的时候,xpath表达式中要使用./的操作。./表示的就是当前的局部数据
# 爬取第一页
import os
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]
# print(title,img_url)
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,'保存成功!!!')
# 爬取多页
import os
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/index.html'
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]
# print(title,img_url)
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).texttree = 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/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()')all_cities
数据解析作业
- url:http://sc.chinaz.com/
- 1.爬取站长素材上前5页的高清图片
- url:http://sc.chinaz.com/tupian/beijingtupian.html
- 难点:图片懒加载
- 以后遇到这种反爬点,记住就往图片链接的src变成src2即可解决
- 2.爬取站长素材上前5页的简历模板
- url:http://sc.chinaz.com/jianli/
- 无难点,就是常规的爬取即可,注意分清哪个是真正的图片url链接
- 爬取思路
- 将每个视频详情页的url进行解析
- 对视频详情页的url进行请求发送
- 在视频详情页源码中进行全局搜索,发现没有找到video标签
- 视频标签是动态加载出来的
- 动态加载的数据方式
- ajax
- js
- 在页面源码中搜索.mp4,定位到了视频的地址(存在于一组js代码)
- 通过正则视频地址解析出来对其发起请求即可。