從高可用IP代理池到千萬級網易雲音樂數據爬取的實現

博客引流

首先 ㊗️大家 1024 快樂

之前寫了第一版 網易雲爬蟲

邏輯比較簡單

總結一下,就是:

  1. 抓取各分類下歌單id
  2. 根據歌單id, 獲得這個歌單id下的歌曲詳情
  3. 把拿到的數據存到落到本地文件,最後利用shell腳本進行數據統計
  4. 爲了提高效率採用多線程
  • 這版線程數開的有點多,建議在docker環境中啓,否則你的電腦就不屬於你了

先 放代碼 傳送門

整體架構圖

在這裏插入圖片描述

Trouble

第一版 爬蟲 看起來沒什麼毛病

但 還是會有一些問題

  1. 你可能會有疑問 這麼大的一個公司 怎麼沒有反爬策略
  • 怎麼可以讓我這麼肆無忌憚的爬
  • 這可是線上服務 一個個請求都是壓力
  1. 落磁盤落在文件裏,雖然處理數據也很方便,但數據的關係不夠明顯
  • 這個看起來很簡單 就是落數據庫

Netease Anti-Spider

第一個問題 其實 你在分析數據的時候就會發現一些端疑

爲啥一個那麼火的歌單隻有一首歌?

實際上這是網易雲音樂的一個防爬機制

在短時間請求比較大的時候會觸發

在我嘗試過程中 基本上在請求 8k-1w次的時候會發現

按每次請求200ms計算,開18個線程 一秒請求100次

QPS就達到6k

如果我多開幾個爬蟲 那麼就會網易雲的監控就會很可怕

要知道PDD一般服務的QPS也就幾十萬

所以 爲了防我們這種新手

網易雲造了一些200的Response,基本數據也是一致的

只是數據量 會少一點

比如說一個歌單隻返回一首歌的信息(劃重點 這是我們接下來 驗證IP是否可用的一個有效判據)

問題找到了,那麼改如何解決:

一種辦法 就是 換物理IP

用人話說 就是 你在大興 爬爬 然後 跑到本部去爬

嗯 LZ 在最開始也幹過這種事情

導致很多物理IP 現在可能也不能用 hhh

當然根本的解決策略就是建立代理IP池

Proxy 代理池

  • 首先 什麼是代理?

代理就是 有一個服務器代替你做 你想做的事情

代理IP做的事情 就是 把你原本自己發出去的請求 藉助代理服務器的✋發出去

保密做的好的 就叫高匿

一般用的ShadowSockets 就是一種Socket5代理

我們這裏要用的則是Http,Https代理

尤其更需要Http的代理

Xici

xici代理 是我爬的第一個Free Proxy 網站

當時爬了20頁只找到 7個能用的

然後隨機選取一個作爲代理

想的挺好的 這次應該不會被封了吧

結果 快到3w歌單的時候 pia 機 沒了

所以 痛定思痛 覺得建立一個Proxy 代理池 而且要是高可用的

以上就是V1.5版

雖然沒有多少代理IP 但藉助着精湛的 轉移技術
還是爬取了總計10.2w歌單12780274首,去重後1099542

怕大家數不清楚 以上 = 1.2kw/ 110w

但拿到數據只是第一步 基於這些數據可以做很多事情

我們看 得到的數據大概4M*73 = 296M

如果數據量達到GB級別 shell就不太適用 就可以用MapReduce進行處理,此處參考寫的另外一篇blog

Goubanjia

在爬代理網站 建立代理池的過程中,發現一些很好玩的事情

比如說這個代理網站 Goubanjia

做最基本的html解析,可以得到下面的內容

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

好像 這不太像一個IP地址

實際上懂一點 Html知識的可能會發現style="display:none;"

這個一個隱藏的style實際上是不顯示的意思

發現這點之後 好像就很簡單了

tds[0].find_all(['div', 'span', not 'p'], class_=not 'port')

但 這只是這個網站兩年前做的版本 好戲還在後頭

我把得到的ip進行測試 然後一驚

woc 費那麼大勁 一個都不能用 一個都不能用 幹嘛還這麼用力來防

總覺得有、不太對

然後突然發現 拿到的Port 和網站上看到的 好像不太一樣

在這裏插入圖片描述

這個時候想到 上課講的wolf字體欺騙

檢查字體發現再正常不過了

再回頭來看這個代碼<span class="port GEA">8174</span>

一開始懷疑對象 也是CSS 這個class會不會有什麼特殊的地方

想了半天 也排查了所以css js

發現如果把http://www.goubanjia.com/theme/goubanjia/javascript/pde.js?v=1.0禁掉 就會顯示Html的內容

有同學說看js代碼 實際上 看不出來什麼東西

