Python边学边用--BT客户端实现之BitTorrent文件解析

2023-11-13

BitTorrent文件使用bencode编码,其中包括了4种数据类型:

'd' 开头表示是dict类型,'e'表示结束

'l' (小写字母L)开头表示是list类型,'e'表示结束

'i'开头表示是integer类型,'e'表示结束,可以表示负数

以数字开头表示string类型,数字为string长度,长度与string内容以':'分割

默认所有text类型的属性为utf-8编码,但是大多数BitTorrent包含codepage 和 encoding属性,指定了text的编码格式

BitTorrent的标准参见:http://www.bittorrent.org/beps/bep_0003.html

以下是自己写的Python实现,初学Python,代码写起来还都是C/C++风格,慢慢改进吧。

 

 torrent_file.py
import os
from datetime import tzinfo
from datetime import datetime

import bcodec

_READ_MAX_LEN = -1

class BTFormatError(BaseException):
    pass
    
class TorrentFile(object):
    
    __metainfo = {}
    __file_name = ''
    
    def read_file(self, filename):
        
        torrent_file = open(filename, 'rb')
        data = torrent_file.read(_READ_MAX_LEN)
        torrent_file.close()
        
        data = list(data)
        metainfo = bcodec.bdcode(data)
        if metainfo and type(metainfo) == type({}):
            self.__file_name = filename
            self.__metainfo = metainfo
        else:
            raise BTFormatError()
           
    def __is_singlefile(self):
        return 'length' in self.__metainfo.keys()
    
    def __decode_text(self, text):
        encoding = 'utf-8'
        resultstr = ''
        if self.get_encoding():
            encoding = self.get_encoding()
        elif self.get_codepage():
            encoding = 'cp' + str(self.get_codepage())
        if text:
            try:
                resultstr = text.decode(encoding=encoding)
            except ValueError:
                return text
        else:
            return None
        return resultstr
    
    def __get_meta_top(self, key):
        if key in self.__metainfo.keys():
            return self.__metainfo[key]
        else:
            return None
    def __get_meta_info(self,key):
        meta_info = self.__get_meta_top('info')
        if meta_info and key in meta_info.keys():
                return meta_info[key]
        return None
    
    def get_codepage(self):
        return self.__get_meta_top('codepage')
    def get_encoding(self):
        return self.__get_meta_top('encoding')
    
    def get_announces(self):
        announces = []
        ann = self.__get_meta_top('announce')
        if ann:
            ann_list = []
            ann_list.append(ann)
            announces.append(ann_list)
        announces.append(self.__get_meta_top('announce-list'))
        return announces
    
    def get_publisher(self):
        return self.__decode_text(self.__get_meta_top('publisher'))
    def get_publisher_url(self):
        return self.__decode_text(self.__get_meta_top('publisher-url'))
    
    def get_creater(self):
        return self.__decode_text(self.__get_meta_top('created by'))
    def get_creation_date(self):
        utc_date = self.__get_meta_top('creation date')
        if utc_date is None:
            return utc_date
        creationdate = datetime.utcfromtimestamp(utc_date)
        return creationdate
    def get_comment(self):
        return self.__get_meta_top('comment')
          
    def get_nodes(self):
        return self.__get_meta_top('nodes')
    
    def get_piece_length(self):
        return self.__get_meta_info('piece length')
    
    def get_files(self):
        
        files = []
        pieces = self.__get_meta_info('pieces')
        name = self.__decode_text(self.__get_meta_info('name'))
        piece_length = self.get_piece_length()
        
        if not pieces or not name:
            return files
        
        if self.__is_singlefile():
            file_name = name
            file_length = self.__get_meta_info('length')
            if not file_length:
                return files
            
            pieces_num = file_length/piece_length
            if file_length % piece_length:
                pieces_num = int(pieces_num) + 1
            if 20*pieces_num > len(pieces):
                return  files
                           
            file_pieces = []
            i = 0
            pn = 0
            while pn < pieces_num:
                file_pieces.append(pieces[i:i+20])
                i += 20
                pn += 1
            
            files.appen({'name':[file_name], 'length':file_length, 'peaces':file_pieces})
            return files
        

        folder = name
        meta_files = self.__get_meta_info('files')
        if not meta_files:
            return files
        
        total_length = 0
        for one_file in self.__get_meta_info('files'):
            
            file_info = {}
            path_list = []
            path_list.append(folder)
                        
            if 'path' not in one_file.keys():
                break
            for path in one_file['path']:
                path_list.append(self.__decode_text(path))
            file_info['name'] = path_list
            
            if 'length' not in one_file.keys():
                break
            
            file_info['length'] =  one_file['length']
            
            piece_index = int(total_length / piece_length)
            total_length += one_file['length']
            pieces_num = int(total_length / piece_length) - piece_index
            pieces_num = int(file_info['length']/piece_length)
            
            if total_length % piece_length:
                pieces_num += 1
            
           # print  (piece_index+pieces_num)*20, len(pieces),pieces_num,file_info['length'], self.get_piece_length()
            
            if (piece_index+pieces_num)*20 > len(pieces):
                break
            
            file_info['pieces'] = []
            
            pn = 0
            while pn < pieces_num:
                file_info['pieces'].append(pieces[piece_index*20:piece_index*20+20])
                pn += 1

            files.append(file_info)
            
        return files
    
