最近研究了一下抖音的爬蟲,目前實(shí)現(xiàn)了熱門話題和熱門音樂下面所有相關(guān)視頻的爬取,并且我已經(jīng)將該爬蟲打包成了一個(gè) Python 庫(kù)并發(fā)布,名稱就叫做 douyin,利用該庫(kù)可以使用不到 10 行代碼完成熱門視頻的下載、相關(guān)音樂的下載以及結(jié)構(gòu)化信息的存儲(chǔ)。
本文就來詳細(xì)介紹一下這個(gè)庫(kù)的用法和一些核心邏輯實(shí)現(xiàn)。
實(shí)例演示
在開始介紹之前,我們就先看看這個(gè)庫(kù)能達(dá)到怎樣的爬取效果吧,這里我們想要爬取的部分是這這樣的:
這里是抖音搜索界面熱門話題和熱門音樂部分,每一個(gè)話題或音樂都有著非常高的熱度,而且每個(gè)熱門話題或音樂下面都是相關(guān)的抖音視頻。
下面我們要做的就是把所有熱門話題和音樂下的相關(guān)視頻都爬取到,并且將爬到的視頻下載下來,同時(shí)還要把視頻所配的音樂也單獨(dú)下載下來,不僅如此,所有視頻的相關(guān)信息如發(fā)布人、點(diǎn)贊數(shù)、評(píng)論數(shù)、發(fā)布時(shí)間、發(fā)布人、發(fā)布地點(diǎn)等等信息都需要爬取下來,并存儲(chǔ)到 MongoDB 數(shù)據(jù)庫(kù)。
聽起來似乎挺繁瑣的是吧?其實(shí)有了 douyin 這個(gè)庫(kù),我們不到 10 行代碼就可以完成上面的任務(wù)了!其 GitHub 地址是:https://github.com/Python3WebSpider/DouYin。
首先第一步我們需要安裝一下 douyin 庫(kù),命令如下:
pip3 install douyin
使用示例如下:
好,這樣就完成了,運(yùn)行這段代碼,即可以完成熱門話題、熱門音樂下面所有視頻和音樂的爬取,并將相關(guān)信息存儲(chǔ)到 MongoDB 數(shù)據(jù)庫(kù)。
另外值得注意的是,在運(yùn)行這段代碼之前首先需要安裝好 MongoDB 數(shù)據(jù)庫(kù)并成功開啟服務(wù),這樣才能確保代碼可以正常連接數(shù)據(jù)庫(kù)并把數(shù)據(jù)成功存儲(chǔ)。
我們看下運(yùn)行效果:
運(yùn)行截圖如下:
在這里我們可以看到視頻被成功存儲(chǔ)到了 MongoDB 數(shù)據(jù)庫(kù),并且執(zhí)行了下載,將視頻存儲(chǔ)到了本地(音頻的的存儲(chǔ)沒有顯示)。
最后我們看下爬取結(jié)果是怎樣的,下面是爬取到的音頻、視頻和視頻相關(guān)信息:
可以看到視頻配的音樂被存儲(chǔ)成了 mp3 格式的文件,抖音視頻存儲(chǔ)成了 mp4 文件,另外視頻相關(guān)信息如視頻描述、作者、音樂、點(diǎn)贊數(shù)、評(píng)論數(shù)等等的信息都已經(jīng)存儲(chǔ)到了 MongoDB 數(shù)據(jù)庫(kù),另外里面還包括了爬取時(shí)間、視頻鏈接、分辨率等等額外的信息。
對(duì)!就是這么簡(jiǎn)單,通過這幾行代碼,我們就得到了如上的三部分結(jié)果,而這只需要安裝 douyin 這個(gè)庫(kù)即可實(shí)現(xiàn)。
代碼解讀
下面我們來剖析一下這個(gè)庫(kù)的關(guān)鍵技術(shù)部分的實(shí)現(xiàn),代碼的地址是在:https://github.com/Python3WebSpider/DouYin,在此之前大家可以先將代碼下載下來大體瀏覽一下。
本庫(kù)依賴的其他庫(kù)有:
- aiohttp:利用它可以完成異步數(shù)據(jù)下載,加快下載速度。
- dateparser:利用它可以完成任意格式日期的轉(zhuǎn)化。
- motor:利用它可以完成異步 MongoDB 存儲(chǔ),加快存儲(chǔ)速度。
- requests:利用它可以完成最基本的 HTTP 請(qǐng)求模擬。
- tqdm:利用它可以進(jìn)行進(jìn)度條的展示。
下面我就幾個(gè)部分的關(guān)鍵實(shí)現(xiàn)對(duì)庫(kù)的實(shí)現(xiàn)進(jìn)行代碼說明。
數(shù)據(jù)結(jié)構(gòu)定義
如果要做一個(gè)庫(kù)的話,一個(gè)很重要的點(diǎn)就是對(duì)一些關(guān)鍵的信息進(jìn)行結(jié)構(gòu)化的定義,使用面向?qū)ο蟮乃季S對(duì)某些對(duì)象進(jìn)行封裝,抖音的爬取也不例外。
在抖音中,其實(shí)有很多種對(duì)象,比如視頻、音樂、話題、用戶、評(píng)論等等,它們之間通過某種關(guān)系聯(lián)系在一起,例如視頻中使用了某個(gè)配樂,那么視頻和音樂就存在使用關(guān)系;比如用戶發(fā)布了視頻,那么用戶和視頻就存在發(fā)布關(guān)系,我們可以使用面向?qū)ο蟮乃季S對(duì)每個(gè)對(duì)象進(jìn)行封裝,比如視頻的話,就可以定義成如下結(jié)構(gòu):
這里將一些關(guān)鍵的屬性定義成 Video 類的一部分,包括 id 索引、desc 描述、author 發(fā)布人、music 配樂等等,其中 author 和 music 并不是簡(jiǎn)單的字符串的形式,它也是單獨(dú)定義的數(shù)據(jù)結(jié)構(gòu),比如 author 就是 User 類型的對(duì)象,而 User 的定義又是如下結(jié)構(gòu):
所以說,通過屬性之間的關(guān)聯(lián),我們就可以將不同的對(duì)象關(guān)聯(lián)起來,這樣顯得邏輯架構(gòu)清晰,而且我們也不用一個(gè)個(gè)單獨(dú)維護(hù)字典來存儲(chǔ)了,其實(shí)這就和 Scrapy 里面的 Item 的定義是類似的。
請(qǐng)求和重試
實(shí)現(xiàn)爬取的過程就不必多說了,這里面其實(shí)用到的就是最簡(jiǎn)單的抓包技巧,使用 Charles 直接進(jìn)行抓包即可。抓包之后便可以觀察到對(duì)應(yīng)的接口請(qǐng)求,然后進(jìn)行模擬即可。
所以問題就來了,難道我要一個(gè)接口寫一個(gè)請(qǐng)求方法嗎?另外還要配置 Headers、超時(shí)時(shí)間等等的內(nèi)容,那豈不是太費(fèi)勁了,所以,我們可以將請(qǐng)求的方法進(jìn)行單獨(dú)的封裝,這里我定義了一個(gè) fetch 方法:
這個(gè)方法留了一個(gè)必要參數(shù),即 url,另外其他的配置我留成了 kwargs,也就是可以任意傳遞,傳遞之后,它會(huì)依次傳遞給 requests 的請(qǐng)求方法,然后這里還做了異常處理,如果成功請(qǐng)求,即可返回正常的請(qǐng)求結(jié)果。
定義了這個(gè)方法,在其他的調(diào)用方法里面我們只需要單獨(dú)調(diào)用這個(gè) fetch 方法即可,而不需要再去關(guān)心異常處理,返回類型了。
好,那么定義好了請(qǐng)求之后,如果出現(xiàn)了請(qǐng)求失敗怎么辦呢?按照常規(guī)的方法,我們可能就會(huì)在外面套一層方法,然后記錄調(diào)用 fetch 方法請(qǐng)求失敗的次數(shù),然后重新調(diào)用 fetch 方法進(jìn)行重試,但這里可以告訴大家一個(gè)更好用的庫(kù),叫做 retrying,使用它我們可以通過定義一個(gè)裝飾器來完成重試的操作。
比如我可以使用 retry 裝飾器這么裝飾 fetch 方法:
這里使用了裝飾器的四個(gè)參數(shù):
- stop_max_attempt_number:最大重試次數(shù),如果重試次數(shù)達(dá)到該次數(shù)則放棄重試。
- wait_random_min:下次重試之前隨機(jī)等待時(shí)間的最小值。
- wait_random_max:下次重試之前隨機(jī)等待時(shí)間的最大值。
- retry_on_exception:判斷出現(xiàn)了怎樣的異常才重試。
這里 retry_on_exception 參數(shù)指定了一個(gè)方法,叫做 need_retry,方法定義如下:
這里判斷了如果是 requests 的 ConnectionError 和 ReadTimeout 異常的話,就會(huì)拋出異常進(jìn)行重試,否則不予重試。
所以,這樣我們就實(shí)現(xiàn)了請(qǐng)求的封裝和自動(dòng)重試,是不是非常 Pythonic?
下載處理器的設(shè)計(jì)
為了下載視頻,我們需要設(shè)計(jì)一個(gè)下載處理器來下載已經(jīng)爬取到的視頻鏈接,所以下載處理器的輸入就是一批批的視頻鏈接,下載器接收到這些鏈接,會(huì)將其進(jìn)行下載處理,并將視頻存儲(chǔ)到對(duì)應(yīng)的位置,另外也可以完成一些信息存儲(chǔ)操作。
在設(shè)計(jì)時(shí),下載處理器的要求有兩個(gè),一個(gè)是保證高速的下載,另一個(gè)就是可擴(kuò)展性要強(qiáng),下面我們分別來針對(duì)這兩個(gè)特點(diǎn)進(jìn)行設(shè)計(jì):
- 高速下載,為了實(shí)現(xiàn)高速的下載,要么可以使用多線程或多進(jìn)程,要么可以用異步下載,很明顯,后者是更有優(yōu)勢(shì)的。
- 擴(kuò)展性強(qiáng),下載處理器要能下載音頻、視頻,另外還可以支持?jǐn)?shù)據(jù)庫(kù)等存儲(chǔ),所以為了解耦合,我們可以將視頻下載、音頻下載、數(shù)據(jù)庫(kù)存儲(chǔ)的功能獨(dú)立出來,下載處理器只負(fù)責(zé)視頻鏈接的主要邏輯處理和分配即可。
為了實(shí)現(xiàn)高速下載,這里我們可以使用 aiohttp 庫(kù)來完成,另外異步下載我們也不能一下子下載太多,不然網(wǎng)絡(luò)波動(dòng)太大,所以我們可以設(shè)置 batch 式下載,可以避免同時(shí)大量的請(qǐng)求和網(wǎng)絡(luò)擁塞,主要的下載函數(shù)如下:
這個(gè) download 方法設(shè)計(jì)了多種數(shù)據(jù)接收類型,可以接收一個(gè)生成器,也可以接收單個(gè)或列表形式的視頻對(duì)象數(shù)據(jù),接著調(diào)用了 process_items 方法進(jìn)行了異步下載,其方法實(shí)現(xiàn)如下:
這里使用了 asyncio 實(shí)現(xiàn)了異步處理,并通過對(duì)視頻鏈接進(jìn)行分批處理保證了流量的穩(wěn)定性,另外還使用了 tqdm 實(shí)現(xiàn)了進(jìn)度條的顯示。
我們可以看到,真正的處理下載的方法是 process_item,這里面會(huì)調(diào)用視頻下載、音頻下載、數(shù)據(jù)庫(kù)存儲(chǔ)的一些組件來完成處理,由于我們使用了 asyncio 進(jìn)行了異步處理,所以 process_item 也需要是一個(gè)支持異步處理的方法,定義如下:
這里我們可以看到,真正的處理邏輯都在一個(gè)個(gè) handler 里面,我們將每個(gè)單獨(dú)的功能進(jìn)行了抽離,定義成了一個(gè)個(gè) Handler,這樣可以實(shí)現(xiàn)良好的解耦合,如果我們要增加和關(guān)閉某些功能,只需要配置不同的 Handler 即可,而不需要去改動(dòng)代碼,這也是設(shè)計(jì)模式的一個(gè)解耦思想,類似工廠模式。
Handler 的設(shè)計(jì)
剛才我們講了,Handler 就負(fù)責(zé)一個(gè)個(gè)具體功能的實(shí)現(xiàn),比如視頻下載、音頻下載、數(shù)據(jù)存儲(chǔ)等等,所以我們可以將它們定義成不同的 Handler,而視頻下載、音頻下載又都是文件下載,所以又可以利用繼承的思想設(shè)計(jì)一個(gè)文件下載的 Handler,定義如下:
這里我們還是使用了 aiohttp,因?yàn)樵谙螺d處理器中需要 Handler 支持異步操作,這里下載的時(shí)候就是直接請(qǐng)求了文件鏈接,然后判斷了文件的類型,并完成了文件保存。
視頻下載的 Handler 只需要繼承當(dāng)前的 FileHandler 即可:
這里其實(shí)就是加了類別判斷,確保數(shù)據(jù)類型的一致性,當(dāng)然音頻下載也是一樣的。
異步 MongoDB 存儲(chǔ)
上面介紹了視頻和音頻處理的 Handler,另外還有一個(gè)存儲(chǔ)的 Handler 沒有介紹,那就是 MongoDB 存儲(chǔ),平常我們可能習(xí)慣使用 PyMongo 來完成存儲(chǔ),但這里我們?yōu)榱思铀?,需要支持異步操作,所以這里有一個(gè)可以實(shí)現(xiàn)異步 MongoDB 存儲(chǔ)的庫(kù),叫做 Motor,其實(shí)使用的方法差不太多,MongoDB 的連接對(duì)象不再是 PyMongo 的 MongoClient 了,而是 Motor 的 AsyncIOMotorClient,其他的配置基本類似。
在存儲(chǔ)時(shí)使用的是 update_one 方法并開啟了 upsert 參數(shù),這樣可以做到存在即更新,不存在即插入的功能,保證數(shù)據(jù)的不重復(fù)性。
整個(gè) MongoDB 存儲(chǔ)的 Handler 定義如下:
可以看到我們?cè)陬愔卸x了 AsyncIOMotorClient 對(duì)象,并暴露了 conn_uri 連接字符串和 db 數(shù)據(jù)庫(kù)名稱,可以在聲明 MongoHandler 類的時(shí)候指定 MongoDB 的鏈接地址和數(shù)據(jù)庫(kù)名。
同樣的 process 方法,這里使用 await 修飾了 update_one 方法,完成了異步 MongoDB 存儲(chǔ)。
好,以上便是 douyin 庫(kù)的所有的關(guān)鍵部分介紹,這部分內(nèi)容可以幫助大家理解這個(gè)庫(kù)的核心部分實(shí)現(xiàn),另外可能對(duì)設(shè)計(jì)模式、面向?qū)ο笏季S以及一些實(shí)用庫(kù)的使用有一定的幫助。
總結(jié)
本文介紹了一個(gè)可以用來爬取抖音熱門視頻的 Python 庫(kù),并介紹了該庫(kù)的基本用法和核心部分實(shí)現(xiàn),希望對(duì)大家有所幫助。
本抖音庫(kù)的 GitHub 地址是:https://github.com/Python3WebSpider/DouYin,如果你對(duì)你有幫助,還請(qǐng)賜予一個(gè) Star!非常感謝!
作者:崔慶才 ,轉(zhuǎn)自:進(jìn)擊的Coder
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
- 進(jìn)門的時(shí)候 一天工作下來,一進(jìn)門 我的天使已經(jīng)遠(yuǎn)遠(yuǎn)的...