最近有個爬「某車之家」網站裏論壇帖子的 spike,遇到一系列的問題,遂這裏整理下。css
這裏以爬此網站的「奔騰T99論壇」 爲例。html
用我最近在搗鼓也最熟悉的框架+庫:scrapy + splashhtml5
帖子表 - carPostListpython
帖子的評論表 - carPostgit
carPost 裏的第一條數據是發帖人的內容github
carPostList 有 api 接口,直接調用獲取 json 數據便可web
carPost 無 api 接口,純服務器端渲染,直接抓取 page source 再用 css selector。mongodb
這裏選擇存入 mongodb。chrome
carPostList - 共 612 條數據docker
carPost - 共 10569 條數據
耗時:約 1 hour(設置了每一個請求間隔 3s)
這章是本文的重點,會列舉我遇到的一系列問題,和解決的思路、方法。
雖然爬 carPostList 直接調 api 就好,可是我最初是用 splash 來抓的 (參考我以前的 blog: Splash 學習筆記),且遇到個棘手的問題,就是帖子那一塊老是加載不出來,一直顯示 loading,以下圖:
一、一開始覺得是網速慢,或者網站自己須要加載更久,因而加大 wait 時間,發現無用。
二、覺得是 request header 的問題,因而加了 User-Agent 、Cookie 等都無用。用 selenuim 作了對比實驗,發現相同的 header 配置 selenuim 卻能夠加載成功,遂排除此條。
注:在用 selenuim 爬的時候,需加上 User-Agent,如
Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36
,不然爬取失敗。
三、覺得是 splash 功能配置的問題,因此我把 splash 的幾乎全部有嫌疑的屬性都打開了:
splash.plugins_enabled = true splash.indexeddb_enabled = true splash.webgl_enabled = true splash.html5_media_enabled = true splash.media_source_enabled = true splash.request_body_enabled = true splash.response_body_enabled = true # 下面都是默認爲true,我仍是覆寫了一遍。 splash.js_enabled = true splash.images_enabled = true
事實證實仍是無用。
關閉瀏覽器的私有模式 (匿名模式) 便可。(默認是打開的)
作法:splash.private_mode_enabled = false
或者啓動 splash 時指定個參數docker run -p 8050:8050 scrapinghub/splash --disable-private-mode
,結果以下圖:
查閱官方文檔(https://splash-cn-doc.readthedocs.io/zh_CN/latest/faq.html#how-do-i-disable-private-mode)可知:
「有時您仍然須要關閉私有模式,WebKit的本地存儲在開啓私有模式時不能正常工做。而且可能沒法爲本地緩存提供JavaScript填充程序。 所以對於某些站點(某些基於AngularJS站)您須要關閉私有模式。」
看來 splash 的私有模式影響了此網站的 DOM Storage。
官方文檔還說了:
「若是您關閉了私有模式,那麼不一樣請求之間可能會使用一樣的瀏覽器信息(cookie 不受影響)。若是 您下共享環境下使用Splash,您發送請求中的相關信息可能會影響其餘用戶發送的請求。」
因此在通常狀況下,仍是建議打開私有模式。
能夠作一個實驗來證明這一點,即打開 chrome,在 settings -> Cookies and site data -> Block 裏添加此網站,而後再訪問,發現網站確實出現了跟以前同樣加載不出的狀況。
在爬取帖子內容時,常常會出現一句話不全的狀況,即少個別字,而這些字在瀏覽器打開則顯示爲粗體樣式,複製粘貼出來則變成了
。
看來此網站作了反爬措施。
我用瀏覽器調試工具,發現這些口
有兩個特色:
一、這些口
,雖然看上去同樣,可是是不同的。用上面」是「這個粗體字來當例子,複製它,粘貼到 站長工具 - unicode 編碼轉化 裏,點 」中文轉 unicode「 ,發現他的 unicode 值爲 \ued19
。
二、這些口
都被一個<span>
包裹着,且用了 CSS3 的 @font-face,即:
<span style="font-family: myfont;"></span>
因而下載網站的字體文件(.ttf),用在線的 百度字體編輯器 打開,以下:
發現 "是" 這個字確實對應的是"$ED19"
。
三、雖然上面咱們找到了字體庫裏包含的口
與實際漢字的對應關係,可是每次打開新的評論頁面或者頁面刷新,這個字體庫都在實時的變化,即 口
與實際漢字的對應關係也在不停的變化。
我嘗試在 github 上搜索有沒有過來人的經驗,真的找到了:https://github.com/StuPeter/AutoHome_spider,利用這個第三方代碼,我:
一、先抓取評論頁的 page source,裏面的口
都顯示爲形如 
二、調用第三方代碼,替換口
爲正常漢字
from AutoHomeFont import get_new_font_dict # 第三方代碼提供的標準.ttf,只管引用就好 standardFontPath = 'standardFont.ttf' # 你當前評論頁對應的.ttf(由於每次評論頁.ttf都在變,因此需時刻保持最新) newFontPath = 'car.ttf' font_dict = get_new_font_dict(standardFontPath, newFontPath) ## 替換文字 # responseStr 爲抓取評論頁的 page source responseStr = "……<span style='font-family: myfont;'></span>……" for key, value in font_dict.items(): new_key = '&#x'+key[3:].lower()+';' new_value = eval(repr(value).replace('\\\\', '\\')) responseStr = responseStr.replace(new_key, new_value)
三、替換完成後,就能夠順利的進行接下來的 css selector 解析、入庫了。
eval(repr(value).replace('\\', '\'))
的做用是,第三方代碼裏對 unicode 的定義多了一個\
,如\\u4f4e
,因此須要在替換前減去一個\
。