文档章节

python开发的 dht网络爬虫

 东昕
发布于 2014/08/22 13:25
字数 1283
阅读 10213
收藏 34

使用 libtorrent 的python绑定库实现一个dht网络爬虫,抓取dht网络中的磁力链接。


dht 网络简介

p2p网络

在P2P网络中,通过种子文件下载资源时,要知道资源在P2P网络中哪些计算机中,这些传输资源的计算机称作peer。在传统的P2P网络中,使用tracker服务器跟踪资源的peer。要下载资源,首先需要取得这些peer。


dht网络

tracker服务器面临一些版权和法律问题。于是出现了DHT,它把tracker上的资源peer信息分散到了整个网络中。dht网络是由分布 式节点构成,节点(node)是实现了DHT协议的p2p客户端。P2P客户端程序既是peer也是node。DHT网络有多种算法,常用的有 Kademlia。


dht网络下载

P2P客户端使用种子文件下载资源时,如果没有tracker服务器,它就向DHT网络查询资源的peer列表, 然后从peer下载资源。


Magnet是磁力链接

资源的标识在DHT网络中称为infohash,是一个通过sha1算法得到的20字节长的字符串。infohash是使用种子文件的文件描述信息 计算得到。磁力链接是把infohash编码成16进制字符串得到。P2P客户端使用磁力链接,下载资源的种子文件,然后根据种子文件下载资源。


Kademlia 算法

Kademlia是DHT网络的一种实现, 具体的算法参见:DHT协议


KRPC 协议

KRPC 是节点之间的交互协议,使用UDP来传送。

包括4种请求:ping,find_node,get_peer,announce_peer。其中get_peer和announce_peer是节点间查询资源的主要消息。


dht 爬虫原理

主要的思路就是伪装为p2p客户端,加入dht网络,收集dht网络中的get_peer和announce_peer消息,这些消息是其他node发送给伪装的p2p客户端的udp消息。


本文dht爬虫的实现

爬虫运行环境

  1. linux 系统

  2. python 2.7

  3. libtorrent 库的python绑定

  4. twisted 网络库

  5. 防火墙开启固定的udp和tcp端口


libtorrent 库的介绍

libtorrent库是p2p下载的客户端库,有丰富的接口,可以用来开发下载p2p网络上的资源。它有python的绑定库,本爬虫就是使用它的python库开发的。

在libtorrent中有几个概念需要解释下。 session 相当于p2p客户端,session开启一个tcp和一个udp端口,用来与其他p2p客户端交换数据。可以在一个进程内定义多个session,也就是多个p2p客户端,来加快收集速度。

alert是libtorrent中用来收集各种消息的队列,每个session都有一个自己的alert消息队列。KRPC协议的get_peer和announce_peer消息也是从这个队列中获取,就是用这两个消息收集磁力链接的。


主要实现代码

爬虫实现的主要代码比较简单

