BT網站--Python開發爬蟲代替.NET

 

BT網站-奧修磁力-Python開發爬蟲代替.NET寫的爬蟲,主要演示訪問速度和在一千萬左右的HASH記錄中索引效率。html

IBMID 磁力下載- WWW.IBMID.COM  如今用的是Python +CENTOS 7 系統node

 

磁力下載()經歷了屢次點技術變動。開源版本使用了django網站框架重寫,以前是Flask,再早期是tornado。電影FM也是使用tornado,後來發現tornado並不適用於任何場景。之內容爲王的網站仍是django比較擅長,只是入門時間比其餘框架都較長。早期數據庫採用了MongoDB,由於配合Python讀寫數據很方便,也不用關注數據結構,搜索功能採用自帶的關鍵詞搜索,不事後來隨着資源數量增長,性能也明顯跟不上。今年換了WiredTiger引擎,自帶的fulltext search仍是不給力。另外Amazon的cloudsearch是個坑,土豪能夠考慮,性能真的很不錯,就是比較貴。最後仍是搭建一個SphinxSearch吧,數據庫也換成MySQL(MyISAM引擎),配合起來也很方便。Sphinx建立全文索引的速度很給力,官方的自評也很高,我本身測試1000w的資源(大概3GB),1分鐘左右就索引完畢。不信,你們能夠自測一下。數據庫

原理以下:
BitTorrent DHT協議 
 
BitTorrent使用一種分佈的、寬鬆的哈希表(DHT)爲沒法track的torrent存儲對等點聯繫信息。這樣,每一個peer都成爲一個 tracker。這個協議基於Kademlia在UDP上實現。

請注意文中使用的術語,以避免引發混淆。peer是一個監聽在TCP端口上,實現BitTorrent協議的客戶端/服務器。節點(node)是一個監聽在UDP端口上,實現DHT協議的客戶端/服務器。DHT網絡由節點組成,存儲peer的位置信息。BitTorrent客戶端包含一個DHT 節點,並經過這個節點聯繫DHT中的其餘節點,以獲取其它peer的位置,這樣就能夠從它們那裏經過BitTorrent協議下載了。django

綜述服務器

每一個節點都有一個全局惟一的標識符,稱爲節點 ID。 節點 ID從160bit空間中隨機選取,與BitTorrent的infohash值的空間相同。距離度量用來比較兩個節點或者節點與infohash之間的遠近程度。節點必須維護一個含有少許其餘節點聯繫信息的路由表。ID越靠近自身ID時,路由表越詳細。節點知道不少離它很近的節點,只知道少許離它很遠地節點。網絡

在Kademlia中,距離度量採用異或計算,結果解釋成一個無符號整數。
distance (A,B)=|A ? B|
值越小,距離越近。數據結構

當一個節點須要查找一個torrent的peer時,它計算torrent的infohash和本地路由表中的節點 ID的距離。而後向與該torrent最近的一些節點請求該當前正在下載該torrent的peer信息。若是某個節點有這些信息,就直接回復。不然,它必須回覆在它的路由表中離這個torrent更近的節點。如此不斷重複的搜索更近的節點,直到找不到。當搜索結束以後,peer將本身的聯繫信息註冊到離 torrent最近的節點。框架

查詢peer請求的返回值(包含一個不透明值),稱之爲令牌。當一個節點通知其餘節點它的peer正在下載一個torrent的時候,它必須使用最近向那個節點查詢peer請求時,得到的令牌。當一個節點試圖公告一個torrent,它所請求過的節點根據這個節點的IP地址檢查它的令牌是否有效。這個機制能夠防止惡意主機向其餘主機註冊torrent。因爲令牌僅僅由請求節點返回給它所接收到令牌的那個節點,因此並無規定具體實現。令牌應該在分發出去以後的一段合理時間內被接受。BitTorrent的實現是,用對方的IP地址和一個密碼(這個密碼每五分鐘更換一次),計算SHA1做爲令牌,這個令牌的有效時間是十分鐘。tornado

