發現問題
打開Fiddler軟件,用瀏覽器打開目標站點(http://www.kuaidaili.com/proxylist/2/) 。可以發現瀏覽器對這個頁面加載了兩次,第一次返回521,第二次才正常返回數據。很多沒有寫過網站或是爬蟲經驗不足的童鞋,可能就會覺得奇怪為什麼會這樣?為什麼瀏覽器可能正常返回數據而代碼卻不行?
仔細觀察兩次返回的結果可以發現:
私信小編007有驚喜大禮包哦!
1、第二次請求比第一次請求的Cookie內容多了個這個 _ydclearance=0c316df6ea04c5281b421aa8-5570-47ae-9768-2510d9fe9107-1490254971
2、第一次返回的內容一些複雜看不懂的JS代碼,第二次返回的就是正確的內容
其實這是網站反爬蟲的常用手段。大致過程是這樣的:首次請求數據時,服務端返回動態的混淆加密過的JS,而這段JS的作用是給Cookie添加新的內容用於服務端驗證,此時返回的狀態碼是521。瀏覽器帶上新的Cookie再次請求,服務端驗證Cookie通過返回數據(這也是為嘛代碼不能返回數據的原因)。
解決問題
既然你用JS生成Cookie, 那麼我也可以將JS函數翻譯成Python版本,原始JS是這樣的:
利用瀏覽器的JS代碼調試功能。這樣一切就迎刃而解,新建一個html文件,將第一次返回的html原文複製進去,保存用瀏覽器打開,在eval之前打上斷點,看到這樣的輸出:
可以看到這個變量po為:
document.cookie='_ydclearance=0c316df6ea04c5281b421aa8-5570-47ae-9768-2510d9fe9107-1490254971; expires=Thu, 23-Mar-17 07:42:51 GMT; domain=.kuaidaili.com; path=/'; window.document.location=document.URL
下面還有個eval("qo=eval;qo(po);")。
JS裡面的eval和Python的差不多,第二句的意思就是將eval方法賦給qo。然後去eval字符串po。而字符串po的前半段的意思是給瀏覽器添加Cooklie,後半段window.document.location=document.URL是刷新當前頁面。
這也印證了我上面的說法,首次請求沒有Cookie,服務端回返回一段生成Cookie並自動刷新的JS代碼。瀏覽器拿到代碼能夠成功執行,帶著新的Cookie再次請求獲取數據。而Python拿到這段代碼就只能停留在第一步。
那麼如何才能使Python也能執行這段JS呢,答案是PyV8。V8是Chromium中內嵌的javascript引擎,號稱跑的最快。PyV8是用Python在V8的外部API包裝了一個python殼,這樣便可以使python可以直接與javascript操作。PyV8的安裝大家可以自行百度。
代碼分析完成,下面切入正題擼代碼。
首先是正常請求網頁,返回帶加密的JS函數的html:
由於返回的是html,並不單純的JS函數,所以需要用正則提取JS函數的參數的參數。
還有一點需要注意,在JS函數中並沒有返回cookie,而是直接將cookie set到瀏覽器,所以我們需要將eval("qo=eval;qo(po);")替換成return po。這樣就能成功返回po中的內容。
這樣返回的cookie是字符串格式,但是用requests.get()需要字典形式,所以將其轉換成字典:
最後帶上解析出來的Cookie再次訪問網頁,成功獲取數據: