2009/02/04

初學 Python - 寫一個 BT 熱烈度偵測器

我發現要學習程式語言,一定得有個目標作為動力(或是推力),學的才會快。比如說我的同學學習 Java,是為了修過物件導向程式設計這門課,無不努力的加以學習,就怕被當XD 前次學習 Ruby 我個人認為成效非常不彰,到現在還沒摸過 RoR 程式,也只會雕蟲小技罷了。不過倒是為了修改 HoneyComing (HERE) 而勉強學了一些。

這一次又學了 Python 其實跟兩件事有關:Google App Engine 跟 BitTorrent,我就慢慢說明。

Google App Engine (GAE) 已經公開申請一段時間了,Google 提供各種資源讓你在上面開發網路應用程式,而且目前是免費的!只可惜支援的程式語言目前僅有 Python (因為 Python 之父 Guido van Rossum 在 Google 工作的關係吧)。雖然計畫未來會支援更多語言 (也許會有 Ruby, PHP, Java?) 但是在那一刻還沒到來之前,這麼爽的東西竟然因為我不會 Python 而不能痛快遊玩感到可惜。一方面前幾個月 Pixmicat! 開發討論版上面有個人做了一個 Python 版的移植 (雖然看起來是移植 futaba 祭典版),感覺很有趣,我想該是接觸新知識的時候了,於是就去下載了 GAE 的 SDK 和安裝 Python 環境。

看了一下 GAE 還是有些限制的,SDK 提供了一個範例,我看了一下似乎提供了一些整合 Framework,但是我連基本的都不會了,要我用什麼 Framework 呢?剛好又看到 GAEO 這個專門為 GAE 撰寫的 Framework,這還是台灣人 ericsk 主導的。當然要玩這些的大前提就是先會懂 Python。

另一個竟然是 BitTorrent,有沒有搞錯?沒搞錯,BitTorrent 原本就是使用 Python 開發的 (在還沒併購 μTorrent 之前,現在的已經是用 C++)。我記得以前有一個 TorrentSpy 軟體,可以幫助使用者在新增下載之前,先查看熱烈度 (Seeder/Leecher 人數),不然萬一早就沒人了豈不是白搭 (尤其是挖了很久以前的 torrent 檔更需要)。但是隨著時代的改變,BT 的通訊協定改變越來越大,2004 年後即停止開發的軟體早就跟不上時代,現在都已經支援多個 Tracker 查詢了,但是此軟體仍只能針對一組作查詢不太便利 (根據這篇修訂紀錄來看也是2008年才成為標準,不能怪他),目前好像也沒看到其他什麼可以取代的軟體,不如自己寫吧。

簡單了解一下我如果要做到一樣的效果,必須要先解析 *.torrent 檔案的結構。這不難,已經有完整的 Spec Wiki 可以看。首先要面對的是 BT 最常用到的 Bencode 編碼法 (念作 Bee encode)。


一般字串: (n:XXXX)
範例: 10:HelloWorld。冒號前面是字串長度,後面則是字串本身

數字: (iNNNNe)
範例: i10e。使用i(integer)...e來包住

列表: (lAAABBBe)
範例: l10:HelloWorldi10ee。使用l(list)...e來包住。
以虛擬碼來說是 ["HelloWorld", 10]

字典: (dXXXAAAYYYBBBe)
範例: d10:HelloWorldi10e4:test4:truee。使用d(dictionary)...e來包住。
以虛擬碼來說是 {"HelloWorld": 10, "test": "true"},注意數字不能做索引鍵,必須使用字串

所以像是
{"info":
 {
  "1" : {"date": 20090204203645, "file": ["A", "B", "C"]},
  "2" : {"date": 20090203123559, "file": ["01.jpg", "2.png", "3333.txt"]}
 }
}

這種結構也能輕易的變成Bencode:
d4:infod1:1d4:datei20090204203645e4:filel1:A1:B1:Cee1:2d4:datei20090203123559e4:filel6:01.jpg5:2.png8:3333.txteeee


藉由官方的Bencode套件我們馬上就可以省去這些繁複的動作,剩下的便是看 Spec 取出適當的資訊組合之後向 Tracker 要求目前的熱烈度如何即可。

又,Tracker得知熱烈度的方法是 GET /scrape?info_hash=XXX 的方法,info_hash是啥碗糕?其實就是 Torrent 檔案內 info 項目的 Bencode 資料經過 SHA-1 後的 20 bytes 資料。這邊利用 Python 提供的 hashlib 函式庫 sha1 方法來達成,至於要抓 info 的原始 bencode 資料也不會很難,我是直接再把解析完的 info 再 bencode 一次,不過照理說直接從檔案中抓效率可能比較好。

Tracker 回傳的格式一樣是 bencode,結構簡單多了。我們需要的正是 complete, incomplete 這兩項。downloaded 是已下載完成的人次,可作為一種熱門參考,但前面兩個值對下載比較重要。



經過解析後的 torrent 檔案,我們需要 announce-list 列表中的 Tracker 作查詢,另外 info 需要被作成 SHA-1 hash 來和 Tracker 聯繫。剩下的便是一一向 Tracker "Scrape" 目前的下載情形 (/announce 改成 scrape 就可以了),將 HTTP GET 的內容給解碼後,就可以抓 'complete', 'incomplete', 'downloaded' 三個有用資料。如此向每一個 Tracker 要求之後就可以得知熱烈度,藉以決定值不值得一試。

最後感謝網際網路的便利性,讓我很快地找到需要的資料:


---
02/05: 修正程式碼中有關 scrape 回傳的 Bencode 偵測問題,有的 Tracker 只回傳 i0e (0) 會導致原本的判斷錯誤,現改用判斷型態為 dict 才作處理。

沒有留言:

張貼留言