知乎模擬登錄

常常在簡書上寫做,寫完後再發布到其餘網站,很是麻煩,因此準備搞一下自動發佈文章的工具。那麼第一步先要模擬登錄幾個網站。今天先從知乎開始。python

環境準備

Python:python3.6
IDE:pycharm
抓包工具:Charles
系統環境:Mac
瀏覽器:Chrome

抓包

首先進行網站登陸抓包,打開Chrome瀏覽器無痕窗口,而後清空全部緩存,打開Charles,在Chrome瀏覽器地址欄輸入www.zhihu.com,打開知乎登陸界面
圖1
圖2web

我是手機號登陸,郵箱沒有試過,不知道請求流程是否同樣,輸入帳號密碼,若是須要驗證碼的話會自動出現驗證碼輸入框,登陸成功後chrome

圖3

通常狀況下咱們能夠輸錯一次密碼或者驗證碼,來多看看請求流程。另外我用chrome瀏覽器抓取了好幾回登陸流程,發現都不同。因此最後就是綜合了一下幾回抓包的信息進行分析了。json

登陸分析

找到登陸請求

圖4

登陸請求通常是POST,這個不多有例外,有些網站通常會是第一個post請求,但是知乎的post請求有點頗多,這個請求中有username和password,那咱們就以這裏爲基準開始分析。看一下紅框中出現了兩個Authentication和Multipart,在其餘的登陸網站中我還沒碰到過這種狀況,這個Multipart好像以前的Form,Authentication應該是一種認證。api

圖5

先搜了一下這個Authentication信息
圖6瀏覽器

看到是在main.app.xxxxx.js腳本中,打開腳本看一下,基本上是固定值,其實經過屢次請求能夠發現這個值是固定的。同時咱們能夠肯定其餘的一些固定參數。看過我以前文章的同窗應該知道,肯定參數最簡單的方式就是屢次請求觀察,很容易就能肯定固定值。緩存

圖7

經過觀察只有signature參數比較麻煩,timestamp是時間戳,captcha是咱們輸入的驗證碼,那麼搞定這個參數咱們就離成功很近了,爲何是很近而不是成功?這個咱們得記住,通常在分析請求的時候參數只是一部分,咱們要關注headers、cookies、提交參數。首先參數咱們確認了只須要分析signature。那麼再來看看headers微信

圖8-抓包狀況一

圖9-抓包狀況二

這裏有兩種抓包狀況,針對這種狀況要單獨分析,咱們接下來再分析。
而後再說說cookies,有時候咱們並不須要一開始就分析cookies,並且不是cookies中的每一項都是必須的。咱們先把cookies信息截取出來。後面再分析cookie

圖10

signature

這裏咱們先分析提交參數signature。首先在charles中查詢參數值session

圖11

沒搜到,這種通常都是經過計算生成的,搜不到很正常。既然搜不到值,那咱們來搜一下參數名。

圖12

看一下搜索結果,main.app.xxxx.js中的比較像

圖13

其餘的參數也出如今這裏,那基本就是這個地方了。仔細觀察一下,SHA-一、setHMACKey、幾個update、getHMAC,第一反應是sha1加密,經過固定字符串+grantType+clientId+com.zhihu.web+時間戳進行加密,結果試了一下好像不是。度娘了一下HMAC / sha1,原來還有這種操做,OK這個參數搞定了,貼下代碼。

圖14

captcha

在抓包請求中咱們能夠看到,驗證碼請求應該是https://www.zhihu.com/api/v3/oauth/captcha?lang=en,在有些抓包結果中後面的lang=cn,不知道這倆是怎麼區分的。

圖15

咱們看到這個請求有三種訪問方式,GET、PUT、POST,分別看一下結果。

圖16

GET請求訪問結果會顯示是否須要輸入驗證碼。

圖17

PUT請求的返回值是圖片的base64加密數據。獲得base64數據後轉換爲圖片顯示便可

import base64
imgdata = base64.b64decode(result.get("img_base64"))
with open("cap.png", "wb") as fp:
    fp.write(imgdata)

POST請求會返回驗證碼驗證結果。

