引言:python
關於爬小姐姐的腳本示例,在個人Gayhub倉庫:ReptileSomething 裏已經有好幾個了,基本都是沒什麼技術含量的,直接解析HTML拿到 圖片的URL,而後下載,特別開一篇寫爬取花瓣網的小姐姐的實戰教程, 是由於爬這個網站的時候會遇到好幾個問題,第一感覺到了反爬蟲的套路, (折騰了我將近2天):git
不信的話能夠試試。 img.hb.aicdn.com/36b521f7177…github
我以爲算是爬圖片裏稍微有點難度的站點了,強烈建議跟着我 一塊兒回顧這個過程!ajax
Chrome抓包的時候,抓到的數據和Elements的內容不同,正則表達式
js動態加載數據,前面已經見識過這種反爬蟲的套路了, 有Selenium在手,根本不虛,模擬一波瀏覽器請求 加載下就能獲得和Elements同樣的內容了。json
兩個問題:瀑布流和Ajax動態加載數據。 怎麼說?且聽我一一道來:瀏覽器
沒事喜歡練手的我意外發現了花瓣網,F12 Chrome抓一波包:bash
隨手寫個代碼看看:app
先Elements看下咱們想扒的是什麼:工具
這裏儘管有個img,可是明顯是個小圖,應該是要點開a那個 /pins/1433175317連接裏纔有大圖,點開: huaban.com/pins/143317…
看下Elements,這個就是咱們想要的圖片url:
恩,一如既往的簡單套路,搞到批量的列表url,而後下載圖片。 看回咱們的利用Selenium獲得的網頁代碼,能夠很穩。
接下來的事情本該就水到渠成的了,而後這時候發生了一件 使人猝不及防的事情:
在網頁那裏滾到底部發現會加載更多的圖片,越滾越多,可是 咱們的Selenium只抓到了30個,咦,這種在以前抓某個網站 的時候就遇到過了,寫個簡單的滾動到底部的js,而後Selenium 循環執行這個腳本圖片數/30次就行了,中途能夠休眠1s給他加載, 執行完畢後,再去調用page_source獲得最終的頁面代碼,而後走波 BeautifulSoup把咱們1000多個小姐姐扒出來就好。
可是實際運行的結果卻出乎咱們的意料,最後獲得的小姐姐列表 仍是30個,臥槽,什麼鬼,接着打開咱們的瀏覽器,滾動的時候 發現,列表內容居然是動態變化的,打開圖片列表節點,滾動網 頁,不由又發出一句,臥槽,什麼鬼,列表是動態變化的???
這些剛還不在的,忽然就蹦出來了,就好像你刷着即刻刷着 刷着就蹦出一個x子。動態加載?想一想野路子,要不咱們本身 量化下滾動偏移量,好比滾動100,咱們抓一波頁面,存一下, 最後作下去重?這野路子不是通常的野:
單不說怎麼量化這個偏移量了,瀏覽器寬度不同時加載的 數目還不必定是30,而後那麼多畫板,你每一個畫板這樣玩? 效率巨低。
苦苦尋覓後,發現了兩個關鍵詞:瀑布流和Ajax動態加載數據
瀑布流:良莠不齊的多欄佈局以及到達底部自動加載的方式 Ajax動態加載數據:在不從新加載整個網頁的狀況下,對網頁的某 部分進行更新
簡單點說就是:
圖片經過JS加載成瀑布流的形式,當到達底部後,會請求後臺拿到更多 的數據,解析後經過Ajax,能夠在不關閉不轉跳不刷新瀏覽器的狀況下 部分更新頁面內容。
So,咱們咱們從新抓抓包,在滾動到底部的時候看下抓到的數據, 點擊篩選XHR(XMLHttpRequest),這個是瀏覽器後臺與服務之間 交換數據的文件,通常爲json格式:
點開,右側看看Headers,果真是json格式的:
發現有這樣的請求頭,先放着,等下再研究規律:
點開右側Previews,發現了傳回來的一大串Json,這裏的 pins應該就是新增長的妹子圖的相關數據了。
爲了方便研究,我又滾動了幾下,加載更多的數據,以方便 找出規律:
從上面咱們能夠獲得一些這樣的信息: 首先是Get請求,固定的基地址:http://huaban.com/boards/18907029/ 這個18907029是畫板id,後面的參數,jcx1ki7y和wfl=1應該是固定的, limit這個是加載數量,通常是分頁用的,就是一次加載20條新數據, 最後這個max:1348131400 暫時不知道是什麼?不過應該是某個什麼id 吧,看下第二個的max是1263961742,複製到第一個返回的json裏搜搜:
臥槽,恰好最後一個,不會那麼巧吧?而後把第三個max:1247629077複製 到第二個的Json裏看看:
果真,好傢伙,這個max就是每次拿到的最後一個圖片的pin_id 知道規則了,模擬一波請求,解析一波json,每次拿到最後這個 pin_id,用做下次請求,當返回的pins裏沒東西,說明已經加載 完全部的了,來,寫一波代碼,先要處理剛進來時加載的列表, 獲得最後的一個圖的pin_id,而後才能開始執行上面那個拿 json的操做。
在我準備用Selenium模擬請求主頁的時候,我發現了用urllib 模擬請求,裏面就能拿到最後一個pin_id,只不過他是寫在js裏 的,咱們能夠經過正則表達式拿到咱們想要的pin_id們:
還有點開一個具體的大圖頁,發現他的url規則居然是: huaban.com/pins/926502… 就是http://huaban.com/pins/ + pin_id,因此咱們只要得到pin_id就能夠了!
規則清楚了,接下來一步步寫代碼來獲取咱們想要的數據吧!
1)首頁數據的獲取
進來的時候會加載一次,不是經過Ajax加載,默認是30個,須要處理 一波網頁得到這個30個數據,而後30個數據的最後一個用於請求Ajax。 經過正則拿到pins這段json。
代碼以下:
測試下代碼:
打開網頁加載更多確認下這個最後的pid是否正確:
能夠正確,接着就來處理json數據了~ (這要注意正則匹配用的是search,我一開始沒留意用的是match, 用一些真這個校驗工具測試本身的正則一直是對的,可是丟程序 裏缺一直不匹配,返回None,要注意!!!)
2)處理Ajax數據
首先是請求頭的設置,和咱們普通的請求不同,若是你直接 用瀏覽器打開ajax加載數據的那個url發現返回的並非json!!!
隨手找個連接試試,就是解析json而已,
簡單測試下:
運行結果:
能夠,拿到數據了,接下來要優化下點東西,若是每次都要去拼接: 這串東西不就很麻煩了,能夠經過正則來進行替換:
這裏用到前面沒有細講的re.sub()替換方法和向前和向後界定 這兩個東西何時用到,當前這個場景就能用到,好比咱們想替換 pin_id,用一個括號括着想替換的部分,感受應該就能替換了:
結果:
是的,前面一大段東西沒了,若是你用了向前(?<=...)和向後界定(?=), 就可讓正則只匹配和替換這兩個中間部分的字符串了~
好的,替換成功,小小整理一下代碼:
好的,pin_id都可以拿到了,接下來經過這些想辦法拿到圖片啦, 點開一個圖片的詳情頁,好比:
查看頁面結構,獲得圖片url:
而後寫個簡單的模擬請求下這個網址,看下返回的數據有沒有這個圖片Url相匹配 的內容,所有搜沒有,而後把後面的分紅三段,一段段搜: 先搜:36b521f717741a4e3e024fd29606f61b8f960318f3763,秒找到, 有六處匹配的,這後面跟着-WzUoLC,就剩下最後的fw658,全局搜搜不到
難道是固定的?打開另外一個詳情頁看看,果真,fw658是固定的:
臥槽,key???前面拿json的數據就能拿到key,臥槽,該不會就 這樣拼接就能夠了吧,找到前面一個:
...使人沒法接受,折騰了那麼舊,原來拿到key就能夠拼接得出圖片url了
嘖嘖,正確的圖片url拿到了,瀏覽器打開也是沒問題的,接着就是無腦 拼url,而後一個個下載了,正當我覺得一切已經結束的時候,才發現了 最後的隱藏關卡:"圖片並不能下載"???右鍵另存爲保存直接失敗, py代碼直接崩潰。
而後,在那個圖片的詳情頁卻是能夠下載,個人天,難不成要我每一個 圖片都用Selenium加載,而後處理頁面數據,在這裏下? 講真,我是很是抗拒用Selenium的,慢不說,還耗內存, 代理也不怎麼好設置,並且別人會說我low,百度谷歌搜了 一大堆,基本都是說了等於沒說...正在我萬念俱灰,想用回 Selenium這種Low比方式的時候,我萌生了一個想法: 會不會是須要登陸後才能下載,因而乎我把連接發給我組 的UI,而後她默認瀏覽器居然是ie,而後奇蹟發現了,沒有 登陸,直接用ie瀏覽器打開了,而後他麼的,能夠右鍵保存 到本地?接着我又把連接發給我隔壁的後臺小哥,一樣用ie 打開,能夠。此時熟悉的BGM響起:
真相:花瓣沒有作ie系列瀏覽器的兼容
因此,模擬ie瀏覽器的User-Agent就能夠下載圖片了!!!
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)'
複製代碼
立馬試試,當看到第一張圖片下載到了個人倉庫裏的時候, 我就知道我猜對了:
剩下的就是組織一波代碼,批量下載了~
import urllib.request
import urllib.error
import re
import json
import coderpig
import os
# 圖片拼接url後,分別是前綴後綴
img_start_url = 'http://img.hb.aicdn.com/'
img_end = '_fw658'
# 獲取pins的正則
boards_pattern = re.compile(r'pins":(.*)};')
# 修改pin_id的正則
max_pattern = re.compile(r'(?<=max=)\d*(?=&limit)')
# 圖片輸出文件
pin_ids_file = 'pin_ids.txt'
# 圖片輸出路徑
pic_download_dir = 'output/Picture/HuaBan/'
json_headers = {
'Host': 'huaban.com',
'Accept': 'application/json',
'X-Request': 'JSON',
'X-Requested-With': 'XMLHttpRequest'
}
# 得到borads頁數據,提取key列表寫入到文件裏,並返回最後一個pid用於後續查詢
def get_boards_index_data(url):
print(url)
proxy_ip = coderpig.get_proxy_ip()
resp = coderpig.get_resp(url, proxy=proxy_ip).decode('utf-8')
result = boards_pattern.search(resp)
json_dict = json.loads(result.group(1))
for item in json_dict:
coderpig.write_str_data(item['file']['key'], pin_ids_file)
# 返回最後一個pin_id
pin_id = json_dict[-1]['pin_id']
return pin_id
# 模擬Ajax請求更多數據
def get_json_list(url):
proxy_ip = coderpig.get_proxy_ip()
print("獲取json:" + url)
resp = coderpig.get_resp(url, headers=json_headers, proxy=proxy_ip).decode('utf-8')
if resp is None:
return None
else:
json_dict = json.loads(resp)
pins = json_dict['board']['pins']
if len(pins) == 0:
return None
else:
for item in pins:
coderpig.write_str_data(item['file']['key'], pin_ids_file)
return pins[-1]['pin_id']
# 下載圖片的方法
def download_pic(key):
proxy_ip = coderpig.get_proxy_ip()
coderpig.is_dir_existed(pic_download_dir)
url = img_start_url + key + img_end
resp = coderpig.get_resp(url, proxy=proxy_ip, ie_header=True)
try:
print("下載圖片:" + url)
pic_name = key + ".jpg"
with open(pic_download_dir + pic_name, "wb+") as f:
f.write(resp)
except (OSError, urllib.error.HTTPError, urllib.error.URLError, Exception) as reason:
print(str(reason))
if __name__ == '__main__':
coderpig.init_https()
if os.path.exists(pin_ids_file):
os.remove(pin_ids_file)
# 一個畫板連接,可自行替換
boards_url = 'http://huaban.com/boards/27399228/'
board_last_pin_id = get_boards_index_data(boards_url)
board_json_url = boards_url + '?jcx38c3h&max=354569642&limit=20&wfl=1'
while True:
board_last_pin_id = get_json_list(max_pattern.sub(str(board_last_pin_id), board_json_url))
if board_last_pin_id is None:
break
pic_url_list = coderpig.load_data(pin_ids_file)
for key in pic_url_list:
download_pic(key)
print("下載完成~")
複製代碼
輸出結果:
參見吾王~
磕磕碰碰,總算是把代碼給擼出來了,成功又收穫了一大波小姐姐, 爬蟲技能點+1,建議仍是少用無腦Selenium吧,另外剛發現, Chrome直接支持右鍵導出XPath,就不用本身慢慢扣了(若是你用lxml的話)。
好的,就說那麼多~
====== 修正 ======
本節源碼下載:
來啊,Py交易啊
想加羣一塊兒學習Py的能夠加下,智障機器人小Pig,驗證信息裏包含: Python,python,py,Py,加羣,交易,屁眼 中的一個關鍵詞便可經過;
驗證經過後回覆 加羣 便可得到加羣連接(不要把機器人玩壞了!!!)~~~ 歡迎各類像我同樣的Py初學者,Py大神加入,一塊兒愉快地交流學♂習,van♂轉py。