自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Python寫一個小白也能懂的分布式知乎爬蟲

開發(fā) 后端 分布式
很早就有采集知乎用戶數(shù)據(jù)的想法,要實現(xiàn)這個想法,需要寫一個網(wǎng)絡(luò)爬蟲(Web Spider)。因為在學(xué)習(xí) python,正好 python 寫爬蟲也是極好的選擇,于是就寫了一個基于 python 的網(wǎng)絡(luò)爬蟲。

很早就有采集知乎用戶數(shù)據(jù)的想法,要實現(xiàn)這個想法,需要寫一個網(wǎng)絡(luò)爬蟲(Web Spider)。因為在學(xué)習(xí) python,正好 python 寫爬蟲也是極好的選擇,于是就寫了一個基于 python 的網(wǎng)絡(luò)爬蟲。

幾個月前寫了爬蟲的初版,后來因為一些原因,暫時擱置了下來,最近重新拾起這個想法。首先優(yōu)化了代碼的結(jié)構(gòu),然后在學(xué)弟的提醒下,從多線程改成了多進程,一臺機器上運行一個爬蟲程序,會啟動幾百個子進程加速抓取。

但是一臺機器的性能是有極限的,所以后來我使用 MongoDB 和 Redis 搭建了一個主從結(jié)構(gòu)的分布式爬取系統(tǒng),來進一步加快抓取的速度。

然后我就去好幾個服務(wù)器廠商申請免費的試用,比如百度云、騰訊云、Ucloud…… 加上自己的筆記本,斷斷續(xù)續(xù)抓取了一個多周,才采集到300萬知乎用戶數(shù)據(jù)。中間還跑壞了運行網(wǎng)站的云主機,還好 自動備份 起作用,數(shù)據(jù)沒有丟失,但那又是另外一個故事了……

廢話不多說,下面我介紹一下如何寫一個簡單的分布式知乎爬蟲。

抓取知乎用戶的個人信息

給大家推薦一個學(xué)習(xí)交流的地方,想要學(xué)習(xí)Python的小伙伴可以一起來學(xué)習(xí),719+139+688,入坑需謹慎,對Python沒啥興趣的就不要來湊熱鬧啦。我們要抓取知乎用戶數(shù)據(jù),首先要知道在哪個頁面可以抓取到用戶的數(shù)據(jù)。知乎用戶的個人信息在哪里呢,當然是在用戶的主頁啦,我們以輪子哥為例 ~

 

用Python寫一個小白也能懂的分布式知乎爬蟲

紅框里的便我們要抓取的用戶關(guān)鍵信息(的一部分)。

最上面是我們的目標URL:https://www.zhihu.com/people/excited-vczh/answers。

觀察一下這個URL的組成:

  • http://www.zhihu.com + /people + /excited-vczh + /answer

可以發(fā)現(xiàn)只有 excited-vczh 這部分是會變化的,它代表著知乎用戶的唯一ID,在知乎的數(shù)據(jù)格式中,它的鍵名叫做 urlToken。

所以我們可以用拼接字符串的形式,得到我們待抓取頁面的URL:

  1. url = '%s/people/%s/answers'%(host,urlToken) 

頁面URL有了,而且從上圖我們可以發(fā)現(xiàn) 不登錄 也可以訪問用戶主頁,這說明我們可以不用考慮模擬登陸的問題,可以自由的獲取用戶主頁面源碼。

那么我們?nèi)绾螐挠脩糁黜摰脑创a中獲取用戶的數(shù)據(jù)呢?一開始我以為需要挨個匹配頁面中對應(yīng)的部分,但我查看源碼的時候發(fā)現(xiàn)知乎把用戶數(shù)據(jù)集集中放到了源碼的一個地方,那就是 id="data" 的 div 的 data-state 屬性的值中,看下圖:

 

用Python寫一個小白也能懂的分布式知乎爬蟲

從上圖我們可以發(fā)現(xiàn),date-state 的屬性值中藏有用戶的信息,比如我們可以依次找到用戶的教育經(jīng)歷(educations)、簡介(headline)、參與的 Live 數(shù)量(participatedLiveCount)、關(guān)注的收藏夾數(shù)量(followingFavlistsCount)、被收藏的次數(shù)(favoritedCount)、關(guān)注他的用戶數(shù)(followerCount)、關(guān)注的話題數(shù)量(followingTopicCount)、用戶描述(description)等信息。通過觀察我們也可以發(fā)現(xiàn),數(shù)據(jù)應(yīng)該是以 JSON 格式存儲。

