當今大數(shù)據(jù)的時代碍彭,網(wǎng)絡(luò)爬蟲已經(jīng)成為了獲取數(shù)據(jù)的一個重要手段晤硕。
但要學習好爬蟲并沒有那么簡單悼潭。首先知識點和方向?qū)嵲谑翘嗔耍P(guān)系到了計算機網(wǎng)絡(luò)舞箍、編程基礎(chǔ)舰褪、前端開發(fā)、后端開發(fā)疏橄、App 開發(fā)與逆向占拍、網(wǎng)絡(luò)安全、數(shù)據(jù)庫捎迫、運維晃酒、機器學習、數(shù)據(jù)分析等各個方向的內(nèi)容窄绒,它像一張大網(wǎng)一樣把現(xiàn)在一些主流的技術(shù)棧都連接在了一起贝次。正因為涵蓋的方向多,因此學習的東西也非常零散和雜亂彰导,很多初學者搞不清楚究竟要學習哪些知識蛔翅,學習過程中遇到反爬也不知道用什么方法來解決,本篇我們來做一些歸納和總結(jié)位谋。
初學爬蟲
一些最基本的網(wǎng)站山析,往往不帶任何反爬措施。比如某個博客站點掏父,我們要爬全站的話就順著列表頁爬到文章頁盖腿,再把文章的時間、作者损同、正文等信息爬下來就可以了。
那代碼怎么寫呢鸟款?用 Python 的 requests 等庫就夠了膏燃,寫一個基本的邏輯,順著把一篇篇文章的源碼獲取下來何什,解析的話用 XPath组哩、BeautifulSoup、PyQuery 或者正則表達式处渣,或者粗暴的字符串匹配把想要的內(nèi)容摳出來伶贰,再加個文本寫入存下來就完事了。
代碼很簡單罐栈,就幾個方法調(diào)用黍衙。邏輯很簡單,幾個循環(huán)加存儲荠诬。最后就能看到一篇篇文章就被我們存到自己的電腦里面了琅翻。當然有的同學可能不太會寫代碼或者都懶得寫位仁,那么利用基本的可視化爬取工具,如某爪魚方椎、某裔采集器也能通過可視化點選的方式把數(shù)據(jù)爬下來聂抢。
如果存儲方面稍微擴展一下的話,可以對接上 MySQL棠众、MongoDB琳疏、Elasticsearch、Kafka 等等來保存數(shù)據(jù)闸拿,實現(xiàn)持久化存儲空盼。以后查詢或者操作會更方便。
反正胸墙,不管效率如何我注,一個完全沒有反爬的網(wǎng)站用最最基本的方式就搞定了。
到這里迟隅,你就說你會爬蟲了嗎但骨?不,還差的遠呢智袭。
Ajax奔缠、動態(tài)渲染
隨著互聯(lián)網(wǎng)的發(fā)展,前端技術(shù)也在不斷變化吼野,數(shù)據(jù)的加載方式也不再是單純的服務(wù)端渲染了⌒0ィ現(xiàn)在你可以看到很多網(wǎng)站的數(shù)據(jù)可能都是通過接口的形式傳輸?shù)模蛘呒词共皇墙涌谀且彩且恍?JSON 的數(shù)據(jù)瞳步,然后經(jīng)過 JavaScript 渲染得出來的闷哆。
這時候,你要再用 requests 來爬那就不頂用了单起,因為 requests 爬下來的源碼是服務(wù)端渲染得到的抱怔,瀏覽器看到頁面的和 requests 獲取的結(jié)果是不一樣的。真正的數(shù)據(jù)是經(jīng)過 JavaScript 執(zhí)行的出來的嘀倒,數(shù)據(jù)來源可能是 Ajax屈留,也可能是頁面里的某些 Data,也可能是一些 ifame 頁面等等测蘑,不過大多數(shù)情況下可能是 Ajax 接口獲取的灌危。
所以很多情況下需要分析 Ajax,知道這些接口的調(diào)用方式之后再用程序來模擬碳胳。但是有些接口帶著加密參數(shù)勇蝙,比如 token、sign 等等挨约,又不好模擬浅蚪,咋整呢藕帜?
一種方法就是去分析網(wǎng)站的 JavaScript 邏輯,死摳里面的代碼惜傲,揪出來這些參數(shù)是怎么構(gòu)造的洽故,找出思路來了之后再用爬蟲模擬或重寫就行了。如果你解出來了盗誊,那么直接模擬的方式效率會高非常多时甚,這里面就需要一些 JavaScript 基礎(chǔ)了,當然有些網(wǎng)站加密邏輯做的太牛逼了哈踱,你可能花一個星期也解不出來荒适,最后放棄了。
那這樣解不出來或者不想解开镣,那咋辦呢刀诬?這時候可以有一種簡單粗暴的方法就是直接用模擬瀏覽器的方式來爬取,比如用 Puppeteer邪财、Pyppeteer陕壹、Selenium、Splash 等树埠,這樣爬取到的源代碼就是真正的網(wǎng)頁代碼糠馆,數(shù)據(jù)自然就好提取了,同時也就繞過分析 Ajax 和一些 JavaScript 邏輯的過程怎憋。這種方式就做到了可見即可爬又碌,難度也不大,同時模擬了瀏覽器绊袋,也不太會有一些法律方面的問題毕匀。
但其實后面的這種方法也會遇到各種反爬的情況,現(xiàn)在很多網(wǎng)站都會去識別 webdriver癌别,看到你是用的 Selenium 等工具皂岔,直接干掉或不返回數(shù)據(jù),所以你碰到這種網(wǎng)站還得來專門解一下這個問題规个。
多進程、多線程姓建、協(xié)程
上面的情況如果用單線程的爬蟲來模擬是比較簡單的诞仓,但是有個問題就是速度慢啊。
爬蟲是 IO 密集型的任務(wù)速兔,所以可能大多數(shù)情況下都在等待網(wǎng)絡(luò)的響應(yīng)墅拭,如果網(wǎng)絡(luò)響應(yīng)速度慢,那就得一直等著涣狗。但這個空余的時間其實可以讓 CPU 去做更多事情谍婉。那怎么辦呢舒憾?多開點線程吧。
所以這時候我們就可以在某些場景下加上多進程穗熬、多線程镀迂,雖然說多線程有 GIL 鎖,但對于爬蟲來說其實影響沒那么大唤蔗,所以用上多進程探遵、多線程都可以成倍地提高爬取速度,對應(yīng)的庫就有 threading妓柜、multiprocessing 了箱季。
異步協(xié)程就更牛逼了,用 aiohttp棍掐、gevent藏雏、tornado 等等的基本上你想搞多少并發(fā)就搞多少并發(fā),但是還是悠著點作煌,別把人家網(wǎng)站搞掛了掘殴。
總之,用上這幾個最疆,爬蟲速度就提上來了杯巨。
但速度提上來了不一定是好事,反爬接著肯定就要來了努酸,封你 IP服爷、封你賬號、彈驗證碼获诈、返回假數(shù)據(jù)仍源,所以有時候龜速爬似乎也是個解決辦法?
分布式
多線程舔涎、多進程笼踩、協(xié)程都能加速,但終究還是單機的爬蟲亡嫌。要真正做到規(guī)暮坑冢化,還得來靠分布式爬蟲來搞挟冠。
分布式的核心是什么于购?資源共享。比如爬取隊列共享知染、去重指紋共享等等肋僧。
我們可以使用一些基礎(chǔ)的隊列或組件來實現(xiàn)分布式,比如 RabbitMQ、Celery嫌吠、Kafka止潘、Redis 等等,但經(jīng)過很多人的嘗試辫诅,自己去實現(xiàn)一個分布式爬蟲凭戴,性能和擴展性總會出現(xiàn)一些問題,當然特別牛逼的除外哈泥栖。不少企業(yè)內(nèi)部其實也有自己開發(fā)的一套分布式爬蟲簇宽,和業(yè)務(wù)更緊密,這種當然是最好了吧享。
現(xiàn)在主流的 Python 分布式爬蟲還是基于 Scrapy 的魏割,對接 Scrapy-Redis、Scrapy-Redis-BloomFilter 或者用 Scrapy-Cluster 等等钢颂,他們都是基于 Redis 來共享爬取隊列的钞它,總會多多少少遇到一些內(nèi)存的問題。所以一些人也考慮對接到了其他的消息隊列上面殊鞭,比如 RabbitMQ遭垛、Kafka 等等,解決一些問題操灿,效率也不差锯仪。
總之,要提高爬取效率趾盐,分布式還是必須要掌握的庶喜。
驗證碼
爬蟲難免遇到反爬,驗證碼就是其中之一救鲤。要會反爬久窟,那首先就要會解驗證碼。
現(xiàn)在你可以看到很多網(wǎng)站都會有各種各樣的驗證碼了本缠,比如最簡單的圖形驗證碼斥扛,要是驗證碼的文字規(guī)整的話,OCR 過一遍或者基本的模型庫都能識別丹锹,不想搞這個的話可以直接去對接個打碼平臺來搞稀颁,準確率還是有的。
然而你可能現(xiàn)在都見不到什么圖形驗證碼了楣黍,都是一些行為驗證碼匾灶,如某驗、某盾等等锡凝,國外也有很多粘昨,比如 reCaptcha 等等垢啼。一些稍微簡單一點的窜锯,比如滑動的张肾,你可以找點辦法識別缺口,比如圖像處理比對锚扎、深度學習識別都是可以的吞瞪。軌跡呢自己寫個模擬正常人行為的,加點抖動之類的驾孔。有了軌跡之后咋模擬呢芍秆,如果你牛逼,那么可以直接去分析驗證碼的 JavaScript 邏輯翠勉,把軌跡數(shù)據(jù)錄入妖啥,那就能得到里面的一些加密參數(shù),直接拿著這些參數(shù)放到表單或接口里面就能直接用了对碌。當然也可以用模擬瀏覽器的方式來拖動荆虱,也能通過一定的方式拿到加密參數(shù),或者直接用模擬瀏覽器的方式把登錄一起做了传趾,拿著 Cookies 來爬也行没卸。
當然拖動只是一種驗證碼迟赃,還有文字點選、邏輯推理等菜枷,要是真不想搞,可以找打碼平臺來解出來再模擬叁丧,但畢竟花錢的啤誊,一些高手就會選擇自己訓(xùn)練深度學習相關(guān)的模型,收集數(shù)據(jù)歹袁、標注坷衍、訓(xùn)練,針對不同的業(yè)務(wù)訓(xùn)練不同的模型条舔。這樣有了核心技術(shù)枫耳,也不用再去花錢找打碼平臺了,再研究下驗證碼的邏輯模擬一下孟抗,加密參數(shù)就能解出來了迁杨。不過有的驗證碼難得很,有的我也沒搞定凄硼。
當然有些驗證碼可能是請求過于頻繁而彈出來的铅协,這種如果換個 IP 什么的也能解。
封 IP
封 IP 也是個令人頭疼的事摊沉,行之有效的方法就是換代理了狐史。
代理很多種,市面上免費的,收費的太多太多了骏全。
首先可以把市面上免費的代理用起來苍柏,自己搭建一個代理池,收集現(xiàn)在全網(wǎng)所有的免費代理姜贡,然后加一個測試器一直不斷測試试吁,測試的網(wǎng)址可以改成你要爬的網(wǎng)址。這樣測試通過的一般都能直接拿來爬你的目標網(wǎng)站楼咳。我自己也搭建過一個代理池熄捍,現(xiàn)在對接了一些免費代理,定時爬母怜、定時測余耽,還寫了個 API 來取,放在 GitHub 了:https://github.com/Python3WebSpider/ProxyPool苹熏,打好了 Docker 鏡像宾添,提供了 Kubernetes 腳本,大家可以直接拿來用柜裸。
付費代理也是一樣缕陕,很多商家提供了代理提取接口,請求一下就能獲取幾十幾百個代理疙挺,我們可以同樣把它們接入到代理池里面扛邑。但這個代理也分各種套餐,什么開放代理铐然、獨享代理等等的質(zhì)量和被封的幾率也是不一樣的蔬崩。
有的商家還利用隧道技術(shù)搭了代理,這樣代理的地址和端口我們是不知道的搀暑,代理池是由他們來維護的沥阳,比如某布云,這樣用起來更省心一些自点,但是可控性就差一些桐罕。
還有更穩(wěn)定的代理,比如撥號代理桂敛、蜂窩代理等等功炮,接入成本會高一些,但是一定程度上也能解決一些封 IP 的問題术唬。
不過這些背后也不簡單薪伏,為啥一個好好的高匿代理就是莫名其妙爬不了,背后的一些事就不多講了粗仓。
封賬號
有些信息需要模擬登錄才能爬嘛嫁怀,如果爬的過快设捐,人家網(wǎng)站直接把你的賬號封禁了,就啥都沒得說了塘淑。比如爬公眾號的挡育,人家把你 WX 號封了,那就全完了朴爬。
一種解決方法當然就是放慢頻率,控制下節(jié)奏橡淆。
還有種方法就是看看別的終端召噩,比如手機頁、App 頁逸爵、wap 頁具滴,看看有沒有能繞過登錄的法子。
另外比較好的方法师倔,那就是分流构韵。如果你號足夠多,建一個池子趋艘,比如 Cookies 池疲恢、Token 池、Sign 池反正不管什么池吧瓷胧,多個賬號跑出來的 Cookies显拳、Token 都放到這個池子里面,用的時候隨機從里面拿一個搓萧。如果你想保證爬取效率不變杂数,那么 100 個賬號相比 20 個賬號,對于每個賬號對應(yīng)的 Cookies瘸洛、Token 的取用頻率就變成原來的了 1/5揍移,那么被封的概率也就隨之降低了。
奇葩的反爬
上面說的是幾種比較主流的反爬反肋,當然還有非常多奇葩的反爬那伐。比如返回假數(shù)據(jù)、返回圖片化數(shù)據(jù)石蔗、返回亂序數(shù)據(jù)喧锦、返回罵人的數(shù)據(jù)、返回求饒的數(shù)據(jù)抓督,那都具體情況看著辦吧燃少。
這些反爬也得小心點,之前見過一個反爬直接返回 rm -rf /
的也不是沒有铃在,你要是正好有個腳本模擬執(zhí)行返回結(jié)果阵具,后果自己想象哈碍遍。
JavaScript 逆向
說到重頭了。隨著前端技術(shù)的進步和網(wǎng)站反爬意識的增強阳液,很多網(wǎng)站選擇在前端上下功夫怕敬,那就是在前端對一些邏輯或代碼進行加密或混淆。當然這不僅僅是為了保護前端的代碼不被輕易盜取帘皿,更重要的是反爬东跪。比如很多 Ajax 接口都會帶著一些參數(shù),比如 sign鹰溜、token 等等虽填,這些前文也講過了。這種數(shù)據(jù)我們可以用前文所說的 Selenium 等方式來爬曹动,但總歸來說效率太低了斋日,畢竟它模擬的是網(wǎng)頁渲染的整個過程,而真實的數(shù)據(jù)可能僅僅就藏在一個小接口里墓陈。
如果我們能夠把一些接口的參數(shù)真正找出其中的邏輯恶守,用代碼來模擬執(zhí)行,那效率就會有成倍的提升贡必,而且還能在一定程度上規(guī)避上述的反爬現(xiàn)象兔港。
但問題是什么?難啊仔拟。
Webpack 是一方面押框,前端代碼都被壓縮和轉(zhuǎn)碼成一些 bundle 文件,一些變量的含義已經(jīng)丟失理逊,不好還原橡伞。然后一些網(wǎng)站再加上一些 obfuscator 的機制,把前端代碼變成你完全看不懂的東西晋被,比如字符串拆散打亂兑徘、變量十六進制化、控制流扁平化羡洛、無限 debug挂脑、控制臺禁用等等,前端的代碼和邏輯已經(jīng)面目全非欲侮。有的用 WebAssembly 等技術(shù)把前端核心邏輯直接編譯崭闲,那就只能慢慢摳了,雖然說有些有一定的技巧威蕉,但是總歸來說還是會花費很多時間刁俭。但一旦解出來了,那就萬事大吉了韧涨。怎么說牍戚?就像奧賽題一樣侮繁,解出來升天,解不出來 GG如孝。
很多公司招聘爬蟲工程師都會問有沒有 JavaScript 逆向基礎(chǔ)宪哩,破解過哪些網(wǎng)站,比如某寶第晰、某多锁孟、某條等等,解出來某個他們需要的可能就直接錄用你茁瘦。每家網(wǎng)站的邏輯都不一樣品抽,難度也不一樣。
App
當然爬蟲不僅僅是網(wǎng)頁爬蟲了腹躁,隨著互聯(lián)網(wǎng)時代的發(fā)展,現(xiàn)在越來越多的公司都選擇將數(shù)據(jù)放到 App 上面南蓬,甚至有些公司只有 App 沒有網(wǎng)站纺非。所以數(shù)據(jù)只能通過 App 來爬。
咋爬呢赘方?基本的就是抓包工具了烧颖,Charles、Fiddler 一把梭窄陡,抓到接口之后炕淮,直接拿來模擬就行了。
如果接口有加密參數(shù)怎么辦呢跳夭?一種方法你可以邊爬邊處理涂圆,比如 mitmproxy 直接監(jiān)聽接口數(shù)據(jù)。另一方面你可以走 Hook币叹,比如上 Xposed 也可以拿到润歉。
那爬的時候又怎么實現(xiàn)自動化呢?總不能拿手來戳吧颈抚。其實工具也多踩衩,安卓原生的 adb 工具也行,Appium 現(xiàn)在已經(jīng)是比較主流的方案了贩汉,當然還有其他的某精靈都是可以實現(xiàn)的驱富。
最后,有的時候可能真的就不想走自動化的流程匹舞,我就想把里面的一些接口邏輯摳出來褐鸥,那就得搞逆向了,IDA Pro赐稽、jdax晶疼、FRIDA 等工具就派上用場了酒贬,當然這個過程和 JavaScript 逆向一樣很痛苦,甚至可能得讀匯編指令翠霍。搞一個案例掉一把頭發(fā)也不是不可能的锭吨。
智能化
上面的這一通,都搞熟了寒匙,恭喜你已經(jīng)超過了百分之八九十的爬蟲玩家了零如,當然專門搞 JavaScript 逆向、App 逆向的都是站在食物鏈頂端的男人锄弱,這種嚴格來說已經(jīng)不算爬蟲范疇了考蕾,這種神我們就不算在里面了,反正我不是会宪。
除了上面的一些技能肖卧,在一些場合下,我們可能也需要結(jié)合一些機器學習的技術(shù)掸鹅,讓我們的爬蟲變得更智能起來塞帐。
比如現(xiàn)在很多博客、新聞文章巍沙,其頁面結(jié)構(gòu)相似度比較高葵姥,要提取的信息也比較類似。
比如如何區(qū)分一個頁面是索引頁還是詳情頁句携?如何提取詳情頁的文章鏈接榔幸?如何解析文章頁的頁面內(nèi)容?這些其實都是可以通過一些算法來計算出來的矮嫉。
所以削咆,一些智能解析技術(shù)也營運而生,比如提取詳情頁蠢笋,一位朋友寫的 GeneralNewsExtractor 表現(xiàn)就非常好态辛。
假如說我來了一個需求,我要爬取一萬個新聞網(wǎng)站數(shù)據(jù)挺尿,要一個個寫 XPath 嗎奏黑?寫死我吧。如果有了智能化解析技術(shù)编矾,在容忍一定錯誤的條件下熟史,完成這個就是分分鐘的事情。
總之窄俏,如果我們能把這一塊也學會了蹂匹,我們的爬蟲技術(shù)就會如虎添翼。
運維
這塊也是一個重頭戲凹蜈。爬蟲和運維也是息息相關(guān)限寞。
比如寫完一個爬蟲忍啸,怎樣去快速部署到 100 臺主機上跑起來。
比如怎么靈活地監(jiān)控每個爬蟲的運行狀態(tài)履植。
比如爬蟲有處代碼改動计雌,如何去快速更新。
比如怎樣監(jiān)控一些爬蟲的占用內(nèi)存玫霎、消耗的 CPU 狀況凿滤。
比如怎樣科學地控制爬蟲的定時運行、
比如爬蟲出現(xiàn)了問題庶近,怎樣能及時收到通知翁脆,怎樣設(shè)置科學的報警機制。
這里面鼻种,部署大家各有各的方法反番,比如用 Ansible 當然可以。如果用 Scrapy 的話有 Scrapyd叉钥,然后配合上一些管理工具也能完成一些監(jiān)控和定時任務(wù)罢缸。不過我現(xiàn)在用的更多是還是 Docker + Kubernetes,再加上 DevOps 一套沼侣,比如 GitHub Actions祖能、Azure Pipelines歉秫、Jenkins 等等蛾洛,快速實現(xiàn)分發(fā)和部署。
定時任務(wù)大家有的用 crontab雁芙,有的用 apscheduler轧膘,有的用管理工具,有的用 Kubernetes兔甘,我的話用 Kubernetes 就多一些了谎碍,定時任務(wù)也是很好實現(xiàn)。
至于監(jiān)控的話洞焙,也有很多蟆淀,專門的一些爬蟲管理工具自帶了一些監(jiān)控和報警功能。一些云服務(wù)也帶了一些監(jiān)控的功能澡匪。我用的是 Kubernetes + Prometheus + Grafana熔任,什么 CPU、內(nèi)存唁情、運行狀態(tài)疑苔,一目了然,報警機制在 Grafana 里面配一下也很方便甸鸟,支持 Webhook惦费、郵件甚至某釘兵迅。
數(shù)據(jù)的存儲和監(jiān)控,用 Kafka薪贫、Elasticsearch 個人感覺也挺方便的恍箭,我主要用的是后者,然后再和 Grafana 配合起來后雷,數(shù)據(jù)爬取量季惯、爬取速度等等監(jiān)控也都一目了然。
結(jié)語
至此臀突,爬蟲的一些涵蓋的知識點也就差不多了勉抓,怎么樣,梳理一下候学,是不是計算機網(wǎng)絡(luò)藕筋、編程基礎(chǔ)、前端開發(fā)梳码、后端開發(fā)隐圾、App 開發(fā)與逆向、網(wǎng)絡(luò)安全掰茶、數(shù)據(jù)庫暇藏、運維、機器學習都涵蓋到了濒蒋?上面總結(jié)的可以算是從爬蟲小白到爬蟲高手的路徑了盐碱,里面每個方向其實可研究的點非常多,每個點做精了沪伙,都會非常了不起瓮顽。
爬蟲往往學著學著,就成為了一名全棧工程師或者全干工程師围橡,因為你可能真的啥都會了暖混。但是沒辦法啊,都是被爬蟲逼的啊翁授,如果不是生活所困拣播,誰愿意一身才華呢?
然而有了才華之后呢收擦?摸摸頭頂贮配,臥槽,我的頭發(fā)呢炬守?
嗯牧嫉,大家都懂的。
最后最重要的,珍愛生命酣藻、珍愛每一根頭發(fā)曹洽。