python爬虫
一.爬虫基础简介
爬虫概念:
1. 通过编写程序模拟浏览器上网,让其去互联网上抓取数据的过程爬虫在使用场景的分类
1. 通用爬虫:
1. 抓取系统重要组成部分。抓取整张页面的数据
2. 聚焦爬虫
1. 是建立在通用爬虫的基础之上,抓取的是页面中特定的局部内容 3. 增量式爬虫
1. 检测网站中数据更新的情况,只会抓取网站中最新更新出来的数据 4. 深层网络爬虫**
爬虫的矛与盾
1. 反扒机制
1. 门户网站,可以通过制定相应的策略或技术手段,防止网站爬虫程序进行网站数据爬取 2. 反反爬机制
1. 爬虫程序可以制定相应的策略或技术手段,破解门户网站中具备的反爬机制,从而获取门户网 站的数据
3. robots.txt协议
1. 规定哪些数据可以被爬虫程序爬取
http协议
概念:服务器与客户端进行数据交互的一种形式
常用的请求头信息
1. user-agent:请求载体的身份标识
2. connection:请求完毕后,是断开连接还是保持连接
常用响应头信息
1. Connect-Type:服务器响应回客户端的数据类型
https协议
1. 安全的超文本传输协议(数据加密)
加密方式
1. 对称密钥加密
2. 非对称密钥加密
3. 证书密钥加密
二.requests模块
通用爬虫
urllib, requests
概念:基于网络请求的模块,功能非常强发,简单便捷
作用:模拟浏览器发请求
使用:(使用requests模块流程)
1. 指定URL,
2. 发起请求(get,post)
3. 获取响应页面数据
4. 持久化存储
环境安装:
pip install requests
实战编码:
1. 爬取搜狗首页的页面数据
实战巩固:
需求:
1. 爬取搜狗指定词条对应的搜索结果页面(建议网页采集器)
1. 反爬机制:UA检测
2. 反反爬策略:UA伪装
# -*- utf-8 -*-
# UA检测:门户网站的服务器会检测对应求情的载体身份标识,若检测到请求的身份标识为某一款浏览器,则说明该请求为正常请求
# 若检测到身份标识不是某一款浏览器,则请求不正常,则是爬虫程序,则服务器拒绝该次请求# UA:User-Agent(请求载体的身份标识)
# UA伪装:将爬虫程序对应的请求标识伪装成某一款浏览器
importrequests
if__name__=="__main__":
# UA伪装:将对应的UA封装到字典中
# 模拟用户浏览器发起请求
header= {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
}
# 1.指定url(unicode)https://www.sogou.com/web?
query=%E5%91%A8%E6%9D%B0%E4%BC%A6
url="https://www.sogou.com/web"
# 处理url携带的参数?query : 封装到字典中
kw=input("请输入关键词:")
param= {
"query": kw
}
# 2.发起请求, 对发起请求的url是携带参数(动态拼接params),并且在请求过程中处理了参数,UA请求
response=requests.get(url=url, params=param, headers=header)
# 3.获取响应数据
page_text=response.text
# 4.持久化保存数据
filename=kw+".html"
withopen(filename, "w", encoding="utf-8") asf:
f.write(page_text)
print("打印完成")
2. 破解百度翻译
1. 对应请求:POST请求(携带参数kw)
2. 响应数据是一组JSON数据
# 1. 对应请求:POST请求(携带参数kw)
# 2. **响应数据是一组JSON数据**
importrequests
importjson
if__name__=="__main__":
# 1.指定获取url
post_url="https://fanyi.baidu.com/sug"
# UA伪装:防止反扒策略
header= {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" }
# 2.发起请求
# 处理post请求携带的参数, 与get 请求参数处理一致
word=input("输入单词:")
data= {
"kw": word
}
# 返回响应对象
response=requests.post(url=post_url, data=data, headers=header)
# 3.获取响应对象的数据(text获取字符串数据)
# json方法直接返回字典对象,如果确认响应数据是json类型,才能使用json方法返回对象(查看content-type)
dict_obj=response.json()
# print(dict_obj)
# 4.持久化存储
filename=word+".json"
fp=open(f"../requests模块/requests模块_破解百度翻译/{filename}", "w", encoding="utf-8")
# 因为数据存在中文所以不适应ascii
json.dump(dict_obj, fp=fp, ensure_ascii=False)
print("保存完成")
3. 爬取豆瓣电影分类排行榜中的电影详情数据
1. get请求(处理参数:字典形式)
2. Ajax请求动态刷新
3. json数据保存,使用json包的dump方法
importrequests
importjson
if__name__=="__main__":
# https://movie.douban.com/j/chart/top_list?
type=24&interval_id=100%3A90&action=&start=120&limit=20 url="https://movie.douban.com/j/chart/top_list" # 也可以动态获取
param= {
"type": "24",
"interval_id": "100:90",
"action": "",
"start": "0", # 从豆瓣的库中第120部电影开始取 "limit": "20", # 一次请求的电影是20个
}
# UA 伪装
header= {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
}
# 响应请求
response=requests.get(url=url, params=param, headers=header)
# 获取响应数据
list_data=response.json()
print(list_data)
# 持久化存储
fp=open("../requests模块/requests模块_豆瓣电影排行榜/1-20电影排行榜数据.json", "w", encoding="utf-8")
json.dump(list_data, fp=fp, ensure_ascii=False)
print("over")
4. 爬取肯德基餐厅查询指定位置的餐厅数量()
1. 翻页,进行循环
2. 写入数据到文件
importrequests
if__name__=='__main__':
# 1.指定url
url='http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
foriinrange(1, 6):
print(f"——————正在打印第{i}页———————")
data= {
"cname": "",
"pid": "",
"keyword": "重庆",
"pageIndex": i,
"pageSize": "10"
}
# 定义列表存储数据
add_list= []
# 伪装UA,防止反爬机制
header= {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" }
# 响应请求
response=requests.post(url=url, data=data, headers=header)
# 获取响应数据
page_text=response.text
print(page_text)
# 持久化存储
withopen("../requests模块/requests模块_肯德基地址/重庆肯德基地址1.txt", "w", encoding="utf-8") asf:
foriinrange(1, 6):
f.write(page_text)
print(f"------第{i}页over------")
5. 爬取药监局监督管理总局中基于中华人名共和国化妆品生产许可证的数据
1. 通过Ajax动态请求
2. CTRL+F搜索
3. 针对需求,可能动态加载的数据,使用抓包工具进行捕获
动态加载数据
首页的数据通过Ajax请求到的
ID url的域名是不变的,通过拼接ID详情页URL
详情页的企业详情数据也是动态加载的
获取字典的数据
dict["ID"]
三.数据解析
聚焦爬虫
1. 爬取页面中指定的页面内容
数据解析分类
1. 正则
2. bs4
3. xpath
数据解析原理概述:
1. 解析的局部文本内容都会在标签之间,或者标签指定的属性中存储
1. 进行指定标签的定位
2. 标签或标签对应的属性中存储的数据值进行提取(解析)
编码流程
1. 指定URL
2. 发起请求
3. 响应数据请求
4. 数据解析
5. 持久化存储
数据解析--正则表达式
#. 需求:爬取糗事百科中糗图板块下的所有糗图图片
ex = '
.? <img src="(.?)" alt.*?
‘
bs4进行数据解析
1. 正则数据解析原理:
1. 标签定位
2. 提取标签,标签属性中存储的数据值
bs4数据解析的原理:
1. 实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中 2. 通过调用BeautifSoup对象中相关的属性或方法进行标签定位和数据提取2. 环境安装
1. bs4
2. lxml解析器
3. 实例化BeautifulSoup对象
1. 将本地的html文档中的数据加载到该对象中
1.
2. frombs4importBeautifulSoup
if__name__=='__main__':
# 将本地的html文档中的数据加载到该对象中
fp=open('../requests模块/requests模块_网页采集器/sougou.html', 'r', encoding='utf-8')
# 实例化该对象
soup=BeautifulSoup(fp, 'lxml')
print(soup)
2. 将互联网上获取的页面源码加载到该对象中
1. page_text = response.text
soup = BeautifulSoup(page_text, 'lxml')
3. 提供的用于数据解析的方法和属性
1. print(soup.a) # soup.tagName 返回的是html中第一次出现的tagName(例如a标 签)
2.
print(soup.find('a')) # 2. soup.find('tagName') == soup.tagNmae
# 属性定位
# 2.2 soup.find('tagName', **kwargs) 指定标签下的属性进行查找print(soup.find('div', class_='top-nav'))
# 2.3 find_all返回符合要求的所有标签
print(soup.find_all('a')) # 以列表的形式返回全部的a标签
3. # 方法3 select 可以放某种选择器(id,类选择器class,标签...选择器),返回的 是一个列表
print(soup.select('.top-nav'))
# 层级选择器
# 3.1 层级选择器, > 表示一个层级
print(soup.select('.top-nav > ul > li > a')[0]) # 大于号> 表示一个层级
print(soup.select('.top-nav > ul a')[0]) # 其中空格表示的是多个层级
4. 定位标签想获取标签中存储的属性值,文本数据
4. 获取标签中的文本数据
1. print(soup.select('.top-nav > ul > li > a')[1].text) # 属性 print(soup.select('.top-nav > ul > li > a')[1].string) # 属性 print(soup.select('.top-nav > ul > li > a')[1].get_text()) # 方 法
# 获取div标签下的所有文本内容
print(soup.find('div').text) # 获取所有文本内容
print(soup.find('div').string) # 获取直系文本内容
5. 获取标签中的属性值
1. soup.a['href']
2. """获取标签中的属性值"""
print(soup.select('.top-nav > ul > li > a')[0]['href'])
# 需求:爬取三国演义小说所有的章节标题和章节内容
importrequests
importbs4
if__name__=='__main__':
header= {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
}
# 对首页的页面进行捕获,创建bs对象
url='https://www.shicimingju.com/book/sanguoyanyi.html' # 发起请求
page_text=requests.get(url=url, headers=header)
# 解决中文乱码问题
page_text.encoding='utf-8'
# 解析出章节的标题和详情页url
soup=bs4.BeautifulSoup(page_text.text, 'lxml')
# 解析章节标题和详情页的url
li_list=soup.select('.book-mulu > ul > li')
# 保存
fp=open('../2.数据解析/bs4/sanguo.txt', 'w', encoding='utf-8') forliinli_list:
# 获取目录名称
title=li.a.text
# 获取详情页url
detail_url='https://www.shicimingju.com'+li.a['href']
# 发起详情页请求
detail_url_text=requests.get(url=detail_url, headers=header)
detail_url_text.encoding='utf-8'
# 解析出详情页中相关的章节内容
page_soup=bs4.BeautifulSoup(detail_url_text.text, 'lxml')
div_tag=page_soup.find('div', class_='chapter_content') # 章节的内容
content=div_tag.text
# 持久化存储
fp.write(title+':'+content+'\n')
print(title, '爬取成功!!')
总结:
1. bs4容易出现中文乱码的问题,使用requests库的时候,使用encoding指定编码的 格式
2. 使用方法进行页面解析,分析页面标签元素
1. find
2. find_all
3. selector
xpath解析
最常用便捷,高效
xpath解析原理
1. 实例化etree对象,将被解析的页面源码加载到该对象中
2. 标签定位,通过调用etree当中的xpath方法,结合xpath表达式实现标签的定位和内容的捕获
环境安装
pip install lxml(解析器)
如何实例化一个etree对象: from lxml import etree
1. 将本地的html文本对象加载到对象中
etree.parse(filePath)
2. 可以将互联网上获取的页面源码数据加载到该对象中
etree.HTML('page_text')
3. 调用xpath方法
xpath('xpath表达式')
在练习lxml数据解析的时候,用parse方法加载本地的html文件时出现如下错误: lxml.etree.XMLSyntaxError: EntityRef: expecting ‘;’, line 2, column 286 原因:
html代码书写不规范(不怪你)
解决方法:
parser = etree.HTMLParser(encoding='utf-8')
tree = etree.parse('test.html',parser=parser)
———————————————
原文链接:
根据层级关系进行标签的定位
if__name__=='__main__':
# 将本地的html文档中的数据加载到该对象中
# 实例化etree对象,且将被解析的源码加载到该对象
# tree = etree.parse('../requests模块/requests模块_网页采集器/sougou.html') parser=etree.HTMLParser(encoding='utf-8')
tree=etree.parse('周杰伦.html', parser=parser)
r=tree.xpath('/html/body/div') # 第一个 / 表示从根节点开始, 返回的数据是列表类型 r=tree.xpath('/html//div') # // 表示多个层级,可以表示从任意位置开始定位,类似于空格bs4
r=tree.xpath('//div[@class="special-wrap title-newblue border-radius baike231026"]') # 属性定位://div[@class="属性名称"]
tag[@attrName="attrValue"]
XPATH表达式:
1. / :表示的是从根节点开始定位。表示的是一个层级
2. // : 表示的是多个层级。可以从任意位置开始定位
3. 属性定位:例如://div[@class="song"]具体写法:tag[@attrName="attrValue"]
4. 索引定位:例如://div[@class="song"]/p[3] 索引是从 1 开始的
5. 取文本:
1. /text(): 获取的标签中直系的文本
2. //text():获取的是非直系的文本内容,即获取的是标签下面所有的文本内容6. 取属性值
1. /@attrName:直接@后面接属性名称====》a/@href
2. r=tree.xpath('//div[@class="book-mulu"]//li/a/@href')
XPATH案例演练
1. 58同城房源
# 需求:爬取58二手房中的相关房源信息
importrequests
fromlxmlimportetree
if__name__=='__main__':
header= {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
}
url='https://cq.58.com/ershoufang/'
# 发起响应请求,得到响应对象
page_text=requests.get(url=url, headers=header).text
# print(page_text)
# 获取互联网的页面源码
tree=etree.HTML(page_text)
# # 编写xpath
h3_list=tree.xpath('//div[@class="property"]/a/div[2]//div[1]/h3')
# print(h3_list)
fp=open('./58cq.txt', 'w', encoding='utf-8')
forh3inh3_list:
content=h3.xpath('./text()')[0] # 因为是列表,不能直接存储到文本文件中,所以需要取出列表中的值来进行存储
# print(content)
fp.write(content+'\n')
2. 4k图片解析下载
1. # 需求:解析下载图片数据 http://pic.netbian.com/4kmeinv/ importos
importrequests
fromlxmlimportetree
if__name__=='__main__':
url='http://pic.netbian.com/4kmeinv/'
header= {
'USer-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' }
# 获得响应数据
page_text=requests.get(url=url, headers=header).text
# 手动设定响应数据的编码
# res.encoding = 'utf-8'
# page_text = res.text
# 解析数据, 解析src的属性值和alt的属性值
# 创建etree对象
tree=etree.HTML(page_text)
li_list=tree.xpath('//ul[@class="clearfix"]/li')
# 使用os模块来调用系统功能创建文件夹
ifnotos.path.exists('./2.数据解析/4kpic'):
os.mkdir('./2.数据解析/4kpic')
# 遍历列表
forliinli_list:
# 获取图片的src属性
img_src='http://pic.netbian.com'+li.xpath('.//img/@src')[0] img_name=li.xpath('.//img/@alt')[0] +'.jpg'
# 通用的处理中文乱码的方案
img_name=img_name.encode('iso-8859-1').decode('gbk')
# 请求图片进行持久化存储,存二进制内容使用content,存储到文件夹中使用os模块img_data=requests.get(url=img_src, headers=header).content
img_path='2.数据解析/4kpic/'+img_name
withopen(img_path, 'wb') asf_wb:
f_wb.write(img_data)
print(f'{img_name}下载成功')
遇到乱码问题首先
1. 排除响应请求数据的内容是否存在乱码问题,如果存在使用 encoding属性进行调试 2. 如果还存在问题可以设置 encode('编码格式').decode('编码中的何种编码'),来进行设 置编码内容
3. 全国城市名称爬取
1. # 使用或运算 | 将两个xpath连接在一起
all_list=tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
all_city_list= []
forainall_list:
all_city_name=a.xpath('./text()')[0]
all_city_list.append(all_city_name)
print(all_city_list, len(all_city_list))
2. 相比下面,上面的内容更加精简
3. # 需求:全国城市名称爬取 https://www.aqistudy.cn/historydata/ importrequests
fromlxmlimportetree
if__name__=='__main__':
url='https://www.aqistudy.cn/historydata/'
header= {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' }
page_text=requests.get(url=url, headers=header).text
tree=etree.HTML(page_text)
#
# all_city_list = []
# # 热门城市
# hot_li_list = tree.xpath('//div[@class="bottom"]/ul/li') #
# for li in hot_li_list:
#
# # 拿去热门城市名称
# hot_city_name = li.xpath('./a/text()')[0]
# # 将热门城市加入到列表
# all_city_list.append(hot_city_name)
#
# # 全部城市
# all_li_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li') # for li in all_li_list:
#
# # 拿到所有城市名称
# all_city_name = li.xpath('./a/text()')[0]
# # 将城市名添加到列表
# all_city_list.append(all_city_name)
#
# print(all_city_list, len(all_city_list))
4. 作业:爬取站长素材中免费简历模板
1.
2. # 需求:爬取站长免费简历模板素材
importos.path
importrequests
fromlxmlimportetree
if__name__=='__main__':
foriinrange(1, 6):
# 获取地址
url='https://aspx.sc.chinaz.com/query.aspx'
param= {
'keyword': '免费简历',
'classID': '864',
'page': i
}
# UA伪装
header= {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
}
# 发起响应请求拿到页面源码数据
res=requests.get(url=url, params=param, headers=header) res.encoding='utf=8'
page_text=res.text
# 创建etree对象
tree=etree.HTML(page_text)
a_list=tree.xpath('//div[@class="box col3 ws_block"]/p')
foraina_list:
jianli_url='https:'+a.xpath('./a/@href')[0]
jianli_name=a.xpath('./a/text()')[0]
detail_page=requests.get(jianli_url, headers=header).text
detail_tree=etree.HTML(detail_page)
# 获取简历下载链接
down_url=detail_tree.xpath('//div[@class="clearfix mt20 downlist"]/ul/li[1]/a')
forhrefindown_url:
jianli=href.xpath('./@href')[0]
# print(jianli)
jianli_data=requests.get(url=jianli, headers=header).content
jianli_lib='jianli/'+jianli_name+'.rar'
withopen(jianli_lib, 'wb') asf:
foriinrange(1, 6):
f.write(jianli_data)
print(f'{jianli_name}下载完成!!')
四.验证码识别
反爬机制:验证码识别验证码图片中的数据,用于登录操作
识别验证码的操作:
1. 人工肉眼识别(不推荐)
2. 第三方自动识别
超级🦅:
识别代码:
#!/usr/bin/env python
# coding:utf-8
importrequests
fromhashlibimportmd5
classChaojiying_Client(object):
def__init__(self, username, password, soft_id):
self.username=username
password=password.encode('utf8')
self.password=md5(password).hexdigest()
self.soft_id=soft_id
self.base_params= {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers= {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
defPostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型参考 http://www.chaojiying.com/price.html """
params= {
'codetype': codetype,
}
params.update(self.base_params)
files= {'userfile': ('ccc.jpg', im)}
r=
requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
returnr.json()
defPostPic_base64(self, base64_str, codetype):
"""
im: 图片字节
codetype: 题目类型参考 http://www.chaojiying.com/price.html """
params= {
'codetype': codetype,
'file_base64':base64_str
}
params.update(self.base_params)
r=
requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, headers=self.headers)
returnr.json()
defReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params= {
'id': im_id,
}
params.update(self.base_params)
r=
requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
returnr.json()
if__name__=='__main__':
chaojiying=Chaojiying_Client('bawanglong', '123456', ' 954697') #用户中心>>软件ID 生成一个替换 96001
im=open('./RandCode.gif', 'rb').read()
#本地图片文件路径来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 1902)['pic_str'])
#1902 验证码类型官方网站>>价格体系 3.4+版 print 后要加()
#print chaojiying.PostPic(base64_str, 1902) #此处为传入 base64代码
其中主程序中返回的是字典类型的数据,得到识别后的验证码需要根据键来去除数据实战:识别古诗文网登录页面中的验证码
1. 使用打码平台识别验证码的大致流程
1. 将验证码图片下载到本地
2. 调用平台提供的示例代码进行图片数据识别
2. 模拟登录
1. 爬取某些基于用户的用户信息
需求:
1. 对古诗文网进行模拟登录
1. 点击登录按钮,发起post请求
2. post请求中会携带登录之前的相关信息(用户名,密码,验证码)
3. 验证码:每次请求会动态变换
2.
http/https协议特性:无状态
没有请求到对应页面的数据:
发起的第二次基于个人页面的请求的时候,服务器并不知道该次请求实在登录状态下的请求cookie: 用于服务器端记录客户端的相关状态
方式一:手动处理(将该值封装在header中)
# 手动cookie处理,用于服务端记录客户端的相关状态
# headers = {
# 'Cookie':
# 'login=flase; wsEmail=1307159954%40qq.com;
Hm_lvt_9007fab6814e892d3020a64454da5a55=1700138374,1700206690;
ASP.NET_SessionId=2byyktcif3caqnlko0wkvcym;
ticketStr=209672754%7cgQFG8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyRHYzT1 FwbGVkN2kxb1ZKLTFCMTcAAgQ5IFdlAwQAjScA; codeyzgswso=e3c0360a68c0c4a3;
gsw2017user=5196003%7cBB2E7E7B0BE2F75792299B18CDF51872%7c2000%2f1%2f1%7c2000%2f1 %2f1; login=flase; wxopenid=defoaltid; gswZhanghao=1307159954%40qq.com;
gswEmail=1307159954%40qq.com; idsShiwen2017=%2c2370%2c71177%2c;
Hm_lpvt_9007fab6814e892d3020a64454da5a55=1700210313'
# }
方式二:自动处理
1. cookie值的来源(客户端登录状态)
1. 模拟登录post请求后,由服务器端创建的
2. session 的会话对象
1. 作用:可以进行请求发送
2. 如果请求过程总中产生了cookie,则该cookie会存在session对象中,模拟登录后,对主页 发起请求,则会携带session中保存的cookie值,自动跳转到主页面
3. 步骤
1. 使用seesion对象进行模拟登录post请求的发送(cookie就会被存储在session中)2. session对象对个人主页对应的get请求进行发送(携带cookie)
3. 语法:session = requests.Session()
五.代理IP
代理:
1. 破解封IP这种反爬机制
代理?:
1. 代理服务器
代理的作用:
1. 突破自身IP访问的限制
2. 隐藏自身真实的IP
代理相关网站:
1. 快代理
2. 西祠代理
3.
代理IP的类型
1. http:只能应用到http协议的url中
2. https:只能应用到https协议的url中
代理IP的匿名度
1. 透明:服务器知道该次请求使用了代理,也知道该次请求的真实IP
2. 匿名:知道使用了代理,但不知道真实IP
3. 高匿:服务器不知道使用了代理,也不知道真实IP
importrequests
url='https://www.baidu.com/s?ie=UTF-8&wd=IP'
header= {
'User-Agent':
''
}
requests.get(url=url, headers=header, proxies={'代理IP地址'}).text
六.高性能异步爬虫
目的:在爬虫中使用异步实现高性能的数据爬取操作
1.同步爬虫
get方法是一个阻塞的方法,同步爬虫,只能执行成功后,才能执行后续的内容
2.异步爬虫的操作
1.多线程或多进程
好处:可以为相关阻塞的操作单独的开启线程或进程,阻塞操作就可以异步操作
弊端:无法无限制的开启多线程或多进程
2.线程池,进程池
1. 好处:降低系统对进程或线程创建或销毁的频率,降低系统的开销2. 弊端:池中的线程或进程的数量有上线
# 导入线程池模块的类
frommultiprocessing.dummyimportPool
# 阻塞对象
defget_page(str):
print('正在下载', str)
time.sleep(2)
print('下载成功', str)
start_time=time.time()
data_list= ['adada', 'bbda', 'ccdas', 'ddasdd']
# 实例化线程池对象
data=Pool(4)
# 将列表中每一个元素传递给get_page方法处理
data.map(get_page, data_list)
end_time=time.time()
print(end_time-start_time)
需求:爬取梨视频的视频数据
1.爬取的视频地址
2.获取页面的源码数据,发起数据解析
3.得到详情页的地址,但是会遇到视频数据动态加载,且要发送请求后才会出现视频的链接地址,但是无法直接通过xpath进行获取
步骤
1. 获取Ajax动态请求,发现携带两个参数,且在详情页面源码中发起播放视频请求后得到真实url如下 图:
2. 但是通过Ajax获取到的json数据中,视频地址url做了伪装,所以做如下处理:
获取json格式数据中的srcUrl字段,使用split进行分割
ajax_json=requests.get(url=ajax_url, params=param,
headers=ajax_header).json()
video_url=ajax_json['videoInfo']['videos']['srcUrl'] # 获取视频下载地址(但是该地址是加密的,需要获取真实的地址)
# https://video.pearvideo.com/mp4/third/20191112/1700467444425-10768115-090200-hd.mp4 伪url
# print(video_url)
# 获取真实下载地址
video_true_url=''
s_list=str(video_url).split('/')
# print(s_list)
foriinrange(0, len(s_list)):
ifi<len(s_list) -1:
video_true_url+=s_list[i] +'/'
else:
ss_list=s_list[i].split('-')
# print(ss_list)
forjinrange(0, len(ss_list)):
ifj==0:
video_true_url+='cont-'+id_+'-' elifj==len(ss_list) -1:
video_true_url+=ss_list[j]
else:
video_true_url+=ss_list[j] +'-' # print(video_true_url)
dict= {
'url': video_true_url,
'name': name
}
urls.append(dict)
使用线程池
defget_video(dict):
url=dict['url']
video=requests.get(url=url, headers=ajax_header).content withopen(dict['name'], 'wb') asf:
f.write(video)
pool=Pool(4)
pool.map(get_video, urls)
pool.close()
pool.join()
3.单线程+异步协程
asyncio
aiohttp:基于异步的请求
requests模块是基于同步爬取的模块
七.selenium模块的基本使用
1. 便捷的获取网站中动态加载的数据
2. 便捷实现模拟登陆
selenium模块
1. 基于浏览器自动化的模块,让浏览器自动化操作
2. 模拟登陆
selenium的使用流程
1. 环境安装
2. 下载浏览器的驱动程序(推荐使用谷歌浏览器)
1. (驱动下载地址)3. 实例化一个浏览器对象
4. 编写基于浏览器的自动化代码
importtime
fromseleniumimportwebdriver
fromselenium.webdriver.chrome.optionsimportOptions fromselenium.webdriver.chrome.serviceimportService fromlxmlimportetree
# 创建Chrome选项
chrome_options=Options()
driver_path='C:/Users/TR/Desktop/chromedriver-win32/chromedriver.exe'
service=Service(driver_path)
driver=webdriver.Chrome(service=service, options=chrome_options)
driver.get('http://www.bspider.top/blogsina/')
# 获取浏览器当前的页面源码数据
page_text=driver.page_source
1. 首先启动浏览器的驱动器
2. 请求网页地址
3. 如果要进行标签的交互,使用send_keys()函数,
4. 进行标签定位,需要使用find_element函数
5. 在新的selenium中,使用到了BY()对象,所以先创建BY对象,再进行选择器的筛选6. 执行js代码,使用函数execute_script函数
7. 关闭浏览器quit()
fromseleniumimportwebdriver
fromselenium.webdriver.chrome.serviceimportService fromselenium.webdriver.chrome.optionsimportOptions fromselenium.webdriver.common.byimportBy
# 初始化
# 创建Chrome选项
chrome_options=Options()
driver_path='C:/Users/TR/Desktop/chromedriver-win32/chromedriver.exe'
service=Service(driver_path)
driver=webdriver.Chrome(service=service, options=chrome_options)
# 请求淘宝网页
driver.get('https://www.taobao.com/')
# # 获取网页源码
# page_text = driver.page_source
# 标签定位
# 创建BY对象
by=By()
# 在搜索框搜索
BY_ID=by.ID
search_input=driver.find_element(by=BY_ID, value='q') # 标签的交互,发送内容
search_input.send_keys('iPhone11')
# 执行一组js程序,向下滚动屏幕
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)') time.sleep(2)
# 点击搜索框
BY_CLASS=by.CLASS_NAME
search_bottom=driver.find_element(by=BY_CLASS, value='btn-search') # 点击搜索按钮
search_bottom.click()
selenium处理iframe(动作链)
1. 如果定位的标签存在于iframe标签中,需要使用方法switch_to.frame(id值),切换作用域
2. 动作链
1. 导入webdriver中的ActionChains,
2. 实例化动作链对象,传递浏览器参数
3. 触发动作链中的操作
4. 使用move_by_offset拖动动作,
5. 使用perform实现动作的操作,
6. 动作完成之后,使用release释放动作链
importtime
fromseleniumimportwebdriver
fromselenium.webdriver.chrome.optionsimportOptions fromselenium.webdriver.chrome.serviceimportService fromselenium.webdriver.common.byimportBy
driver_path='./chromedriver.exe'
options=Options()
service=Service(driver_path)
driver=webdriver.Chrome(service=service, options=options)
driver.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
# 实例化By对象
BY=By()
BY_ID=BY.ID
# 如果定位的标签存在于iframe标签中,则必须通过以下操作进行标签定位
driver.switch_to.frame('iframeResult') # 切换浏览器标签的作用域,不切换则默认作用域外部
div=driver.find_element(by=BY_ID, value='draggable')
# (动作链条)拖动
action=ActionChains(driver)
action.click_and_hold(div) # 按住鼠标左键不放,点击长按指定的标签
foriinrange(5):
# 必须传入x于y两个参数
action.move_by_offset(xoffset=25, yoffset=0).perform() # 让动作连立即执行
perform
time.sleep(0.3) # 停一会
# 释放动作链
action.release()
time.sleep(2)
driver.close()
如果定位的标签存在于iframe标签中,则必须通过以下操作进行标签定位
driver.switch_to.frame('iframeResult') # 切换浏览器标签的作用域,不切换则默认作用域外部div = driver.find_element(by=BY_ID, value='draggable')
ActionChains类的使用方法
click(on_element=None) ——单击鼠标左键
click_and_hold(on_element=None) ——点击鼠标左键,不松开
context_click(on_element=None) ——点击鼠标右键
double_click(on_element=None) ——双击鼠标左键
drag_and_drop(source, target) ——拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开
key_down(value, element=None) ——按下某个键盘上的键
key_up(value, element=None) ——松开某个键
move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标
move_to_element(to_element) ——鼠标移动到某个元素
move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素(左上角坐标)多少距离的位置
perform() ——执行链中的所有动作
release(on_element=None) ——在某个元素位置松开鼠标左键
send_keys(*keys_to_send) ——发送某个键到当前焦点的元素
send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素
项目案例:
模拟登陆qq空间
importtime
fromseleniumimportwebdriver
fromselenium.webdriverimportActionChains
fromselenium.webdriver.chrome.serviceimportService fromselenium.webdriver.chrome.optionsimportOptions fromselenium.webdriver.common.byimportBy
driver_path='./chromedriver.exe'
options=Options()
service=Service(driver_path)
driver=webdriver.Chrome(service=service, options=options)
driver.get('https://i.qq.com/')
# 切换作用域
driver.switch_to.frame('login_frame')
# 点击密码登录
BY=By()
# 1.定位登录标签
BY_ID=BY.ID# 通过ID属性定位
login=driver.find_element(by=BY_ID, value='switcher_plogin')
# 点击密码登录按钮
# 1.实例化动作链对象
action=ActionChains(driver)
action.click(login).perform() # 立即点击
# 等待1秒输入账号密码
time.sleep(1)
# 定位到输入账号与密码的文本框
user=driver.find_element(by=BY_ID, value='u') # 通过ID属性定位到输入账号的文本框pwd=driver.find_element(by=BY_ID, value='p') # 通过ID属性定位到输入密码的文本框
# 发送账号与密码
user.send_keys('1307159954')
time.sleep(2)
pwd.send_keys('556883820lonh')
time.sleep(1)
# 点击登录按钮
log_in=driver.find_element(by=BY_ID, value='login_button') log_in.click()
time.sleep(5)
driver.close()
selenium无可视化界面(无头浏览器)
# 可实现无可视化界面
fromselenium.webdriver.chrome.optionsimportOptions
# 规避被检测的风险
fromselenium.webdriverimportChromeOptions
# 无头浏览器(反检测),不会出现可视化界面
# 实现让selenium规避被检测到的风险
option=ChromeOptions()
option.add_argument('--headless')
option.add_argument('--disable-gpu')
option.add_experimental_option('excludeSwitches', ['enable-automation'])
绕过验证码检测方法
1. 将账号密码添加到浏览器的cookie中
# 需求:登录抖音平台
importtime
fromseleniumimportwebdriver
fromselenium.webdriver.chrome.serviceimportService fromselenium.webdriverimportChromeOptions, ActionChains fromselenium.webdriver.common.byimportBy
driver_path='./chromedriver.exe'
service=Service(driver_path)
# 规避检测
options=ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 实例化浏览器对象
driver=webdriver.Chrome(service=service, options=options)
driver.get('https://i.qq.com/')
# page_text = driver.page_source
# print(page_text)
# 切换作用域
driver.switch_to.frame('login_frame')
# # 获取登录的标签
BY=By()
by_id=BY.ID
log_btn=driver.find_element(by=by_id, value='switcher_plogin') #
# # 动作链
action=ActionChains(driver)
action.click(log_btn).perform()
time.sleep(2)
# 定位到输入账号与密码的文本框
user=driver.find_element(by=by_id, value='u') # 通过ID属性定位到输入账号的文本框
pwd=driver.find_element(by=by_id, value='p') # 通过ID属性定位到输入密码的文本框
user.send_keys('1307159954')
pwd.send_keys('556883820lonh')
time.sleep(1)
login=driver.find_element(by=by_id, value='login_button') action.click(login).perform()
time.sleep(2)
# 将密码添加到浏览器的cookie中,绕过验证码检测
driver.add_cookie({'name': 'u', 'value': 'user'})
driver.add_cookie({'name': 'p', 'value': 'pwd'})
time.sleep(1)
driver.back()
driver.get('https://user.qzone.qq.com/1307159954')
time.sleep(5)
driver.quit()
八.scrapy框架
框架:继承了很多功能且具有很强通用性的一个项目模板
scrapy:
1. 爬虫中封装好的一个框架
1. 功能:
1. 高性能持久化存储操作
2. 异步的数据下载
3. 高性能的数据解析
4. 分布式
scrapy框架的基本使用:
1.windows端
1. pip install wheel
2. 下载twisted:3. 安装twisted: pip install 下载的文件
4. pip install pywin32
5. pip install scrapy
2.创建scrapy工程
1. scrapy startproject 工程名
2. 在spider创建爬虫文件
1. scrapy genspider spiderName (url)
2.
3. 执行工程
1. scrapy crawl spdierName
4. scrapy的数据解析
# 数据解析
defparse(self, response, **kwargs):
# 作者的名称和段子的内容
a_list=response.xpath('//div[@class="pl2"]/a')
# print(a_list)
foraina_list:
# xpath返回的是列表,但是列表元素一定是Selector类型的对象
# extract可以将Selector对象中data参数存储对字符串取出来
title=a.xpath("./text()")[0].extract()
# 列表也调用了extract,则表示将列表中内一个Selector对象中的data对应的字符串提
取出来
# print(title)
# break
# print(response.text)
5. scrapy持久化存储
首先创建存储的位置 1. open_spider, 在创建存储的内容 process_item ,最后关闭文件close_spider
1. 基于终端命令
1. 要求:只可以将parse方法的返回值存储到本地的文本文件中
2. 持久化存储的文本文件内容有限制
3. 指令scrapy crawl spiderName -o 存储路径
4. 好处:简洁高效便捷,但是局限性比较强,只能存储到指定后缀的文本文件中2. 基于管道(pipeline)
1. 编码流程
1. 数据解析
2. 在item类中定义相关的属性
3. 将解析的数据封装存储到item类型的对象中
4. 将item类型的对象提交到管道进行持久化存储
5. 在管道类的process_item类中,将其接收到的 item 对象持久化存储6. 在配置文件中开启管道
面试题:将爬取的数据一份存储到数据库,一份存储到本地
1. 管道类中一个管道对应的是将数据存储到一种平台
2. 爬虫文件提交的item只会给管道文件中第一个被执行的管道类接收
3. 在process_item中的 return item表示将item传递给下一个即将被执行的管道类
多管道类
ITEM_PIPELINES= {
"scrapyPro.pipelines.ScrapyproPipeline": 300, # 300表示优先级,数值越小优先级越高
"scrapyPro.pipelines.MysqlPipleine": 301,
}
# 定义管道类将数据存储到数据库
classMysqlPipleine(object):
# 配置数据库链接
conn=None
# 配置游标执行sql语句
cursor=None
defopen_spider(self, spider):
self.conn=pymysql.Connect(host='127.0.0.1', port=3306,
user='root',
password='123456789', db='scrapy',
charset='utf8')
defprocess_item(self, item, spider):
self.cursor=self.conn.cursor()
try:
# 游标执行方法调用执行sql
self.cursor.execute('insert into movie values("%s")'% item["title"])
self.conn.commit()
exceptExceptionase:
print(e)
self.conn.rollback()
returnitem
defclose_spider(self, spider):
self.cursor.close()
self.conn.close()
基于spider的全站数据爬取
爬取彼岸图片:
1. 将网站中的某板块下的全部页码对应的页面数据进行爬取和解析
实现方式:
1. 将所有的url添加到start_url中,繁琐
2. 自行手动的进行爬取
1. 手动发送请求
1. yield scrapy.Request(url=url模板, callback=self.parse) callback:进行数据解析2. 创建一个新的url模板
start_urls= ["https://pic.netbian.com/4kmeinv/"]
# 生成一个通用的url模板通用的url,不能被改变
page_num=2
url='https://pic.netbian.com/4kmeinv/index_%d.html'
defparse(self, response, **kwargs):
b_list=response.xpath('//ul[@class="clearfix"]/li/a/b')
forbinb_list:
img_name=b.xpath('./text()').extract_first() # 图片名print(img_name)
# print(f'-------打印共{self.page_num}页--------')
# 提交到管道进行持久化存储
item=BizhiItem()
item['img_name'] =img_name
yielditem
ifself.page_num<=8:
# 执行翻页操作
self.page_num+=1
# 新建一个变量表示请求新的url页面,使用format进行格式化new_url=format(self.url%self.page_num)
# 手动发送请求:callback 回调函数,用作数据解析
yieldscrapy.Request(url=new_url, callback=self.parse)
scrapy五大核心组件
请求传参
使用场景:
1. 爬取解析的数据不在同一张页面中(深度爬取)请求传参
2. 需求:爬取boss直聘的岗位名称,岗位描述
图片类型的爬取ImagePipeline
1. 基于scrapy爬取字符串类型的数据和爬取图片类型的数据的区别
1. 字符串:只需要使用xpath进行解析并提交到管道进行持久化存储
2. 图片:xpath解析出图片src的属性值,单独对图片的地址发起请求获取图片的二进制类型数 据
2. ImagesPipeline
1. 只需要对img的src的属性值进行解析,提交到管道,管道就会对图片的src进行请求发送并获 取图片的二进制数据
3. 使用流程
需求:爬取站长素材的高清图片
1. 数据解析(图片的地址)
2. 将存储图片地址的item提交到定制的管道类
3. 在管道文件中定制一个基于ImagesPipeline的一个管道类,并重写其中的三个方法
4. 在配置文件中
1. 将图片存储到指定的位置添加 IMAGES_STORE = './images' 2. 指定开启的管道类
中间件
1. 引擎与下载器存在一个中间件(下载中间件:批量拦截到工程中所有的请求和响应) 1. 拦截请求:
1. 可以进行UA伪装 process_request(在settings中的UA伪装是全局的) 2. 代理IP process_exception:return request
2. 拦截响应
1. 篡改响应数据,响应对象
2. 需求:爬取网易新闻的新闻标题和新闻的内容
1. 通过数据解析获取五大板块所要获取的url
2. 每个板块对应的新闻标题都是动态加载出来的
3. 通过解析出每一条新闻详情页的url,获取页面源码解析出新闻的内容2. 引擎与spider存在一个中间件(爬虫中间件)
CrawlSpider:类,Spider的一个子类
全站数据爬取的方式
1. 基于spider:手动请求发送
2. 基于CrawlSpider类
3. CrawlSpider的使用
1. 创建一个工程
2. cd xxx
3. 创建爬虫文件(CrawlSpider)
指令:scrapy genspider -t crawl xxx
链接提取器:根据指定的规则(allow=“指定规则”)
规则解析器:将链接提取器提取到到内容进行指定规则的解析及(callback参数指定的方法进行数据解析)
importscrapy
fromscrapy.linkextractorsimportLinkExtractor
fromscrapy.spidersimportCrawlSpider, Rule
classSunSpider(CrawlSpider):
name="sun"
# allowed_domains = ["www.xxx.com"]
start_urls= ["https://wz.sun0769.com/political/index/politicsNewest?
id=1&page="]
# 链接提取器:根据指定规则进行指定连接的提取(allow="正则")
# link = LinkExtractor(allow=r"id=1&page=\d+"),
rules= (
# 规则解析器,实例化rule对象,将链接提取器提取道德链接进行制定规则的解析操作(callback="parse_item")
Rule(LinkExtractor(allow=r"id=1&page=\d+"), callback="parse_item", follow=False),
)
defparse_item(self, response):
print(response)
自定义作业:爬取动态网站加载的图片数据
已做完