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
相关文章
相关标签/搜索