python 获取html js 变量_Python爬虫与反反爬虫实践
因為要收集數據,所以打算自己擼一個爬蟲,期間碰到網站的反爬措施,讓我非常頭疼,在此記錄一下。
基礎
爬蟲的基礎是不需要自己手動通過瀏覽器訪問網頁,而是通過程序構造網絡請求,獲取網站返回的結果。例如使用python的requests庫發送請求:
import requestsurl = 'https://www.baidu.com' ret = requests.get(url) print(ret.text)得到返回的html代碼后,可以自己解析數據,獲取感興趣的內容。
前期分析
首先分析要爬的網站,本質是一個信息查詢系統,提供了搜索頁面。例如我想獲取某個case,需要利用這個case的id或者name字段,才能搜索到這個case的頁面。因為我是希望數據盡可能不同,id和name分布得比較廣泛,自己構造的話特別麻煩,于是看看有沒有什么別的辦法。
好在網站還提供了熱搜功能,可以點擊熱搜鏈接,獲取每日/周/月的熱搜條目,每個熱搜榜單有50條信息。熱搜的網站鏈接是www.xxx.com/search?city=0 ,并且網站還提供了各地查詢系統的鏈接,例如tj.xxx.com/search?city=2 ,tj是天津的縮寫,2應該就是城市對應的索引。
前期的信息就那么多,先構造個請求試試:
可以看到服務器網站返回的狀態碼是521,后面是返回的text,看起來是個js腳本。
沒有正常的返回狀態,我首先想到的是用瀏覽器打開時,本地會保存cookies,可能是cookies沒對上。后來經過驗證,確實如此。
繞過反爬蟲
出于對安全的考慮,有些網站會做一些反爬的措施,例如前面說的需要判斷user-angent和cookies,或者判斷請求的ip是否在短時間內多次訪問。該網站用的是知道創宇的安全服務,頻繁訪問會提示ip行為不正常。
爬到的結果瀏覽器本質也是一個應用程序,只要ip不被封,既然可以通過瀏覽器訪問,那么我們自己寫程序來請求也是應該沒有問題的。
一些常見的繞過反爬蟲的措施有:
- 構造消息頭:如上所說的user-angent和cookies都包含在消息頭當中。
- 延長請求間隔:如果快速頻繁的發送請求,會大量搶占服務器資源,一般這種情況下很容易被網站的安全措施檢測出來并且封掉ip。所以適當的延長請求間隔,例如隨機隔2-5秒不等再發送下一次請求。
- 使用代理ip,解決ip檢測問題。
這些使用和實現起來都不是很麻煩,網上資料也比較多。
實戰
前面說到該網站需要cookies才能正常返回,但是該網站的cookies過期很快,我總不能用瀏覽器開發者工具獲取cookies,然后讓程序跑一會兒,每隔幾分鐘再手動獲取cookies,再讓程序繼續跑吧。如果反復這樣工作,那么寫爬蟲也就沒意義了。便開始對cookies進行分析。
從瀏覽器的開發者工具獲取到的cookies大約有10個字段,經過反復測試,能讓網站正常返回的只需要兩個字段,分別為__jsluid_h=011a522dbxxxxxxxxc1ce59d336e5e60和__jsl_clearance=1581880640.794|0|trTB4c6b%2BZpvxxxxxxxx8YqCOOo%3D (中間打碼處理)。
經過測試,如果請求的時候不自己構造cookies,默認會返回__jsluid_h :
我似乎明白了什么,看到之前返回那堆看不懂的js腳本,奧妙一定就在其中!
先嘗試了將那段js腳本保存下來,包裝成一個html文件打開,發現瀏覽器不停的刷新,也并沒起什么作用。那就分析一下js腳本,原來的代碼是單行的,自己整理一下并加了一些變量名和log,大概是這么個樣子:
第一次腳本將第16行的變量cmd打印出來看看,發現是另一段類似的腳本:
第二次腳本可以看到第二段腳本已經開始設置cookies的__jsl_clearence 字段了。這些顯然就是混淆后的js腳本,但是分析到這里也就大概弄明白了從發送請求到網站返回是怎么回事。之所以在本地跑這段代碼會不斷刷新,是因為第二行的setTimeout會讓其在1.5秒后重新請求,但是我們本地沒有服務處理請求讓其停止,所以會不斷的刷新。
而第一段腳本當中,變量y是完整的js代碼 ,代碼中的變量名和關鍵字被進行編碼了,變量x存儲的是用來替換的變量名和關鍵字,后面是解碼函數。所以現在的問題變成了獲取第一段腳本當中的cmd代碼,執行后再獲取第二段代碼的document.cookie的內容即可。
可是對于python和js的交互我完全沒接觸過,嘗試了PyExecJS和Js2Py,都沒辦法正常執行第一段腳本。無奈之下,我用python復現了第一段腳本,然后用Js2Py獲取了cookie。在請求一次過后,構造cookies,再請求一次,就可以了:
def test():url = REQUEST_URL# url = 'https://www.baidu.com'request_header = get_header()html = requests.get(url, headers=request_header)print(html)jscode = html.text# print(jscode)# tryjs.get_cookies()為復現的js代碼,以及用Js2Py獲取cookies的代碼request_cookies = try_js.get_cookies(jscode) request_cookies += ';__jsluid_h=' + html.cookies['__jsluid_h']request_header['Cookie'] = request_cookiesprint(request_header)html = requests.get(url, headers=request_header, timeout=5)print('new connection')print(html)print(html.text)其它
在實際操作中,我使用了西刺代理提供的代理ip,全軍覆沒??赡苁蔷W站的安全系統默認屏蔽了這些ip,所以最終我還是用自己的本機ip發送請求。
根據我的網絡與服務器的實際情況,我將請求間隔設置為10-20秒,超時設置為15秒。
因為我并不要求爬取網站的所有數據,所以有些返回不正常的頁面和條目可以忽略,在處理和清晰的時候去掉這些數據即可。
經過麓戰20多小時,差點就吐了,還好最終爬蟲成功跑了起來。
總結
以上是生活随笔為你收集整理的python 获取html js 变量_Python爬虫与反反爬虫实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: layer.alert 延迟自动关闭_自
- 下一篇: 绵阳python培训_《绵》字意思读音、