1. requests模块实现数据爬取的流程
- 指定url
- 发起请求
- 获取响应数据
- 持久化存储
在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指定数据解析。因为大多数情况下的需求,我们都会指定去使用聚焦爬虫,也就是爬取页面中指定部分的数据值,而不是整个页面的数据。因此,本次课程中会给大家详细介绍讲解三种聚焦爬虫中的数据解析方式。至此,我们的数据爬取的流程可以修改为: - 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
2 python如何实现数据解析?
2.1正则表达式
案列:
# 2020-10-28
# 分页爬取糗事百科 热图版块所有图片
import requests
import re
import os
if __name__=="__main__":
if not os.path.exists('./qiutu'): #创建文件夹 判断是否存在 若不存在则创建
os.mkdir('./qiutu')
# ua伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
# 指定url
# url = 'https://www.qiushibaike.com/imgrank/'
url = 'https://www.qiushibaike.com/imgrank/page/%d/'
for PageNum in range(1,3):
# 使用通用流程 爬取目标一整张页面
new_url = format(url%PageNum)
response = requests.get(url=new_url,headers=headers)
page_text = response.text
#使用聚焦爬虫对页面中的图片进行解析
# < div class ="thumb" >
# < a href = "/article/123725630" target = "_blank" >
# < img src = "//pic.qiushibaike.com/system/pictures/12372/123725630/medium/RJFYAKHCS96PMWUO.jpg"
# alt = "糗事#123725630" class ="illustration" width="100%" height="auto" >< / a >
# < / div >
#ex = '<div class ="thumb">.*?<img src="(.*?)" alt.*?</div>' #正则表达式 错误写法多了一个class后空格
# 上面的html代码 为为我们需要爬取的图片的标签 通过正则表达式 取出每张图片路径src
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
#print(img_src_list)
for src in img_src_list: #遍历列表 通过src获取图片
src= "https:"+src #图片地址
response = requests.get(url=src,headers=headers)
ima_data = response.content
img_name = src.split('/')[-1] #生成图片名称 如第一张 CHJY6LJ7Y00R9C18.jpg
img_path = './qiutu/'+img_name #生成存储每张图片的路径
with open(img_path,'wb') as fp:
fp.write(ima_data)
print(img_name,'爬取完成')
2.2 bs4解析
如何实例化BeautifulSoup对象
from bs4 import BeautifulSoup
1、将本地的html文档中的数据加载到该对象中
fp=open(’./test.html’,‘r’,encoding=‘uft-8’)
soup=BeautifulSoup(fp,‘lxml’)
2、将互联网上获取的页面源码加载到该对象中
page_text=response.text
soup=BeautifulSoup(page_text,‘lxml’)
提供的用于数据解析的方法和属性:
(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选择器返回永远是列表,需要通过下标提取指定的对象
案例 爬取三国演义
# -*- coding: utf-8 -*-
import lxml
import requests
from bs4 import BeautifulSoup
if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
#爬取首页信息
url='http://www.shicimingju.com/book/sanguoyanyi.html'
page_text=requests.get(url=url,headers=headers).text
#在首页中解析出章节标题和详情页的url
#1、实例化BeautifulSoup对象
soup=BeautifulSoup(page_text,'lxml')
#2、解析章节标题和url
li_list = soup.select('.book-mulu > ul > li')
#文本文件相关
fp = open('./三国演义.txt','w',encoding='utf-8')
for li in li_list:
title=li.a.string
detail_url='http://www.shicimingju.com'+li.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+':'+'\n'+content+'\n')
print(title,"获取成功")
print("三国演义爬取完成")
2.3 xpath解析
(1)引入
xpath解析是我们在爬虫中最常用也是最通用的一种数据解析方式,由于其高效且简介的解析方式受到了广大程序员的喜爱。在后期学习scrapy框架期间,也会再次使用到xpath解析。
(2) 环境安装
pip install lxml
(3) 解析原理
- 使用通用爬虫爬取网页数据
- 实例化etree对象,且将页面数据加载到该对象中
- 使用xpath函数结合xpath表达式进行标签定位和指定数据提取
(4) 常用xpath表达式
属性定位:
找到class属性值为song的div标签
div[@class="song"]
层级&索引定位:
找到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
etree对象实例化
- 本地文件:tree = etree.parse(文件名)
tree.xpath(“xpath表达式”) - 网络数据:tree = etree.HTML(网页内容字符串)
tree.xpath(“xpath表达式”)
案列1 爬取58二手房信息
# 2020-10-31
import requests
from lxml import etree
if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
url = 'https://huizhou.58.com/ershoufang/'
page_text = requests.get(url=url, headers=headers).text
tree = etree.HTML(page_text)
#xpath表达式 获取所有li标签内容
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
fp = open('58.txt','w',encoding='utf-8')
for li in li_list:
#获取li标签里的标题title 注意./是定位到当前li标签下的 一定要加点
title = li.xpath('./div[2]/h2/a/text()')[0]
price = li.xpath('./div[3]/p/b/text()')[0]
t = title.replace("\u00a0", "") #去掉空格
print(t)
fp.write(t+'-'+price+'\n')
fp.close()
案例2 爬取图片
# 2020-10-31 需求 通过xpath 解析4k图片数据
import requests
from lxml import etree
import os
if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
url = 'http://pic.netbian.com/4kmeinv/'
response = requests.get(url=url,headers=headers)
#出现乱码 手动设定响应数据的格式
#response.encoding='utf-8'
page_text = response.text
tree = etree.HTML(page_text)
# li_list = tree.xpath('//ul[@clearfix]/li') 定位错误 导致li_list为空列表
li_list = tree.xpath('//div[@class="slist"]/ul/li')
# print(li_list)
if not os.path.exists('./4kmm'):
os.mkdir('./4kmm')
for li in li_list:
#获取每张图片地址src
img_src ='http://pic.netbian.com/' + li.xpath('./a/img/@src')[0]
#获取alt属性
img_alt = li.xpath('./a/img/@alt')[0]
img_name = img_alt+'.jpg'
#通用处理乱码解决
img_name = img_name.encode('iso-8859-1').decode('gbk')
# print(img_name,img_src)
#对图片地址发送请求
response = requests.get(url=img_src,headers=headers)
img_date = response.content
img_path='4kmm/'+img_name
# print(img_path)
with open(img_path,'wb') as fp:
fp.write(img_date)
print(img_name, '下载完成!')
问题:
1 .在个图片命名时出现乱码
解决方法:
- 手动设定响应数据的格式 response.encoding=‘utf-8’
- 通用处理乱码解决 img_name = img_name.encode(‘iso-8859-1’).decode(‘gbk’)
- xpath表达式一定要写正确