一個(gè)Python爬蟲(chóng)工程師學(xué)習(xí)養(yǎng)成記
大數(shù)據(jù)的時(shí)代,網(wǎng)絡(luò)爬蟲(chóng)已經(jīng)成為了獲取數(shù)據(jù)的一個(gè)重要手段。
但要學(xué)習(xí)好爬蟲(chóng)并沒(méi)有那么簡(jiǎn)單。首先知識(shí)點(diǎn)和方向?qū)嵲谑翘嗔?,它關(guān)系到了計(jì)算機(jī)網(wǎng)絡(luò)、編程基礎(chǔ)、前端開(kāi)發(fā)、后端開(kāi)發(fā)、App 開(kāi)發(fā)與逆向、網(wǎng)絡(luò)安全、數(shù)據(jù)庫(kù)、運(yùn)維、機(jī)器學(xué)習(xí)、數(shù)據(jù)分析等各個(gè)方向的內(nèi)容,它像一張大網(wǎng)一樣把現(xiàn)在一些主流的技術(shù)棧都連接在了一起。正因?yàn)楹w的方向多,因此學(xué)習(xí)的東西也非常零散和雜亂,很多初學(xué)者搞不清楚究竟要學(xué)習(xí)哪些知識(shí),學(xué)習(xí)過(guò)程中遇到反爬也不知道用什么方法來(lái)解決,本篇我們來(lái)做一些歸納和總結(jié)。
初學(xué)爬蟲(chóng)
一些最基本的網(wǎng)站,往往不帶任何反爬措施。比如某個(gè)博客站點(diǎn),我們要爬全站的話(huà)就順著列表頁(yè)爬到文章頁(yè),再把文章的時(shí)間、作者、正文等信息爬下來(lái)就可以了。
那代碼怎么寫(xiě)呢?用 Python 的 requests 等庫(kù)就夠了,寫(xiě)一個(gè)基本的邏輯,順著把一篇篇文章的源碼獲取下來(lái),解析的話(huà)用 XPath、BeautifulSoup、PyQuery 或者正則表達(dá)式,或者粗暴的字符串匹配把想要的內(nèi)容摳出來(lái),再加個(gè)文本寫(xiě)入存下來(lái)就完事了。
代碼很簡(jiǎn)單,就幾個(gè)方法調(diào)用。邏輯很簡(jiǎn)單,幾個(gè)循環(huán)加存儲(chǔ)。最后就能看到一篇篇文章就被我們存到自己的電腦里面了。當(dāng)然有的同學(xué)可能不太會(huì)寫(xiě)代碼或者都懶得寫(xiě),那么利用基本的可視化爬取工具,如某爪魚(yú)、某裔采集器也能通過(guò)可視化點(diǎn)選的方式把數(shù)據(jù)爬下來(lái)。
如果存儲(chǔ)方面稍微擴(kuò)展一下的話(huà),可以對(duì)接上 MySQL、MongoDB、Elasticsearch、Kafka 等等來(lái)保存數(shù)據(jù),實(shí)現(xiàn)持久化存儲(chǔ)。以后查詢(xún)或者操作會(huì)更方便。
反正,不管效率如何,一個(gè)完全沒(méi)有反爬的網(wǎng)站用最最基本的方式就搞定了。
到這里,你就說(shuō)你會(huì)爬蟲(chóng)了嗎?不,還差的遠(yuǎn)呢。
Ajax、動(dòng)態(tài)渲染
隨著互聯(lián)網(wǎng)的發(fā)展,前端技術(shù)也在不斷變化,數(shù)據(jù)的加載方式也不再是單純的服務(wù)端渲染了?,F(xiàn)在你可以看到很多網(wǎng)站的數(shù)據(jù)可能都是通過(guò)接口的形式傳輸?shù)模蛘呒词共皇墙涌谀且彩且恍? JSON 的數(shù)據(jù),然后經(jīng)過(guò) JavaScript 渲染得出來(lái)的。
這時(shí)候,你要再用 requests 來(lái)爬那就不頂用了,因?yàn)?requests 爬下來(lái)的源碼是服務(wù)端渲染得到的,瀏覽器看到頁(yè)面的和 requests 獲取的結(jié)果是不一樣的。真正的數(shù)據(jù)是經(jīng)過(guò) JavaScript 執(zhí)行的出來(lái)的,數(shù)據(jù)來(lái)源可能是 Ajax,也可能是頁(yè)面里的某些 Data,也可能是一些 ifame 頁(yè)面等等,不過(guò)大多數(shù)情況下可能是 Ajax 接口獲取的。
所以很多情況下需要分析 Ajax,知道這些接口的調(diào)用方式之后再用程序來(lái)模擬。但是有些接口帶著加密參數(shù),比如 token、sign 等等,又不好模擬,咋整呢?
一種方法就是去分析網(wǎng)站的 JavaScript 邏輯,死摳里面的代碼,揪出來(lái)這些參數(shù)是怎么構(gòu)造的,找出思路來(lái)了之后再用爬蟲(chóng)模擬或重寫(xiě)就行了。如果你解出來(lái)了,那么直接模擬的方式效率會(huì)高非常多,這里面就需要一些 JavaScript 基礎(chǔ)了,當(dāng)然有些網(wǎng)站加密邏輯做的太牛逼了,你可能花一個(gè)星期也解不出來(lái),最后放棄了。
那這樣解不出來(lái)或者不想解,那咋辦呢?這時(shí)候可以有一種簡(jiǎn)單粗暴的方法就是直接用模擬瀏覽器的方式來(lái)爬取,比如用 Puppeteer、Pyppeteer、Selenium、Splash 等,這樣爬取到的源代碼就是真正的網(wǎng)頁(yè)代碼,數(shù)據(jù)自然就好提取了,同時(shí)也就繞過(guò)分析 Ajax 和一些 JavaScript 邏輯的過(guò)程。這種方式就做到了可見(jiàn)即可爬,難度也不大,同時(shí)模擬了瀏覽器,也不太會(huì)有一些法律方面的問(wèn)題。
但其實(shí)后面的這種方法也會(huì)遇到各種反爬的情況,現(xiàn)在很多網(wǎng)站都會(huì)去識(shí)別 webdriver,看到你是用的 Selenium 等工具,直接干掉或不返回?cái)?shù)據(jù),所以你碰到這種網(wǎng)站還得來(lái)專(zhuān)門(mén)解一下這個(gè)問(wèn)題。
多進(jìn)程、多線(xiàn)程、協(xié)程
上面的情況如果用單線(xiàn)程的爬蟲(chóng)來(lái)模擬是比較簡(jiǎn)單的,但是有個(gè)問(wèn)題就是速度慢啊。
爬蟲(chóng)是 IO 密集型的任務(wù),所以可能大多數(shù)情況下都在等待網(wǎng)絡(luò)的響應(yīng),如果網(wǎng)絡(luò)響應(yīng)速度慢,那就得一直等著。但這個(gè)空余的時(shí)間其實(shí)可以讓 CPU 去做更多事情。那怎么辦呢?多開(kāi)點(diǎn)線(xiàn)程吧。
所以這時(shí)候我們就可以在某些場(chǎng)景下加上多進(jìn)程、多線(xiàn)程,雖然說(shuō)多線(xiàn)程有 GIL 鎖,但對(duì)于爬蟲(chóng)來(lái)說(shuō)其實(shí)影響沒(méi)那么大,所以用上多進(jìn)程、多線(xiàn)程都可以成倍地提高爬取速度,對(duì)應(yīng)的庫(kù)就有 threading、multiprocessing 了。
異步協(xié)程就更牛逼了,用 aiohttp、gevent、tornado 等等的基本上你想搞多少并發(fā)就搞多少并發(fā),但是還是悠著點(diǎn),別把人家網(wǎng)站搞掛了。
總之,用上這幾個(gè),爬蟲(chóng)速度就提上來(lái)了。
但速度提上來(lái)了不一定是好事,反爬接著肯定就要來(lái)了,封你 IP、封你賬號(hào)、彈驗(yàn)證碼、返回假數(shù)據(jù),所以有時(shí)候龜速爬似乎也是個(gè)解決辦法?
分布式
多線(xiàn)程、多進(jìn)程、協(xié)程都能加速,但終究還是單機(jī)的爬蟲(chóng)。要真正做到規(guī)?;?,還得來(lái)靠分布式爬蟲(chóng)來(lái)搞。
分布式的核心是什么?資源共享。比如爬取隊(duì)列共享、去重指紋共享等等。
我們可以使用一些基礎(chǔ)的隊(duì)列或組件來(lái)實(shí)現(xiàn)分布式,比如 RabbitMQ、Celery、Kafka、Redis 等等,但經(jīng)過(guò)很多人的嘗試,自己去實(shí)現(xiàn)一個(gè)分布式爬蟲(chóng),性能和擴(kuò)展性總會(huì)出現(xiàn)一些問(wèn)題,當(dāng)然特別牛逼的除外哈。不少企業(yè)內(nèi)部其實(shí)也有自己開(kāi)發(fā)的一套分布式爬蟲(chóng),和業(yè)務(wù)更緊密,這種當(dāng)然是最好了。
現(xiàn)在主流的 Python 分布式爬蟲(chóng)還是基于 Scrapy 的,對(duì)接 Scrapy-Redis、Scrapy-Redis-BloomFilter 或者用 Scrapy-Cluster 等等,他們都是基于 Redis 來(lái)共享爬取隊(duì)列的,總會(huì)多多少少遇到一些內(nèi)存的問(wèn)題。所以一些人也考慮對(duì)接到了其他的消息隊(duì)列上面,比如 RabbitMQ、Kafka 等等,解決一些問(wèn)題,效率也不差。
總之,要提高爬取效率,分布式還是必須要掌握的。
驗(yàn)證碼
爬蟲(chóng)難免遇到反爬,驗(yàn)證碼就是其中之一。要會(huì)反爬,那首先就要會(huì)解驗(yàn)證碼。
現(xiàn)在你可以看到很多網(wǎng)站都會(huì)有各種各樣的驗(yàn)證碼了,比如最簡(jiǎn)單的圖形驗(yàn)證碼,要是驗(yàn)證碼的文字規(guī)整的話(huà),OCR 過(guò)一遍或者基本的模型庫(kù)都能識(shí)別,不想搞這個(gè)的話(huà)可以直接去對(duì)接個(gè)打碼平臺(tái)來(lái)搞,準(zhǔn)確率還是有的。
然而你可能現(xiàn)在都見(jiàn)不到什么圖形驗(yàn)證碼了,都是一些行為驗(yàn)證碼,如某驗(yàn)、某盾等等,國(guó)外也有很多,比如 reCaptcha 等等。一些稍微簡(jiǎn)單一點(diǎn)的,比如滑動(dòng)的,你可以找點(diǎn)辦法識(shí)別缺口,比如圖像處理比對(duì)、深度學(xué)習(xí)識(shí)別都是可以的。軌跡呢自己寫(xiě)個(gè)模擬正常人行為的,加點(diǎn)抖動(dòng)之類(lèi)的。有了軌跡之后咋模擬呢,如果你牛逼,那么可以直接去分析驗(yàn)證碼的 JavaScript 邏輯,把軌跡數(shù)據(jù)錄入,那就能得到里面的一些加密參數(shù),直接拿著這些參數(shù)放到表單或接口里面就能直接用了。當(dāng)然也可以用模擬瀏覽器的方式來(lái)拖動(dòng),也能通過(guò)一定的方式拿到加密參數(shù),或者直接用模擬瀏覽器的方式把登錄一起做了,拿著 Cookies 來(lái)爬也行。
當(dāng)然拖動(dòng)只是一種驗(yàn)證碼,還有文字點(diǎn)選、邏輯推理等,要是真不想搞,可以找打碼平臺(tái)來(lái)解出來(lái)再模擬,但畢竟花錢(qián)的,一些高手就會(huì)選擇自己訓(xùn)練深度學(xué)習(xí)相關(guān)的模型,收集數(shù)據(jù)、標(biāo)注、訓(xùn)練,針對(duì)不同的業(yè)務(wù)訓(xùn)練不同的模型。這樣有了核心技術(shù),也不用再去花錢(qián)找打碼平臺(tái)了,再研究下驗(yàn)證碼的邏輯模擬一下,加密參數(shù)就能解出來(lái)了。不過(guò)有的驗(yàn)證碼難得很,有的我也沒(méi)搞定。
當(dāng)然有些驗(yàn)證碼可能是請(qǐng)求過(guò)于頻繁而彈出來(lái)的,這種如果換個(gè) IP 什么的也能解。
封 IP
封 IP 也是個(gè)令人頭疼的事,行之有效的方法就是換代理了。
代理很多種,市面上免費(fèi)的,收費(fèi)的太多太多了。
首先可以把市面上免費(fèi)的代理用起來(lái),自己搭建一個(gè)代理池,收集現(xiàn)在全網(wǎng)所有的免費(fèi)代理,然后加一個(gè)測(cè)試器一直不斷測(cè)試,測(cè)試的網(wǎng)址可以改成你要爬的網(wǎng)址。這樣測(cè)試通過(guò)的一般都能直接拿來(lái)爬你的目標(biāo)網(wǎng)站。
付費(fèi)代理也是一樣,很多商家提供了代理提取接口,請(qǐng)求一下就能獲取幾十幾百個(gè)代理,我們可以同樣把它們接入到代理池里面。但這個(gè)代理也分各種套餐,什么開(kāi)放代理、獨(dú)享代理等等的質(zhì)量和被封的幾率也是不一樣的。
有的商家還利用隧道技術(shù)搭了代理,這樣代理的地址和端口我們是不知道的,代理池是由他們來(lái)維護(hù)的,比如某布云,這樣用起來(lái)更省心一些,但是可控性就差一些。
還有更穩(wěn)定的代理,比如撥號(hào)代理、蜂窩代理等等,接入成本會(huì)高一些,但是一定程度上也能解決一些封 IP 的問(wèn)題。
不過(guò)這些背后也不簡(jiǎn)單,為啥一個(gè)好好的高匿代理就是莫名其妙爬不了,背后的一些事就不多講了。
封賬號(hào)
有些信息需要模擬登錄才能爬嘛,如果爬的過(guò)快,人家網(wǎng)站直接把你的賬號(hào)封禁了,就啥都沒(méi)得說(shuō)了。比如爬公眾號(hào)的,人家把你 WX 號(hào)封了,那就全完了。
一種解決方法當(dāng)然就是放慢頻率,控制下節(jié)奏。
還有種方法就是看看別的終端,比如手機(jī)頁(yè)、App 頁(yè)、wap 頁(yè),看看有沒(méi)有能繞過(guò)登錄的法子。
另外比較好的方法,那就是分流。如果你號(hào)足夠多,建一個(gè)池子,比如 Cookies 池、Token 池、Sign 池反正不管什么池吧,多個(gè)賬號(hào)跑出來(lái)的 Cookies、Token 都放到這個(gè)池子里面,用的時(shí)候隨機(jī)從里面拿一個(gè)。如果你想保證爬取效率不變,那么 100 個(gè)賬號(hào)相比 20 個(gè)賬號(hào),對(duì)于每個(gè)賬號(hào)對(duì)應(yīng)的 Cookies、Token 的取用頻率就變成原來(lái)的了 1/5,那么被封的概率也就隨之降低了。
奇葩的反爬
上面說(shuō)的是幾種比較主流的反爬,當(dāng)然還有非常多奇葩的反爬。比如返回假數(shù)據(jù)、返回圖片化數(shù)據(jù)、返回亂序數(shù)據(jù)、返回罵人的數(shù)據(jù)、返回求饒的數(shù)據(jù),那都具體情況看著辦吧。
這些反爬也得小心點(diǎn),之前見(jiàn)過(guò)一個(gè)反爬直接返回 rm -rf / 的也不是沒(méi)有,你要是正好有個(gè)腳本模擬執(zhí)行返回結(jié)果,后果自己想象哈。
JavaScript 逆向
說(shuō)到重頭了。隨著前端技術(shù)的進(jìn)步和網(wǎng)站反爬意識(shí)的增強(qiáng),很多網(wǎng)站選擇在前端上下功夫,那就是在前端對(duì)一些邏輯或代碼進(jìn)行加密或混淆。當(dāng)然這不僅僅是為了保護(hù)前端的代碼不被輕易盜取,更重要的是反爬。比如很多 Ajax 接口都會(huì)帶著一些參數(shù),比如 sign、token 等等,這些前文也講過(guò)了。這種數(shù)據(jù)我們可以用前文所說(shuō)的 Selenium 等方式來(lái)爬,但總歸來(lái)說(shuō)效率太低了,畢竟它模擬的是網(wǎng)頁(yè)渲染的整個(gè)過(guò)程,而真實(shí)的數(shù)據(jù)可能僅僅就藏在一個(gè)小接口里。
如果我們能夠把一些接口的參數(shù)真正找出其中的邏輯,用代碼來(lái)模擬執(zhí)行,那效率就會(huì)有成倍的提升,而且還能在一定程度上規(guī)避上述的反爬現(xiàn)象。
但問(wèn)題是什么?難啊。
Webpack 是一方面,前端代碼都被壓縮和轉(zhuǎn)碼成一些 bundle 文件,一些變量的含義已經(jīng)丟失,不好還原。然后一些網(wǎng)站再加上一些 obfuscator 的機(jī)制,把前端代碼變成你完全看不懂的東西,比如字符串拆散打亂、變量十六進(jìn)制化、控制流扁平化、無(wú)限 debug、控制臺(tái)禁用等等,前端的代碼和邏輯已經(jīng)面目全非。有的用 WebAssembly 等技術(shù)把前端核心邏輯直接編譯,那就只能慢慢摳了,雖然說(shuō)有些有一定的技巧,但是總歸來(lái)說(shuō)還是會(huì)花費(fèi)很多時(shí)間。但一旦解出來(lái)了,那就萬(wàn)事大吉了。怎么說(shuō)?就像奧賽題一樣,解出來(lái)升天,解不出來(lái) GG。
很多公司招聘爬蟲(chóng)工程師都會(huì)問(wèn)有沒(méi)有 JavaScript 逆向基礎(chǔ),破解過(guò)哪些網(wǎng)站,比如某寶、某多、某條等等,解出來(lái)某個(gè)他們需要的可能就直接錄用你。每家網(wǎng)站的邏輯都不一樣,難度也不一樣。
App
當(dāng)然爬蟲(chóng)不僅僅是網(wǎng)頁(yè)爬蟲(chóng)了,隨著互聯(lián)網(wǎng)時(shí)代的發(fā)展,現(xiàn)在越來(lái)越多的公司都選擇將數(shù)據(jù)放到 App 上面,甚至有些公司只有 App 沒(méi)有網(wǎng)站。所以數(shù)據(jù)只能通過(guò) App 來(lái)爬。
咋爬呢?基本的就是抓包工具了,Charles、Fiddler 一把梭,抓到接口之后,直接拿來(lái)模擬就行了。
如果接口有加密參數(shù)怎么辦呢?一種方法你可以邊爬邊處理,比如 mitmproxy 直接監(jiān)聽(tīng)接口數(shù)據(jù)。另一方面你可以走 Hook,比如上 Xposed 也可以拿到。
那爬的時(shí)候又怎么實(shí)現(xiàn)自動(dòng)化呢?總不能拿手來(lái)戳吧。其實(shí)工具也多,安卓原生的 adb 工具也行,Appium 現(xiàn)在已經(jīng)是比較主流的方案了,當(dāng)然還有其他的某精靈都是可以實(shí)現(xiàn)的。
最后,有的時(shí)候可能真的就不想走自動(dòng)化的流程,我就想把里面的一些接口邏輯摳出來(lái),那就得搞逆向了,IDA Pro、jdax、FRIDA 等工具就派上用場(chǎng)了,當(dāng)然這個(gè)過(guò)程和 JavaScript 逆向一樣很痛苦,甚至可能得讀匯編指令。搞一個(gè)案例掉一把頭發(fā)也不是不可能的。
智能化
上面的這一通,都搞熟了,恭喜你已經(jīng)超過(guò)了百分之八九十的爬蟲(chóng)玩家了,當(dāng)然專(zhuān)門(mén)搞 JavaScript 逆向、App 逆向的都是站在食物鏈頂端的男人,這種嚴(yán)格來(lái)說(shuō)已經(jīng)不算爬蟲(chóng)范疇了,這種神我們就不算在里面了,反正我不是。
除了上面的一些技能,在一些場(chǎng)合下,我們可能也需要結(jié)合一些機(jī)器學(xué)習(xí)的技術(shù),讓我們的爬蟲(chóng)變得更智能起來(lái)。
- 比如現(xiàn)在很多博客、新聞文章,其頁(yè)面結(jié)構(gòu)相似度比較高,要提取的信息也比較類(lèi)似。
 - 比如如何區(qū)分一個(gè)頁(yè)面是索引頁(yè)還是詳情頁(yè)?如何提取詳情頁(yè)的文章鏈接?如何解析文章頁(yè)的頁(yè)面內(nèi)容?這些其實(shí)都是可以通過(guò)一些算法來(lái)計(jì)算出來(lái)的。
 
