模拟完成知乎登录的三步走

一、请求进入登录页面获取cookies

用的是requests_html模块中的HTMLSession,实例化后的对象发送请求都会自动保存cookie发送。所以后续就不需要进行获取、保存、校验等操作

def get_first_cookie(self):
        self.session.get(url=self.head_page_url)

二、校验验证码

在校验验证码之前肯定要发请求获取校验码保存,再输入校验码,最后再发请求校验验证码。。

因为在知乎登录中有校验码有三种情况

  • 没有校验码

  • 英文校验码:输入4位数字或字母组合,url链接中带有lang=en。这种校验码比较好处理,这里就采用这个api
  • 正文校验码:点击下面倒着的中文,url链接中带有lang=en

有三个请求,api为同一个,就是请求方式和携带的参数不同

(1)get请求:判断是否有验证码,不携带参数,如果有校验码会响应{"show_captcha":true},否则为{"show_captcha":false}

(2)put请求:获取验证码,不携带参数,返回的响应是{"img_base64":"xxxxxx"},base64编码后的一串字符

(3)post请求:校验验证码,携带参数,校验成功返回的响应是{"success":true},否则为{"success":false}

def handle_captcha(self):
    r = self.session.get(url=self.captcha_api)  # 发送get判断是否有验证码
    res = r.json()
    
    if res.get('show_captcha'):
        r = self.session.put(url=self.captcha_api)  # 有验证码,发送请求获取验证码
        img_base64 = r.json().get('img_base64')
        # 将得到的验证码解码后存为capcha.png图片,这里就需要导入base64这个模块,用b64decode解码
        with open('captcha.png', 'wb') as f:
            f.write(base64.b64decode(img_base64))
        # open打开图片,show显示出来。需要导入从PIL中导入Image模块
        img = Image.open('captcha.png')
        img.show()
        
        # 输入验证码,携带验证码发生post请求,参数是从浏览器中查看的
        self.captcha = input('请输入校验码:')
        r = self.session.post(url=self.captcha_api,data={"input_text":self.captcha})
        res = r.json()
        if res.get('success'):
            print('验证码正确')
        else:
            self.handle_captcha()

三、登录校验

从浏览器的抓包工具中看到,前端对携带的参数进行了加密处理,所以我们需要知道前端代码中是在哪里进行了加密处理,参数都有哪些?

  • 搜索 encrypt(加密) 找到有该字的js文件(static.zhihu.com/heifetz/main.app.8b5cc380b705bb3ed141.js文件),找到这行加密函数
var b = function(e) {
        return __g._encrypt(encodeURIComponent(e))  // encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
    };
  • 是不是不知道e是什么鬼,在这行代码上打上断点运行,然后在Console输入e回车就知道e是什么了
client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&grant_type=password&timestamp=1571675789070&source=com.zhihu.web&signature=652fa1c9bd1831abce73a5ee87d2dd9f748ce308&username=%2B86182000000001&password=xxxx&captcha=%7B%22img_size%22%3A%5B200%2C44%5D%2C%22input_points%22%3A%5B%5B93.33331298828125%2C21.052078247070312%5D%2C%5B158.33331298828125%2C21.052078247070312%5D%5D%7D&lang=cn&utm_source=&ref_source=other_https%3A%2F%2Fwww.zhihu.com%2Fsignin%3Fnext%3D%252F"

一长串的字符,仔细观察下,他们其实就是携带的参数,其中signature是加密的字符串,现在找到signature的加密函数

  • 同样搜索signature,找到signature的加密函数,现在就是要把js代码转成python。python中有个模块execjs可以在python中运行js代码。那么先打加密相关的函数拷贝到js文件中,然后在代码中用execjs来操作这个文件

  • 携带加密后的数据,发送登录请求

