磁力链接(Magnet Links)是一种流行的用于共享文件的链接格式。与传统的种子文件不同,磁力链接直接包含文件的哈希值,使得用户无需下载种子文件就能开始下载文件。在这篇教程中,我们将介绍如何使用 Python 和 Aria2 异步下载和解析磁力链接的元数据,并提取出文件的详细信息。
在开始之前,请确保你已经安装了以下工具和库:
1. **Aria2**:一个轻量级多协议和多源命令行下载工具。你可以从 [Aria2 官方网站](https://aria2.github.io/) 下载并安装。
2. **Python**:本教程使用 Python 3.7 及以上版本。你可以从 [Python 官方网站](https://www.python.org/) 下载并安装。
3. **bencodepy**:用于解析 `.torrent` 文件的 Python 库。你可以使用以下命令安装它:
pip install bencodepy
接下来,我们将编写一个 Python 脚本,分为以下几个部分:
import asyncio
import subprocess
import bencodepy
import os
import re
def is_magnet_link(msg: str) -> bool:
magnet_pattern = re.compile(r'magnet:\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,}')
return bool(magnet_pattern.search(msg))
async def download_torrent_from_magnet(magnet_link, save_path='.'):
aria2c_path = r'E:\Aria2\aria2c.exe'
aria2c_command = [
aria2c_path,
magnet_link,
'--dir=' + save_path,
'--bt-metadata-only=true',
'--bt-save-metadata=true'
]
process = await asyncio.create_subprocess_exec(
*aria2c_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = await process.communicate()
if process.returncode == 0:
print(f"元数据下载成功: {magnet_link}")
btih = magnet_link.split(':')[3].split('&')[0].replace('btih=', '').upper()
torrent_file = os.path.join(save_path, f"{btih}.torrent")
return torrent_file
else:
print(f"下载元数据失败: {magnet_link}")
print(f"错误信息: {stderr.decode()}")
return None
def parse_torrent_file(torrent_file):
with open(torrent_file, 'rb') as f:
torrent_data = bencodepy.decode(f.read())
info = torrent_data[b'info']
name = info[b'name'].decode('utf-8')
if b'files' in info:
files = [{'path': os.path.join(name, *[part.decode('utf-8') for part in file[b'path']]), 'size': file[b'length']} for file in info[b'files']]
else:
files = [{'path': name, 'size': info[b'length']}]
return {'name': name, 'files': files}
def format_size(size_in_bytes):
if size_in_bytes < 1024:
return f"{size_in_bytes} B"
elif size_in_bytes < 1024 ** 2:
return f"{size_in_bytes / 1024:.2f} KB"
elif size_in_bytes < 1024 ** 3:
return f"{size_in_bytes / 1024 ** 2:.2f} MB"
else:
return f"{size_in_bytes / 1024 ** 3:.2f} GB"
async def mage_main(magnet_links: list, save_path: str=r"E:\Aria2\dows") -> list:
tasks = [download_torrent_from_magnet(magnet_link, save_path) for magnet_link in magnet_links]
torrent_files = await asyncio.gather(*tasks)
data_l = []
for magnet_link, torrent_file in zip(magnet_links, torrent_files):
if torrent_file:
metadata = parse_torrent_file(torrent_file)
name = metadata['name'].replace('\\', '/')
bt_data = {name: []}
for file in metadata['files']:
files = {'size': format_size(file['size']), 'path': file['path'].replace('\\', '/')}
bt_data[name].append(files)
data_l.append([magnet_link, bt_data])
else:
data_l.append([magnet_link, None])
return data_l
if __name__ == "__main__":
magnet_links = [
'magnet:?xt=urn:btih:磁力链接'
# 你可以添加更多磁力链接
]
save_path = r"E:\Aria2\dows" # 替换为你希望保存bt种子的路径
results = asyncio.run(mage_main(magnet_links, save_path))
for result in results:
print(result)
保存以上代码为 `torrent_downloader.py` 文件。在命令行中导航到该文件所在的目录,并运行以下命令:
python torrent_downloader.py
如果你设置的磁力链接有效,脚本将下载磁力链接的元数据,并解析 `.torrent` 文件,输出文件的详细信息。
import asyncio
import subprocess
import bencodepy
import os
import redef is_magnet_link(msg: str) -> bool:
"""
判断给定字符串是否为磁力链接参数:
msg (str): 待检测的字符串返回:
bool: 如果是磁力链接返回 True,否则返回 False
"""
magnet_pattern = re.compile(r'magnet:\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,}')
return bool(magnet_pattern.search(msg))def extract_magnet_links(text: str) -> list:
"""
从给定文本中提取所有磁力链接参数:
text (str): 包含磁力链接的文本返回:
list: 提取出的磁力链接列表
"""
magnet_pattern = re.compile(r'magnet:\?xt=urn:btih:[a-zA-Z0-9]{32,}')
magnet_links = magnet_pattern.findall(text)
return magnet_linksasync def download_torrent_from_magnet(magnet_link: str, save_path: str='.') -> str:
"""
使用 Aria2 下载磁力链接的种子文件并保存参数:
magnet_link (str): 磁力链接
save_path (str): 保存种子文件的路径,默认为当前目录返回:
str: 下载的种子文件路径,如果下载失败返回 None
"""
aria2c_path = r'E:\Aria2\aria2c.exe' # Aria2 的路径aria2c_command = [
aria2c_path,
magnet_link,
'--dir=' + save_path,
'--bt-metadata-only=true',
'--bt-save-metadata=true'
]process = await asyncio.create_subprocess_exec(
*aria2c_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)stdout, stderr = await process.communicate()
if process.returncode == 0:
print(f"元数据下载成功: {magnet_link}")
btih = magnet_link.split(':')[3].split('&')[0].replace('btih=', '').upper()
torrent_file = os.path.join(save_path, f"{btih}.torrent")
return torrent_file
else:
print(f"下载元数据失败: {magnet_link}")
print(f"错误信息: {stderr.decode()}")
return Nonedef parse_torrent_file(torrent_file: str) -> dict:
"""
解析种子文件,提取文件信息参数:
torrent_file (str): 种子文件路径返回:
dict: 种子文件包含的文件信息
"""
with open(torrent_file, 'rb') as f:
torrent_data = bencodepy.decode(f.read())
info = torrent_data[b'info']
name = info[b'name'].decode('utf-8')
if b'files' in info:
files = [{'path': os.path.join(name, *[part.decode('utf-8') for part in file[b'path']]), 'size': file[b'length']} for file in info[b'files']]
else:
files = [{'path': name, 'size': info[b'length']}]
return {'name': name, 'files': files}def format_size(size_in_bytes: int) -> str:
"""
格式化文件大小参数:
size_in_bytes (int): 文件大小(字节)返回:
str: 格式化后的文件大小字符串
"""
if size_in_bytes < 1024:
return f"{size_in_bytes} B"
elif size_in_bytes < 1024 ** 2:
return f"{size_in_bytes / 1024:.2f} KB"
elif size_in_bytes < 1024 ** 3:
return f"{size_in_bytes / 1024 ** 2:.2f} MB"
else:
return f"{size_in_bytes / 1024 ** 3:.2f} GB"async def mage_main(magnet_links: list, save_path: str=r"E:\Aria2\dows") -> list:
"""
主函数,处理一组磁力链接,下载种子文件并提取文件信息参数:
magnet_links (list): 磁力链接列表
save_path (str): 保存种子文件的路径返回:
list: 包含每个磁力链接对应的文件信息
"""
tasks = [download_torrent_from_magnet(magnet_link, save_path) for magnet_link in magnet_links]
torrent_files = await asyncio.gather(*tasks)
data_l = []
for magnet_link, torrent_file in zip(magnet_links, torrent_files):
if torrent_file:
metadata = parse_torrent_file(torrent_file)
name = metadata['name'].replace('\\', '/')
bt_data = {name: []}
for file in metadata['files']:
files = {'size': format_size(file['size']), 'path': file['path'].replace('\\', '/')}
bt_data[name].append(files)
data_l.append([magnet_link, bt_data])
else:
data_l.append([magnet_link, None])
return data_lif __name__ == "__main__":
magnet_links = [
'magnet:?xt=urn:btih:你的磁力链'
# 你可以添加更多磁力链接
]
save_path = r"E:\Aria2\dows"
results = asyncio.run(mage_main(magnet_links, save_path))
for result in results:
print(result)