所以,一些智能解析技術(shù)也營(yíng)運(yùn)而生,比如提取詳情頁(yè),一位朋友寫(xiě)的 GeneralNewsExtractor 表現(xiàn)就非常好。
假如說(shuō)我來(lái)了一個(gè)需求,我要爬取一萬(wàn)個(gè)新聞網(wǎng)站數(shù)據(jù),要一個(gè)個(gè)寫(xiě) XPath 嗎?寫(xiě)死我吧。如果有了智能化解析技術(shù),在容忍一定錯(cuò)誤的條件下,完成這個(gè)就是分分鐘的事情。
總之,如果我們能把這一塊也學(xué)會(huì)了,我們的爬蟲(chóng)技術(shù)就會(huì)如虎添翼。
運(yùn)維
這塊也是一個(gè)重頭戲。爬蟲(chóng)和運(yùn)維也是息息相關(guān)。
- 比如寫(xiě)完一個(gè)爬蟲(chóng),怎樣去快速部署到 100 臺(tái)主機(jī)上跑起來(lái)。
 - 比如怎么靈活地監(jiān)控每個(gè)爬蟲(chóng)的運(yùn)行狀態(tài)。
 - 比如爬蟲(chóng)有處代碼改動(dòng),如何去快速更新。
 - 比如怎樣監(jiān)控一些爬蟲(chóng)的占用內(nèi)存、消耗的 CPU 狀況。
 - 比如怎樣科學(xué)地控制爬蟲(chóng)的定時(shí)運(yùn)行、
 - 比如爬蟲(chóng)出現(xiàn)了問(wèn)題,怎樣能及時(shí)收到通知,怎樣設(shè)置科學(xué)的報(bào)警機(jī)制。
 
