模拟登陆改版后的知乎(最新版)

今天,想着看看视频,把模拟登陆这一块学习学习,以后弄把梯子,去爬爬FaceBook什么的。就拿知乎练练手吧,可曾想,知乎竟然改版了!!之前的教程书籍对现在的知乎来说,都是扯淡,连页面都找不到了。下面一起谈谈改版后的纸糊的模拟登陆吧。

页面分析

抓包

首先,打开页面:https://www.zhihu.com/signup?next=%2F(登录网址都变了…),F12输入账号密码(记得把密码输错),点击网络所有,剩下的如图所示,得到请求头。发现几个请求头和正常的不一样(如图所示):
这里写图片描述
1. authorization: 应该是js生成的。
2. content-type: 第一次看感觉很没有头绪,接下来和请求参数一起看就会一目了然了。
3. x-udidx-sxrftoken:这两个是验证参数,在网页源码中可以找到。
接下来看一下请求参数:
这里写图片描述
然后我们结合请求头里 content-type:multipart/form-data; boundary=…---------------22839196617062
其中multipart/form-data就是一种表单提交方式;后面的 boundary=…---------------22839196617062就是参数中的 “分割线”:
这里写图片描述
所以,直接不看那一串,参数就相当于:
client_id = c3cef7c66a1843f8b3a9e6a1e3160e20
grant_type = password
等11个参数。其中比如账户、密码这些都是固定的,多次请求后,发现client_idtimestamp(时间戳) 也是固定的,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-udidx-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

多次运行测试,发现都没返回图片。
这里写图片描述
但是不排除有返回的可能性。
今天就到这吧,欢迎留言交流。
欢迎关注我的个人公众号。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值