路由表性能

每一個節點維護一個路由表,由它所知道的好節點組成。路由表中的節點被用做在DHT中發送請求的起點。當其餘節點查詢時,就返回路由表中的節點。

並非咱們所知的每一個節點都是同樣的。一些是好的,而另外一些不是。不少使用DHT的節點均可以發送請求和接收應答,可是不能應答其餘節點的請求。有一點很重要:每一個節點路由表中的節點都應該是好節點。一個節點在過去的15分鐘內應答過本節點的的某個請求,它就是一個好節點;若是它曾經應答過本節點的某個請求,而且在過去的15分鐘內向本節點發送過請求,它也是一個好節點。若是一個節點15分鐘沒有活動,它就變成可疑節點。若是它連續屢次未能應答請求,它就變成壞節點了。咱們所知的好節點被賦予較未知狀態的節點更高的優先級。

路由表覆蓋整個節點ID空間(從0到2160)。路由表被細分紅「桶」,每一個桶覆蓋一部分空間。一張空表只有一個桶,它覆蓋的空間是min=0,max=2160 。當一個ID是N的節點插入路由表中時,它被放進min<=N<max的桶中。一張空表只有一個桶,所以任何一個節點均可以放進去。每一個桶在裝滿以前,最多隻能存放K個節點,目前是8個。當一個桶裝滿了好節點以後,就不能再往裏面加入別的節點,除非當前節點的ID落入桶的覆蓋範圍以內。這樣的話,這個桶將被兩個新桶替換掉,兩個新桶分別覆蓋原來一半的空間,而且原來桶裏面的節點從新分發到新桶之中。對一張只有一個桶的新表來講,滿的桶老是被分割成兩個分別覆蓋0-2159和2159-2160的桶。

當一個桶裝滿了好的節點,新的節點將被丟棄。若是桶中的某個節點變壞了,那麼它將被一個新節點替換。若是桶中一些可疑節點長達15分鐘沒有活動,最久不活躍節點將被ping。若是被ping節點響應了,那麼將依次ping下一個最久不活躍可疑節點,直到某一個未能響應,或者桶中的全部節點都是好的了。若是桶中的某個節點未能響應ping,建議在丟棄並用新的好節點替換它以前再試一次。這樣,路由表中將填滿穩定的長期活躍的節點。

每一個桶都要維護一個最後變化屬性來標誌它的新舊程度。當桶中的一個節點被ping並且回覆了,或者一個節點加入桶中,或者一個節點被另外一個節點替換,桶的最後變化屬性將被更新。桶若是15分鐘沒有變化,就應該刷新——從它覆蓋的ID空間中隨機選擇一個ID,在上面執行一個 find_nodes搜索。能夠接收其餘節點請求的節點一般不須要常常刷新桶。不能接收其餘節點請求的節點則須要按期刷新全部桶,保證在DHT須要的時候路由表中的都是好節點。
啓動時,節點在它的路由表中插入第一個節點,而後應該嘗試查找DHT中最近鄰的其餘節點——向鄰近節點發送find_node命令,再向更近的節點發送該命令,直到不能找到更近鄰的節點。在客戶端軟件每次調用路由表時,應該保存路由表。

BitTorrent協議擴展

BitTorrent協議被擴展了,以便讓tracker所告知的peer相互之間能夠交換UDP端口號。這樣,客戶端就能夠得到常規torrent下載時自動生成的路由表。新安裝的客戶端第一次試圖下載一個沒法track的torrent時,路由表中沒有任何節點,須要torrent中的聯繫信息。

支持DHT的peer設置在BitTorrent協議握手交換的保留標誌位8字節的最後一位。peer接收到遠程節點的握手消息,若是標誌支持DHT,那麼應該回復一個PORT消息。它以0x09開始,而後是兩字節的UDP端口,採用網絡字節順序。peer接收到這個消息應該試圖ping遠程peer上對應IP和端口的那個節點。若是收到ping的響應,這個節點應該嘗試按照一般規則,把這個新的聯繫信息插入路由表中。

