Web Crawler with Python - 08.模擬登錄 (知乎)
(P.S.你也可以在我的博客閱讀這篇文章)
在抓取數據的過程中,經常會遇到需要登錄的網站,尤其是抓取社交(微博、豆瓣等)網站,幾乎無法避開模擬登錄。由於自己本身很喜歡玩知乎,加上知乎的模擬登錄並不是十分複雜,十分利於教學其他人,這篇博客將以知乎的模擬登錄為例,講述如何使用Python代碼登錄一個網站。
和之前一樣,我們打開Chrome的開發者工具,如圖所示:
注意上圖選中的"Preserve log"選項,很多情況下,網站的登錄操作完成之後都會伴隨著一個跳轉操作,如跳轉到首頁(如知乎)或跳轉到個人頁(QQ空間等),這會使我們的登錄操作的網路請求記錄被之後的請求覆蓋掉(這樣描述似乎不太準確,原諒我的語文水平一般)。當我們選中此項時,此刻起所有的歷史請求都會被保留下來,方便我們查看。
OK,那麼我們填入用戶名和密碼,點擊登錄按鈕,看看發生了什麼有趣的操作吧(雖然只是一個小號,還是把密碼藏起來吧):
有朋友私信問我,Network下一般有很多請求記錄,怎樣才能找到哪個是我們需要的請求。一般來講,對於登錄操作,會是一個POST請求,名字中帶有login或者signin的會更加可疑,另外一般可以排除掉js、css或者圖片的請求,再在剩下的請求中找找,多幾次經驗之後就很準確了,和那什麼事情是一樣的,你懂的。
對於這個請求,我們通過右側的"headers"這個tab可以得到如下信息:
- 請求地址及方式:http://www.zhihu.com/login/email, POST
- 請求參數:
- _xsrf:一會兒我們一起猜
- email:註冊用戶名
- password:密碼
- captcha:驗證碼
- remember_me:記住我
- 請求頭:request headers那一長串
關於什麼是xsrf/csrf,這裡不過多講解,摘抄一段Google的解釋:
CSRF(Cross Site Request Forgery, 跨站域請求偽造)是一種網路的攻擊方式,該攻擊可以在受害者毫不知情的情況下以受害者名義偽造請求發送給受攻擊站點,從而在並未授權的情況下執行在許可權保護之下的操作,有很大的危害性。
這個參數反應到對應的網頁源碼上,是這個:
<input type="hidden" name="_xsrf" value="e0b12dd958040e47458a3e0dc59ee197"/>n
還剩下最後一個需要解決的問題:驗證碼。這裡主要是模擬登錄知乎,所以不會過多涉及驗證碼方面的問題,對於這個例子,我們將手動輸入驗證碼,不過在代碼的設計上會考慮到如何替換為自動識別驗證碼的代碼。現在我們需要做的,是找到驗證碼對應的URL。通過點擊驗證碼可以獲取新的驗證碼圖片,這個過程中,實際上是向知乎伺服器發送了一個請求。通過Chrome的開發者工具(配合知乎JS代碼),可以看到得到驗證碼實際上是向"http://www.zhihu.com/captcha.gif"發送了一個GET請求,帶有一個參數為當前Unix時間戳。
那麼,我們從頭理一下,在我們使用瀏覽器登錄知乎的時候,我們究竟做了什麼:
- 打開知乎登錄頁(GET,https://www.zhihu.com/#signin)
- 瀏覽器(自動)從知乎載入驗證碼
- 輸入用戶名、密碼、驗證碼
- 點擊登錄
所以,對於我們模擬登錄的代碼,我們也將復原上述步驟。
首先我們設計一個驗證碼識別的規範:通過一個函數,接收驗證碼圖片內容,返回驗證碼文本字元串。這樣的介面我們可以通過手動輸入識別驗證碼,也可以通過人肉打碼服務,或者通過OCR做機器識別。但是無論怎樣的識別方式,我們都可以做到更換實現的時候不影響其它代碼。如下,便是通過手動輸入的驗證碼識別實現:def kill_captcha(data):n with open(captcha.png, wb) as fp:n fp.write(data)n return raw_input(captcha : )n
然後,我們的思路是通過一個函數模擬上面分析的步驟登錄知乎,返回登錄成功的requests.Session對象,我們通過持有這個對象,來完成登錄之後才能完成的事情。函數的實現如下:
import timenimport requestsnfrom xtls.util import BeautifulSoupnndef login(username, password, oncaptcha):n session = requests.session()nn _xsrf = BeautifulSoup(session.get(https://www.zhihu.com/#signin).content).find(input, attrs={name: _xsrf})[value]n captcha_content = session.get(http://www.zhihu.com/captcha.gif?r=%d % (time.time() * 1000)).contentn data = {n _xsrf: _xsrf,n email: username,n password: password,n remember_me: true,n captcha: oncaptcha(captcha_content)n }n resp = session.post(http://www.zhihu.com/login/email, data).contentn assert 登陸成功 in respn return sessionn
由於知乎登錄成功之後會返回一個JSON格式的字元串,我們用assert來判斷返回的字元串中是否包含登錄成功會返回的內容,如果成功,則會返回這個requests.Session對象。另外這裡的BeautifulSoup是通過xtls.util導入進來的,是因為默認創建BeautifulSoup對象時需要指定一個解析器,否則就會報一個警告,我實在懶得寫,又不想看警告,就自己進行了一些封裝,它會自己在你目前已有的解析器中選擇(我認為)最好的一個。
按照我們分析的邏輯拼裝好相應的代碼之後,就可以真正測試一下是否可行了,測試代碼非常簡單:
if __name__ == __main__:n session = login(email, password, kill_captcha)n print BeautifulSoup(session.get("https://www.zhihu.com").content).find(span, class_=name).getText()n
登錄過程中會要求手輸驗證碼,當然如果你通過其他方式識別了驗證碼會更加方便。如果登錄成功,那麼這段測試代碼會列印你的知乎昵稱到終端上。
小結
這篇博客用登錄知乎的示例講解了如何模擬登錄,總結成一句話就是:分析出你的瀏覽器是怎麼操作的,模擬它。看過之後你會明白,原來模擬登錄如此簡單,那麼,換一個網站自己試試吧(比如試試豆瓣吧),如果覺得非常簡單,那麼就挑戰下微博的模擬登錄吧。
好了,這篇博客到此結束,這些天比較忙,更新的速度較慢,見諒~~~
推薦閱讀: