session或scrapy实现模拟登录知乎
2017-07-28 16:00
465 查看
session 模拟登陆
推荐我的个人博客: blog.wuzhenyu.com.cn大家知道,登陆知乎需要验证码,那么如何通过 session 来模拟登陆知乎呢。首先通过分析知乎登陆请求, 如下图所示
上图中标注的部分就是我们需要模拟的请求,也就是说,我们使用 session 登陆的时候,首先需要构造我们的请求头部 header。
在上图中,有一个
x-xsrftoken字段,这个字段是知乎登陆请求中必须有的字段,那么这个字段是如何获取的呢,在我们打开知乎登陆页面的时候,查看网页源代码,在这个里面,我们就能获取到这个信息,如下图所示
这是一个隐藏的标签,在页面上是不会显示的。(这种手法,有点类似于,当关闭浏览器 cookie 功能的时候,服务器传递
session_id,也可以采用这种做法,发送一个隐藏的标签,保存
session_id),那么,获取这个
xsrf token就变得非常简单了,首先获取登录页面,然后通过正则表达式获取这个值,代码如下所示:
def get_xsrf_token(): response = session.get("https://www.zhihu.com", headers=header) # 这个参数必须是 headers match_obj = re.search('.*name="_xsrf" value="(.*?)"', response.text) # match vs search if match_obj: return match_obj.group(1) else: return ""
大家可能发现,在上面的代码中,我已经使用到了 header,我构造的 header 如下
agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0" header = { "Host": "www.zhihu.com", "Referer": "https://www.zhihu.com/", "Upgrade-Insecure-Requests": "1", "User-Agent": agent }
注意,上面的代码的正则表达式部分,使用的是 search,不是 match,match 可能匹配不了正确的结果,不明之处可以查看 python documentation 中的
match vs search的部分
既然 header 和
xsrf_token都已经获取到了,那我们试着使用 session 来模拟登陆一下,在登录之前,我们需要构造
post_data发送用户名和密码进行登录
def zhihu_login(account, password): """ 模拟知乎登录 """ post_data = {} post_url = "" if re.match("^1\d{10}", account): # 手机号码 print("phone login") post_url = "https://www.zhihu.com/login/phone_num" post_data = { "_xsrf": get_xsrf_token(), "phone_num": account, "password": password } print(header) print(post_data) else: if "@" in account: print("email login") post_url = "https://www.zhihu.com/login/email" # 邮箱登录 post_data = { "_xsrf": get_xsrf_token(), "email": account, "password": password } response_text = session.post(post_url, data=post_data, headers = header)
这段代码运行的时候,登录失败,错误提示是验证码错误,
F12浏览器分析,查看登录的参数,如下
参数包含四个值,分别是
_xsrf,
password,
phone_num,
captcha_type,我们在上面的代码中构建的
post_data缺少验证码参数,所以模拟登录失败。
知乎登录的时候,如果登录成功,再次登录的时候,可能不需要输入验证码,这是因为在浏览器的 cookies 中保存了相关的数据,F12进入调试模式,将 storage 中的 cookie 删除,再登录,就需要输入验证码了
同时,还需要注意的一点就是,
captcha_type这个参数是
cn,验证码是中文,让你点击倒立的汉字,点击正确才能通过验证码验证,如下所示
我们获取验证码的方式比较直接,就是通过将验证码图片下载下来,打开之后,输入验证码,构造
post_data然后发送请求,这样来模拟登录。但是如果使用中文这种点击的方式,就无法采用这种方法了,通过观察参数我们发现,
captcha_type这个参数为
cn的时候返回的验证码是中文,如果我们将这个值改成
en是不是就是字母了呢,修改后发送请求如下所示(浏览器的调试功能里面,有一个 edit and send 的功能)
获取的验证码就是数字加字母了,这样,我们就能通过先获取验证码,再输入的方式模拟登录了,好了,说了这么多,那么如何获取验证码图片呢?我们在回头来看下验证码的请求
验证码的请求为
https://www.zhihu.com/captcha.gif?r=1501223240460&type=login&lang=en,中间
r=1501223240460这个参数是一个随时间变动的参数,可以通过时间
time.time()获取,代码如下
checkcode_url = "https://www.zhihu.com/captcha.gif?r=" + str(int(time.time()*1000)) + "&type=login&lang=en" resp = session.get(checkcode_url, headers=header).content with open("checkcode.gif", "wb") as f: f.write(resp) f.close() try: im = Image.open("checkcode.gif") im.show() im.close() except: pass
注意:这个里面有一个比较大的坑,就是 request 和 session 的问题。只能使用
session.get,而不能使用
requests.get, 因为一次 session 就是一次会话,在后面继续使用验证码登录时,是在同一个 session 中的,session 会记录很多值,cookie 等。但是,如果使用的是 requests ,前后就不再同一个会话中,那么后面将带有验证码的
post_data发送过去时,会显示验证码无效,登录失败
上面的代码,就是将验证码图片获取之后,保存在
checkcode.gif这个图片中,然后通过 Image 类的 show 方法打开图片,然后后面构造
post_data的时候加上验证码参数即可
post_data["captcha"] = input("输入验证码: ")
Image 这个类,是在 PIL 这个库中的,如果没有安装,需要先安装
pip install pillow
然后,在使用的时候,import 进来就可以了
from PIL import Image
好了,介绍了这么多,知乎模拟登录就完成了。最后,我们可以加上 cookie,在登录之前,加载cookie,如果没有,那么在发送
post_data之后,通过 session 将 cookie 保存起来,在 python2 中,是通过 cookielib 来实现的, python3 中发生了变化,这个方法被移到了 cookiejar 中。完整的代码如下所示
import requests
import re
import time
from PIL import Image
__author__ = ""
# python 中的 cookie lib,能够读取本地的 cookie 文件,生成 cookie,然后赋值给requests 的 cookie
try:
import cookielib # python 2版本
except ImportError:
import http.cookiejar as cookielib # python3 版本
session = requests.session() # 某一次连接,是一个长链接,keep-alive,这样就不需要每一次 request 的时候,都要去建立一次连接
# 使用 cookielib 实例化 session的cookies,然后才能使用save() 保存cookies,不然在后面就会报错
session.cookies = cookielib.LWPCookieJar(filename="cookies.txt")
try:
# 成功加载 load cookie 之后,将不再返回首页
session.cookies.load(ignore_discard=True)
except Exception:
print("can not load cookies")
agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0" header = { "Host": "www.zhihu.com", "Referer": "https://www.zhihu.com/", "Upgrade-Insecure-Requests": "1", "User-Agent": agent }
def get_xsrf_token(): response = session.get("https://www.zhihu.com", headers=header) # 这个参数必须是 headers match_obj = re.search('.*name="_xsrf" value="(.*?)"', response.text) # match vs search if match_obj: return match_obj.group(1) else: return ""
def zhihu_login(account, password):
"""
模拟知乎登录
"""
post_data = {}
post_url = ""
if re.match("^1\d{10}", account): # 手机号码
print("phone login")
post_url = "https://www.zhihu.com/login/phone_num"
post_data = {
"_xsrf": get_xsrf_token(),
"phone_num": account,
"password": password
}
print(header)
print(post_data)
else:
if "@" in account:
print("email login")
post_url = "https://www.zhihu.com/login/email"
post_data = {
"_xsrf": get_xsrf_token(),
"email": account,
"password": password
}
# get check code image
checkcode_url = "https://www.zhihu.com/captcha.gif?r=" + str(int(time.time()*1000)) + "&type=login&lang=en"
resp = session.get(checkcode_url, headers=header).content
with open("checkcode.gif", "wb") as f:
f.write(resp)
f.close()
try:
im = Image.open("checkcode.gif")
im.show()
im.close()
except:
pass
post_data["captcha"] = input("checkcode:")
response_text = session.post(post_url, data=post_data, headers = header)
session.cookies.save() # 将服务器返回的 cookies 保存到本地,下一次登录的时候,直接从文件中获取
后面进入知乎后,通过
session.get就可以获取登录后的数据。
scrapy 模拟登录
在 python 中,我们可以通过requests.session()获取 session,然后模拟登录,但是在 scrapy 中是没有 session,的,那么如何实现模拟登录呢。我使用的是 scrapy 中的 basic 模板。
scrapy 的流程是:
入口函数是
start_requests(),对
start_urls这个列表中的每一个 url 进行处理,返回一个 Request(),这个函数将 url 给 scrapy 下载器下载,如果没有传递
callback,将调用默认的回调函数
parse函数进行解析。
那么,我的思路就是,重载
start_requests()函数,在
start_requests函数中,获取验证码,然后返回 Request 中,调用登录的回调函数,传入 header 和
post_data(此时是带有验证码参数的)进行模拟登录,这样就能保证验证码获取后的登录是在一个会话中了。
完整代码如下所示:
def start_requests(self): """ 返回登录页面,在回调函数中,提取 xsrf 的值 """ return [scrapy.Request('https://www.zhihu.com/#signin', callback=self.login, headers=self.headers)] def login(self, response): response_text = response.text xsrf = "" match_obj = re.search('.*name="_xsrf" value="(.*?)"', response_text) if match_obj: xsrf = match_obj.group(1) if xsrf: post_url = "https://www.zhihu.com/login/phone_num" post_data = { "_xsrf": xsrf, "phone_num": "13530210085", "password": "wuzhenyu25977758", "captcha": "" } t = str(int(time.time() * 1000)) checkcode_url = "https://www.zhihu.com/captcha.gif?r={0}&type=login".format(t) """ 必须保证验证码的使用,与得到验证码的链接请求是在同一个会话连接中,此处使用 Request 的方法, 通过 scrapy 将请求下载之后,使用回调函数,在回调函数中进行处理,保证回调函数中的 response 与获取验证码时 是在同一个会话中 """ yield scrapy.Request(checkcode_url, meta={"post_data": post_data}, headers=self.headers, callback=self.login_with_checkcode) def login_with_checkcode(self, response): post_url = "https://www.zhihu.com/login/phone_num" post_data = response.meta.get("post_data", {}) with open("checkcode.gif", "wb") as f: f.write(response.body) f.close() try: im = Image.open("checkcode.gif") im.show() # im.close() # no close function except: pass post_data["captcha"] = input("checkcode: ") return [scrapy.FormRequest( url=post_url, formdata=post_data, headers=self.headers, callback=self.check_login )] def check_login(self, response): """ 检查登录是否成功 """ text_json = json.loads(response.text) if "msg" in text_json and text_json["msg"] == "登录成功": for url in self.start_urls: yield scrapy.Request(url, dont_filter=True, headers=self.headers) # 默认回调函数为 parse
首先,在
start_requests函数中,通过 scrapy 下载登录页面,通过回调函数获取
xsrf_token的值,然后再构造验证码的url,通过 scrapy 下载,注意验证码图片数据此时保存在了
response.body中,通过回调函数
login_with_checkcode构造
post_data,然后使用
scrapy.FormRequest模拟登录,在
check_login中对登录结果进行验证,查看登录是否成功。
最后,在
check_login函数中,如果登录成功,再回到之前
start_requests本身应该处理的内容中,将
start_urls中的 url 进行处理,调用默认回调函数
parse对页面进行解析。
parse中是实际的页面解析结果,如何写这部分,大家可以看我的另一篇博客 爬取伯乐在线所有文章
相关文章推荐
- python3下使用scrapy实现模拟用户登录与cookie存储 —— 基础篇(马蜂窝)
- 使用Scrapy实现模拟登录的方法
- 【python爬虫03】使用Scrapy框架模拟登录知乎
- 用scrapy 模拟知乎的登录过程
- python3下使用scrapy实现模拟用户登录与cookie存储—— 中级篇(百度云俱乐部)
- 使用requests和scrapy模拟知乎登录
- python爬虫框架scrapy实现模拟登录操作示例
- Scrapy框架学习 - 模拟登录的实现方式
- 通过scrapy,从模拟登录开始爬取知乎的问答数据
- Scrapy模拟登录知乎
- redis实现session共享,实现单点登录
- 新浪微博模拟登录 java实现
- 三种模拟自动登录和提交POST信息的实现方法
- 使用SQL Server保存Session状态,实现单点登录
- JAVA通过Session和Cookie实现网站自动登录的技术
- scrapy的basic模板模拟登录、requests模拟登录
- 用phpcurl实现模拟登录带验证码urp教务系统获取成绩
- tomcat使用redis共享session并实现单点登录
- Redis缓存Mysql模拟用户登录Java实现实例
- 第三百三十五节,web爬虫讲解2—Scrapy框架爬虫—豆瓣登录与利用打码接口实现自动识别验证码