Torrent文件擴展

一個沒法track的torrent字典中不包括"announce"這個鍵,而是有一個"nodes"鍵。這個鍵應該設置成離生成torrent的節點路由表中最近的K個節點。或者,由生成torrent的人把這個鍵設置成已知的好節點。請不要把"router.bittorrent.com"自動加入 torrent文件中,也不要把它加入到客戶端的路由表中。

DE<nodes = [["<host>", <port>], ["<host>", <port>], ...] nodes = [["127.0.0.1", 6881], ["your.router.node", 4804]]DE<

KRPC協議

KRPC協議是一個簡單的RPC機制,由在UDP上發送的bencode字典組成。發送一個請求包,回覆一個響應包,沒有重試。有三種消息類型: query, response, error。對於DHT協議,有四種query :ping, find_node, get_peers, announce_peer。

一個KRPC消息是一個字典,包括兩個通用鍵和多個依消息類型而定的其餘鍵。每一個消息都有一個"t"鍵和一個字符串值,表明transaction ID。這個transaction ID由請求節點生成,在回覆的時候回顯。這樣回覆能夠關聯同一個節點的多個請求。KRPC中的另外一個通用鍵是"y",也是一個字符串做爲值,表示消息的類型。"y"的值有:"q"(query,請求),"r"(response,回覆,響應),"e"(error,錯誤)。

聯繫編碼

peer的聯繫信息編碼成一個6字節的串,也叫"緊密的IP地址/端口信息",4字節的IP地址緊接2字節端口號,都是採用網絡字節順序。

節點的聯繫信息編碼成一個26字節的串,也叫"緊密的節點信息",20字節的節點ID緊接緊密的IP地址/端口信息,一樣採用網絡字節順序。

請求

請求,即"y"的值是"q"的KRPC消息,包括兩個附加鍵"q"和"a"。"q"的值是請求的方法名,"a"的值是請求的參數。

響應

響應,即"y"的值是"r"的KRPC消息,包括一個附加鍵"r"。"r"的值是請求的返回值。當成功完成一個請求時,發送響應消息。

錯誤

錯誤,即"y"的值是"e"的KRPC消息,包括一個附加鍵"e"。"e"的值是一個列表,其中的第一個元素是一個整數,表示錯誤代碼。第二個元素是一個錯誤消息的字符串。當請求沒法完成時,發送錯誤。下表是可能出現的錯誤:

201 通常錯誤
202 服務器錯誤
203 協議錯誤,好比異常消息包,無效參數,無效令牌等
204 方法未知

錯誤包示例

DE<generic error = {'t':0, 'y':'e', 'e':[201, "A Generic Error Ocurred"]} bencoded = d1:eli201e23:A Generic Error Ocurrede1:ti0e1:y1:eeDE<

DHT請求

全部的請求都有一個id的鍵,值是請求節點的ID。全部的響應都有一個id的鍵,值是響應節點的ID。

ping

最基本的請求是ping, "q"="ping"。 ping請求只有一個參數"id",值是發送者的節點ID,20字節的,網絡字節順序。相應的響應也只有一個id鍵,值是響應結點的ID 。

DE<arguments: {"id" : "<querying nodes id>"} response: {"id" : "<queried nodes id>"}DE<

示例包

DE<ping Query = {"t":"0", "y":"q", "q":"ping", "a":{"id":"abcdefghij0123456789"}} bencoded = d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t1:01:y1:qeDE<

DE<Response = {"t":"0", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}} bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t1:01:y1:reDE<

find_node

find_node用來查找一個給定ID的節點聯繫信息,"q"=="find_node"。 find_node請求有兩個參數,"id"包含請求結點的ID;"target"包含請求節點要查找的目標節點ID。當一個節點接收到一個 find_node請求後,它的響應應該包含一個"node"鍵,值是這個目標節點,或者它路由表中K(8)個離目標節點最近的好節點的緊密的節點信息

