Python爬蟲:頭條小姐姐們都來給你拜年啦!
覺得上面的小姐姐漂亮的,可以舉個爪子。
今天就來教大家來爬取頭條上的美女。
但是,不要著急,在學爬蟲之前,大家需要先學會分析Ajax請求。
前言
有時候我們會用requests抓取頁面的時候,得到的結果可能和瀏覽器中看到的不一樣:在瀏覽器中可以看到正常顯示的頁面數據,但是使用requests得到的結果并沒有。這是因為requests獲取的是原始的HTML文檔,而瀏覽器中的頁面則是經過javascript處理數據后生成的結果,這些數據的來源有多種,可能通過Ajax加載的,可能是包含在HTML文檔當中,也有可能是經過javascript特定算法計算后生成的。
對于第一種情況:Ajax加載數據是一種異步加載方式,原始的農業面最初是不會包含這些數據的,原始頁面加載完成之后,會再向服務器請求某個接口的數據,然后數據就會被處理從而呈現到網頁上,這就是一個Ajax請求。
按照目前web的發展形式,這種頁面會越來越多。網頁的原始HTML中不會包含任何的數據,數據是通過Ajax統一加載后呈現出來的,這樣在web開發上可以做到前后分離,而且降低了服務器直接渲染頁面帶來的壓力。
因此,直接利用requests來獲取原始HTML,是無法獲取到有效的數據的,這時需要分析網頁后臺向接口發送的Ajax請求,如果可以用requests來模擬Ajax請求,那么就可以正常抓取數據了。
什么是Ajax
Ajax是異步的javascript和xml。它不是一門編程語言,而是利用javascript保證頁面不被刷新,URL不變的情況下與服務器交換數據并更新部分網頁的技術。
對于傳統的網頁來說,要想更新數據就必須刷新整個頁面,但是有了Ajax之后,便可以在頁面不全部刷新的情況下更新內容。在這個過程中實際上是在后臺與服務器進行了數據的交換,獲取到數據之后,再利用javascript改變網頁,這樣頁面就會刷新了。
Ajax分析方法
這里以微博為例,我們知道拖動刷新的內容由Ajax加載,而且頁面的URL沒有任何變化,那么應該去哪里查看這些Ajax請求呢?
查看請求
這里還需要借助瀏覽器的開發者工具,下面以Chrome瀏覽器為例子進行簡單的介紹。
首先,打開微博的首頁隨便點擊任意一條微博,隨后在頁面中點擊鼠標右鍵,從彈出的快捷鍵菜單中選擇“檢查”選項,此時就會彈出開發者工具,如下圖所示:
此時在element選項卡中便會觀察到網頁的源代碼。不過這個不是我們需要尋找的內容,切換到network選項卡,隨后重新刷新頁面,可以發現這里多出了不少的條目,如下圖所示:
這里是頁面加載過程中瀏覽器與服務器之間發送請求和接收響應的所有內容。
Ajax其實是特殊的請求類型,它叫做xhr。在下面的圖片中,我們可以看到以big為開頭的請求,其類型type為xhr,其實這個就是Ajax請求。
鼠標點擊該請求的時候,右側可以看到該請求的Request Headers、URL和Response Headers等信息。其中Request Headers中有個信息為x-requested-with: XMLHttpRequest,這個就是標記此請求就是Ajax請求。如下圖所示:
隨后點擊一下preview,即可看到響應內容,它是json格式的數據,這里的Chrome為我們自動做了解析,點擊內容即可展開和收起相應的內容。如下圖所示:
由于里面的內容較多,我將它復制下來,比較容易觀察。
從上面的圖片可以觀察到,返回的內容其實是評論者的昵稱、評論內容、評論時間以及數。
另外,我們也可以切換到該頁面URL的請求,查看它的Response是什么,如下圖所示:
經過觀察,其實很容易發現,這里的代碼非常簡單,除了簡單的HTML代碼之外,其他的都是javascript。所以說,我們看到的微博頁面的數據并不是原始的頁面返回的,而是后來執行javascript后,再次向后臺發送的Ajax請求,瀏覽器拿到數據之后做進一步的渲染。
過濾請求
接下來利用瀏覽器中的開發者工具進行篩選,可以將Ajax請求全部篩選出來。在請求的上方有一層篩選欄,直接點擊XHR,此時在下方顯示的所有請求便都是Ajax請求了,如下圖所示:
接下來,不斷的滑動頁面,可以看到頁面底部有一條條新的微博評論被刷出,而開發者工具下方也會有一個個Ajax請求出現,這樣我們就可以捕捉到所有的Ajax請求了。如下圖所示:
至此,我們基本上可以分析出Ajax請求的一些詳細信息了,接下來只需要用程序模擬這些Ajax請求,就可以輕松的獲取到我們需要的信息了。
實戰
分析Ajax爬取今日頭條小姐姐
準備工作
在項目開始之前,請確保已經安裝好requests庫,如果沒有安裝,請參考下面的安裝方法:
pip?install?requests
抓取分析
在抓取之前,首先要分析抓取的邏輯。打開今日頭條的首頁:https://www.toutiao.com/
在左上角有一個搜索接口,這里嘗試抓取美女圖片,所以輸入“美女”二字搜索一下,如下圖所示:
這時打開開發者工具并刷新整個頁面,你會發現在network選項卡中會出現大量的數據請求信息。點擊第一個請求,并點擊response選項,搜索“寫真”,遺憾的事情發生了,里面沒有出現任何的關于寫真的字眼。
因此,可以初步判斷這些內容是由Ajax加載的,然后利用javascript渲染出來,接下來切換到XHR選項卡,查看有沒有Ajax請求,如下圖所示:
不出所料,這里果然出現了一些比較常規的Ajax請求,看看它的結果是否包含了頁面中的相關數據。
點擊data字段展開,發現這里許多條數據。點擊第一條展開,發現有article_url字段和title字段與網頁內容基本符合。
切換回headers選項卡,觀察URL和headers信息,如下圖所示:
可以看到這是一個get請求,請求的URL參數有:aid、app_name、offset、format、keyword、autoload、count、en_qc、cur_tab、from、pd、timestamp、_signature
繼續下滑網頁,你會發現在XHR選項卡中會出現越來越多的符合條件的Ajax請求。
如下所示:
https://www.toutiao.com/api/search/content/?aid=24&app_name=web_search&offset=0&format=json&keyword=%E7%BE%8E%E5%A5%B3&autoload=true&count=20&en_qc=1&cur_tab=1&from=search_tab&pd=synthesis×tamp=1612428382721&_signature=_02B4Z6wo00f015gKQTwAAIDAKPut9b7JdoeYL0WAAIYhGfqDyZWOUeA.JwSONVd37BCTuUrJwE1gIOy3vdsi5j5EDUTmLQeASGQij2uMyxRGe3E8Peoo2hzHzz5RwATyAVC1zu18zMfLZ5S3a
https://www.toutiao.com/api/search/content/?aid=24&app_name=web_search&offset=20&format=json&keyword=%E7%BE%8E%E5%A5%B3&autoload=true&count=20&en_qc=1&cur_tab=1&from=search_tab&pd=synthesis×tamp=1612429526639&_signature=_02B4Z6wo00d01lR4bAgAAIDB5ImAw3OJbZpUXWiAAPUUQO2SS7CFcMRjXUZZEIHjqP2egcZBSpXYmI6hWj.iKGkzEg0JBWChgQPoggOPf63cZGszmYGGuZdc1vbIO9gVPQmgwTOGo6qCxbjK94
https://www.toutiao.com/api/search/content/?aid=24&app_name=web_search&offset=40&format=json&keyword=%E7%BE%8E%E5%A5%B3&autoload=true&count=20&en_qc=1&cur_tab=1&from=search_tab&pd=synthesis×tamp=1612429531243&_signature=_02B4Z6wo00d01GfNiKwAAIDD1zxkZOf0K2hn6IwAAHnwQO2SS7CFcMRjXUZZEIHjqP2egcZBSpXYmI6hWj.iKGkzEg0JBWChgQPoggOPf63cZGszmYGGuZdc1vbIO9gVPQmgwTOGo6qCxbjKdd
https://www.toutiao.com/api/search/content/?aid=24&app_name=web_search&offset=60&format=json&keyword=%E7%BE%8E%E5%A5%B3&autoload=true&count=20&en_qc=1&cur_tab=1&from=search_tab&pd=synthesis×tamp=1612429538566&_signature=_02B4Z6wo00d01tH81gwAAIDBYQ06xI8rJLrR2dKAANRxQO2SS7CFcMRjXUZZEIHjqP2egcZBSpXYmI6hWj.iKGkzEg0JBWChgQPoggOPf63cZGszmYGGuZdc1vbIO9gVPQmgwTOGo6qCxbjK95
通過觀察,可以發現變化的內容有offset,timestamp和_signature ? ?。
timestamp指的是時間戳,而_signature指的是簽名認證。我自己也看了很多的教程,大多數關于這個 _signature的破解其實是js的逆向。本來我也想要寫用js逆向來破解這個 _signature,但是想到大家對于js逆向的方法可能不熟悉,畢竟我還沒有寫過該類文章,因此就不用這個方法了。經過測試發現不添加 _signature這個參數,對于獲取json響應并無影響。
并且通過分析該Ajax請求可以發現,里面有image_list字段,如下圖所示:
將這三個URL地址依次復制到瀏覽器之后可以發現,正是圖片的URL地址,但是需要注意的是這個并不是高清大圖的URL地址。
那問題就出現了,如何獲取高清大圖的URL地址呢?
如上圖所示,可以依次點擊URL地址,點擊進去之后就會發現里面的都是高清大圖的美女圖片了。如下圖所示:
那接下來就可以來看看json數據的URL和高清大圖的URL存在著什么樣的區別。
json數據內的URL
http://p1-tt.byteimg.com/list/190x124/pgc-image/183b0a981cf04498958beb194613fe43
高清大圖的URL
https://p6-tt.byteimg.com/origin/pgc-image/183b0a981cf04498958beb194613fe43?from=pc
通過觀察上面的兩個URL,其實區別也不是很大,只需要替換一些字符就可以了。
功能需求與實現
獲取json數據
獲取json數據倒不是特別難,主要是構造URL參數,具體代碼如下所示:
def?get_page(offset):
global?headers
headers?=?{
'cookie':?'tt_webid=6925326312953529869;?s_v_web_id=verify_kkqm44un_jaeWrYZ1_CnOS_4Xuz_BNoi_lE3QmTQ37uHC;?csrftoken=280e107c397cea753911229202dc0c3d;?ttcid=45904355bfa4470f9543c9cdeb94869f30;?tt_webid=6925326312953529869;?csrftoken=280e107c397cea753911229202dc0c3d;?__ac_signature=_02B4Z6wo00f01Coh3wgAAIDDmtAzwcHMKowqBduAAGqaWRSmr26jOpvMaDLR3MsdEfPZTRN9mxTbUMgGifTuVJdj6FgWrIu6yKXVS3Dsp.wz3Pxl9vgfeguqCWDdZ4ocFVxb4JgkNBJTefPR41;?__tasessionId=pyia55cn11612433914121;?MONITOR_WEB_ID=2264d055-a390-4e4b-9b1f-4a3e2a6ac47c;?tt_scid=pcBhM4miMb3tReqLN21gkwPxqHE92TFulL0hVtP4mdhm0UL1X0v0T1r158U8hVOua37f',
'referer':?'https://www.toutiao.com/search/?keyword=%E7%BE%8E%E5%A5%B3',
'user-agent':?'Mozilla/5.0?(Windows?NT?10.0;?Win64;?x64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/87.0.4280.66?Safari/537.36',
'x-requested-with':?'XMLHttpRequest'
}
params?=?{
'aid':'24',
'app_name':'web_search',
'offset':offset,
'format':'json',
'keyword':'美女',
'autoload':'true',
'count':'20',
'en_qc':'1',
'cur_tab':'1',
'from':'search_tab',
'pd':'synthesis',
'timestamp':int(time.time())
}
url?=?'https://www.toutiao.com/api/search/content/?'
try:
response?=?requests.get(url,?headers=headers,?params=params)
response.content.decode('utf-8')
if?response.status_code?==?200:
return?response.json()
except?requests.ConnectionError?as?e:
print('連接失敗',?e)
獲取圖片地址
經過上面的分析發現,json內部的圖片鏈接并不是高清大圖,所以在這里需要獲取高清大圖時需要做簡單的字符串替換。
具體代碼如下所示:
def?get_info(json):
new_img_lists?=?[]
image_lists?=?jsonpath.jsonpath(json,?'$.data[*].image_list..url')
for?image_list?in?image_lists:
new_img_list?=?image_list.replace('p1',?'p6').replace('p3',?'p6').replace('list','origin').replace('/190x124',?'')
new_img_lists.append(new_img_list)
return?new_img_lists
保存圖片
保存圖片的時候只需要向上面獲取到的圖片地址依次發送請求即可。具體代碼如下所示:
def?save_img(new_image_lists):
global?name
for?image?in?new_image_lists:
print('-------正在獲取第{}張----------'.format(name))
data?=?requests.get(image,?headers=headers).content
with?open(f'../image/{name}.jpg',?'wb')?as?f:
f.write(data)
name?+=?1
結果展示
關于翻頁
其實翻頁問題相當的好解決,通過對上面Ajax的接口就可以發現offset其實就是實現翻頁效果的參數,每一次都是20的倍數,因此只需要傳遞offset的偏移量即可。
具體代碼如下所示:
def?main(offset):
json?=?get_page(offset)
#?print(type(json))
new_image_lists?=?get_info(json)
save_img(new_image_lists)
if?__name__?==?'__main__':
pool?=?Pool()
groups?=?[i?*?20?for?i?in?range(4)]?#?0?20?40
pool.map(main,?groups)?#?傳遞偏移量
pool.close()
pool.join()
最后結果
通過上圖以及代碼可以知道,我只傳遞了3個偏移量,就獲取到了229張的美女圖片。
以上就是今天分享的所有內容了。
本篇文章告訴了大家:什么是Ajax?、如何查看與過濾Ajax請求。希望各位小伙伴以后遇到類似的問題可以舉一反三,加強練習。
最后
來自啃書君警告:
美女雖好,切莫貪杯,要是自己的ip被封了就得不償失了。
啃書君說:
沒有一件事是可以一蹴而就的,哪有什么三天速成,七天速成,沒有堅持何來成功!生活如此,學習亦是如此!
文章的每一個字,都是我用心敲出來的,只希望對得起每一位關注我的人。
在文章末尾點個【贊】與【在看】,讓我知道,你們也在為自己的學習拼搏和努力著。
掃一掃下面的二維碼,加我一起交流哦(拉你加Python交流群)~
“掃一掃加我”
Ajax Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。