1. 程式人生 > >Python-requests-知乎模擬登入

Python-requests-知乎模擬登入

繼續我的python爬蟲旅程,開始寫部落格的時候,說一天一篇,真的只是動動嘴皮子,做起來還真的難,其實是自己給自己找理由…

  • 不管怎樣,今天來更新一篇,寫個知乎的模擬登入,感覺最開始學習爬蟲的時候,大家都期盼著可以寫那種需要登入的網站,或者有各種驗證碼的,那時候看人家在群裡說誰能破解驗證碼,登入某網站抓取資訊的時候,羨慕的不行,想要迫切的去學習,慢慢的學會了,感覺驗證碼的破解也就那麼回事,有些很難,花很多功夫,或者直接放棄。再或者直接接個打碼平臺,也不用手動去登入,再學著學著,感覺驗證碼的破解也就沒那麼重要了,並沒有在驗證碼的破解上投入很多的時間,可以直接交給打碼平臺,專業處理驗證碼的人去處理就好了,不過常用的一些還是要了解一些,原理是什麼。

  • 再就是還有很多請求需要提交引數,有的明文直接寫到data,或者params引數裡面直接提交,有些網站給它加密或者做很多處理,讓人很煩躁,還有很多引數用js程式碼去生成,對像我一樣不太懂js得人來說,真的是蛋痛。

  • 說了這麼多,其實就是說要學習真的太多,但是這個懶病該怎麼治!!!

廢話不多說,來看看知乎的模擬登入~!

知乎今年改版了好像網頁,記得去年寫過一次,今年再一看變了不少,百度搜的程式碼基本上都是以前版本的,很少有目前版本的模擬登入部落格或者文章什麼的。

#簡單整理下思路:

  • 手動登入,抓包
  • 分析
  • 驗證碼獲取
  • 驗證碼識別
  • 驗證碼驗證,資料提交
  • 登入

#手動登入,抓包

  • 用fiddler來抓包,簡單瀏覽分析下 image.png

#驗證碼獲取

  • 手動登陸的時候,發現有的時候並沒有驗證碼,有的時候是英文字母的驗證碼,有的時候是選擇倒立漢字的驗證碼
  • show_captcha 為false 的時候,不需要驗證碼
  • show_captcha 為true的時候,需要驗證碼
  • 所以,搞個if判斷,是否要驗證碼就可以

#驗證碼識別

  • 上面說到有2種驗證碼,怎麼判斷是哪種,其實,驗證碼的連結只有1個字每的差別
  • 多試幾次,就會發現
  • 怎麼識別呢?
  • 判斷 show_captcha 為true後,發現下面它又緊接著再次訪問了下同一個連結,但是是put請求,看下它的內容會發現,它就是我們要的驗證碼圖片,把它儲存到本地 image.png
  • 本文章就使用英文字母的驗證碼,漢字的其實跟12306的差不多,也是提交座標進行驗證,不過它進行了處理,每個座標都除以了2,而且y的值貌似必須按照 ?.03125的格式,x可以是整數或者?.5的形式 -漢字: {“img_size”:[200,44],“input_points”:[[50,25.53125],[151,23.53125]]} size固定大小好像,測試了很多都是200,400
  • 字母:直接提交字母就可以 image.png

#驗證碼提交,進行驗證

#最後,登入

  • 要提交的引數還是挺多的,一個一個來看 image.png

  • client_id:固定值 c3cef7c66a1843f8b3a9e6a1e3160e20

  • grant_type 固定值 password

  • timestamp 13位時間戳

  • username 你的賬號

  • password 你的密碼

  • captcha 驗證碼

  • lang 貌似是驗證碼的那個lang 中文驗證碼的話 寫cn 英文字母的寫en

  • ref_source 固定值 other_

  • utm_source 可以為空,也可以寫baidu,貌似是從哪進入的

  • 最後來說下至關重要的引數signature,也是比較噁心的一個了,它是js生成的,我們來找下他的js檔案,直接ctrl+f搜尋signature 查詢就OK image.png

  • 再在js裡面搜尋下signature image.png

  • 它是用了hmac加密方法,把grant_type + client_id + source + timestamp一起放到裡面加密了得到的結果

  • 簡單的用Python方法實現就可以得到signature