# 事件通知处理函数
    def _handle_alerts(self, session, alerts):
        while len(alerts):
            alert = alerts.pop()
            # 获取dht_announce_alert和dht_get_peer_alert消息
            # 从这两消息收集磁力链接
            if isinstance(alert, lt.add_torrent_alert):
                alert.handle.set_upload_limit(self._torrent_upload_limit)
                alert.handle.set_download_limit(self._torrent_download_limit)
            elif isinstance(alert, lt.dht_announce_alert):
                info_hash = alert.info_hash.to_string().encode('hex')
                if info_hash in self._meta_list:
                    self._meta_list[info_hash] += 1
                else:
                    self._meta_list[info_hash] = 1
                    self._current_meta_count += 1
            elif isinstance(alert, lt.dht_get_peers_alert):
                info_hash = alert.info_hash.to_string().encode('hex')
                if info_hash in self._meta_list:
                    self._meta_list[info_hash] += 1
                else:
                    self._infohash_queue_from_getpeers.append(info_hash)
                    self._meta_list[info_hash] = 1
                    self._current_meta_count += 1

    def start_work(self):
        '''主工作循环,检查消息,显示状态'''
        # 清理屏幕
        begin_time = time.time()
        show_interval = self._delay_interval
        while True:
            for session in self._sessions:
                session.post_torrent_updates()
                # 从队列中获取信息
                self._handle_alerts(session, session.pop_alerts())
            time.sleep(self._sleep_time)
            if show_interval > 0:
                show_interval -= 1
                continue
            show_interval = self._delay_interval

            # 统计信息显示
            show_content = ['torrents:']
            interval = time.time() - begin_time
            show_content.append('  pid: %s' % os.getpid())
            show_content.append('  time: %s' %
                                time.strftime('%Y-%m-%d %H:%M:%S'))
            show_content.append('  run time: %s' % self._get_runtime(interval))
            show_content.append('  start port: %d' % self._start_port)
            show_content.append('  collect session num: %d' %
                                len(self._sessions))
            show_content.append('  info hash nums from get peers: %d' %
                                len(self._infohash_queue_from_getpeers))
            show_content.append('  torrent collection rate: %f /minute' %
                                (self._current_meta_count * 60 / interval))
            show_content.append('  current torrent count: %d' %
                                self._current_meta_count)
            show_content.append('  total torrent count: %d' %
                                len(self._meta_list))
            show_content.append('\n')

            # 存储运行状态到文件
            try:
                with open(self._stat_file, 'wb') as f:
                    f.write('\n'.join(show_content))
                with open(self._result_file, 'wb') as f:
                    json.dump(self._meta_list, f)
            except Exception as err:
                pass

            # 测试是否到达退出时间
            if interval >= self._exit_time:
                # stop
                break

            # 每天结束备份结果文件
            self._backup_result()

        # 销毁p2p客户端
        for session in self._sessions:
            torrents = session.get_torrents()
            for torrent in torrents:
                session.remove_torrent(torrent)


运行效率

在我的一台512M内存,单cpu机器上。爬虫刚开始运行稍慢,运行几分钟后收集速度稳定在 180个每分钟,1小时采集10000左右。

运行状态

run times: 12

torrents:
  pid: 11480  time: 2014-08-18 22:45:01
  run time: day: 0, hour: 0, minute: 12, second: 25
  start port: 32900
  collect session num: 20
  info hash nums from get peers: 2222
  torrent collection rate: 179.098480 /minute
  current torrent count: 2224
  total torrent count: 58037


爬虫完整代码

完整的代码参见:https://github.com/blueskyz/DHTCrawler

还包括一个基于twisted的监控进程,用来查看爬虫状态,在爬虫进程退出后重新启动。


原文链接:python开发的 dht网络爬虫


© 著作权归作者所有

粉丝 12
博文 22
码字总数 16965
作品 0
浦东
架构师
私信 提问
加载中

评论(24)

李伟滨
File "collector.py", line 216, in <module>
sd.create_session(32900)
File "collector.py", line 106, in create_session
session.set_alert_mask(lt.alert.category_t.all_categories)
OverflowError: can't convert negative value to unsigned

我的一直提示这个。。
东昕

引用来自“selastmomo”的评论

哥~动动你的小手,帮助下一个起步学习python语言的孩子,看看这个错误怎么解决好么,debian64位7.0版本 PY2.7版本。
下面是错误信息
cp: 无法获取"result.json" 的文件状态(stat): 没有那个文件或目录
Traceback (most recent call last):
File "collector.py", line 216, in <module>
sd.create_session(32800)
File "collector.py", line 112, in create_session
settings = session.get_settings()
AttributeError: 'session' object has no attribute 'get_settings'


111
你使用的libtorrent版本是多少,是不是新的版本中get_settings这个方法改名字了?
selastmomo
selastmomo
哥~动动你的小手,帮助下一个起步学习python语言的孩子,看看这个错误怎么解决好么,debian64位7.0版本 PY2.7版本。
下面是错误信息
cp: 无法获取"result.json" 的文件状态(stat): 没有那个文件或目录
Traceback (most recent call last):
File "collector.py", line 216, in <module>
sd.create_session(32800)
File "collector.py", line 112, in create_session
settings = session.get_settings()
AttributeError: 'session' object has no attribute 'get_settings'


111
东昕

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?

引用来自“kingsOSZT”的评论

但是 迅雷 BT QQ旋风 不都是一样的道理么

引用来自“zgclark”的评论

对于下载来说一样,对于获取p2p网络的信息,外面的点不能udp发消息给你,你在内网,路由器需要有机制转发消息给你。

引用来自“kingsOSZT”的评论

