·聚焦爬虫:爬取页面中指定的页面内容
--编码流程:
-- 指定url
-- 发起请求
-- 获取响应数据
-- 数据解析(new)
-- 持久化存储
·数据解析分类:
-正则
-bs4
-xpath(***)
·数据解析原理概述:
-- 解析的局部的文本内容都会在标签之间或者标签对应的属性中进行存储
-- 1.进行指定标签的定位
-- 2.标签或者标签对应的属性中存储的数据值进行提取(解析)
1.数据解析–正则表达式:
爬虫中常用的正则表达式:
正则练习:
正则化案例:爬取对应网址中的所有图片
# -*- coding: utf-8 -*-
#预备知识:如何爬取图片数据?
import requests
if __name__ == "__main__":
#如何爬取图片数据
url = 'https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1607000570&di=09cddbf85d0328a11f4b36486c7f2ef6&src=http://a4.att.hudong.com/27/67/01300000921826141299672233506.jpg'
#content返回的是二进制形式的图片数据
#text(字符串) content(二进制) json()(对象)
img_data = requests.get(url=url).content
with open('erha.jpg','wb') as fp:#wb以二进制形式写入
fp.write(img_data)
#需求: 爬取糗事百科热图板块下的所有图片
'''
正则表达式设计:
从网站element中看出,img的路径所在的最小div块为:
<div class="thumb">
<a href="/article/123838470" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12383/123838470/medium/JD1K68EA95VQ9HVT.jpg" alt="糗事#123838470" class="illustration" width="100%" height="auto">
</a>
</div>
因为a和img模块中都有独特的id,所以最近只能找div
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
(.*?表示两者中的所有字符,因此第一个表示img前的a标签内容,第二个表示图片地址,第三个表示地址后,div结束块前的字符)
(括号包含的就是指定内容)
思路:
1.先用通用爬虫爬一整张页面
2.根据页面html分布,使用正则表达式解析,找到图片的地址
3.拼接图片的完整地址,再get一次,拿到完整图片
'''
import requests
import re
import os
if __name__ == "__main__":
#创建一个文件夹,保存所有图片
if not os.path.exists('./qiutuLibs'):
os.mkdir('./qiutuLibs')#没有就创建
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'#伪装浏览器
}
#设置一个通用的url模板,可以让他一次提取多个页面的图片
url = 'https://www.qiushibaike.com/imgrank/page/%d/'#页面进行传值操作
for pageNum in range(1,3):
#对应页码的url,将pageNum赋值到%d中
new_url = format(url%pageNum)
#首先使用通用爬虫对url对应的一整张页面进行爬取,然后再用聚焦爬虫爬指定数据
page_text = requests.get(url=url,headers=headers).text#2,3步合并
#使用聚焦爬虫对页面中的图片进行解析
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)#正则解析,re.S表示单行解析,反正爬虫中都用re.S
#写入图片
for src in img_src_list:
#拼接完整的图片url
src = 'https:'+src#拼接字符串
#请求图片的二进制数据
img_data = requests.get(url=src,headers=headers).content
#生成图片名称
img_name = src.split('/')[-1]#使用/分割字符串,并拿最后一段
#图片的存储路径
imgPath = './qiutuLibs/'+img_name
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
2.bs4
bs4进行数据解析:
- 数据解析的原理:
- 1.标签定位
- 2. 提取标签、标签属性中存储的数据值
- bs4数据解析的原理
- 1.实例化一个BeautifulSoup对象,并且将页面源码加载到该对象中
- 2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
- 环境安装:
- pip install bs4
- pip install lxml
- 如何实例化BeautifulSoup对象:
- from bs4 import BeautifulSoup
- 对象的实例化:
- 1.将本地的html文档中的数据加载到该对象中
fp = open('本地html路径','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
- 2.将互联网上获取的页面源码加载到该对象中
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
-提供的用于数据解析的方法和属性:
- soup.tagName:返回文档中第一次出现对应tagName的标签
- soup.find():
- find('tagName'):等同于上一方法(soup.tagName)
- 属性定位:
- soup.find('div',class_/id/attr = 'song')#获取对应div class名为song的数据
- soup.find_all('tag_Name') : 返回符合条件的所有标签(存在列表中)
- select:
- select('某种选择器 (id,class,标签...选择器)'),返回的是一个列表
- 层级选择器:
- soup.select('.tang > ul > li > a'):>表示的是一个层级(搜索class名为tang的<div>下的<ul>标签下的<li>标签下的<a>的数据)
- soup.select('.tang>ul a'):空格表示多个层级
- 获取标签之间的文本数据:
- soup.a.text/string/get_text()
- text/get_text():可以获取某一个标签中的所有标签,包括下属标签
- string: 只可以获取该标签下面直系的文本内容(不包括下属标签内容)
- 获取标签中属性值:
- soup.a['href'] : 获取a标签中,href属性中的值
ba4基本使用:
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
if __name__ == '__main__':
#将本地的html文档中的数据加载到该对象中
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
#print(soup)
#print(soup.a)#返回html中第一次出现的a标签
#find('a')#效果同上
#print(soup.find('div')) #print(soup.div)
#print(soup.find('div',class_='song').string)#返回第一个class名叫song的div中的文本数据
#print(soup.find_all('a'))#返回所有的a标签
#print(soup.select('.tang'))#返回叫tang的标签(列表形式)
#print(soup.select('.tang>ul a')[0]['href'])#返回class名为tang标签中的以一个ul和a标签中的href属性值
bs4爬取三国文章案例
# -*- coding: utf-8 -*-
#需求:爬取三国演义小说中所有的章节标题和章节内容
#https://www.shicimingju.com/book/sanguoyanyi.html
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.198 Safari/537.36'#伪装浏览器
}
url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url=url,headers=headers).text
#再首页中解析出章节的标题,再解析出详情页的url
#1.首先实例化BeautifulSoup对象,需要将页面源码数据加载到该对象中
soup = BeautifulSoup(page_text,'lxml')
#解析章节标题和详情页的url
#讲过观察发现,标题包含在book-mulu的div中的ul,li的a中,详情页则在a的href属性中
li_list = soup.select('.book-mulu > ul > li')
fp = open('./sanguo.txt','w',encoding='utf-8')
for li in li_list:
title = li.a.string
detail_url = 'https://www.shicimingju.com'+li.a['href']#拼接网址
#对详情页发起请求,解析出章节内容
detail_page_text = requests.get(url=detail_url,headers=headers).text
#解析出详情页中的章节内容
detail_soup = BeautifulSoup(detail_page_text,'lxml')
#章节内容存在详情页中叫chapter_content的div中
div_tag = detail_soup.find('div',class_= 'chapter_content')
#解析到了章节的内容
content = div_tag.text
fp.write(title+':'+content+'\n')#每一次读取后都写入到文件中
print(title,'爬取成功!!!')
3.xpath
最常用且最便捷高效的一种解析方式,通用性高
- xpath解析原理:
- 1.实例化一个etree对象,且需要将被解析的页面源码数据加载到该对象中
- 2.调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
- 环境的安装:
- pip install lxml
- 如何实例化一个etree对象: from lxml import etree
- 1.将本地的html文档中的源码数据加载到etree对象中:
etree.parse(filePath)
- 2.将互联网上获取的源码数据加载到该对象中:
etree.HTML('page_text')
- xpath(‘xpath表达式’)
- xpath表达式
- /:表示的是从根节点开始定位。表示的是一个层级
- //:表示的是多个层级。可以表示从任何位置开始定位
- 属性定位://div[@class='song'] ; tag[@attrName="attrValue"](@后面跟属性名=“属性名称”)
- 索引定位://div[@class="song"]/p[3] 索引从1开始(表示找class名为song的div块的第三个p标签)
- 取文本:
- /text() 获取的是标签中的直系的文本内容
- //text() 标签中非直系的文本内容(所有的文本内容)
- 取属性:
/@attrName ⇒ img/@src (取img标签下的src属性信息)
xpath基础解析
from lxml import etree
if __name__ == "__main__":
#实例化好了一个etree对象,且将被解析的源码加载到了该对象中
tree = etree.parse('test.html')
# r = tree.xpath('/html/body/div')#拿到html标签下bodu块中的div信息
# r = tree.xpath('/html//div')#效果同上
# r = tree,xpath('//div')#效果同上
# r = tree.xpath('//div[@2class="song"]')#拿到class名为song的div信息
# r = tree.xpath('//div[@class="tang"]//li[5]/a/text()')[0]#拿到第一个class名为tang的第5个li标签中的a标签中的文本
# r = tree.xpath('//li[7]//text()')#拿到全文第7个li中的文本内容
# r = tree.xpath('//div[@class="tang"]//text()')#拿到类名为tang的div中的所有文本信息
r = tree.xpath('//div[@class="song"]/img/@src')#拿到class名为song的div中第一个img标签中的src属性的值
xpath–58二手房案例(xpath路径应用)
# -*- coding: utf-8 -*-
import requests
from lxml import etree
#需求:爬取58二手房中的房源信息
#https://bj.58.com/ershoufang/
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.198 Safari/537.36'#伪装浏览器
}
#爬取页面源码数据
url = 'https://bj.58.com/ershoufang/'
#首先先爬下整个页面数据
page_text = requests.get(url=url,headers=headers).text
#数据解析
tree = etree.HTML(page_text)#先将text数据存到etree对象中
#存储的就是li标签对象
#信息存储的最大外包层从 house-list-wrap的ul开始
#先将所有li信息存到列表中,然后再一个个遍历拿到里面的数据
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
fp = open('58.txt','w',encoding='utf-8')
for li in li_list:
#局部解析
title = li.xpath('./div[2]/h2/a/text()')[0]#接着列表之前的地址继续向下找,./开头
print(title)
fp.write(title+'\n')
xpath–爬取网页上图片资源(中文乱码问题解决)
# -*- coding: utf-8 -*-
#需求:爬取网站上的图片
#http://pic.netbian.com/4kqiche/
import requests
from lxml import etree
import os
if __name__ == '__main__':
url = 'http://pic.netbian.com/4kqiche/'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'#伪装浏览器
}
response = requests.get(url=url,headers=headers)
#如果遇到中文乱码问题
#解决方案1:手动设定响应数据的编码格式
#response.encoding = 'utf-8'
page_text = response.text
#拿到页面数据后,可以对其进行解析
#解析src属性(拿到图片地址,下载)attr属性(作为文件名)
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="slist"]/ul/li')
#创建一个文件夹存储文件
if not os.path.exists('./picLibs'):
os.mkdir('./picLibs')
for li in li_list:
img_src = 'http://pic.netbian.com/4kqiche'+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')
print(img_name,img_src)
#对图片持久化存储
img_data = requests.get(url=img_src,headers=headers).content#图片二进制写入
img_path = 'picLibs/'+img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
xpath爬取全国城市名(多路径合并)
# -*- coding: utf-8 -*-
import requests
from lxml import etree
#需求:解析出所有城市名称
#https://www.aqistudy.cn/historydata/
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.198 Safari/537.36'#伪装浏览器
}
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
# #可以从网页源码中看到城市名称是分热门城市和一般城市的
# #先解析热门城市的城市名称
# host_li_list = tree.xpath('//div[@class="bottom"]/ul/li')
# all_city_names = []#弄一个列表存储所有城市名
# #从热门城市列表中解析出名称
# for li in host_li_list:
# hot_city_name = li.xpath('./a/text()')[0]#拼接形式
# #将获得的热门城市名加入到城市列表中
# all_city_names.append(hot_city_name)
# #一般城市
# city_name_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
# for li in city_name_list:
# city_name = li.xpath('./a/text()')[0]
# all_city_names.append(city_name)
# print(all_city_names,len(all_city_names))
#将两个路径合并起来的方法
#从源码中可以看到两个城市的标签是不一样的
#//div[@class="bottom"]/ul/li/a 热门城市a标签的层级关系
#//div[@class="bottom"]/ul/div[2]/li/a 一般城市的a标签
a_list = tree.xpath('//div[@class="bottom"]/ul/li/a|//div[@class="bottom"]/ul/div[2]/li/a')#其实就是逻辑或,两个路径都要
all_city_names = []
for a in a_list:
city_name = a.xpath('./text()')[0]
all_city_names.append(city_name)
print(all_city_names,len(all_city_names))