python3.7 模拟登录知乎 10-29

python3.7 模拟登录知乎 10-29

写在前面:第一次发表文章,笔者是一个初级程序猿,能提供给大家有用的东西是一件很高兴的事。不好的地方欢迎提出。还有…不用急着问我为什么代码不能用,请先认真看代码,困了请来一杯咖啡谢谢~。知乎那边也会不间断的更新,代码也会过时,主要是想让大家知道模拟登录的思路。

正文:

需要用到的库:
base64、PIL —获取验证码图片需要用到的
hmac—加密库,由于要携带签名信息,而签名信息是用我们携带的一些参数加密得到的
json、re、time—这个不解释了
hashlib—加密库,使用到了sha1加密方式
requests、BeautifulSoup—爬虫经常用到的库
库的安装命令我就不在这里写了。(由于没有整理这个,请谅解。)

登录的过程— 我们请求的时候需要携带一个data,里面装的是{账号、密码、签名等} 信息,请求头{User-Agent、X-Xsrftoken、X-UDID、X-ZSE-83、x-requested-with}至于我为什么知道需要这些东西,请往下看

首先 我们先用浏览器真正登录一次(当然要输错一次密码),就能看到很多东西。
python3.7 模拟登录知乎 10-29

因为我们是请求方,所以要查看请求头到底需要什么,请求头我没有截完整,总之经过测试需要的参数
{User-Agent、X-Xsrftoken、X-UDID、X-ZSE-83、x-requested-with}就是这些,那么我们看看有哪些参数是已经固定的
python3.7 模拟登录知乎 10-29
看起来好像是 X-ZSE-83 x-requested-with 那么我们先写死了。其他两个我们看看其他地方有没有(反正就是在这个response里呗),那么笔者经过一番查找
python3.7 模拟登录知乎 10-29