def login_in(self):
    # 请求携带的参数
    formdata = {
        "client_id": "xxxxxxxx",  # 这是客户端id,打印e直接粘贴复制过来
        "grant_type": "password",
        "timestamp": str(int(time.time() * 1000)),
        "source": "com.zhihu.web",
        "signature": self.signature,  # 看js的加密函数,其实就是用了hmac加盐加密,详情参见完整代码中的get_signature(self)方法
        "username": "+86182000000",  # 用户名,请输入自己的
        "password": "xxxxx",  # 密码
        "captcha": self.captcha,
        "lang": "en",
        "utm_source": '',
        "ref_source": "other_https://Fwww.zhihu.com%2Fsignin%3Fnext%3D%252F"
    }
    with open('知乎加密.js','rt',encoding='utf-8') as f:
        # 读取js代码
        js = f.read()
        # 编译。cwd是依赖的环境。js执行代码通常是在node中,所以需要npm install jsdom安装依赖
        execjs_obj = execjs.compile(js,cwd='node_modules')
        # execjs_obj.call(函数名,参数)
        res = execjs_obj.call('b',urlencode(formdata))
        
        # 请求头,必须要有下面的参数,不然会报错
        headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36",
            "content-type": "application/x-www-form-urlencoded",  # 数据类型
            "x-zse-83": "3_2.0"  # 版本
        }
        r = self.session.post(url=self.sign_in_api, data=res,headers=headers)
        # 登录成功的状态码,在这里是201
        if r.status_code == 201:
            print('登录成功')
            r = self.session.get(url='https://www.zhihu.com/')  # 登录成功后调到知乎首页
            print(r.text)
            else:
                print('用户名或密码错误')

四、完整代码

import time,hmac
from hashlib import sha1
from requests_html import HTMLSession
import base64,execjs
from PIL import Image
from urllib.parse import urlencode

class Spider():
    def __init__(self):
        self.session = HTMLSession()
        self.captcha_api = "https://www.zhihu.com/api/v3/oauth/captcha?lang=en"
        self.sign_in_api = "https://www.zhihu.com/api/v3/oauth/sign_in"
        self.head_page_url = "https://www.zhihu.com/signin?next=%2F"
        self.headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
        }
        self.captcha = ''
        self.signature = ''

    def get_first_cookie(self):
        self.session.get(url=self.head_page_url)

    def handle_captcha(self):
        r = self.session.get(url=self.captcha_api)
        res = r.json()
        if res.get('show_captcha'):
            r = self.session.put(url=self.captcha_api)
            img_base64 = r.json().get('img_base64')
            with open('captcha.png', 'wb') as f:
                f.write(base64.b64decode(img_base64))
            img = Image.open('captcha.png')
            img.show()
            self.captcha = input('请输入校验码:')
            r = self.session.post(url=self.captcha_api,data={"input_text":self.captcha})
            res = r.json()
            if res.get('success'):
                print('验证码正确')
            else:
                self.handle_captcha()

    def get_signature(self):
        r = hmac.new(b'd1b964811afb40118a12068ff74a12f4',digestmod=sha1)  # 第一个是加盐,digestmod参数是加密算法
        r.update(b"password")
        r.update(b"c3cef7c66a1843f8b3a9e6a1e3160e20")
        r.update(b"com.zhihu.web")
        r.update(str(int(time.time()*1000)).encode('utf-8'))
        self.signature = r.hexdigest()

    def login_in(self):
        formdata = {
            "client_id": "xxxxxxxx",
            "grant_type": "password",
            "timestamp": str(int(time.time() * 1000)),
            "source": "com.zhihu.web",
            "signature": self.signature,
            "username": "+86182000000",  # 用户名,请输入自己的
            "password": "xxxxx",  # 密码
            "captcha": self.captcha,
            "lang": "en",
            "utm_source": '',
            "ref_source": "other_https://Fwww.zhihu.com%2Fsignin%3Fnext%3D%252F"
        }
        with open('知乎加密.js','rt',encoding='utf-8') as f:
            js = f.read()
            execjs_obj = execjs.compile(js,cwd='node_modules')
        # execjs_obj.call(函数名,参数)
        res = execjs_obj.call('b',urlencode(formdata))
        headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36",
            "content-type": "application/x-www-form-urlencoded",
            "x-zse-83": "3_2.0"
        }
        r = self.session.post(url=self.sign_in_api, data=res,headers=headers)
        if r.status_code == 201:
            print('登录成功')
            r = self.session.get(url='https://www.zhihu.com/')
            print(r.text)
        else:
            print('用户名或密码错误')

    def run(self):
        self.get_first_cookie()
        self.handle_captcha()
        self.get_signature()
        self.login_in()

if __name__ == '__main__':
    zhihu = Spider()
    zhihu.run()

猜你喜欢

转载自www.cnblogs.com/863652104kai/p/11717447.html