哟西。。。。明白啦! 我了个去 难道下载 不应该跟获取种子信息 一个道理么?
下载是建立tcp连接,对外建立连接就可以了。获取信息是分布式dht网络协议,交换信息的方式不一样。你可以买个vps折腾下,一年也就一两百。
kingsOSZT
kingsOSZT

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?

引用来自“kingsOSZT”的评论

但是 迅雷 BT QQ旋风 不都是一样的道理么

引用来自“zgclark”的评论

对于下载来说一样,对于获取p2p网络的信息,外面的点不能udp发消息给你,你在内网,路由器需要有机制转发消息给你。
哟西。。。。明白啦! 我了个去 难道下载 不应该跟获取种子信息 一个道理么?
东昕

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?

引用来自“kingsOSZT”的评论

但是 迅雷 BT QQ旋风 不都是一样的道理么
对于下载来说一样,对于获取p2p网络的信息,外面的点不能udp发消息给你,你在内网,路由器需要有机制转发消息给你。
kingsOSZT
kingsOSZT

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?
但是 迅雷 BT QQ旋风 不都是一样的道理么
kingsOSZT
kingsOSZT

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?
是属于局域网的, 这么说 不能使用了???
kingsOSZT
kingsOSZT

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我

引用来自“zgclark”的评论

你用的什么机器? vps? 有公网ip么?
就是我自己电脑 win7 64
东昕

引用来自“kingsOSZT”的评论

任何协议任何端口都放过啦 还是一样的强迫关闭 OHYEAH 我认输了哥哥 快救我
你用的什么机器? vps? 有公网ip么?
基于 DHT 网络的磁力链接和BT种子的搜索引擎架构

上周开发了一个磁力链接和 BT 种子的搜索引擎 {Magnet & Torrent},本文简单介绍一下主要的系统功能和用到的技术。 系统包括几个独立的部分: 使用 Python 的 Scrapy 框架开发的网络爬虫,用...

justjavac
2015/02/01
3.8K
6
你写过 Web 爬虫, 那么你写过 P2P 爬虫吗?

网络爬虫爱好者为了爬取视频, 图片, 文档, 软件, 可能只想到写一个 Web 爬虫, 从各大网站爬取. 但是你知道吗? 这个世界上, 还有 DHT 协议, BitTorrent 协议. 想想看, 全世界每天有那么多人通...

CrazySpiderMan
2016/03/03
2.2K
13
5个python爬虫教材,让小白也有爬虫可写,含视频教程!

认识爬虫   网络爬虫,如果互联网是一张蜘蛛网,网络爬虫既是一个在此网上爬行的蜘蛛,爬了多少路程即获取到多少数据。 python写爬虫的优势   其实以上功能很多语言和工具都能做,但是用...

柯西带你学编程
2018/06/12
0
0
高手问答第 202 期 —— 想要玩转 Python?不妨从 Python 网络爬虫开始

OSCHINA 本期高手问答(2018 年 6 月 20 日 — 6 月 26 日)我们邀请到了黄永祥@XyHJw 和大家一起讨论关于 Python 爬虫的问题。 黄永祥,信息管理与信息系统专业学士,曾从事过系统开发和自动化...

局长
2018/06/19
5.5K
44
Python未来有哪几个最具有潜力发展方向?

近些年来,Python语言的热度越来越高,因为Python简单,学起来快,是不少新手程序员入门的首选语言。 Python是一门脚本语言,因为Python编程语言能将其他各种编程语言写的模块粘接在一起,所...

Python燕大侠
2018/06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
今天
4
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
6
0
hive

一、hive的定义: Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再在Hadoop平台上运行,达到快速开发的目的 Hive中的表是纯逻辑表,就只是表的定义,即表的元数据。本质就是Hadoop的目...

霉男纸
今天
3
0
二、Spring Cloud—Eureka(Greenwich.SR1)

注:本系列文章所用工具及版本如下:开发工具(IDEA 2018.3.5),Spring Boot(2.1.3.RELEASE),Spring Cloud(Greenwich.SR1),Maven(3.6.0),JDK(1.8) Eureka: Eureka是Netflix开发...

倪伟伟
昨天
13
0
eclipse常用插件

amaterasUML https://takezoe.github.io/amateras-update-site/ https://github.com/takezoe/amateras-modeler modelGoon https://www.cnblogs.com/aademeng/articles/6890266.html......

大头鬼_yc
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部