发现_xsrf 是有,但是没有X-UDID,不过这个d_c0这个值好像就是X-UDID,只不过好像多了点东西。那么我们不管他了,把多余的去掉,拿出我们要的东西就好。
请求头搞定了。
接下来是data
既然是模拟d登录,那么肯定少不了账号 密码 而且验证码的位子也要留。。
可是到底需要哪些参数?
其实是在这里
python3.7 模拟登录知乎 10-29
知乎只不过是为了安全 把表单数据加密了。
笔者是通过查阅一些资料发现 可以通过查看js来获得这些东西。js文件我这里只告诉大家在哪里
用浏览器登录一次(使用正确的账号密码)
如下图(找不到别忘了搜索。。)
python3.7 模拟登录知乎 10-29
至于我怎么知道要找这些东西,其实通过代码。 我只给 账号密码和验证码,他会在response中告诉我缺什么样的参数
这个是获得它缺少参数的代码
resp = session.post(‘https://www.zhihu.com/api/v3/oauth/sign_in’, data, headers=headers).content
print(BeautifulSoup(resp, ‘html.parser’))
python3.7 模拟登录知乎 10-29
其实这些参数他只给了clientId source 。timestamp这个就是时间戳,创建一个就好。grantType因为我们是通过账号密码登录的,所以这里值是password,grantType 这个参数你们可以自己百度一下,我就不在这里说明了。signalture从js图里看到它将这几个参数使用SHA-1 的加密方式 并且是获取他们的16进制的数据。那我们也要想办法在代码中通过我们获取到的几个参数以相同的方式加密。相当于模拟生成一个signalture,具体自己看代码吧。

总结一点:
知乎的模拟登录相对复杂 它不仅仅要的是你给他的参数 ,为了防止反爬他还需要的参数是:将你给他的参数以某种方式加密来得到的。大大增加了模拟登录的难度。

最后上代码:

# -*- coding:UTF-8 -*-

import base64
import hmac
import json
import re
from hashlib import sha1

import requests
import time
from PIL import Image
from bs4 import BeautifulSoup

#import Properties 


def get_identifying_code(headers):
    '''
      判断页面是否需要填写验证码
      如果需要填写则弹出验证码,进行手动填写
      请求验证码的url 后的参数lang=en,意思是取得英文验证码
    '''
    response = session.get('https://www.zhihu.com/api/v3/oauth/captcha?lang=en', headers=headers)
    # {"show_captcha":false} 表示不用验证码
    r = re.findall('"show_captcha":(\w+)', response.text)
    if r[0] == 'false':
        return ''
    else:
        response = session.put('https://www.zhihu.com/api/v3/oauth/captcha?lang=en', headers=headers)
        show_captcha = json.loads(response.text)['img_base64']
        with open('captcha.jpg', 'wb') as f:
            f.write(base64.b64decode(show_captcha))
        im = Image.open('captcha.jpg')
        im.show()
        im.close()
        captcha = input('输入验证码:')
        session.post('https://www.zhihu.com/api/v3/oauth/captcha?lang=en', headers=headers,
                     data={"input_text": captcha})
        return captcha


def get_signature(grantType, clientId, source, timestamp):
    ''' 处理签名 '''

    hm = hmac.new(b'd1b964811afb40118a12068ff74a12f4', None, sha1)
    hm.update(str.encode(grantType))
    hm.update(str.encode(clientId))
    hm.update(str.encode(source))
    hm.update(str.encode(timestamp))

    return str(hm.hexdigest())


def login(username, password, session, headers):
    ''' 处理登录 ,先用浏览器F12查看他要什么参数'''

    # https://www.zhihu.com 尝试出来的,只有去请求这个才能得到_xsrf d_c0 具体什么原因,我也不清楚。。。
    resp1 = session.get('https://www.zhihu.com', headers=headers)  # 拿cookie:_xsrf
    # print(resp1.cookies['_xsrf'])
    _xsrf = resp1.cookies['_xsrf']
    d_c0 = resp1.cookies['d_c0']
    x_udid = d_c0.split("|")[0].lstrip("\"")  # 获取 x_udid
    print(x_udid)
    headers.update({  # 更新请求头,知乎防反爬真的很厉害。
        "X-Xsrftoken": _xsrf,
        "X-UDID": x_udid,
        "X-ZSE-83": '3_1.1',
        "x-requested-with": "fetch"
    })

    grantType = 'password'
    clientId = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
    source = 'com.zhihu.web'
    t = time.time() * 1000
    print(t)
    timestamp = str(t).split('.')[0]  # 签名只按这个时间戳变化

    # captcha_content = session.get('https://www.zhihu.com/captcha.gif?r=%d&type=login' % t,
    #                               headers=headers).content  # 验证码
    captcha = get_identifying_code(headers)
    data = {
        "client_id": clientId,
        "grant_type": grantType,
        "timestamp": timestamp,
        "source": source,
        "signature": get_signature(grantType, clientId, source, timestamp),  # 获取签名
        "username": username,
        "password": password,
        #"lang": "en",
        "captcha": captcha  # oncaptcha(captcha_content, need_cap)  # 获取图片验证码
        # "ref_source": "other_",
        # "utm_source": ""
    }

    print("**2**: " + str(data))
    print("-" * 50)
    # session.post('https://www.zhihu.com/api/v3/oauth/sign_in', data, headers=headers).content
    resp = session.post('https://www.zhihu.com/api/v3/oauth/sign_in', data, headers=headers).content
    print(BeautifulSoup(resp, 'html.parser'))

    print("-" * 50)
    # return resp


if __name__ == "__main__":
    session = requests.Session()
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/56.0.2924.87 Mobile Safari/537.36',
        # 'authorization': 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20',
        # "Referer": "https://www.zhihu.com/",
        # "Host": "www.zhihu.com"
    }
    # print(session.headers)   #'User-Agent': 'python-requests/2.20.0'
    # prp = Properties.parse("D:\project\spider_demo\my_spider\AccAndPwd")  创建读取Properties文件的实例
    # account = prp.get('account')
    # password = prp.get('password')
    login(‘账号’, ‘密码’, session, headers)  # 用户名密码换自己的就好了,我这里引用的是一个自己写的加载properties类,用来提取账号密码

    resp = session.get('https://www.zhihu.com/people/edit', headers=headers)  # 登录进去了,可以看个人信息页面了
    print(BeautifulSoup(resp.content, 'html.parser'))

参考文章:
[1]: https://blog.csdn.net/Marksinoberg/article/details/69569353?utm_source=blogxgwz0
[2]: https://blog.csdn.net/nvliba/article/details/80521348
[3]: https://bbs.csdn.net/topics/392410502 因为表单数据被加密,从这个地方得出需要看JS文件
还有很多很多就不一一例举了