03 数据解析
03 数据解析
引入
- 回顾requests模块实现数据爬取的流程
- 指定url
- 发起请求
- 获取响应数据
- 持久化存储
- 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指定数据解析。因为大多数情况下的需求,我们都会指定去使用聚焦爬虫,也就是爬取页面中指定部分的数据值,而不是整个页面的数据。因此,本次课程中会给大家详细介绍讲解三种聚焦爬虫中的数据解析方式。至此,我们的数据爬取的流程可以修改为:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
python如何实现数据解析
- 正则表达式
- xpath解析
- bs4解析
数据解析原理概述:
解析的局部文本内容都会在 标签之间或者标签对应的属性中进行存储
1.进行制定标签的定位
2.标签或者标签对应属性中存储数据的提取(解析)
数据解析---正则表达式
常用正则表达式回顾
案例:
import requests
import re
import os
if __name__ == '__main__':
url = 'http://jandan.net/ooxx'
headers = {
#定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}
#创建文件
if not os.path.exists('./pics'):
os.mkdir('./pics')
#使用通用爬虫爬取整张页面
page_text=requests.get(url=url,headers=headers).text
#使用聚焦爬虫
ex = '<img src="(.*?)".*?>'
img_src_list = re.findall(ex,page_text,re.S)
for img_src in img_src_list:
img_src_url = 'https:'+img_src
pic_bytes=requests.get(url=img_src_url,headers=headers).content
pic_name = img_src_url.split('/')[-1]
pic_path = './pics/'+ pic_name
with open(pic_path,'wb') as f:
f.write(pic_bytes)
print(pic_name,'ok!')
数据解析---bs4解析
环境安装
- 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等
- windows
(1)打开文件资源管理器(文件夹地址栏中)
(2)地址栏上面输入 %appdata%
(3)在这里面新建一个文件夹 pip
(4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可
[global]
timeout = 6000
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = mirrors.aliyun.com
- linux
(1)cd ~
(2)mkdir ~/.pip
(3)vi ~/.pip/pip.conf
(4)编辑内容,和windows一模一样
- 需要安装:pip install bs4
bs4在使用时候需要一个第三方库,把这个库也安装一下
pip install lxml
基础使用
使用流程:
- 导包:from bs4 import BeautifulSoup
- 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容
(1)转化本地文件:
- soup = BeautifulSoup(open('本地文件'), 'lxml')
(2)转化网络文件:
- soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
(3)打印soup对象显示内容为html文件中的内容
基础巩固:
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
(3)获取内容
- soup.a.string
- soup.a.text
- soup.a.get_text()
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
- 层级选择器:
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .lala 只能是下面一级
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
项目巩固
需求:使用bs4实现将诗词名句网站中三国演义小说的每一章的内容爬去到本地磁盘进行存储
import requests
from bs4 import BeautifulSoup
import lxml
import os
if __name__ == '__main__':
if not os.path.exists('./sanguo'):
os.mkdir('./sanguo')
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
headers = {
#定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}
respnose=requests.get(url=url,headers=headers).text
#数据解析
soup = BeautifulSoup(respnose,'lxml')
a_list = soup.select('.book-mulu a')# 列表
for a in a_list :
detail_url = 'http://www.shicimingju.com'+ a['href'] # print(a['href']) #/book/sanguoyanyi/1.html
title = a.string
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
file_path = './sanguo/'+title
with open(file_path+'.html','w',encoding='utf-8') as f:
f.write(content)
print(title,'success!!!!!')
print('over!')
数据解析---xpath解析
引入
xpath解析是我们在爬虫中最常用也是最通用的一种数据解析方式,由于其高效且简介的解析方式受到了广大程序员的喜爱。在后期学习scrapy框架期间,也会再次使用到xpath解析。
环境安装
- pip install lxml
解析原理
- 使用通用爬虫爬取网页数据
- 实例化etree对象,且将页面数据加载到该对象中
- 调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获
如何实例化一个etree对象:
from lxml import etree
1.将本地的html文档中的源码数据加载到etree对象中
tree=etree.parse(filePath)
2.可以将互联网上获取的源码数据加载到该对象中
etree.HTML('page_text')
常用xpath表达式
属性定位:
#找到class属性值为song的div标签
//div[@class="song"]
层级&索引定位:
- xpath('xpath表达式') 根据层级关系进行标签定位
tree.xpath('/html/body/div')
tree.xpath('/html//div')
tree.xpath('//div') 匹配所有div
/: 表示的是从跟节点开始定位,表示的是一个层级
//:表示的是多个层级 (不只是2个),相当于找所有
#找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
//div[@class="tang"]/ul/li[2]/a
逻辑运算:
#找到href属性值为空且class属性值为du的a标签
//a[@href="" and @class="du"]
模糊匹配:
//div[contains(@class, "ng")]
//div[starts-with(@class, "ta")]
取文本:
# /表示获取某个标签下的文本内容
# //表示获取某个标签下的文本内容和所有子标签下的文本内容
//div[@class="song"]/p[1]/text()
//div[@class="tang"]//text()
取属性:
//div[@class="tang"]//li[2]/a/@href
案例:xpath解析58二手房源信息
import requests
from lxml import etree
if __name__ == '__main__':
url = 'https://bj.58.com/ershoufang/'
headers ={
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
#爬取页面源码数据
page_text = requests.get(url=url,headers=headers).text
#数据解析
tree =etree.HTML(page_text)
#观察源码 拿到li列表
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
for li in li_list:
title=li.xpath('./div[2]/h2[@class="title"]/a//text()')[0] #./ 表示li标签的位置
price=li.xpath('./div[3]/p[@class="sum"]//text()')[0] + li.xpath('./div[3]/p[@class="sum"]//text()')[1] #./ 表示li标签的位置
print(title,price)
爬取4k网图片:
import requests
from lxml import etree
import os
if __name__ == '__main__':
url = 'http://pic.netbian.com/'
headers ={
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
#爬取页面源码数据
response = requests.get(url=url,headers=headers)
# 可以手动把响应数据编码成utf-8
# response.encoding='GBK'
page_text = response.text
#数据解析
tree =etree.HTML(page_text)
#观察源码 拿到li列表
li_list = tree.xpath('//div[@class="slist"]/ul/li')
if not os.path.exists('./picLibs'):
os.mkdir('./picLibs')
#拿到图片的src,和alt
for li in li_list:
img_src = 'http://pic.netbian.com/'+ li.xpath('./a//img/@src')[0]
img_name = li.xpath('./a//img/@alt')[0] + '.jpg'
img_name = img_name.encode('iso-8859-1').decode('gbk') #手动编码出现编码问题的地方 (iso编码)
# print(img_src,img_name)
#请求图片进行持久化存储
img_data =requests.get(url=img_src,headers=headers).content
with open('./picLibs/'+img_name,'wb') as f:
f.write(img_data)
print(img_name,'success!!')
爬取所有城市的名称
'''
- 项目需求:解析出所有城市名称https://www.aqistudy.cn/historydata/
'''
import requests
from lxml import etree
import os
if __name__ == '__main__':
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/72.0.3626.121 Safari/537.36'
}
#爬取页面源码数据
response = requests.get(url=url,headers=headers)
# 可以手动把响应数据编码成utf-8
# response.encoding='GBK'
page_text = response.text
#数据解析
tree =etree.HTML(page_text)
li_list=tree.xpath('//div[@class="bottom"]//li')
for li in li_list:
city_name = li.xpath('./a/text()')[0]
print(city_name)
站长素材下载简历模板
'''
- 项目需求:解析出所有城市名称https://www.aqistudy.cn/historydata/
'''
import requests
from lxml import etree
import os
if __name__ == '__main__':
if not os.path.exists('./jainliLibs'):
os.mkdir('./jainliLibs')
url = 'http://sc.chinaz.com/jianli/free.html'
headers ={
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
#爬取页面源码数据
response = requests.get(url=url,headers=headers)
# 可以手动把响应数据编码成utf-8
# response.encoding='GBK'
page_text = response.text
#数据解析
tree =etree.HTML(page_text)
#拿到每一模板对应的链接
a_list_url = tree.xpath('//div[contains(@class,"main_list")]/div/a/@href')
for href_url in a_list_url:
detail_text = requests.get(url=href_url,headers=headers).text
tree=etree.HTML(detail_text)
#拿到下载链接
down_url=tree.xpath('//div[contains(@class,"downlist")]/ul/li[1]/a/@href')[0]
down_name = down_url.split('/')[-1]
# print(down_url)
#下载模板
down_content=requests.get(url=down_url,headers=headers).content
with open('./jainliLibs/'+down_name,'wb') as f:
f.write(down_content)
print(down_name,'success!')