這里面,部署大家各有各的方法,比如用 Ansible 當(dāng)然可以。如果用 Scrapy 的話(huà)有 Scrapyd,然后配合上一些管理工具也能完成一些監(jiān)控和定時(shí)任務(wù)。不過(guò)我現(xiàn)在用的更多是還是 Docker + Kubernetes,再加上 DevOps 一套,比如 GitHub Actions、Azure Pipelines、Jenkins 等等,快速實(shí)現(xiàn)分發(fā)和部署。
定時(shí)任務(wù)大家有的用 crontab,有的用 apscheduler,有的用管理工具,有的用 Kubernetes,我的話(huà)用 Kubernetes 就多一些了,定時(shí)任務(wù)也是很好實(shí)現(xiàn)。
至于監(jiān)控的話(huà),也有很多,專(zhuān)門(mén)的一些爬蟲(chóng)管理工具自帶了一些監(jiān)控和報(bào)警功能。一些云服務(wù)也帶了一些監(jiān)控的功能。我用的是 Kubernetes + Prometheus + Grafana,什么 CPU、內(nèi)存、運(yùn)行狀態(tài),一目了然,報(bào)警機(jī)制在 Grafana 里面配一下也很方便,支持 Webhook、郵件甚至某釘。
數(shù)據(jù)的存儲(chǔ)和監(jiān)控,用 Kafka、Elasticsearch 個(gè)人感覺(jué)也挺方便的,我主要用的是后者,然后再和 Grafana 配合起來(lái),數(shù)據(jù)爬取量、爬取速度等等監(jiān)控也都一目了然。
結(jié)語(yǔ)
至此,爬蟲(chóng)的一些涵蓋的知識(shí)點(diǎn)也就差不多了,怎么樣,梳理一下,是不是計(jì)算機(jī)網(wǎng)絡(luò)、編程基礎(chǔ)、前端開(kāi)發(fā)、后端開(kāi)發(fā)、App 開(kāi)發(fā)與逆向、網(wǎng)絡(luò)安全、數(shù)據(jù)庫(kù)、運(yùn)維、機(jī)器學(xué)習(xí)都涵蓋到了?上面總結(jié)的可以算是從爬蟲(chóng)小白到爬蟲(chóng)高手的路徑了,里面每個(gè)方向其實(shí)可研究的點(diǎn)非常多,每個(gè)點(diǎn)做精了,都會(huì)非常了不起。
爬蟲(chóng)往往學(xué)著學(xué)著,就成為了一名全棧工程師或者全干工程師,因?yàn)槟憧赡苷娴纳抖紩?huì)了。但是沒(méi)辦法啊,都是被爬蟲(chóng)逼的啊,如果不是生活所困,誰(shuí)愿意一身才華呢?















 
 
 




 
 
 
 