簡(jiǎn)介
最近突發(fā)奇想,想獲取一下B站上的用戶數(shù)據(jù)做個(gè)分析啥的。這個(gè)東西已經(jīng)有很多人做過了,所以網(wǎng)上的成功案例也比較多甚侣。但是不少的信息已經(jīng)不適合現(xiàn)在使用了。比如一些使用api.bilibili.com
的接口间学。這里記錄一下趟坑的經(jīng)歷殷费。
流程
由于我最初的目標(biāo)只是通過用戶名獲得一個(gè)用戶的個(gè)人空間地址。分析了一下url之后發(fā)現(xiàn)低葫,所有用戶都會(huì)通過一個(gè)自增的mid進(jìn)行區(qū)分详羡。
最初看的幾篇教程中,獲得用戶的數(shù)據(jù)不僅可以通過mid這個(gè)參數(shù)嘿悬,還可以通過用戶名实柠,也就是user字段來進(jìn)行查詢。即http://api.bilibili.cn/userinfo
這個(gè)接口善涨。但是發(fā)現(xiàn)這個(gè)接口已經(jīng)不能用了窒盐。發(fā)送消息返回404狀態(tài)草则。
那么就只有根據(jù)瀏覽器行為分析找找接口了。檢查發(fā)現(xiàn)了一個(gè)這樣的接口https://space.bilibili.com/ajax/member/GetInfo
參數(shù)是mid字段蟹漓。但是無法通過用戶名獲取mid炕横。
接下來的想法是找找還有什么地方可以獲取這個(gè)人的mid,比如試圖抓取關(guān)注的up注的關(guān)注者葡粒,發(fā)現(xiàn)只能抓取前五頁份殿。或者是使用百度之類的搜索引擎的高級(jí)搜索嗽交,看看能不能搜索到相關(guān)的信息卿嘲。
以上嘗試都失敗的情況下,嘗試獲取bilibili全部的用戶數(shù)據(jù)轮纫。因?yàn)閙id是一個(gè)從1開始自增的數(shù)據(jù)腔寡,因此可以用這種方法來遍歷這些用戶的信息焚鲜,來找到相關(guān)的數(shù)據(jù)掌唾。
bilibili有接近一億用戶。直接單線程跑數(shù)據(jù)忿磅,一秒一條的話糯彬,需要27777個(gè)小時(shí)才能抓取完。時(shí)間太久了葱她,嘗試使用各種方法來縮短這個(gè)時(shí)間撩扒。
比如改成一秒發(fā)送兩條請(qǐng)求。這個(gè)接口的限制貌似是一分鐘100條左右吨些,因此這里每0.5秒發(fā)送一個(gè)請(qǐng)求搓谆。
還可以開啟多線程,利用ip代理池來發(fā)送請(qǐng)求豪墅,這樣避免了ip被限制的問題泉手。或許可以縮短一些時(shí)間偶器。
此外還可以用其他手段縮短查找范圍斩萌。因?yàn)閙id這個(gè)字段是自增的,還可以查看注冊(cè)時(shí)間屏轰,如果能獲得賬號(hào)的注冊(cè)時(shí)間颊郎,就可以嘗試通過注冊(cè)時(shí)間二分查找這個(gè)賬號(hào)的mid。
嘗試使用參考9中的代理ip池來進(jìn)行代理操作霎苗。按照這個(gè)代理池中的說明姆吭,安裝了SSDB,然后開啟了代理池唁盏。感覺沒什么問題内狸。
在代碼中用requests的方式調(diào)用了代理池的接口瘤睹,并使用代理訪問查看本機(jī)ip的網(wǎng)址http://ip.chinaz.com/
。代理的使用沒有問題答倡。
之后嘗試使用代理拉取bilibili的mid為1到10的數(shù)據(jù)轰传。表現(xiàn)良好。
嘗試接入之前使用mysql寫的數(shù)據(jù)庫模塊中瘪撇。發(fā)現(xiàn)之前寫的東西不支持多線程获茬。需要加入多線程。暫時(shí)使用單線程來拉取數(shù)據(jù)倔既,并寫入數(shù)據(jù)庫恕曲。看起來沒什么問題渤涌。
多線程試圖通過threading和multiprocessing模塊來進(jìn)行處理佩谣。還需要考慮中斷的繼續(xù)和現(xiàn)場(chǎng)的保存。
** 注意多進(jìn)程之間的數(shù)據(jù)交互還是要使用multiprocess提供的方法实蓬,否則進(jìn)程之間無法傳遞數(shù)據(jù)茸俭。 **
threading模塊對(duì)于多核的使用沒有multiprocessing好,一個(gè)是安皱。multiprocessing能開啟多個(gè)進(jìn)程调鬓。這就涉及到了進(jìn)程之間的通信問題。
開始的時(shí)候考慮實(shí)現(xiàn)一個(gè)能迭代的manager來返回下一個(gè)需要處理的mid酌伊,然后使用multiprocessing提供的pool和map方法來處理腾窝。但是這樣產(chǎn)生了消息傳遞的問題。但是因?yàn)樾枰幚韜eb請(qǐng)求的成功和失敗居砖,所以本來使用單例模式的manager在多進(jìn)程中的共享產(chǎn)生了問題虹脯。
搜索了一下考慮使用multiprocessing提供的可供多進(jìn)程使用的變量,如Value奏候,Array等等循集。
使用的時(shí)候發(fā)現(xiàn)其實(shí)直接在主線程里對(duì)mid進(jìn)行管理即可。這樣需要獲取一個(gè)拉取的狀態(tài)鼻由。
保存現(xiàn)場(chǎng)最終選擇在一個(gè)本地的文本文件中序列化了一串字符串暇榴,記錄了多線程的處理狀態(tài)和主線程mid管理的進(jìn)度,方便下次繼續(xù)蕉世。
拉取到的數(shù)據(jù)選擇存儲(chǔ)在本地的一個(gè)數(shù)據(jù)庫文件中蔼紧。Python內(nèi)置了一個(gè)sqlite支持。使用這個(gè)創(chuàng)建一個(gè)db文件即可狠轻。
拉取數(shù)據(jù)的時(shí)候插入數(shù)據(jù)庫使用replace而不是使用insert奸例,因?yàn)閿帱c(diǎn)續(xù)傳的時(shí)候有可能會(huì)遇到id已經(jīng)之前存儲(chǔ)過了的狀況。這種狀況并不是難以忍受的問題,所以這樣的容錯(cuò)處理就可以了查吊。
最終拉取的數(shù)據(jù)還是要利用sql從數(shù)據(jù)庫文件中獲取谐区。
總結(jié)
這次實(shí)踐實(shí)際的掌握了一下前端頁面的network請(qǐng)求以及Python對(duì)于數(shù)據(jù)的處理。雖然看起來量不小逻卖,但是其實(shí)并不難也沒有設(shè)置太多的障礙宋列。可以