圖18

經過圖15的截圖(固然POST返回錯誤是我故意輸錯驗證碼的結果)咱們知道驗證碼並非在登陸請求的時候才進行驗證的,須要提早驗證。

headers

authorization的值和client_id值相等,那就都是固定值,不用管了。X-Xsrftoken:先搜一下這個值

圖19

圖20

在上面的請求response中設置的cookies值,那咱們直接從cookies中拿出_xsrf值,設置到headers中便可

圖21

X-UDID:跟X-Xsrftoken同樣,也是在cookies中設置的

圖22

拆分一下便可

圖23

既然咱們須要取cookies中的_xsrf和d_c0的值,那麼就得保證cookies中必須有這個值,稍微往上翻一下能夠看到Set Cookie在最初訪問www.zhihu.com中設置的
圖24

那麼訪問一下首頁就能夠了。分析到這裏就差很少了。接下來就是發送請求了。

multipart/form-data

圖25

這種表單也沒提交過,有點懵逼。可是看起來跟這個Content-Type: multipart/form-data有關係,請求發送類型。搜一下requests multipart,找到一個請求庫requests_toolbelt,使用方式以下:

from requests_toolbelt import MultipartEncoder
data = {
    "client_id":"xxxx",
    "grant_type":"password"
}
boundary = "----WebKitFormBoundaryJKBWBR54EIz5oTAY"
encode_data = MultipartEncoder(data, boundary=boundary)
headers["Content-Type"] = encode_data.content_type
requests.post(url, data=encode_data.to_string())

這裏有個boundary須要關注一下,首先幾回請求的boundary信息不同,那確定是不同的,因此我大概搜了一下boundary這個值,沒發現這個值。度娘了一下WebKitFormBoundary,好像說這個boundary其實不發送也沒什麼關係。主要是我剛開始寫的時候這個boundary默認是空的,結果居然登陸成功了。我以爲既然值是隨意的,那麼有總歸比沒有好點吧,防止被ban碼。WebKitFormBoundary後面那16位大概是大小寫字母和數字隨機吧。那我就僞造了一下。

def random_boundary():
    factor = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    boundary = "----WebKitFormBoundary{}".format("".join(random.choices(factor, k=16)))
    return boundary

好啦,這回算是全都搞定了,最後要驗證一下登陸是否成功,訪問一下我的中心便可:https://www.zhihu.com/people/wan-rou-2

最後咱們總結一下登陸過程:

  1. 訪問https://www.zhihu.com
  2. 訪問驗證碼連接,進行驗證
def get_captcha(self):
        captcha = ""
        url = "https://www.zhihu.com/api/v3/oauth/captcha?lang=en"
        # 請求頭中增長Authorization
        self.session.headers["Authorization"] = "oauth c3cef7c66a1843f8b3a9e6a1e3160e20"
        r = self.session.get(url)
        result = r.json()
        if not result.get("show_captcha"):
            # 不須要輸入驗證碼
            return ""
        else:
            while True:
                # 獲取驗證碼圖片base64
                r = self.session.put(url)
                result = r.json()
                imgdata = base64.b64decode(result.get("img_base64"))
                with open("cap.png", "wb") as fp:
                    fp.write(imgdata)
                im = Image.open("cap.png")
                im.show()
                captcha = input("輸入驗證碼>>")

                #驗證碼
                boundary = self.random_boundary()
                cap_data = MultipartEncoder(fields={"input_text":captcha}, boundary=boundary)
                self.session.headers["Content-Type"] = cap_data.content_type
                # 發送驗證碼進行驗證
                r = self.session.post(url, data=cap_data.to_string())
                result = r.json()
                if not result.get("error", None):
                    print("驗證碼正確")
                    break
                else:
                    # 驗證碼不正確,從新請求
                    print(result.get("error").get("message"))
            return captcha
  1. 發送登陸請求
  2. 訪問我的中心

若是你以爲個人文章還能夠,能夠關注個人微信公衆號:Python爬蟲實戰之路
也能夠掃描下面二維碼,添加個人微信號
公衆號

微信號

相關文章
相關標籤/搜索