DE<arguments: {"id" : "<querying nodes id>", "target" : "<id of target node>"} response: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}DE<

示例包

DE<find_node Query = {'t':0, 'y':'q', 'q':'find_node', 'a': {'id':'abcdefghij0123456789', 'target':'mnopqrstuvwxyz123456'}} bencoded = d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:ti0e1:y1:qeDE<

DE<Response = {'t':0, 'y':'r', 'r': {'id':'0123456789abcdefghij', 'nodes': 'def456...'}} bencoded = d1:rd2:id20:0123456789abcdefghij5:nodes9:def456...e1:ti0e1:y1:reDE<

get_peers

get_peers與一個torrent的infohash關聯,"q"="get_peers"。get_peers請求有兩個參數:"id"包含請求結點的ID;"info_hash"包含torrent的infohash。若是接收請求的節點知道infohash的peer,它把這些peer 的緊密的IP地址/端口信息鏈接成一個串列表,以"value"做爲鍵,回覆給請求節點。若是接收請求的節點沒有infohash的 peer ,它回覆路由表中離infohash最近的K個節點,以"nodes"做爲鍵。任何一種狀況,"token"鍵都包含在返回值中。在未來發送 announce_peer請求的時候,token值也是必須的。

DE<arguments: {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>"} response: {"id" : "<queried nodes id>", "values" : ["<compact peer info string>"]} or: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}DE<

示例包

DE<get_peers Query = {'t':0, 'y':'q', 'q':'get_peers', 'a': {'id':'abcdefghij0123456789', 'info_hash':'mnopqrstuvwxyz123456'}} bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:ti0e1:y1:qeDE<

DE<Response with peers = {'t':0, 'y':'r', 'r': {'id':'abcdefghij0123456789', 'token':'aoeusnth', 'values': ['axje.uidhtnmbrl']}} bencoded = d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl15:axje.uidhtnmbrlee1:ti0e1:y1:reDE<

DE<Response with closest nodes = {'t':0, 'y':'r', 'r': {'id':'abcdefghij0123456789', 'token':'aoeusnth', 'nodes': 'def456...'}} bencoded = d1:rd2:id20:abcdefghij01234567895:nodes9:def456...5:token8:aoeusnthe1:ti0e1:y1:reDE<

announce_peer

聲明請求節點的peer正在一個端口上下載一個torrent。announce_peer有四個參數:"id"是請求節點ID; "info_hash"是torrent的infohash;"port"是正在下載的端口號,整數;"token"是上次接收get_peers請求響應時得到的。接收announce請求的節點必須根據IP地址檢查令牌(token),即上次它做爲請求節點時發給它的令牌與如今提供的令牌相同。而後接收請求的節點應該存儲請求節點的IP地址和提供的與infohash關聯的端口到本地peer聯繫信息存儲池中。

DE<arguments: {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>", "port" : <port number>, "token" : "<opaque token>"} response: {"id" : "<queried nodes id>"}DE<

示例包

DE<announce_peers Query = {'t':0, 'y':'q', 'q':'announce_peers', 'a': {'id':'abcdefghij0123456789', 'info_hash':'mnopqrstuvwxyz123456', 'port' : 6881, 'token' : 'aoeusnth'}} bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:
mnopqrstuvwxyz1234564:porti6881e5:token8:aoeusnthe1:q14:announce_peers1:ti0e1:y1:qeDE<

DE<Response = {"t":"0", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}} bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t1:01:y1:reDE<

腳註

  1. "Kademlia: A Peer-to-peer Information System Based on the XOR Metric",
    Petar Maymounkov and David Mazieres,
  2. Use SHA1 and plenty of entropy to ensure a unique ID
 
 
研究DHT的羣:  199824689
相關文章
相關標籤/搜索