再看引了JQuery的包 猜想應該是JQuery動態修改Html

但知道 這個並沒有用 並不能幫助我們解密

這就是一個encode decode的過程

在這裏插入圖片描述

好像 port後面的字母和端口號有一一映射關係

那麼 我們 進入到最原始的方式:通過枚舉 找規律

在這裏插入圖片描述

然後我們就會 變得很機智 發現這個密碼就是把字母轉化成數字 然後/8

嗯 這應該是 第一個解密goubanjie騷操作的blog

然後 我們發現這個網站更新很頻繁 但一次只能拿到20條

於是 寫個定時任務 就是一個很合理的需求

gatherproxy

其實國內代理 都太勢利了 能用的本來就不多 還收費

國外的代理 就很慷慨

比如說gatherproxy 這也是我們的主力代理Ip

和別的不一樣 這個網站吧所有ip都開放給你下載 不提倡寫爬蟲

那麼問題就變爲 如何在較短時間內把1w+ 對應的http/https 代理是否可用檢驗出來 然後寫到DB中

想要快 只能 開多線程

但寫庫不能在多線程中

我們知道Innodb因爲資瓷事務 有嚴格的寫鎖機制

短時間 競爭寫操作 會造成 寫失敗操作

於是第一套方案 就是等所有 判斷結束之後 再寫

測試發現 寫效率 挺高的 1s內完成1k條Insert語句

但實際上頻繁的寫操作不太友好

所以改成聚類 通過一次sql操作 完成1k條數據的插入

這樣就解決了慢SQL的問題

TestDb()

當然 代理具有極強的時效性

如何在短時間內判斷數據庫中大量的代理數據是否可用(目前爲止已經有2.2w代理ip數據) 也是一個問題

解決方案 同樣是 多線程

但同時爲了保證代理Ip的質量 採用3次驗證機制

通過is_failured字段 進行判斷 每失效一次+1 直到is_failured到5則不在檢測

如果可用一次is_failured置爲0

不可靠

實際上就算之前的三層檢驗 拿到的可用的代理

在實際運用當中 還是會出現請求失敗的現象

所以 對於真實爬取場景 爲保證每一個數據的都能被爬取到

對每個任務增加Retry機制 並記錄爬取進度 To DB

然後 其實Proxy特別依賴network

比如說 有一次連上了 隔壁寢室的WiFi別問我怎麼連上的 密碼真的簡單

然後經過testdb之後可用的 Ip數就掉零

然後 實驗證明 Https的代理 比較不穩定 十分需要retry機制

對於本次爬蟲而言 實際上Https的接口沒有加 反爬機制 不用代理也行

DB

DB 採用MySQL

一個是 因爲熟悉

另外一個可用方便顯示數據的關係

但實際上大數據下MySQL的性能優化 有很多功課可以做

慢SQL

前面說的 讀寫IP池 是一種慢SQL

實際上 寫 playlist_queue表也是

我們一次拿到1k+個歌單Id 需要在短時間 進行判斷寫入/更新進DB

我們可以用Replace Into 代替Update進行更新

所有操作做聚類 一條SQL 代替數k條SQL

但在playlist_detail這張表中

首先 單條數據Size大

其次 需要一次插入七八萬條數據 這已經是聚類過的 單classify進行統計處理

這 Insert 也不管用

測試中 6w數據

分成5k一組 一條也寫不進去
3k一組 能寫三條
1k一組 能寫10條
500一組 中間休息0.2s 能寫20組

仔細看一下 發現block大小能寫入的量 直接 並沒有直接關係

該寫不進去的 照樣寫不進去

最後 採用先寫到本地文件中

再通過Load data 導入MySQL

那 我們 爲啥 還要寫庫 二進制文件不是挺好的嗎 shell腳本多少好用

在這裏插入圖片描述

Finish

於是第二版 主要解決了以上技術難題

剩下還有一些零零散散的小問題 主要是多線程 一些寫、更新比較繁瑣的地方

總的來說 實現

  1. 高可用代理IP庫建立
  2. 資瓷記錄爬蟲進度的自動化網易雲音樂歌單數據爬蟲
  3. 完成6百w(5801119)數據爬取,寫庫操作

在這裏插入圖片描述

在這裏插入圖片描述

Next

  1. 通過Kafka消費消息隊列 來解決 寫庫量大的問題
  2. 數據分析 挖頻繁模式
  3. 只爬了playlist的數據 其實網易雲還有很多可以做的 比如說用戶畫像 評論之類很有意思的方向

Result

附上出現頻次排名前55的歌曲

至於爲什麼是前55 e

– 數據採樣於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 起風了(Cover 高橋優)
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 全部都是你
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章