BT下載與用python輕松自建種子搜索引擎

種子站點的集中特性使得它們很容易被關(guān)閉(提供音樂、電影等版權(quán)內(nèi)容的種子文件的網(wǎng)站經(jīng)常會因法律原因而被關(guān)閉,如海盜灣等的關(guān)?;蛘弑粔Γ^近的如17年5月17日Extra Torrent關(guān)停)。
而來自土耳其·伊斯坦布爾的19歲程序員Bora想要解決這個問題,致力于真正的去中心化文件分享,他用python制作了一個開源軟件,使得可以輕松地在自己的電腦上開啟一個種子搜索引擎。(進一步,用pyinstaller打包成exe,前端用electron或者其它工具簡單封裝一下,完全不熟悉python 的普通用戶也可以輕松使用)。
實際上再進一步,如果每個人將自己搜索到的種子數(shù)據(jù)在開放無審查的零網(wǎng)上進行分享互換(這也可以用python自動化),就可以實現(xiàn)完全無審查去中心化的文件分享機制。(零網(wǎng)參見開放的零網(wǎng)與個人站點嘗試

使用效果圖


  • 在騰訊云最低配服務(wù)器上跑了兩天的效果,可以看出速度還是很快的,一天上萬種子。

快速安裝

  • linux使用:在python3的虛擬環(huán)境下(小白可以參看云服務(wù)器簡單配置pip3 install magneticod就安裝好了種子爬蟲,在虛擬環(huán)境下命令行執(zhí)行magneticod就可以運行了,等待一會兒,可以看到如下的日志輸出,表明爬蟲正在運行,并且收集到種子了。

    (也可以使用magneticod -d運行顯示更詳細的信息,-d參數(shù)表示輸出debug信息。)
    網(wǎng)頁顯示與查詢功能安裝,同樣在虛擬環(huán)境下pip install magneticow就好了,然后命令行magneticow --port 8080 --user 用戶名 密碼就可以運行在8080端口了。使用localhost:8080訪問,輸入自己更改的用戶名和密碼即可。

  • windows使用:windows下直接使用pip安裝暫時有點問題(可以先嘗試像上面那樣pip安裝),需要到github上下載源碼包 - download zip,如下圖:


    然后解壓進入magneticod文件夾,在當前目錄打開命令行,python setup.py install安裝,再進入magneticow文件夾同樣命令安裝。還需要做一件事,就是找到你的python動態(tài)鏈接庫中的sqlite3.dll(我的在Anaconda3\DLLs路徑下),在SQLite Download Page找到適合你版本的sqlite-dll替換掉它,我用的是sqlite-dll-win64-x64-3200000.zip。(參考stackoverflow)這樣就可以成功運行了。
    windows的使用效果不如linux(暫時認為是windows的bug,udp也會報錯——[WinError 10054] 遠程主機強迫關(guān)閉了一個現(xiàn)有的連接),限制速度之后就會好。
    windows不限速

種子與磁力鏈接

  • 說實話,作為一個從不開車、偶爾上車的良好公民 :),我對這些東西的了解十分有限。
  • 我一直習(xí)慣使用的是基于ipv6的非公開BT站點,類似北郵人BT、6維空間這樣需要注冊的BT站點。想要資源時,在站點上下載相應(yīng)的種子(torrent)文件,使用utorrent客戶端下載即可,至于其中的原理不求甚解,大概知道的就是下載完成該資源且開啟ut客戶端的用戶越多我的下載速度越快,同時下載的人越多速度越快,因為下載的同時會互相上傳文件的不同部分給對方,下載完成后要盡保種的義務(wù),盡量開啟ut客戶端,方便其他用戶下載該資源,增加我的上傳量。分享文件做種時,要生成一個種子文件,并提交到站點服務(wù)器。
  • 為了有更清楚的認知,打開ut客戶端研究一下,順便下載下權(quán)力的游戲最新集。用ut客戶端打開種子進行下載,在下載時觀察下面的狀態(tài)欄,第二欄就是Trackers,看來是非常關(guān)鍵的一個東西。

基本概念 - Tracker:收集下載者信息的服務(wù)器,并將此信息提供給其他下載者,使下載者們相互連接起來,傳輸數(shù)據(jù)。

  • 原來,該BT站點提供了一個Tracker服務(wù)器(倒數(shù)一二行),記錄了所有下載者的信息或者文件分享者的信息,當我下載文件時,這個服務(wù)器會告訴我其他人的下載狀態(tài)(誰擁有資源,誰下載完成了,誰正在下載),也告訴其他人我的下載狀態(tài),讓我們互通有無,互相傳遞資源,增加下載速度,而tracker服務(wù)器充當了信息交換中心的角色??梢钥吹剑谝恍械腄HT被禁用了,是為了不走外網(wǎng)流量,DHT非常重要,下文會講到,暫時不管。

  • 在peers一欄我們可以看到其他下載者的詳細信息,大家互相幫助上傳與下載。

  • 在pieces一欄可以發(fā)現(xiàn)文件被分割成為很多份,每小份為1M大小,而我當前下載的視頻為4個多G。在files欄里可以看到所有區(qū)塊的下載狀況。



  • 在對資源下載過程有了初步認識后,我們來詳細地看下種子文件到底是什么。原來種子文件本質(zhì)上是一種B編碼后的文本文件,它包含了資源的詳細信息,要打開它看里面的內(nèi)容需要先解碼,我們可以使用BEncode Editor這個軟件。隨便打開一個外網(wǎng)站點上下載的種子。

  • 在BEncode Editor中顯示如下,這個73KB的種子包含了豐富的信息,在它的tracker服務(wù)器列表中可以看到名為海盜黨的希臘域名,這個種子文件也才創(chuàng)建不久。


  • 而最關(guān)鍵的信息全在info字典中,它是種子文件元信息。點開查看詳細內(nèi)容??梢钥吹较螺d文件的分塊機制和我們之前在ut客戶端看到的一致。在種子文件的元信息中包含了所有內(nèi)容信息,以及所有分塊的哈希驗證碼(數(shù)字指紋),來確保文件的真實性。在下載時我們會向種子文件中記錄的tracker服務(wù)器發(fā)出請求,得到其他有該資源的用戶的地址,一小塊一小塊的下載,每小塊下載完成后都與種子文件中的該小塊的哈希值進行比對,看是否被篡改。

  • 每個種子文件也具有一個唯一標識碼,稱為種子文件的info_hash,是種子中所有info信息B編碼后的SHA-1哈希值:20個字節(jié),即40個16進制碼。根據(jù)這串碼就能找到對應(yīng)的種子文件。

  • 除了種子,我們還會遇到磁力鏈接,如下圖,磁力鏈接又是什么呢?

  • 下圖是一個磁力鏈接的分解,來自阮一峰老師的BT下載的未來,詳細見wiki百科-磁力鏈接

  • 可以得出磁力鏈接最重要的是紅線勾出來的那40個16進制字符碼,也就是種子文件的info_hash,根據(jù)它就能找到對應(yīng)的種子文件,得到資源的詳細信息,進而下載資源。

  • 擁有這串16進制碼,我們可以輕松構(gòu)造出磁力鏈,通常我們會打開迅雷下載,迅雷會自動搜索相應(yīng)的種子。


  • 或者我們也可以到提供服務(wù)的站點,如thetorrent.org,由哈希碼得到相應(yīng)種子文件再進行下載。


    通過磁力鏈接中的info_hash碼獲取種子文件
  • 沒有服務(wù)商或站點情況下,通過info_hash碼可以直接獲取到相應(yīng)的種子元信息及其資源嗎?可以,基于DHT協(xié)議(BEP-5: DHT Protocol - 翻譯),該協(xié)議基于Kademila算法,用udp實現(xiàn)。DHT是分布式哈希表的簡寫,用來存儲種子的下載者(peer)的聯(lián)系信息。一般每個下載者(peer)擁有一個節(jié)點(node),而DHT網(wǎng)絡(luò)由節(jié)點組成(node)。每個節(jié)點(node)擁有一個唯一的ID:20字節(jié)標識碼,和種子文件的info_hash一樣長。每個節(jié)點都維持一個自己的路由表,存儲了一小部分其他節(jié)點的聯(lián)系方式,同時也存儲了一些下載者的聯(lián)系信息。節(jié)點之間互相聯(lián)系幫助尋找資源。比如節(jié)點A拿著資源X的唯一標識碼(info_hash)去問在它路由表中的節(jié)點B有沒有資源X的下載者信息,如果節(jié)點B中沒有資源X的記錄信息,節(jié)點B會在自己的路由表中選出最可能擁有資源X的k個節(jié)點,把他們的聯(lián)系方式返回給節(jié)點A節(jié)點A再根據(jù)節(jié)點B的返回信息去聯(lián)系這些節(jié)點進行詢問,依次迭代。(每一個節(jié)點比其他節(jié)點對它周邊的節(jié)點有更好的感知能力,周邊與否由Kademila算法定義)(node用來查找和存儲信息,peer負責(zé)下載)(詳細信息見BEP文檔

  • 只要找到一個種子的下載者(peer)就可以使用
    BEP-9: Extension for Peers to Send Metadata Files拓展協(xié)議從該下載者(peer)處下載種子元信息(info)(同樣可以使用元信息的哈希值(info_hash)來驗證該信息的真實性),當然也可以從它那下載資源。這樣只需要一個磁力鏈接在沒有中心服務(wù)器的情況下也可以下載資源了。(注:ut客戶端中會自動保存一份已下載資源的種子文件)

補充1:在前面我們看到的種子文件中,announce鍵記錄了tracker服務(wù)器的地址信息,在BEP-5的Torrent File Extensions小節(jié)中提到無tracker的種子文件中沒有announce鍵,而有nodes鍵記錄了良好的節(jié)點地址??傊?,在一個新節(jié)點(自身路由表為空)加入DHT網(wǎng)絡(luò)時,需要一個引導(dǎo)過程,要知道一個已經(jīng)在該網(wǎng)絡(luò)中的節(jié)點。要么通過tracker服務(wù)器獲取節(jié)點,要么是直接得到節(jié)點,并沒有那么自由。此外,tracker服務(wù)器的速度還是比DHT查找要快,一般下載過程是兩者的結(jié)合。

補充2:文件分享過程

補充3:Kademlia算法概要 - Kademlia基于兩個節(jié)點之間的距離計算,該距離是兩個網(wǎng)絡(luò)節(jié)點ID號的異或,計算的結(jié)果最終作為整型數(shù)值返回。資源的info_hash和節(jié)點ID有同樣的格式和長度,因此,可以使用同樣的方法計算資源(info_hash)和節(jié)點ID之間的距離節(jié)點ID一般是一個大的隨機數(shù),選擇該數(shù)的時候所追求的一個目標就是它的唯一性(希望在整個網(wǎng)絡(luò)中該節(jié)點ID是唯一的)。異或距離跟實際上的地理位置沒有任何關(guān)系,只與ID相關(guān)。因此很可能來自德國和澳大利亞的節(jié)點由于選擇了相似的隨機ID而成為鄰居。選擇異或是因為通過它計算的距離享有幾何距離公式的一些特征

(此外還有用戶交換 (PEX)協(xié)議,暫不討論)

源碼分析

注:網(wǎng)絡(luò)分享繁多,很多話的正確性都不是那么高,深入探索還得自己去讀官方英文協(xié)議和論文。別人的話都只是你通往更高處的一個墊腳石,不能停留在上面。

  • 之前網(wǎng)絡(luò)上已經(jīng)有不少開源的dht種子搜索的python代碼。比較有名的是手撕包菜種子搜索引擎網(wǎng)站的python代碼(開源在github上),但在項目介紹里的相關(guān)博文鏈接已經(jīng)失效,來看源碼,關(guān)鍵的種子嗅探爬蟲都在目錄workers下。而最重要的info_hash碼(即種子唯一標識碼)爬蟲為simdht_worker.py文件。主要使用了CreateChen/simDownloader項目中的代碼。而與fanpei91/simDHT基本一致。

  • simDHT最簡單易讀,單線程,建議先閱讀。思路是偽裝成一個DHT節(jié)點。初始化時,給自己隨機生成一個20位的ID,通過大的tracker服務(wù)器(如router.bittorrent.com)獲取其他節(jié)點的地址信息(find_node操作),進入DHT網(wǎng)絡(luò),利用KRPC協(xié)議傳輸B編碼的字典信息,即DHT查詢信息(4種,見bep05),與DHT網(wǎng)絡(luò)中的其他節(jié)點互相通信。

  • 由于在初始化時已經(jīng)從大的Tracker服務(wù)器獲取了一定量的節(jié)點信息,接下來向這些節(jié)點發(fā)送find_node請求,參數(shù)中:1. 將自己的ID構(gòu)造成被請求節(jié)點的(按異或)相近ID(代碼為get_neighbor函數(shù),如向ID為A的節(jié)點發(fā)送find_node請求,將自己ID構(gòu)造成A[:end]+X[:20-end],也就是構(gòu)造的ID的前end位與A節(jié)點ID相同,而后(20-end)位隨意,end取10-15,這樣偽裝成A的周邊節(jié)點,而按規(guī)則每個節(jié)點對周邊節(jié)點的感知能力要好,它很可能將你記錄在它的路由表上,使得它自己或引導(dǎo)其他節(jié)點主動向你通信),2. 要查找的節(jié)點ID隨機生成即可。大部分隨機生成的節(jié)點ID是不可能直接查找到的,那么被請求節(jié)點會返回另一批節(jié)點信息,在接受到返回的新節(jié)點信息后,向新節(jié)點繼續(xù)發(fā)送請求,依此迭代進行,目的就是不斷地和其他節(jié)點混臉熟,即auto_send_find_node函數(shù)。這里維護了一個有限長的雙端隊列(deque)存儲節(jié)點。節(jié)點不斷從隊首取出,向其發(fā)送find_node請求,收到的應(yīng)答中的新節(jié)點不斷被添加到隊尾。如果隊列為空,則重新初始化一下。

  • 一旦和越來越多的節(jié)點混臉熟了,就會有不斷的查詢請求從其他節(jié)點發(fā)送過來,作為爬蟲,只要處理get_peersannounce_peer請求就夠了。get_peers是一個節(jié)點向另一個節(jié)點發(fā)出的查詢與info_hash相關(guān)的下載者信息,包含的info_hash參數(shù)就是我們需要的種子的info_hash,但是,get_peers中包含的info_hash對應(yīng)的種子可能已經(jīng)失效或者難連接上,不采用。這時我們要回應(yīng)它,關(guān)鍵是給他一個token(自己以一定方式生成,不固定,用來校驗的)與一個空的nodes參數(shù)(我們沒有種子的下載者信息,按協(xié)議應(yīng)當返回最有可能有該信息的K個節(jié)點給查詢節(jié)點,但是也可以返回空值)。

  • 這個向我查詢的節(jié)點如果最終(通過其他節(jié)點)找到了資源(其他下載者,即peer),而控制該節(jié)點的下載者開始下載資源了,該節(jié)點很可能向我發(fā)送announce_peer消息,該消息告訴我們它的下載者信息。消息參數(shù)中的info_hash和下載者地址就是我們需要的,同時要驗證參數(shù)中的token是否就是我之前發(fā)送給該節(jié)點的,保證真實性。返回給它的消息只是自己節(jié)點的ID。

  • 對于其他節(jié)點發(fā)送給自己的pingfind_node查詢不用管即可。(可以思考,對于這兩種查詢是否有某種響應(yīng)方式可以給自己帶來更多收益)

  • 以上就是simDHT的內(nèi)容了。

  • 而進一步,光有種子的info_hash碼還不夠,能直接拿到種子的元信息就好了。這就是手撕包菜里的simMetadata.py實現(xiàn)的通過bep9拓展協(xié)議從之前announce_peer消息中的下載者那里獲取到種子的元信息。

  • 此外,wuzhenda/simDHT0x0d/dhtfck實現(xiàn)了K桶等,有一定注釋和他個人的理解,但作為爬蟲可能并不需要這個功能,還有NanYoMyDHT-woodworm 以及DHT-simDHT增加了一點新特性,可以瀏覽下。

  • 以上都是python2的,基于python3的異步IO特性的DHT爬蟲并不多,有whtsky/maga,而zrools基于maga,寫了asyncDHT,并有圖文并茂的博文,DHT爬蟲:18.4GB種子分析小記,值得一看

  • 另外,B編碼的編解碼庫:python2使用的多為bencode,pip install bencode。python可以使用的有bcoding,pip install bcodingbetter-bencodepip install better-bencode。

  • 實現(xiàn)bttorrent客戶端的python庫libtorrent,貌似只支持python2,libtorrent庫的使用可以看: 從磁力鏈獲取種子文件 - Magnet_To_Torrent2.py的43行到67行(其他都是次要代碼)。還可以參考creating daemon using Python libtorrent for fetching meta data of 100k+ torrents。(注:還發(fā)現(xiàn)有個Simple libtorrent,pip install SimpleTorrentStreaming

  • 回到文章開頭使用的magneticod,它使用了python3提供的asyncio機制。主要嗅探代碼部分magneticod/dht.py中的思路和之前介紹的simDHT基本相似,有點不同的是,它維護了一個自己的節(jié)點字典self._routing_table,每一輪(間隔一秒)向里面所有節(jié)點發(fā)出find_node查詢?nèi)缓笄蹇兆值?,如果收到自己發(fā)出的find_node請求的響應(yīng)時,字典中的所有節(jié)點數(shù)超出self.__n_max_neighbours數(shù)則不再加入新節(jié)點。(主要函數(shù)為async def tick_periodically(self),這個機制還有可以斟酌的地方)

  • 在大的異步結(jié)構(gòu)上繼承了官方的asyncio.DatagramProtocol,可以先看官方樣例UDP echo client protocolUDP echo server protocol,很簡明,數(shù)據(jù)的發(fā)送和接收都封裝好了,并且可以通過pause_writingresume_writing控制流量。

  • 在處理announce_peer消息時,用asyncio.ensure_future新建異步任務(wù)來抓取種子元信息,且對同一個種子元信息的多個抓取進行管理。

  • 在數(shù)據(jù)存儲方面使用了python自帶的sqlite,不用安裝就能使用,很方便。數(shù)據(jù)庫存儲位置管理使用了appdirs庫。

  • 作者表示近期會有一次大的重構(gòu),讓我們拭目以待。

論文

可用參考

其他

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容