首先 祝?大家 1024 快樂
之前寫了第一版 網(wǎng)易云爬蟲
邏輯比較簡單
總結(jié)一下耗绿,就是:
- 抓取各分類下歌單id
- 根據(jù)歌單id, 獲得這個(gè)歌單id下的歌曲詳情
- 把拿到的數(shù)據(jù)存到落到本地文件留攒,最后利用shell腳本進(jìn)行數(shù)據(jù)統(tǒng)計(jì)
- 為了提高效率采用多線程
- 這版線程數(shù)開的有點(diǎn)多稍途,建議在docker環(huán)境中啟樊破,否則你的電腦就不屬于你了
整體架構(gòu)圖
Trouble
第一版 爬蟲 看起來沒什么毛病
但 還是會(huì)有一些問題
- 你可能會(huì)有疑問 這么大的一個(gè)公司 怎么沒有反爬策略
- 怎么可以讓我這么肆無忌憚的爬
- 這可是線上服務(wù) 一個(gè)個(gè)請求都是壓力
- 落磁盤落在文件里,雖然處理數(shù)據(jù)也很方便,但數(shù)據(jù)的關(guān)系不夠明顯
- 這個(gè)看起來很簡單 就是落數(shù)據(jù)庫
Netease Anti-Spider
第一個(gè)問題 其實(shí) 你在分析數(shù)據(jù)的時(shí)候就會(huì)發(fā)現(xiàn)一些端疑
為啥一個(gè)那么火的歌單只有一首歌?蛤
實(shí)際上這是網(wǎng)易云音樂的一個(gè)防爬機(jī)制
在短時(shí)間請求比較大的時(shí)候會(huì)觸發(fā)
在我嘗試過程中 基本上在請求 8k-1w次的時(shí)候會(huì)發(fā)現(xiàn)
按每次請求200ms計(jì)算,開18個(gè)線程 一秒請求100次
QPS就達(dá)到6k
如果我多開幾個(gè)爬蟲 那么就會(huì)網(wǎng)易云的監(jiān)控就會(huì)很可怕
要知道PDD一般服務(wù)的QPS也就幾十萬
所以 為了防我們這種新手
網(wǎng)易云造了一些200的Response油猫,基本數(shù)據(jù)也是一致的
只是數(shù)據(jù)量 會(huì)少一點(diǎn)
比如說一個(gè)歌單只返回一首歌的信息(劃重點(diǎn) 這是我們接下來 驗(yàn)證IP是否可用的一個(gè)有效判據(jù))
問題找到了,那么改如何解決:
一種辦法 就是 換物理IP
用人話說 就是 你在大興 爬爬 然后 跑到本部去爬
嗯 LZ 在最開始也干過這種事情
導(dǎo)致很多物理IP 現(xiàn)在可能也不能用 hhh
當(dāng)然根本的解決策略就是建立代理IP池
Proxy 代理池
- 首先 什么是代理柠偶?
代理就是 有一個(gè)服務(wù)器代替你做 你想做的事情
代理IP做的事情 就是 把你原本自己發(fā)出去的請求 借助代理服務(wù)器的?發(fā)出去
保密做的好的 就叫高匿
一般用的ShadowSockets 就是一種Socket5代理
我們這里要用的則是Http情妖,Https代理
尤其更需要Http的代理
Xici
xici代理 是我爬的第一個(gè)Free Proxy 網(wǎng)站
當(dāng)時(shí)爬了20頁只找到 7個(gè)能用的
然后隨機(jī)選取一個(gè)作為代理
想的挺好的 這次應(yīng)該不會(huì)被封了吧
結(jié)果 快到3w歌單的時(shí)候 pia 機(jī) 沒了
所以 痛定思痛 覺得建立一個(gè)Proxy 代理池 而且要是高可用的
以上就是V1.5版
雖然沒有多少代理IP 但借助著精湛的 轉(zhuǎn)移技術(shù)
還是爬取了總計(jì)10.2w歌單12780274首,去重后1099542
怕大家數(shù)不清楚 以上 = 1.2kw/ 110w
但拿到數(shù)據(jù)只是第一步 基于這些數(shù)據(jù)可以做很多事情
我們看 得到的數(shù)據(jù)大概4M*73 = 296M
如果數(shù)據(jù)量達(dá)到GB級(jí)別 shell就不太適用 就可以用MapReduce進(jìn)行處理诱担,此處參考寫的另外一篇blog
Goubanjia
在爬代理網(wǎng)站 建立代理池的過程中毡证,發(fā)現(xiàn)一些很好玩的事情
比如說這個(gè)代理網(wǎng)站 Goubanjia
做最基本的html解析,可以得到下面的內(nèi)容
In [9]: html = a.get_html('http://www.goubanjia.com', {}, 'www.goubanjia.com')
In [10]: trs = html.find_all('tr', class_=['warning', 'success'])
In [11]: tds = trs[0].find_all('td')
In [12]: tds[0].find_all(['div', 'span', 'p'])
Out[12]:
[<p style="display: none;">4</p>,
<span>4</span>,
<div style="display:inline-block;">7.</div>,
<span style="display: inline-block;"></span>,
<div style="display:inline-block;">9</div>,
<p style="display: none;">3</p>,
<span>3</span>,
<div style="display: inline-block;"></div>,
<p style="display: none;">.2</p>,
<span>.2</span>,
<span style="display:inline-block;">5</span>,
<p style="display: none;">1.</p>,
<span>1.</span>,
<span style="display:inline-block;">9</span>,
<p style="display:none;"></p>,
<span></span>,
<div style="display: inline-block;">4</div>,
<span class="port GEA">8174</span>]
好像沒什么異常 就是把Ip分開來了 拼接一下不就行了
447.933.251.1.94:8174
好像 這不太像一個(gè)IP地址
實(shí)際上懂一點(diǎn) Html知識(shí)的可能會(huì)發(fā)現(xiàn)style="display:none;"
這個(gè)一個(gè)隱藏的style實(shí)際上是不顯示的意思
發(fā)現(xiàn)這點(diǎn)之后 好像就很簡單了
tds[0].find_all(['div', 'span', not 'p'], class_=not 'port')
但 這只是這個(gè)網(wǎng)站兩年前做的版本 好戲還在后頭
我把得到的ip進(jìn)行測試 然后一驚
woc 費(fèi)那么大勁 一個(gè)都不能用 一個(gè)都不能用 干嘛還這么用力來防
總覺得有蔫仙、不太對(duì)
然后突然發(fā)現(xiàn) 拿到的Port 和網(wǎng)站上看到的 好像不太一樣
這個(gè)時(shí)候想到 上課講的wolf字體欺騙
檢查字體發(fā)現(xiàn)再正常不過了
再回頭來看這個(gè)代碼<span class="port GEA">8174</span>
一開始懷疑對(duì)象 也是CSS 這個(gè)class會(huì)不會(huì)有什么特殊的地方
想了半天 也排查了所以css js
發(fā)現(xiàn)如果把http://www.goubanjia.com/theme/goubanjia/javascript/pde.js?v=1.0
禁掉 就會(huì)顯示Html的內(nèi)容
有同學(xué)說看js代碼 實(shí)際上 看不出來什么東西
再看引了JQuery的包 猜想應(yīng)該是JQuery動(dòng)態(tài)修改Html
但知道 這個(gè)并沒有用 并不能幫助我們解密
這就是一個(gè)encode decode的過程
好像 port后面的字母和端口號(hào)有一一映射關(guān)系
那么 我們 進(jìn)入到最原始的方式:通過枚舉 找規(guī)律
然后我們就會(huì) 變得很機(jī)智 發(fā)現(xiàn)這個(gè)密碼就是把字母轉(zhuǎn)化成數(shù)字 然后/8
嗯 這應(yīng)該是 第一個(gè)解密goubanjie騷操作的blog
然后 我們發(fā)現(xiàn)這個(gè)網(wǎng)站更新很頻繁 但一次只能拿到20條
于是 寫個(gè)定時(shí)任務(wù) 就是一個(gè)很合理的需求
gatherproxy
其實(shí)國內(nèi)代理 都太勢利了 能用的本來就不多 還收費(fèi)
國外的代理 就很慷慨
比如說gatherproxy 這也是我們的主力代理Ip
和別的不一樣 這個(gè)網(wǎng)站吧所有ip都開放給你下載 不提倡寫爬蟲
那么問題就變?yōu)?如何在較短時(shí)間內(nèi)把1w+ 對(duì)應(yīng)的http/https 代理是否可用檢驗(yàn)出來 然后寫到DB中
想要快 只能 開多線程
但寫庫不能在多線程中
我們知道Innodb因?yàn)橘Y瓷事務(wù) 有嚴(yán)格的寫鎖機(jī)制
短時(shí)間 競爭寫操作 會(huì)造成 寫失敗操作
于是第一套方案 就是等所有 判斷結(jié)束之后 再寫
測試發(fā)現(xiàn) 寫效率 挺高的 1s內(nèi)完成1k條Insert語句
但實(shí)際上頻繁的寫操作不太友好
所以改成聚類 通過一次sql操作 完成1k條數(shù)據(jù)的插入
這樣就解決了慢SQL的問題
TestDb()
當(dāng)然 代理具有極強(qiáng)的時(shí)效性
如何在短時(shí)間內(nèi)判斷數(shù)據(jù)庫中大量的代理數(shù)據(jù)是否可用(目前為止已經(jīng)有2.2w代理ip數(shù)據(jù)) 也是一個(gè)問題
解決方案 同樣是 多線程
但同時(shí)為了保證代理Ip的質(zhì)量 采用3次驗(yàn)證機(jī)制
通過is_failured字段 進(jìn)行判斷 每失效一次+1 直到is_failured到5則不在檢測
如果可用一次is_failured置為0
不可靠
實(shí)際上就算之前的三層檢驗(yàn) 拿到的可用的代理
在實(shí)際運(yùn)用當(dāng)中 還是會(huì)出現(xiàn)請求失敗的現(xiàn)象
所以 對(duì)于真實(shí)爬取場景 為保證每一個(gè)數(shù)據(jù)的都能被爬取到
對(duì)每個(gè)任務(wù)增加Retry機(jī)制 并記錄爬取進(jìn)度 To DB
然后 其實(shí)Proxy特別依賴network
比如說 有一次連上了 隔壁寢室的WiFi別問我怎么連上的 密碼真的簡單
然后經(jīng)過testdb之后可用的 Ip數(shù)就掉零
然后 實(shí)驗(yàn)證明 Https的代理 比較不穩(wěn)定 十分需要retry機(jī)制
對(duì)于本次爬蟲而言 實(shí)際上Https的接口沒有加 反爬機(jī)制 不用代理也行
DB
DB 采用MySQL
一個(gè)是 因?yàn)槭煜?/p>
另外一個(gè)可用方便顯示數(shù)據(jù)的關(guān)系
但實(shí)際上大數(shù)據(jù)下MySQL的性能優(yōu)化 有很多功課可以做
慢SQL
前面說的 讀寫IP池 是一種慢SQL
實(shí)際上 寫 playlist_queue表也是
我們一次拿到1k+個(gè)歌單Id 需要在短時(shí)間 進(jìn)行判斷寫入/更新進(jìn)DB
我們可以用Replace Into 代替Update進(jìn)行更新
所有操作做聚類 一條SQL 代替數(shù)k條SQL
但在playlist_detail這張表中
首先 單條數(shù)據(jù)Size大
其次 需要一次插入七八萬條數(shù)據(jù) 這已經(jīng)是聚類過的 單classify進(jìn)行統(tǒng)計(jì)處理
這 Insert 也不管用
測試中 6w數(shù)據(jù)
分成5k一組 一條也寫不進(jìn)去
3k一組 能寫三條
1k一組 能寫10條
500一組 中間休息0.2s 能寫20組
仔細(xì)看一下 發(fā)現(xiàn)block大小 和 能寫入的量 直接 并沒有直接關(guān)系
該寫不進(jìn)去的 照樣寫不進(jìn)去
最后 采用先寫到本地文件中
再通過Load data 導(dǎo)入MySQL
那 我們 為啥 還要寫庫 二進(jìn)制文件不是挺好的嗎 shell腳本多少好用
Finish
于是第二版 主要解決了以上技術(shù)難題
剩下還有一些零零散散的小問題 主要是多線程 一些寫料睛、更新比較繁瑣的地方
總的來說 實(shí)現(xiàn)
- 高可用代理IP庫建立
- 資瓷記錄爬蟲進(jìn)度的自動(dòng)化網(wǎng)易云音樂歌單數(shù)據(jù)爬蟲
- 完成6百w(5801119)數(shù)據(jù)爬取,寫庫操作
Next
- 通過Kafka消費(fèi)消息隊(duì)列 來解決 寫庫量大的問題
- 數(shù)據(jù)分析 挖頻繁模式
- 只爬了playlist的數(shù)據(jù) 其實(shí)網(wǎng)易云還有很多可以做的 比如說用戶畫像 評(píng)論之類很有意思的方向
Result
附上出現(xiàn)頻次排名前55的歌曲
至于為什么是前55 e
-- 數(shù)據(jù)采樣于2018.10.23 --
前1k名單見GitHub
time song_name
----|-----
6784 Something Just Like This
5814 Shape of You
5720 Time
5585 Alone
5151 Intro
4916 Hello
4833 You
4787 Closer
4312 Nevada
4217 Stay
4142 Faded
4089 說散就散
4070 Animals
3894 往后余生
3650 Home
3645 Without You
3535 Counting Stars
3515 That Girl
3410 HandClap
3300 Higher
3265 Despacito (Remix)
3229 Unity
3198 Havana
3181 起風(fēng)了(Cover 高橋優(yōu))
3148 Forever
3141 Victory
3108 Please Don't Go
3101 Sugar
3080 Beautiful Now
3077 See You Again
3022 Fade
2969 Summer
2940 Seve
2938 The truth that you leave
2861 Life
2853 可能否
2825 We Don't Talk Anymore
2799 Superstar
2795 #Lov3 #Ng?u H?ng
2793 Try
2759 アイロニ
2730 Hope
2714 Hero
2705 追光者
2679 遇見
2678 いつも何度でも
2654 Let Me Love You
2646 There For You
2643 Trip
2634 BOOM
2626 Fire
2606 Wolves
2600 Friendships (Original Mix)
2597 Freaks (Radio Edit)
2577 全部都是你