Python-requests-知乎模擬登入
繼續我的python爬蟲旅程,開始寫部落格的時候,說一天一篇,真的只是動動嘴皮子,做起來還真的難,其實是自己給自己找理由…
-
不管怎樣,今天來更新一篇,寫個知乎的模擬登入,感覺最開始學習爬蟲的時候,大家都期盼著可以寫那種需要登入的網站,或者有各種驗證碼的,那時候看人家在群裡說誰能破解驗證碼,登入某網站抓取資訊的時候,羨慕的不行,想要迫切的去學習,慢慢的學會了,感覺驗證碼的破解也就那麼回事,有些很難,花很多功夫,或者直接放棄。再或者直接接個打碼平臺,也不用手動去登入,再學著學著,感覺驗證碼的破解也就沒那麼重要了,並沒有在驗證碼的破解上投入很多的時間,可以直接交給打碼平臺,專業處理驗證碼的人去處理就好了,不過常用的一些還是要了解一些,原理是什麼。
-
再就是還有很多請求需要提交引數,有的明文直接寫到data,或者params引數裡面直接提交,有些網站給它加密或者做很多處理,讓人很煩躁,還有很多引數用js程式碼去生成,對像我一樣不太懂js得人來說,真的是蛋痛。
-
說了這麼多,其實就是說要學習真的太多,但是這個懶病該怎麼治!!!
廢話不多說,來看看知乎的模擬登入~!
知乎今年改版了好像網頁,記得去年寫過一次,今年再一看變了不少,百度搜的程式碼基本上都是以前版本的,很少有目前版本的模擬登入部落格或者文章什麼的。
#簡單整理下思路:
- 手動登入,抓包
- 分析
- 驗證碼獲取
- 驗證碼識別
- 驗證碼驗證,資料提交
- 登入
#手動登入,抓包
- 用fiddler來抓包,簡單瀏覽分析下
#驗證碼獲取
- 手動登陸的時候,發現有的時候並沒有驗證碼,有的時候是英文字母的驗證碼,有的時候是選擇倒立漢字的驗證碼
- show_captcha 為false 的時候,不需要驗證碼
- show_captcha 為true的時候,需要驗證碼
- 所以,搞個if判斷,是否要驗證碼就可以
#驗證碼識別
- 上面說到有2種驗證碼,怎麼判斷是哪種,其實,驗證碼的連結只有1個字每的差別
- 多試幾次,就會發現
- 怎麼識別呢?
- 判斷 show_captcha 為true後,發現下面它又緊接著再次訪問了下同一個連結,但是是put請求,看下它的內容會發現,它就是我們要的驗證碼圖片,把它儲存到本地
- 本文章就使用英文字母的驗證碼,漢字的其實跟12306的差不多,也是提交座標進行驗證,不過它進行了處理,每個座標都除以了2,而且y的值貌似必須按照 ?.03125的格式,x可以是整數或者?.5的形式 -漢字: {“img_size”:[200,44],“input_points”:[[50,25.53125],[151,23.53125]]} size固定大小好像,測試了很多都是200,400
- 字母:直接提交字母就可以
#驗證碼提交,進行驗證
#最後,登入
-
要提交的引數還是挺多的,一個一個來看
-
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
-
再在js裡面搜尋下signature
-
它是用了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加強