知道了用戶數(shù)據(jù)都藏在 date-state 中,我們 用 BeautifulSoup 把該屬性的值取出來,然后作為 JSON 格式讀取,再把數(shù)據(jù)集中存儲用戶數(shù)據(jù)的部分提取出來即可,看代碼:

 

  1. # 解析htmls = BS(html,'html.parser')# 獲得該用戶藏在主頁面中的json格式數(shù)據(jù)集data = s.find('div',attrs={'id':'data'})['data-state' 
  2. data = json.loads(data)  
  3. data = data['entities']['users'][urlToken] 

如此,我們便得到了某一個用戶的個人信息。

抓取知乎用戶的關(guān)注者列表

剛剛我們討論到可以通過抓取用戶主頁面源碼來獲取個人信息,而用戶主頁面可以通過拼接字符串的形式得到 URL,其中拼接的關(guān)鍵是 如何獲取用戶唯一ID —— urlToken?

我采用的方法是 抓取用戶的關(guān)注者列表。

每個用戶都會有關(guān)注者列表,比如輪子哥的:

 

用Python寫一個小白也能懂的分布式知乎爬蟲

 

用Python寫一個小白也能懂的分布式知乎爬蟲

和獲取個人信息同樣的方法,我們可以在該頁面源碼的 date-state 屬性值中找到關(guān)注他的用戶(一部分):

 

用Python寫一個小白也能懂的分布式知乎爬蟲

名為 ids 的鍵值中存儲有當前列表頁的所有用戶的 urlToken,默認列表的每一頁顯示20個用戶,所以我們寫一個循環(huán)便可以獲取當前頁該用戶的所有關(guān)注者的 urlToken。

 

  1. # 解析當前頁的 html url = '%s/people/%s/followers?page=%d'%(host,urlToken,page)  
  2. html = c.get_html(url)  
  3. s = BS(html,'html.parser')# 獲得當前頁的所有關(guān)注用戶data = s.find('div',attrs={'id':'data'})['data-state' 
  4. data = json.loads(data)  
  5. items = data['people']['followersByUser'][urlToken]['ids']for item in items: if item!=None and item!=False and item!=True and item!='知乎用戶'.decode('utf8'): 
  6. node = item.encode('utf8' 
  7. follower_list.append(node) 

再寫一個循環(huán)遍歷關(guān)注者列表的所有頁,便可以獲取用戶的所有關(guān)注者的 urlToken。

有了每個用戶在知乎的唯一ID,我們便可以通過拼接這個ID得到每個用戶的主頁面URL,進一步獲取到每個用戶的個人信息。

我選擇抓取的是用戶的關(guān)注者列表,即關(guān)注這個用戶的所有用戶(follower)的列表,其實你也可以選擇抓取用戶的關(guān)注列表(following)。我希望抓取更多知乎非典型用戶(潛水用戶),于是選擇了抓取關(guān)注者列表。當時抓取的時候有這樣的擔心,萬一這樣抓不到主流用戶怎么辦?畢竟很多知乎大V雖然關(guān)注者很多,但是主動關(guān)注的人相對都很少,而且關(guān)注的很可能也是大V。但事實證明,主流用戶基本都抓取到了,看來基數(shù)提上來后,總有縫隙出現(xiàn)。

反爬蟲機制

頻繁抓取會被知乎封IP,也就是常說的反爬蟲手段之一,不過俗話說“道高一尺,魔高一丈”,既然有反爬蟲手段,那么就一定有反反爬蟲手段,咳,我自己起的名……

言歸正傳,如果知乎封了你的IP,那么怎么辦呢?很簡單,換一個IP。這樣的思想催生了 代理IP池 的誕生。所謂代理IP池,是一個代理IP的集合,使用代理IP可以偽裝你的訪問請求,讓服務(wù)器以為你來自不同的機器。

于是我的 應(yīng)對知乎反爬蟲機制的策略 就很簡單了:全力抓取知乎頁面 --> 被知乎封IP --> 換代理IP --> 繼續(xù)抓 --> 知乎繼續(xù)封 --> 繼續(xù)換 IP..... (手動斜眼)

使用 代理IP池,你可以選擇用付費的服務(wù),也可以選擇自己寫一個,或者選擇用現(xiàn)成的輪子。我選擇用七夜寫的 qiyeboy/IPProxyPool 搭建代理池服務(wù),部署好之后,修改了一下代碼讓它只保存https協(xié)議的代理IP,因為 使用http協(xié)議的IP訪問知乎會被拒絕。

搭建好代理池服務(wù)后,我們便可以隨時在代碼中獲取以及使用代理 IP 來偽裝我們的訪問請求啦!

(其實反爬手段有很多,代理池只是其中一種)

簡單的分布式架構(gòu)

多線程/多進程只是***限度的利用了單臺機器的性能,如果要利用多臺機器的性能,便需要分布式的支持。

如何搭建一個簡單的分布式爬蟲?

我采用了 主從結(jié)構(gòu),即一臺主機負責(zé)調(diào)度、管理待抓取節(jié)點,多臺從機負責(zé)具體的抓取工作。

具體到這個知乎爬蟲來說,主機上搭建了兩個數(shù)據(jù)庫:MongoDB 和 Redis。MongoDB 負責(zé)存儲抓取到的知乎用戶數(shù)據(jù),Redis 負責(zé)維護待抓取節(jié)點集合。從機上可以運行兩個不同的爬蟲程序,一個是抓取用戶關(guān)注者列表的爬蟲(list_crawler),一個是抓取用戶個人資料的爬蟲(info_crawler),他們可以配合使用,但是互不影響。

我們重點講講主機上維護的集合,主機的 Redis 數(shù)據(jù)庫中一共維護了5個集合:

  1. waiting:待抓取節(jié)點集合
  2. info_success:個人信息抓取成功節(jié)點集合
  3. info_failed:個人信息抓取失敗節(jié)點集合
  4. list_success:關(guān)注列表抓取成功節(jié)點集合
  5. list_failed:關(guān)注列表抓取失敗節(jié)點集合

這里插一句,之所以采用集合(set),而不采用隊列(queue),是因為集合天然的帶有唯一性,也就是說可以加入集合的節(jié)點一定是集合中沒有出現(xiàn)過的節(jié)點,這里在5個集合中流通的節(jié)點其實是 urlToken。

(其實集合可以縮減為3個,省去失敗集合,失敗則重新投入原來的集合,但我為了測速所以保留了5個集合的結(jié)構(gòu))

他們的關(guān)系是:

 

用Python寫一個小白也能懂的分布式知乎爬蟲

舉個具體的栗子:從一個 urlToken 在 waiting 集合中出現(xiàn)開始,經(jīng)過一段時間,它被 info_crawler 爬蟲程序從 waiting 集合中隨機獲取到,然后在 info_crawler 爬蟲程序中抓取個人信息,如果抓取成功將個人信息存儲到主機的 MongoDB 中,將該 urlToken 放到 info_success 集合中;如果抓取失敗則將該 urlToken 放置到 info_failed 集合中。下一個階段,經(jīng)過一段時間后,list_crawler 爬蟲程序?qū)?info_success 集合中隨機獲取到該 urlToken,然后嘗試抓取該 urlToken 代表用戶的關(guān)注者列表,如果關(guān)注者列表抓取成功,則將抓取到的所有關(guān)注者放入到 waiting 集合中,將該 urlToken 放到 list_success 集合中;如果抓取失敗,將該 urlToken 放置到 list_failed 集合中。