#登入

  • 按照樣式製作表單data
  • 提交連結https://www.zhihu.com/api/v3/oauth/sign_in

最後,上程式碼

# -*- coding: utf-8 -*-
import base64
import hmac
import re
from hashlib import sha1
import requests
import time
import json

default_headers = {
    'accept': 'application/json, text/plain, */*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Host': 'www.zhihu.com',
    'Origin': 'https://www.zhihu.com',
    'Referer': 'https://www.zhihu.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
    'X-Requested-With': 'Fetch',
}

class ZhihuCrawler():

    def __init__(self):
        self.sess = requests.Session()
        self.sess.headers = default_headers
        self.show_captcha_url = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
        self.login_url = 'https://www.zhihu.com/api/v3/oauth/sign_in'
        self.base_url = 'https://www.zhihu.com/'
        self.client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
        self.source = 'com.zhihu.web'
        self.username = '+8618306420050'
        self.passwd = 'pengchao123'

    def params(self):
        '''
        訪問首頁獲取兩個引數 _xsrf和d_c0
        :return: 
        '''
        self.sess.get(self.base_url)
        cookies = self.sess.cookies
        xsrf = cookies.get('_xsrf')
        xuuid = cookies.get('d_c0')

        return xsrf,xuuid

    def show_captcha(self):
        '''
        判斷是否需要驗證碼
        :return: 
        '''

        content = self.sess.get(self.show_captcha_url).json()

        if content.get('show_captcha'):
            return True
        else:
            return False

    def captcha_image_download(self):
        '''
        下載驗證碼圖片
        :return: 
        '''

        base64_img = self.sess.put(self.show_captcha_url).json().get('img_base64')
        imgdata = base64.b64decode(base64_img)
        with open('captcha_img.jpg', 'wb') as f:
            f.write(imgdata)
            f.close()

    def get_input_values(self):
        '''
        手動輸入驗證碼英文字母
        :return: 
        '''
        input_values = input('請輸入驗證碼:')

        return input_values

    def captcha_validation(self):
        '''
        提交驗證碼進行驗證
        :return: 
        '''

        input_values = self.get_input_values()

        validation_data = {
            'input_text': input_values
        }
        result = self.sess.post(self.show_captcha_url, data=validation_data).text
        print(result)
        return input_values

   def get_signature(self, time_str):
        '''
        獲取signature引數
        :param time_str:
        :return:
        '''
        signature_hmac = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)
        grant_type = 'password'
        client_id = self.client_id
        source = self.source
        now = time_str
        signature_hmac.update((grant_type + client_id + source + now).encode('utf-8'))
        return signature_hmac.hexdigest()

    def login_zhihu(self):
        '''
        登入
        :return:
        '''
        xxsrftoken,xudid = self.params()
        self.sess.headers.update({
            'X-Xsrftoken': xxsrftoken,
            'X-UDID': re.sub(r'(\|\d+)|(")','',xudid),
        })

        if self.show_captcha():
            self.captcha_image_download()
            input_values = self.captcha_validation()
        else:
            input_values = ''
            print('無驗證碼')

        time_str = str(int(time.time()*1000))

        login_data = {
            'client_id': self.client_id,
            'grant_type' : 'password',
            'timestamp' : time_str,
            'source' : self.source,
            'signature' : self.get_signature(time_str),
            'username' : self.username,
            'password' : self.passwd,
            'captcha' : json.dumps(input_values),
            'lang' : 'cn',
            'ref_source' : 'other_',
            'utm_source' : 'baidu',
        }

        login = self.sess.post(self.login_url, data=login_data)
        print(login.text)


if __name__ == '__main__':
    crawler = ZhihuCrawler()
    crawler.login_zhihu()

#總結:

  • 可以儲存cookies 下次登入的時候不用再次獲取了
  • 驗證碼可以接入打碼平臺
  • 編碼問題
  • js加強