if __name__ == '__main__':
    #filename = r".\huapi2.torrent"
    #filename = r".\mh5t3tJ0EC.torrent"
    filename = r".\huapi2.1.torrent"   
    torrent = TorrentFile()

    print "begin to read file"
    try:
        torrent.read_file(filename)
    except (IOError,BTFormatError), reason:
        print "Read bittorrent file error! Error:%s" %reason
     
    print "end to read file"

    print "announces: " , torrent.get_announces() 
    print "peace length:", torrent.get_piece_length()
    print "code page:" , torrent.get_codepage()
    print "encoding:" , torrent.get_encoding()
    print "publisher:" ,torrent.get_publisher()
    print "publisher url:", torrent.get_publisher_url()
    print "creater:" , torrent.get_creater()
    print "creation date:", torrent.get_creation_date()
    print "commnent:", torrent.get_comment()
    print "nodes:", torrent.get_nodes()
    torrent.get_files()
    for one_file in torrent.get_files():
        print 'file name:', '\\'.join(one_file['name'])
        print 'file length:', one_file['length']
        print 'pieces:', list(one_file['pieces'])
bcodec.py
  1 '''
  2 Created on 2012-9-30
  3 
  4 @author: ddt
  5 '''
  6 def bdcode(data):
  7     data = list(data)
  8     return _read_chunk(data)
  9     
 10 def _read_chunk(data):
 11     
 12     chunk = None
 13     
 14     if len(data) == 0:
 15         return chunk
 16     
 17     leading_chr = data[0]
 18                      
 19     if leading_chr.isdigit():
 20         chunk = _read_string(data)
 21     elif leading_chr == 'd':
 22         chunk = _read_dict(data)
 23     elif leading_chr == 'i':
 24         chunk = _read_integer(data)
 25     elif leading_chr == 'l':
 26         chunk = _read_list(data)
 27 
 28     #print leading_chr, chunk
 29     return chunk
 30                            
 31 def _read_dict(data):
 32     
 33     if  len(data) == 0 or data.pop(0) != 'd': 
 34         return None
 35     
 36     chunk = {} 
 37     while len(data) > 0 and data[0] != 'e':
 38         
 39         key = _read_chunk(data)
 40         value = _read_chunk(data)
 41         
 42         if key and value and type(key) == type(''):
 43             chunk[key] = value
 44         else:
 45             return None
 46         
 47     if len(data) == 0 or data.pop(0) != 'e':
 48         return None
 49     
 50     return chunk
 51 
 52 def _read_list(data):
 53 
 54     if  len(data) == 0 or data.pop(0) != 'l': 
 55         return None
 56     
 57     chunk = []
 58     while len(data) > 0 and data[0] != 'e':
 59         value = _read_chunk(data)
 60         if value:
 61             chunk.append(value)
 62         else:
 63             return None
 64         
 65     if len(data) == 0 or data.pop(0) != 'e': 
 66         return None
 67 
 68     return chunk
 69 
 70 def _read_string(data):
 71     
 72     str_len = ''
 73     while len(data) > 0 and data[0].isdigit():
 74         str_len +=  data.pop(0)
 75     
 76     if len(data) == 0 or data.pop(0) != ':':
 77         return None
 78     
 79     str_len = int(str_len)
 80     if str_len > len(data):
 81         return None
 82     
 83     value = data[0:str_len]
 84     del data[0:str_len]
 85     return ''.join(value)
 86 
 87 def _read_integer(data):
 88    
 89     integer = ''
 90     if len(data) < len('i2e') or data.pop(0) != 'i': 
 91         return None
 92     
 93     sign = data.pop(0)
 94     if sign != '-' and not sign.isdigit():
 95         return None
 96     integer += sign
 97     
 98     while len(data) > 0 and data[0].isdigit():
 99         integer += data.pop(0)