如此,主機維護的數(shù)據(jù)庫,配合從機的 info_crawler 和 list_crawler 爬蟲程序,便可以循環(huán)起來:info_crawler 不斷從 waiting 集合中獲取節(jié)點,抓取個人信息,存入數(shù)據(jù)庫;list_crawler 不斷的補充 waiting 集合。

主機和從機的關(guān)系如下圖:

 

用Python寫一個小白也能懂的分布式知乎爬蟲

主機是一臺外網(wǎng)/局域網(wǎng)可以訪問的“服務(wù)器”,從機可以是PC/筆記本/Mac/服務(wù)器,這個架構(gòu)可以部署在外網(wǎng)也可以部署在內(nèi)網(wǎng)。

責(zé)任編輯:未麗燕 來源: 云棲社區(qū)
相關(guān)推薦

2016-09-30 10:13:07

分布式爬蟲系統(tǒng)

2021-09-07 09:26:13

Python 開發(fā)編程語言

2025-04-14 00:00:00

MCPjson 信息地理編碼

2018-07-23 08:32:49

分布式鏡像倉庫

2017-06-16 21:00:02

Python爬蟲

2025-02-26 00:48:32

2018-05-22 15:30:30

Python網(wǎng)絡(luò)爬蟲分布式爬蟲

2020-07-30 09:35:09

Redis分布式鎖數(shù)據(jù)庫

2022-06-27 08:36:27

分布式事務(wù)XA規(guī)范

2017-05-24 15:07:19

Python爬蟲爬取

2021-10-25 10:33:29

Python 開發(fā)編程語言

2021-06-25 10:45:43

Netty 分布式框架 IO 框架

2014-03-12 10:42:10

equeue分布式消息隊列

2021-06-24 10:27:48

分布式架構(gòu)系統(tǒng)

2015-04-21 09:39:03

javajava分布式爬蟲

2018-05-09 09:44:51

Java分布式系統(tǒng)

2017-09-11 15:17:01

分布式集群負載均衡

2013-09-11 16:02:00

Spark分布式計算系統(tǒng)

2022-04-14 07:56:30

公平鎖Java線程

2024-02-19 00:00:00

Redis分布式
點贊
收藏

51CTO技術(shù)棧公眾號