python爬虫之模拟登录
1.验证码识别
1.1 验证码和爬虫之间的联系?
验证码属于门户网站的一种反爬机制。若需使用爬虫来进行网站爬取数据的同时,需要识别验证码,则需先识别验证码图片中的数据,用于模拟登录操作。
1.2 识别验证码的操作
(1)人工肉眼识别。简而言之,就是事先将需爬取网站所需要识别的图片验证码下载下来,或先通过浏览器打开需爬取网站页面,识别获取图片验证码数据。不推荐此类方法,效率低且不适用于图片复杂图较高的验证码识别。
(2)第三方自动识别。通过借助于第三方线上平台(这些平台可以制定相关图片验证的操作和机制),帮助我们识别某些数据类型的验证码中的相关数据。推荐使用。 如:超级鹰验证码识别-专业的验证码云端识别服务,让验证码识别更快速、更准确、更强大 (chaojiying.com)
1.3 超级鹰的使用流程
(1)注册:普通用户和开发者用户
(2)登录
- 普通用户的登录:查询该用户是否还有剩余的题分
- 开发者用户的登录:
- 创建一个软件:用户中心 —》软件ID —》生成一个ID(soft_id) —》录入软件名称 —》提交(软件ID和密钥)
- 下载示例代码:开发文档 --》 超级鹰图像识别python语言demo下载 --》点击这里下载 —》运行时登录创建的用户账号,扣除用户积分进行图像识别,一次识别扣除10积分
实战案例:识别古诗文网登录页面中的验证码。
# 分析:使用超级鹰平台识别验证码的编码流程:
# 1.将验证码图片进行本地下载
# 2.调用平台提供的示例代码进行图片数据识别
import requests
from lxml import etree
import os
import chaojiying
if __name__ == "__main__":
# 指定古诗文网站的url
url = "https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx"
headers = {
'User-Agent': ''
}
# 进行古诗文网站的页面爬取,获得页面数据
page_text = requests.get(url=url,headers=headers).text
# 对古诗文网站源码数据进行解析,获取验证码数据
tree = etree.HTML(page_text)
img_url = "https://so.gushiwen.cn" + tree.xpath('//img[@id="imgCode"]/@src')[0]
print(img_url)
# 获取验证码图片,并将其保存至本地
img_data = requests.get(url=img_url,headers=headers).content
img_name = "验证码.jpg"
# 图片保存路径
# if not os.path.exists:
# os.mkdir('../../data/验证码识别/')
img_path = "../../data/验证码识别/" + img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
# 识别验证码数据并返回
code_data = chaojiying.Chaojiying_Client(username='账户名',password='密码',soft_id='9007').Recongination(file_name='../../data/验证码识别/验证码.jpg')['pic_str']
print(code_data)
2.模拟登录
2.1 登录本质
网页上以及app中输入账号密码的过程称为登录,不经过登录有些页面是无法直接跳转的。而模拟登录就是模拟手动输入账号密码的过程,获得登录成功后的cookie,以此cookie直接访问需要登录才能进入的页面(例如用户个人信息页面),从而获取数据。
2.2 什么是模拟登录?
模拟登录就是通过程序发送post请求某个应用网站的登录地址,获取登录cookie信息。
2.3为什么需要模拟登录?
有些网站的信息**需要用户登录之后方可获取,故需要模拟用户登录,以便爬取基于某些用户的用户信息。**
2.4 模拟登录的目的
爬取基于某些用户的用户网站信息。
2.5 案例
(1)对古诗文网进行模拟登录
- 分析:
- 点击登录按钮之后会发起一个post请求
- post请求中会携带登录之前录入的相关的登录信息(用户名,密码,验证码…)
- 验证码具有的特点:每次发起请求,验证码都会动态改变,需重点分析
- 代码如下:
# 编码流程:
# 1.验证码的识别:获取验证码图片的文字数据
# 2.对post请求进行发送(处理请求参数)
# 3.对响应数据进行持久化存储
import requests
from lxml import etree
from chaojiying_Python import chaojiying
if __name__ == "__main__":
# 1.验证码的识别
# 1.1 获取验证码的图片数据
url = "https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx"
headers = {
'User-Agent': ''
}
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
img_url = "https://so.gushiwen.cn" + tree.xpath('//img[@id="imgCode"]/@src')[0]
img_data = requests.get(url=img_url,headers=headers).content
img_path = '../data/test_1.jpg'
with open(img_path,'wb') as fp:
fp.write(img_data)
print('验证码保存成功!!!')
# 1.2 识别验证码数据
code_data = chaojiying.Chaojiying_Client(username='账户名',password='密码',soft_id='9007').Recongination(file_name='../data/test_1.jpg')['pic_str']
# 2.对post请求进行发送
login_url = "https://so.gushiwen.cn/user/login.aspx"
data = {
"__VIEWSTATE":"mvj4doUURZ6yq81juT7sT8WEIM7xs2N19W58taJhJIorP6ZE9lkAgFaAMERbLe8AEZqnptvYKGHI9AJEqqLfGYO++5HIfPY/00dwpTOmRdeclaN337FNaDaE6xQh4goBbGubTbTixa+18xBIrwSMH6igDLU=",
"__VIEWSTATEGENERATOR":"C93BE1AE",
"from":"http://so.gushiwen.cn/user/collect.aspx",
"email":"", # 账户名
"pwd":"", # 密码
"code":code_data,
"denglu":"登录"
}
response = requests.post(url=login_url,headers=headers,data=data)
if response.status_code == 200:
login_page_text = response.text
with open('gushiwen.html','w',encoding='utf-8') as fp:
fp.write(login_page_text)
print('登录成功!')
else:
print('登陆失败')
(2)模拟古诗网用户登录,并获取用户收藏数据
此案例中涉及到的问题:
(1)无法请求到对应页面数据(用户个人中心)
- 形成原因:
- 发起的第二次基于个人主页页面请求的时候,服务器端并不知道该次请求是基于登录状态下的请求。
- 核心原因:服务器会根据请求中的cookie值来判断用户是否在登录状态,若是,才会显示与用户数据对应的用户主页;反之,则会显示没有携带用户数据的页面。
- http与https协议的特点:并不会保存客户端发起请求的用户状态信息,即若只是通过url进行页面数据跳转,则用户状态信息默认为未登录
- 注意:若只是单纯的通过requests库对用户的个人中心界面发起请求,会导致触发网站的登录锁(即网站会通过获取网页端的cookie进行用户登录状态判断,若并未登录用户,则不会显示用户数据的个人中心界面。
- 解决办法:通过获取用户登录的cookie数据,发送第二次请求时携带用户登录的cookie数据
- cookie的作用:用来让服务器端记录客户端的相关状态。
- cookie值的来源:客户端或爬虫模拟登录发起post请求后,由服务器端创建。在客户端发起登录请求后,服务器端会对该请求的响应头设置**set-cookie**,其对应的便是cookie值。
- 设置cookie的方法:
-
(1)手动处理:通过抓包工具获取cookie值,将该值封装到headers中。(不建议使用这种方法。理由如下)
- cookie具有一定**时效性,若使用cookie超过一定时间,cookie会失效;其次,某些网站里的cookie具有动态性,即每发起一次请求,对应的cookie值会相应发生改变,通用性不高**。
-
(2)自动处理:通过使用session对象进行模拟登录post请求的发送
-
session:在计算机中,尤其是在网络应用中,称为“会话控制”。
-
Session对象的特性:session对象会存储特定用户会话所需的属性及配置信息。
-
session的作用:
- 存储用户的相关状态信息,直到此次会话结束(一个会话就是一个请求,若用户在选择关闭浏览器后,对应的session会被销毁)。即若在*请求过程中产生了cookie*,则**该cookie会被自动存储或携带在该session对象中**。
- 可以进行请求的发送。
-
注意:一个Session的概念需要包括特定的客户端,特定的服务器端以及不中断的操作时间。A用户和C服务器建立连接时所处的Session同B用户和C服务器建立连接时所处的Session是两个不同的Session。
-
session的来源:当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。
-
自动处理实现流程:
-
1.创建一个session对象:session = requests.Session()
-
2.使用session对象进行模拟登录post请求的发送(cookie就会被存储在session中)。
-
3.session对象对个人主页对应的get请求进行发送(携带了cookie)
-
-
# 流程分析:
# 1.验证码的识别:获取验证码图片的文字数据
# 2.对post请求进行发送(处理请求参数)
# 3.对响应数据进行持久化存储
# 4.进入用户个人中心界面
# 5.解析用户个人中心界面数据,获取用户个人信息数据
# 注意:此案例并未成功获取到用户个人数据
# 没有请求到对应页面数据的原因:
# 发起的第二次基于个人主页页面请求的时候,服务器端并不知道该次请求是基于登录状态下的请求。
if __name__ == "__main__":
# 1.验证码的识别
# 1.1 获取验证码的图片数据
url = "https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx"
headers = {
'User-Agent': ''
}
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
img_url = "https://so.gushiwen.cn" + tree.xpath('//img[@id="imgCode"]/@src')[0]
img_data = requests.get(url=img_url,headers=headers).content
img_path = '../data/验证码识别/test_1.jpg'
with open(img_path,'wb') as fp:
fp.write(img_data)
print('验证码保存成功!!!')
# 1.2 识别验证码数据
code_data = chaojiying.Chaojiying_Client(username='超级鹰账户',password='超级鹰账户密码',soft_id='07').Recongination(file_name='../data/test.jpg')['pic_str']
# 2.对post请求进行发送
login_url = "https://so.gushiwen.cn/user/login.aspx"
data = {
"__VIEWSTATE":"mvj4doUURZ6yq81juT7sT8WEIM7xs2N19W58taJhJIorP6ZE9lkAgFaAMERbLe8AEZqnptvYKGHI9AJEqqLfGYO++5HIfPY/00dwpTOmRdeclaN337FNaDaE6xQh4goBbGubTbTixa+18xBIrwSMH6igDLU=",
"__VIEWSTATEGENERATOR":"C93BE1AE",
"from":"http://so.gushiwen.cn/user/collect.aspx",
"email":"", #古诗文账户
"pwd":"", #古诗文密码
"code": code_data,
"denglu":"登录"
}
print('验证码识别为:',code_data)
# 创建session对象
session = requests.Session()
# 使用session对象发起用户模拟登录请求
response = session.post(url=login_url,headers=headers,data=data)
if response.status_code == 200:
login_page_text = response.text
with open('登录后界面.html','w',encoding='utf-8') as fp:
fp.write(login_page_text)
print('登录成功!')
# 对我的个人中心页面进行数据分析,获取我的收藏中的诗文数据
# index_tree = etree.HTML(login_page_text)
# college_url = "https://so.gushiwen.cn" + index_tree.xpath('//div[@class="son1"]/a[6]/@href')[0]
# print(college_url)
# 进入到我的个人中心页面
# 编码流程分析:用户登录---》 进入用户端的首页 ---》 进入我的页面 ---》 点击诗词 ---》 获取到我的关于诗词的收藏的数据
# 注意,若无法进入到用户主页,需使用携带cookie的session对象进行请求发送,服务器会根据cookie值判断用户是否登录
college_url = 'https://so.gushiwen.cn/user/collect.aspx?type=s&id=4380460&sort=t'
response = session.get(url=college_url,headers=headers)
response.encoding = response.apparent_encoding
content_page_text = response.text
with open("我的个人中心界面.html",'w',encoding='utf-8') as fp:
fp.write(content_page_text)
content_page_tree = etree.HTML(content_page_text)
# 解析获取我的收藏中诗词数据
div_list = content_page_tree.xpath('//div[@class="sons"]/div[@class="cont"]')
print(div_list)
for div in div_list:
content_text = div.xpath('./a/text()')
print("我的收藏词句为:",content_text)
else:
print('登陆失败')
注意:以上代码并未成功获取到对应用户的收藏数据,并未进入到用户个人中心界面
3.代理
3.1 代理初始
大多数网站都有反爬虫机制,若同一时间段内同一个IP发送的请求次数过多,超过了爬取网站定义的请求次数阈值,服务器会拒绝此IP发起的请求,并直接禁封该IP。
此时,通过设置代理便可解决此类问题。
什么是代理?
代理,即代理服务器的简称。其功能是代理网络用户,获取相关的网络信息。可以将代理服务器理解为建立在用户客户端和服务器端之间的桥梁,用户客户端发起请求会先将其请求发送至代理服务器中,而后再由代理服务器向对应的服务器发送客户端的请求,服务器响应给客户端的数据会先经过代理服务器处理后,在将其处理后的数据发送至用户客户端。
即,发送请求:客户端 ----》 代理服务器 —》服务器;
响应数据:服务器 —》代理服务器 —》 客户端
注意,在此过程中,web服务器识别出的真实的IP地址并不是用户客户端的真实的IP了,因为经过了代理服务器的IP处理。
代理的作用
(1)突破自身IP访问的限制
(2)隐藏自身真实IP
代理相关网站
快代理、西刺代理、米扑代理等
代理IP的类型
- http: 应用到http协议对应的url中
- https: 应用到https协议对应的url中
代理ip的匿名度
- 透明:服务器知道该次请求使用了代理,也知道请求对应的真实ip
- 匿名:服务器知道此次请求使用了代理,但是不知道客户端的真实IP
- 高匿:服务器不知道此次请求使用了代理,更不知道客户端的真实IP
练习:
# 需求:使用代理服务器发起请求,并验证IP与代理IP
import requests
if __name__ == '__main__':
url = "http://www.baidu.com/s?wd=ip"
headers = {
'User-Agent': ''
}
# 使用免费的代理服务器,proxies为指定的代理服务器,其值为字典类型,http为代理服务器中对应的网络协议为http协议,
# 91.241.39.118表示代理服务器的ip,8080表示该代理服务器的端口号
page_text = requests.get(url=url,headers=headers,proxies={'HTTP':'91.241.39.118:8080'}).text
with open('ip_w.html','w',encoding='utf-8') as fp:
fp.write(page_text)