模拟登陆改版后的知乎(最新版)
今天,想着看看视频,把模拟登陆这一块学习学习,以后弄把*,去爬爬FaceBook什么的。就拿知乎练练手吧,可曾想,知乎竟然改版了!!之前的教程书籍对现在的知乎来说,都是扯淡,连页面都找不到了。下面一起谈谈改版后的纸糊的模拟登陆吧。
页面分析
抓包
首先,打开页面:https://www.zhihu.com/signup?next=%2F(登录网址都变了…),F12
,输入账号密码
(记得把密码输错),点击网络
,所有
,剩下的如图所示,得到请求头
。发现几个请求头和正常的不一样(如图所示):
1. authorization
: 应该是js生成的。
2. content-type
: 第一次看感觉很没有头绪,接下来和请求参数一起看就会一目了然了。
3. x-udid
、x-sxrftoken
:这两个是验证参数,在网页源码中可以找到。
接下来看一下请求参数:
然后我们结合请求头里 content-type:multipart/form-data; boundary=…---------------22839196617062
其中multipart/form-data
就是一种表单提交方式;后面的 boundary=…---------------22839196617062
就是参数中的 “分割线”:
所以,直接不看那一串,参数就相当于: client_id = c3cef7c66a1843f8b3a9e6a1e3160e20
grant_type = password
等11个参数。其中比如账户、密码这些都是固定的,多次请求后,发现client_id
、timestamp(时间戳)
也是固定的,signature
是变动的,那么这个signature
是什么东西呢?
确定参数位置
authorization
通过更换不同的账号进行抓包,发现 authorization
的值是不变的,所以可以说明是直接写到js文件里的,为了验证这一点,找了半小时,总算在[https://static.zhihu.com/heifetz/main.app.da67b2ab04cd46a8caa1.js] 里找到这个:
所以,上面假设得到证实。而且发现,后面那一串和 client_id
的值一样。
signature
同样是上面的那个js文件,[https://static.zhihu.com/heifetz/main.app.da67b2ab04cd46a8caa1.js],找到这样一块代码:
function (e, t, n) {
"use strict";
function r(e, t) {
var n = Date.now(), r = new a.a("SHA-1", "TEXT");
return r.setHMACKey("d1b964811afb40118a12068ff74a12f4", "TEXT"), r.update(e), r.update(i.a), r.update("com.zhihu.web"), r.update(String(n)), c({
clientId: i.a,
grantType: e,
timestamp: n,
source: "com.zhihu.web",
signature: r.getHMAC("HEX")
}, t)
}
所以,signature
的值就是一些变量进行 HMAC
得到的值。
x-udid
、x-sxrftoken
直接查找网站源代码,搜索全局,定位了这两个:
后续就是使用正则把他们匹配出来。
代码设计
主函数的书写
需要导入的python库有:
import scrapy
from scrapy.http import Request,FormRequest
import base64
import re
import execjs
import time
from PIL import Image
import os
设置爬虫通用的请求头:
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/58.0',
'authorization': 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20'
}
时间戳的和获取方法:
timestamp = int(time.time() * 1000)
客户端 id
client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
定向登录原网站,这里用到 Request()
方法
def start_requests(self):
url = 'https://www.zhihu.com/signup?next=%2F'
return [Request(url, meta={'cookiejar': 1}, callback=self.parse)]
定位验证码网站
def parse(self, response):
pat = re.compile('xsrf":"(.*?)&')
self.xtoken = pat.findall(response.body.decode('utf-8', 'ignore'))[0]
tempurl = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
yield Request(tempurl, headers=self.header, meta={"cookiejar": True}, callback=self.end)
确认是否需要验证码,无需验证码直接 post
登陆,需要则 put
访问验证码网址获取验证码。改变后的验证码接口改为:[https://www.zhihu.com/api/v3/oauth/captcha?lang=cn].
def end(self, response):
data = response.text
if re.search('true', data):
self.i = self.i + 1
print('需要验证码')
yield Request('https://www.zhihu.com/api/v3/oauth/captcha?lang=cn', method='put', headers=self.header,
meta={"cookiejar": True},
callback=lambda response, ki='cn': self.parser_captcha(response, ki))
else:
print('无需验证码')
username = '+86手机号'
password = '你的密码'
signature = self.add().call('run', 'password', self.timestamp)
data = {
'client_id': self.client_id, 'grant_type': 'password',
'timestamp': str(self.timestamp), 'source': 'com.zhihu.web',
'signature': signature, 'username': username,
'password': password, 'captcha': self.captcha,
'lang': 'en', 'ref_source': 'homepage', 'utm_source': ''
}
urls = 'https://www.zhihu.com/api/v3/oauth/sign_in'
yield FormRequest(urls, method='post', headers=self.header, meta={"cookiejar": response.meta["cookiejar"]},
formdata=data, callback=self.next, )
登陆所需方法:验证码模块。(知乎的验证比较变态,需要post两次。)
POST
第一次,发送数据给验证码来源网站.
def parser_captcha(self, response, ki):
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/58.0',
'x - udid': 'ACBCVUdOYguPTkCvnaFKpSly5-s9HmXSCPg=',
'authorization': 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20',
'X-Xsrftoken': self.xtoken
}
print(ki)
pat = re.compile('(?s)"img_base64":"(.*?)"')
data = eval(repr(pat.findall(response.body.decode('utf-8', 'ignore'))[0]).replace('\\\\', '\\'))
k = base64.b64decode(data)
with open('D:/captcha.jpg', 'wb') as f:
f.write(k)
f.close()
try:
im = Image.open('D:/captcha.jpg')
im.show()
im.close()
except:
print(u'请到 %s 目录找到captcha.jpg 手动输入' % os.path.abspath('captcha.jpg'))
self.captcha = input("please input the captcha\n>")
form = {'input_text': str(self.captcha)}
yield FormRequest(response.url, headers=header, meta={'cookiejar': response.meta['cookiejar']}, formdata=form,
callback=self.finall)
POST
第二次,发送数据给真正的登陆接口。
def finall(self, response):
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/58.0',
'x - udid': 'ACBCVUdOYguPTkCvnaFKpSly5-s9HmXSCPg=',
'authorization': 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20',
'X-Xsrftoken': self.xtoken
}
username = '+86手机号'
print('1')
password = '你的密码'
timestamp = int(time.time() * 1000)
signature = self.add().call('run', 'password', timestamp)
print(signature)
data = {
'client_id': self.client_id, 'grant_type': 'password',
'timestamp': str(timestamp), 'source': 'com.zhihu.web',
'signature': signature, 'username': username,
'password': password, 'captcha': str(self.captcha),
'lang': 'en', 'ref_source': 'homepage', 'utm_source': ''
}
urls = 'https://www.zhihu.com/api/v3/oauth/sign_in'
print(2)
print(data)
yield FormRequest(urls, method='post', headers=header, meta={"cookiejar": response.meta["cookiejar"]},
formdata=data, callback=self.next, )
js解密方式,获取 signature
。
直接把这个js文件下载下[https://static.zhihu.com/heifetz/main.app.da67b2ab04cd46a8caa1.js],用 python 执行就可以了。
def add(self):
js1 = execjs.compile("""
粘贴代码(由于篇幅太长,没展示)
""")
return js1
多次运行测试,发现都没返回图片。
但是不排除有返回的可能性。
今天就到这吧,欢迎留言交流。
欢迎关注我的个人公众号。
上一篇: 模拟题【Week15实验】
下一篇: 程序设计模拟题【Week2】
推荐阅读