上次爬取网易云音乐,折腾js调试了好久,难受。。。。今天继续练练手,研究下知乎登陆,让痛苦更猛烈些。

1.简单分析

  很容易就发现登陆的url=“https://www.zhihu.com/api/v3/oauth/sign_in”,post方法提交,需要的请求头和表单数据如下两图,请求头中有一个特殊的x-xsrftoken,表单数据为加密后的一长串字符窜,因此需要构造这两个值即可。

(八)爬虫之js调试(登陆知乎)

 

 (八)爬虫之js调试(登陆知乎)

2. 获取 x-xsrftoken值

   首先是这个特殊的x-xsrftoken,发现通过访问url="https://www.zhihu.com/",返回的cookies里面能拿到(会自动重定向,需要禁止重定向拿到requests.get(url,headers=headers,allow_redirects=False)),代码如下:

headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",}
def get_xsrf(headers): url
="https://www.zhihu.com/" r = requests.get(url,headers=headers,allow_redirects=False) #禁止重定向时,cookies里面有xsrf参数 xsrf = r.cookies["_xsrf"]

 

3. 构造表单数据

  然后就是表单数据的构造和加密了, 根据前辈们的提示,提交url为url=“https://www.zhihu.com/api/v3/oauth/sign_in”,一般表单会和末尾部分/oauth/sign_in有关系,于是在js文件里面搜索sign_in时,发现了下面的API,通过添加断点,点击登录,然后执行到断点处时,看到了如下的表单数据。变换不同账号试了下,发现只有captcha,signature,timestamp三个值在改变,其他参数不变。很明显,captcha为验证码,timestamp为时间戳(注意是13位),signature也像一个加密值,接下来就是寻找这几个值构造表单了。

表单数据:

(八)爬虫之js调试(登陆知乎)

  3.1 构造signature

    对于signature,在js文件中搜索signature,发现了下面的signature关键字,同样打断点,发现signature是采用hmac对四个数据加密后的结果;加密方  法为sha1,salt值如下,然后加密的参数依次是e=“password”, u="c3cef7c66a1843f8b3a9e6a1e3160e20"(就是clientId), source="com.zhihu.web", n为13为  时间戳。用python实现代码如下:

def get_signature(grantType,clientId,source,timestamp):
    h = hmac.new("d1b964811afb40118a12068ff74a12f4","",hashlib.sha1)
    h.update(grantType+clientId+source+str(timestamp))
    return h.hexdigest()

(八)爬虫之js调试(登陆知乎)

  3.2 构造captcha

    然后是处理验证码captcha参数,发现有三种情况:

    1. 不需要验证码,captcha=""

    2. 请求验证码的url为"https://www.zhihu.com/api/v3/oauth/captcha?lang=cn",返回为汉字图片,需要点击图片中倒立的汉字,captcha为坐标值

               3.请求验证码的url为"https://www.zhihu.com/api/v3/oauth/captcha?lang=en",返回英文字母图片,输入图片中英文字符即可,captcha为英文字符

   验证码的请求和处理流程如下:

    首先向上述两个验证码请求url中任一个发送get请求,如果返回{show_captcha:False},不需要验证码,captcha="",直接返回即可;如果返回{show_captcha:True},则需要验证码,继续向该url发送put请求(需要第一步的cookie),服务器会返回base64编码的验证码图片,利用base64解码写入文件即为验证码图片。打开图片,根据要求输入验证码或点击图片即为captcha的值,这里需要先携带cookie和验证码值,向服务器发送post请求,返回success才表示验证成功。比较特殊的是中文验证码处理,验证码的值为几组坐标值,如下第二张图片所示,可以利用matplotlib.pyplot模块来获取图片点击的坐标值(注意提交结果为实际点击坐标的一半)。

验证码:

(八)爬虫之js调试(登陆知乎)

 验证码结果返回:

(八)爬虫之js调试(登陆知乎)

验证码处理的代码如下:

def get_captcha(lang,headers):
    if lang=="cn":
        api = "https://www.zhihu.com/api/v3/oauth/captcha?lang=cn"
    else:
        api = "https://www.zhihu.com/api/v3/oauth/captcha?lang=en"
        
    ret = requests.get(api,headers=headers)
    cookies = ret.cookies
    show_captcha = re.search("true",ret.text)
    captcha=""
    if show_captcha:
        img_res = requests.put(api,headers=headers,cookies=cookies)  #得带上第一步的cookie,否则返回,{u'code': 120002, u'name': u'ERR_CAPSION_TICKET_NOT_FOUND'}
        img_json = json.loads(img_res.text)
        img_data = img_json["img_base64"].replace("\n","")
        with open("captcha.jpg","wb") as i:
            i.write(base64.b64decode(img_data))
        img = Image.open("captcha.jpg")
        if lang=="cn":
            plt.imshow(img)
            print("点击图片中所有倒立的汉字,在命令行中按回车键提交")
            points = plt.ginput(7) #阻塞点击七次后返回(或者中途点击回车键返回),返回包含坐标组的列表,格式:[(44.661290322580641, 49.951612903225794)]
            captcha = json.dumps({
                "img_size":[200,44],
                "input_points":[[i[0]/2,i[1]/2] for i in points]  #获取的坐标得除2
            })
            
        else:
            img_thread = threading.Thread(target=img.show)
            img_thread.setDaemon(True)
            img_thread.start()
            captcha = raw_input("请输入图片里的验证码:")  #python 2.7
        r = requests.post(api,headers=headers,data={"input_text":captcha},cookies=cookies)  #先提交验证码结果
        print(r.text)
    return captcha,cookies
验证码处理

相关文章: