一、requests基础
-
爬虫中一个基于网络请求的模块
-
安装:pip install requests
-
作用:模拟浏览器发起请求
-
编码流程
-
- 指定url
-
- 发起请求
-
- 获取响应数据(爬取到的也买你源码数据)
-
- 持久化存储
-
-
爬取搜狗首页的源码数据
import requests
# 1.指定url
url = 'https://www.sogou.com'
# 2.发起请求 get方法的返回值为响应对象
response = requests.get(url=url)
# 3.获取响应数据
# .text:返回的是字符串形式的响应数据
page_text = response.text
# 4.持久化存储
with open('./sogou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
- 实现一个简易网页采集器
- 基于搜狗针对不同的关键字将其对应的页面数据进行爬取
- 参数动态化:
- 如果请求url携带的参数,且我们想要将携带的参数进行动态操作那么我们必须:
- 1.将携带的动态参数以键值对的形式封装到一个字典中
- 2.将盖子点作用到get方法的params参数中即可
- 3.需要将原始携带参数的url中将携带的额参数删除即可
- 如果请求url携带的参数,且我们想要将携带的参数进行动态操作那么我们必须:
keyWord = input('enter a key word>>>:')
# 携带了请求参数的url,如果想要爬取不同关键字对应的页面,我们需要将url携带的参数进行动态化
# 实现参数动态化:
params = {
'query':keyWord
}
url = 'https://www.sogou.com/web'
# params参数(字典):保存请求时url携带的参数
response = requests.get(url=url,params=params)
page_text = response.text
fileName = keyWord + '.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(fileName,'爬取完毕!!!')
enter a key word>>>:jay
jay.html 爬取完毕!!!
-
上述建议采集器代码出现问题:
- 1.乱码的问题
- 2.数据丢失
-
解决乱码问题
keyWord = input('enter a key word>>>:')
# 携带了请求参数的url,如果想要爬取不同关键字对应的页面,我们需要将url携带的参数进行动态化
# 实现参数动态化:
params = {
'query':keyWord
}
url = 'https://www.sogou.com/web'
# params参数(字典):保存请求时url携带的参数
response = requests.get(url=url,params=params)
# 修改相应数据的编码格式
# encoding返回的是响应数据的原始编码格式,如果给其赋值表示修改了响应数据的编码格式
requests.encoding = 'utf-8'
page_text = response.text
fileName = keyWord + '.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(fileName,'爬取完毕!!!')
enter a key word>>>:jay
jay.html 爬取完毕!!!
-
处理乱码后,页面显示【异常访问请求】导致请求数据的缺失。
- 异常的访问请求
- 网站后台已经检测出该次请求不是通过浏览器发起的请求而是通过爬虫程序发起的请求。(不是通过浏览器发起的请求都是异常请求)
- 网站的后台是如何知道请求是不是通过浏览器发起的呢?
- 是通过判定请求的请求头中的user-agent判定的
- 什么是User-Agent
- 请求载体的身份标识
- 什么是请求载体:
- 浏览器
- 浏览器的身份标识是统一固定的,身份标识可以从抓包工具中获取
- 爬虫程序
- 身份标识是各不相同的
- 浏览器
- 异常的访问请求
-
第二种反爬机制
- UA检测:网站后台会检测请求对应的User-Agent,以判断当前请求是否为异常请求
-
反反爬策略:
- UA伪装:被作用到了部分的网站中,日后我们写爬虫程序时都默认带上UA检测操作
- 伪装流程
- 从抓包工具中捕获到某个基于浏览器请求的User-Agent的值,将其伪装作用到一个字典中,将该字典作用到请求方法(get、post)的headers参数中即可。
keyWord = input('enter a key word>>>:')
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'
}
# 携带了请求参数的url,如果想要爬取不同关键字对应的页面,我们需要将url携带的参数进行动态化
# 实现参数动态化:
params = {
'query':keyWord
}
url = 'https://www.sogou.com/web'
# params参数(字典):保存请求时url携带的参数
response = requests.get(url=url,params=params,headers=headers)
# 修改相应数据的编码格式
# encoding返回的是响应数据的原始编码格式,如果给其赋值表示修改了响应数据的编码格式
requests.encoding = 'utf-8'
page_text = response.text
fileName = keyWord + '.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(fileName,'爬取完毕!!!')
- 爬取豆瓣电影中的电影详细数据
- url:https://movie.douban.com/typerank?type_name=动作&type=5&interval_id=100:90&action=
- 动态加载数据的捕获
- 什么叫做动态加载的数据?
- 我们通过requests模块进行数据爬取无法每次都是先可见即可得。
- 有些数据是通过非地址栏中的url请求到的数据,而是其他请求请求到的数据,那么这些通过其他请求到的数据就是动态加载的数据
- 什么叫做动态加载的数据?
- 如何检测网页中是否存在动态加载数据?
- 基于抓包工具进行局部搜索。
- 当前网页中打开抓包工具,捕获到地址栏对应的数据包,在该数据包的response选项卡搜索我们想要爬取的数据,如果搜素到了则表示不是动态加载的,否则表示数据为动态加载的
- 基于抓包工具进行局部搜索。
url = 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='
response = requests.get(url=url,headers=headers)
page_text = response.text
with open('./douban.html','w',encoding='utf-8') as fp:
fp.write(page_text)
- 如果数据为动态加载,那么我们如何获取到动态加载的数据?
- 基于抓包工具进行全局搜索
- 定位到动态加载数据对应的数据包,从该数据包中就可以提取出
- 请求url
- 请求方式
- 请求携带的参数
- 看到响应对象
- 基于抓包工具进行全局搜索
# 豆瓣现在内部做了更新,目前此种方法不可行。但思路是正确的。豆瓣好鸡贼啊,爬取时需要在请求头里面加上reference参数,用来标识当前请求的url是从哪里跳转过来的url = 'https://movie.douban.com/j/chart/top_list?'params = { 'type': '5', 'interval_id': '100:90', 'action':' ', 'start': '0', 'limit': '50',}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', 'Referer': 'https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action='}response = requests.get(url=url,params=params,headers=headers)# .json()将获取的字符串形式的json数据反序列化成字典或者列表对象page_json = response.json()#解析出电影的名称+评分for movie in page_json: name = movie['title'] score = movie['score'] print(name,score)
-
思考:基于抓包工具进行全局搜索不一定可以每次都能定位到动态加载数据对应的数据包?
-
原因:如果动态加载的数据是经过加密的密文处理数据
-
分页数据的爬取操作
- 爬取肯德基的餐厅位置数据
-
分析:
- 1.在录入关键字的文本框中录入关键字按下搜索按钮,发起的是一个ajax请求
- 当前页面刷新出来的位置信息一定是通过ajax请求请求到的数据
- 2.基于抓包工具定位到的ajax请求的数据包,从该数据包中捕获到:
- 请求的url
- 请求方式
- 请求携带的参数
- 看到响应数据
- 1.在录入关键字的文本框中录入关键字按下搜索按钮,发起的是一个ajax请求
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'data = { 'cname': '', 'pid': '', 'keyword': '北京', 'pageIndex': '1', 'pageSize': '10',}headers = { 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',}# data参数就是post方法中处理参数动态化的参数response = requests.post(url=url,headers=headers,data=data)page_json = response.json()for dic in page_json['Table1']: title = dic['storeName'] addr = dic['addressDetail'] print(title,addr)
# 爬取的是多页数据url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'for page in range(1,9): data = { 'cname':'', 'pid':'', 'keyword': '北京', 'pageIndex': str(page), 'pageSize': '10', } # data参数就是post方法中处理参数动态化的参数 response = requests.post(url=url,headers=headers,data=data) page_json = response.json() for dic in page_json['Table1']: title = dic['storeName'] addr = dic['addressDetail'] print(title,addr)
requests基础作业
- 爬取药监总局中的企业详情数据
- url:http://scxk.nmpa.gov.cn:81/xk/
- 需求:
- 将首页中每一家企业的详情数据进行爬取。
- 每一家企业详情页对应的数据
- 将前五页企业的数据爬取即可。
- 将首页中每一家企业的详情数据进行爬取。
- 难点:
- 用不到数据解析
- 所有的数据都是动态加载出来的
- 提示:先试着将一家企业额详情页的详情数据怕取出来,然后子啊去爬取多家企业的数据
二、requests高级
- cookies
- 代理机制
- 验证码的识别
- 模拟登录
cookies:
-
是存储在客户端的一组键值对
-
web中cookies的典型应用:
- 免密登录
-
cookies和爬虫之间的关联
- 有时,对一张页面进行请求的时候,如果请求的过程中不懈怠cookies的话,那么我们是无法请求到正确的页面数据的。因此cookies是爬虫中一个非常典型且常见的反爬机制
-
需求:爬取雪球网中的咨询信息:https://www.xueqiu.com
-
分析:
- 1.判定爬取的咨询数据是否为动态加载的
- 相关的更多咨询数据是动态加载的,滑动滚轮到底部的时候会自动加载出更多的咨询数据。
- 2.定位到ajax请求的数据包,提取出请求的url。响应数据为json形式的咨询数据
- 1.判定爬取的咨询数据是否为动态加载的
import requestsheaders = { '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',}
url = 'https://stock.xueqiu.com/v5/stock/realtime/quotec.json?symbol=.DJI,.IXIC,.INX&_=1604326144962'page_text = requests.get(url=url,headers=headers).json()page_text
-
问题:我们没有请求到我们想要的数据
-
原因:我们没有严格意义上模拟浏览器发起请求。
- 处理:可以将浏览器发起请求的请求头,全部粘贴到headers字典中,将headers作用到requests的请求操作中即可
-
cookies的处理方式
- 方式一:手动处理
- 将抓包工具中的cookies粘贴到headers中
- 弊端:cookies如果过了有效时长则该方式失效
- 方式二:自动处理
- 基于Session对象实现自动化处理
- 如何获取一个session对象:requests.Session()返回一个session对象
- session对象的作用
- 该对象可以向requests一样调用get和post发起指定的请求。只不过如果在使用session发起请求,则该此请求就是携带cookies进行的请求发送。
- 在爬虫中使用session的时候,session对象至少会被使用几次?
- 两次。第一次使用session是为了将cookies捕获且存储到session对象中。第二次请求就是携带cookies进行的请求发送。
- 方式一:手动处理
session = requests.Session() # 创建好了session对象# 第一次使用session捕获且存储cookies,猜测对雪球网首页发起请求的时候可能会产生cookiesmain_url = 'https://xueqiu.com'session.get(url=main_url,headers=headers) # 捕获且存储cookiesurl = 'https://stock.xueqiu.com/v5/stock/realtime/quotec.json?symbol=.DJI,.IXIC,.INX&_=1604326144962'page_text = session.get(url=url,headers=headers).json() # 携带cookies发起的请求page_text
代理操作
-
在爬虫中,所谓的代理指的是什么?
- 就是代理服务器
-
代理服务器的作用是什么
- 就是用来转发请求和响应的。
-
在爬虫中为什么需要使用代理服务器?
- 如果我们的爬虫在短时间内对服务器发起高频的请求,那么服务器会检测到这样一个异常的行为请求,就会江该请求对饮你给的IP禁掉,以为这client设备就无法对服务器端再次进行请求(IP被禁止掉了)
- 如果IP被禁止掉,我们就可以使用代理服务器进行请求转发,破解IP被禁的反爬机制。因为使用代理后,服务器接收到的请求对应的IP地址就是代理服务器而不是我们真正的客户端。
-
代理服务器分为不同的匿名度
- 透明代理:如果你使用了该形式的代理,服务器知道你是用哪个了代理机制也知道你的真实IP
- 匿名代理:知道你使用代理,但是不知道你的真实IP
- 高匿代理:不知道你是用来代理也不知带你的真实IP
-
代理的类型
- http:代理只能转发http的请求
- https:代理只能转发https的请求
-
代理服务器
- 快代理
- 西刺代理
- goubanjia
- 代理精灵(推荐):http://http.zhiliandaili.cn
-
封装一个代理池
url = '代理ip地址'page_text = requests.get(url=url,headers=headers).texttree = etree.HTML(page_text)proxy_list = tree.xpath('//body//text()')http_proxy = [] # 代理池for proxy in proxy_list: dic = { 'https':proxy } http_proxy.append(dic)
- 对西刺代理翻一个高频请求,将其让我的本级ip封禁
# url模板url = 'https://www.xicidaili.com/nn/%d'ips = []for page in range(1,11): new_url = format(url%page) page_text = requests.get(url=new_url,headers=headers).text tree = etree.HTML(page_text) # 在xpath表达式中不可以出现tbody标签 tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:] for tr in tr_list: ip = tr.xpath('./td[2]/text()'[0] ips.append(ip)print(len(ips))
- 使用代理机制破解ip被封禁的效果
import random
url = '代理ip地址'page_text = requests.get(url=url,headers=headers).texttree = etree.HTML(page_text)proxy_list = tree.xpath('//body//text()')http_proxy = [] # 代理池for proxy in proxy_list: dic = { 'https':proxy } http_proxy.append(dic)# url模板url = 'https://www.xicidaili.com/nn/%d'ips = []for page in range(1,11): new_url = format(url%page) # 让当次请求使用代理机制,就可以更换请求的ip地址 page_text = requests.get(url=new_url,headers=headers,proxies=random.choices(http_proxy)).text tree = etree.HTML(page_text) # 在xpath表达式中不可以出现tbody标签 tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:] for tr in tr_list: ip = tr.xpath('./td[2]/text()'[0] ips.append(ip)print(len(ips))
验证码识别
- 基于线上的打码平台识别验证码
- 大码平台
- 超级鹰(推荐):http://www.chaojiying.com/
- 1.注册【用户中心的身份】
- 2.代理【用户中心的身份】
- 1.查询余额,请充值
- 2.创建一个软件ID
- 3.下载一个势力代码
- 云打码
- 打码兔
- 超级鹰(推荐):http://www.chaojiying.com/
import requestsfrom hashlib import md5class Chaojiying_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)', } def PostPic(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) return r.json() def ReportError(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) return r.json()
def transformImgCode(imgPath,imgType): """ imgPath:验证码图片地址 imgType:验证码的难度等级,详情在超级鹰中查看 """ chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '96001') im = open(imgPath, 'rb').read() return chaojiying.PostPic(imgPath, imgType)['pic_str']
模拟登陆
- 流程
- 对电极登陆按钮对应的请求进行发送(post请求)
- 处理请求参数:
- 用户名
- 密码
- 验证码
- 其他的防伪参数
# 识别验证码
url = 'https://so.gushiwen.org/user/login.aspx?from=http://gushiwen.rog/user/collect..aspx'
page_text = requests.get(url=url,headers=headers).text
# 解析出验证码图片的地址
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.org' + tree.xpath('//*[@id="imgCode"]/@src')[0]
# 将验证码图片保存到本地
img_data = requests.get(url=img_src,headers=headers).content
with open('./code.jpg','wb') as fp:
fp.write(img_data)
# 识别验证码
code_text = transformImgCode('./code.jpg',1902)
print(code_text)
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'
data = {
'__VIEWSTATE': 'H93glJnBqY143NQ1cBlUXPRVPPSrbMUiZ3aRPa6P03btoVRKlQFnHWzYbwWfZmid1DjejOs8I0kgd1xRhpQqjaAWz/QmiSWhEbSLDcM1NOww/u79iGRFgGnxLvM=',
'__VIEWSTATEGENERATOR': 'C93BE1AE',
'from': 'http://so.gushiwen.cn/user/collect.aspx',
'email': '2631937454@qq.com',
'pwd': 'xc2631937454@',
'code': 'upei',
'denglu': '登录',
}
# 对点击登录按钮发起请求。获取了成功登录后对应的页面源码数据
page_text_login = requests.post(url=login_url,headers=headers,data=data).text
with open('./gushiwen.html','w',encoding='utf-8') as fp:
fp.write(page_text_login)
-
模拟登陆失败了
- 原因
- cookies
- 不可能是验证码的问题
- 原因
-
携带cookies进行模拟登陆
session = requests.Session()# 识别验证码url = 'https://so.gushiwen.org/user/login.aspx?from=http://gushiwen.rog/user/collect..aspx'page_text = session.get(url=url,headers=headers).text# 解析出验证码图片的地址tree = etree.HTML(page_text)img_src = 'https://so.gushiwen.org' + tree.xpath('//*[@id="imgCode"]/@src')[0]# 将验证码图片保存到本地img_data = session.get(url=img_src,headers=headers).contentwith open('./code.jpg','wb') as fp: fp.write(img_data)# 识别验证码code_text = transformImgCode('./code.jpg',1902)print(code_text)login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'data = { '__VIEWSTATE': 'H93glJnBqY143NQ1cBlUXPRVPPSrbMUiZ3aRPa6P03btoVRKlQFnHWzYbwWfZmid1DjejOs8I0kgd1xRhpQqjaAWz/QmiSWhEbSLDcM1NOww/u79iGRFgGnxLvM=', '__VIEWSTATEGENERATOR': 'C93BE1AE', 'from': 'http://so.gushiwen.cn/user/collect.aspx', 'email': '2631937454@qq.com', 'pwd': 'xc2631937454@', 'code': code_text, # 动态变化 'denglu': '登录',}# 对点击登录按钮发起请求。获取了成功登录后对应的页面源码数据page_text_login = session.post(url=login_url,headers=headers,data=data).textwith open('./gushiwen.html','w',encoding='utf-8') as fp: fp.write(page_text_login)
-
在请求参数中如果看到一组乱序的请求参数,最好去验证这组请求参数是否为动态变化
-
处理:
- 方式1:常规来讲一般动态变化的请求参数会被影藏在前端页面中,那么我们就去前端页面源码中获取
- 方式2:如果前端页面没有的话,我们可以基于抓包工具进行全局搜索
-
基于百度AI是吸纳的爬虫功能
- 图像识别
- 语音识别/合成
- 自然语言处理
-
使用流程:
- 点击控制台进行登录
- 选择想要实现的功能
- 实现功能下创建一个app
- 选择对应的PythonSDK文档进行代码实现
requests高级作业
- 将段子王中的段子内容爬取到本地,然后给予语音合成将段子合称为mp3格式的音频文件。
- 爬取梨视频中的短视频数据:
- url:http://www.pearvideo.com/category_1
- 将最热板块下的短视频存储到本地
拓展
- 站长素材高清图片下载
- 反爬机制:图片懒加载,广泛应用于一些图片的网站中
- 只有当图片被显示在浏览器可视化的范围内才会将img的伪属性变成真正的的属性。如果是requests发起的请求,requests请求是没有可视化范围的,因此我们一定要解析的是img伪属性的属性值(图片地址)
- 反爬机制:图片懒加载,广泛应用于一些图片的网站中
- 学过的反爬机制
- robotes
- UA伪装
- 动态加载数据的捕获
- 图片懒加载