100     
101     if len(data) == 0 or data.pop(0) != 'e':
102         return None
103 
104     return  int(integer)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python边学边用--BT客户端实现之BitTorrent文件解析 的相关文章

随机推荐

  • 如何用多线程执行 unittest 测试用例实现方案

    前言 使用python做过自动化测试的小伙伴 想必都知道unittest和pytest这两个单元测试框架 其中unittest是python的官方库 功能相对于pytest来要逊色不少 但是uniitest使用上手简单 也受到的很多的小伙伴
  • 企业应该选择哪种区块链

    随着探索如何把区块链应用在各种场景 许多人就想到 也许不需要全世界的人共同参与 也不需要挖矿 我们只需要用到区块链的可信任 可追溯特性 通过较少节点达到拜占庭将军容错 于是私有链就诞生了 但私有链仍是中心化的 难以维持去中心化的优势 因此又
  • webdriver版本不匹配,重新下载webdriver后不知道应该放在哪个文件夹

    1 从官网上面按照对应的版本下载了对应浏览器的webdriver版本 我的是chrome windows 113开头的版本 2 下载并解压之后发现有很多个webdriver exe的程序 正确步骤是解压后复制exe文件放在原来的文件夹即可
  • Ubuntu根目录文件作用分析

    Ubuntu Linux的文件结构与Windows的文件结构不同 Windows将硬盘分成 等盘 也就是分成这些分区 而Linux操作系统不是把硬盘分 成这样的分区 它有一个根目录 用 表示 一个目录就相当于一个文件夹 根目录就相当于Lin
  • 360周鸿祎:互联网好产品六字法则——刚需、痛点、高频

    如何找到好的产品 它必须满足三个条件 刚需 痛点 高频 6月6号 奇虎360创始人董事长兼CEO周鸿祎走上颠覆式创新研习社的讲台 以他的产品经历 带来移动互联网产品观 干货满满 全程无尿点 课程实录分为上 下两部分 研习社根据演讲整理 未经
  • vue Antd单独隐藏Modal.confirm(this.$confirm方式)对话框的默认ok或cancel按钮

    有时候我们需要单独隐藏Modal对话框的默认确定或取消按钮 设置 footer null 会把两个按钮都隐藏 Antd有提供两个参数用于单独修改确定 取消按钮 对于确定按钮 设置 ok button props style display
  • STM32学习记录——74HC595四位数码管显示

    数模管作为STM32的一个重要外设 由于其成本低 稳定 被用于许多场景中 本篇文章来介绍下四位数码管的使用方法 数码管显示 一 数码管的分类 二 74HC595芯片 串入并出 三 原理图 四 代码主要操作 五 代码分析 1 void HC5
  • PyCharm安装

    lt 一 gt PyCharm安装 下载地址 http www jetbrains com pycharm download section windows professional 表示专业版 community 是社区版 推荐安装社区版
  • CSS3 Flexbox轻松实现元素的水平居中和垂直居中

    网上有很多关于Flex的教程 对于Flex的叫法也不一 有的叫Flexbox 有的叫Flex 其实这两种叫法都没有错 只是Flexbox旧一点 而Flex是刚出来不久的东西而已 为了方便说明 赶上新技术 下面我就把这种布局叫Flex布局 元
  • Unity --- 动画脚本

    1 什么是动画 在Unity中 首先如果游戏物体想要在场景中动起来的话 就必须使其对应组件中的属性发生合适的变化 而将属性的变化过程保存下来后形成的就是动画 以后如果想播放动画的话 只需要调用这段保存下来的属性变化就可以了 二 基础知识部分
  • 内存管理之一__align字节对齐

    转 http www cnblogs com ye moooooo p 4601189 html 一 什么是字节对齐 为什么要对齐 现代计算机中内存空间都是按照byte划分的 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始 但实际情
  • 日志 - 客户端及服务端写法

    一 客户端 先来看一个日志类的实现方法 这个日志类也是代表着大多数客户端日志的主流写法 log h 1 ifndef LOG H 2 define LOG H 3 4 include
  • vue-echarts自适应大小的实现

    vue echarts图表自适应用到了vue resize 完整流程为 1 下载vue resize npm i vue resize S 2 main js中引入 import ResizeObserver from vue resize
  • gtj2018如何生成工程量报表_广联达BIM土建计量平台GTJ2018

    一 概述 建造整个世界 度量每种可能 相比13产品 100 项的增加与优化 量筋合 业务扩展10 以上 效率提升20 30 土建BIM模型数据上下游无缝连接 量筋合一 一次建模 无需互导 土建计量业务范围更广 工作效率提升更高 效率快 量筋
  • 【前端】Vue项目:旅游App-(22)detail:房屋信息、房屋设施、插槽

    文章目录 目标 过程与代码 房屋信息部分info 相似结构的组件section 房屋设施facility 效果 总代码 修改或添加的文件 detail section detail facility detail info detail 参
  • 通俗易懂的告诉你,https是如何保证数据安全的

    HTTP存在的问题 窃听风险 通信使用明文 不加密 内容可能会被窃听 第三方可能获知通信内容 冒充风险 不验证通信方的身份 因此有可能遭遇伪装 篡改风险 无法证明报文的完整性 所以有可能已遭篡改 HTTPS HTTPS网站 可以看到 HTT
  • redis数据类型(图解)

    一些命令的使用场景 1 由于集合是无序的 spop命令可以用于抽奖活动 2 商品销量排行榜可以用zset做 3 setnx命令实现分布式锁 4 消息队列模型 lpush rpop 以上是本人的学习笔记 若有不对的地方 请大家指正 转载于 h
  • 【多模态】3、CLIP

    文章目录 一 背景 二 方法 2 1 使用自然语言来监督训练 2 2 建立一个超大数据集 2 3 选择预训练的方式 对比学习而非预测学习 2 4 模型缩放和选择 三 效果 四 思考 论文 Learning Transferable Visu
  • python函数编程 返回函数 匿名函数 装饰器 偏函数

    返回函数 函数的返回对象可以是一个函数 返回时不立即执行 而是调用返回对象的时候再执行 def lazy sum args def sum ax 0 for n in args ax ax n return ax return sum gt
  • Python边学边用--BT客户端实现之BitTorrent文件解析

    BitTorrent文件使用bencode编码 其中包括了4种数据类型 d 开头表示是dict类型 e 表示结束 l 小写字母L 开头表示是list类型 e 表示结束 i 开头表示是integer类型 e 表示结束 可以表示负数 以数字开头