尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python自动化下载:利用qBittorrent WebAPI实现BT资源自动管理

Python自动化下载:利用qBittorrent WebAPI实现BT资源自动管理 1. 项目概述与核心价值最近在折腾家庭媒体库发现一个挺烦人的事儿每次在PT站或者资源论坛看到好片子都得手动复制磁力链或者下载种子文件再打开下载软件添加任务。有时候在外面用手机看到还得等回家才能操作黄花菜都凉了。这流程太不“自动化”了作为一个懒人程序员这能忍于是我把目光投向了手头一直在用的 qBittorrent。这玩意儿有个挺香的 WebUI用浏览器就能管理。一查文档果然它还提供了一套完整的 WebAPI。这不就相当于给 qBittorrent开了个后门允许我们用代码远程操控它吗用 Python 写个脚本监听 RSS 订阅、解析网页或者对接一些资源发布平台的 API一旦有符合条件的新资源出现脚本就能自动抓取链接然后通过 WebAPI 指挥 qBittorrent 开始下载。实现真正的“看到即下到”完全解放双手。这个项目的核心就是利用 Python 作为“大脑”和“协调员”通过 HTTP 请求与 qBittorrent 的 WebAPI “对话”将下载任务的管理从手动点击变为程序化、自动化的流程。它特别适合以下几种场景PT/BT 资源自动追更自动订阅剧集 RSS新集一出自动下载。资源聚合与自动抓取监控特定论坛板块或网站按规则如关键词、发布者、体积筛选并自动添加任务。远程下载管理在外通过手机触发家里的下载任务无需远程桌面。下载流程集成作为更大自动化流程的一环比如下载完成后自动调用媒体整理工具如 TinyMediaManager, FileBot进行重命名和入库。接下来我就带你从零开始拆解如何用 Python 玩转 qBittorrent 的 WebAPI实现自动化下载。我会把原理、踩过的坑、以及如何写得健壮都讲清楚。2. 环境准备与 qBittorrent 配置工欲善其事必先利其器。在写代码之前我们得先把“受控端” qBittorrent 配置好。2.1 qBittorrent 的安装与基础设置首先确保你安装的是qBittorrent Enhanced Edition或者官方原版并且版本不要太老建议 v4.3.0 以上以保证 WebAPI 的完整性和稳定性。Enhanced Edition 自带了一些隐私增强和优化我个人更推荐。安装完成后打开 qBittorrent进入工具 - 选项或Options - Preferences。启用 WebUI在左侧找到“Web UI”选项。勾选“启用 Web 用户界面 (远程控制)”。重要将“验证”方式旁的“绕过本地主机的身份验证”也勾选上。这样当你的 Python 脚本和 qBittorrent 运行在同一台电脑上时可以省去每次请求都携带 Cookie 的麻烦让脚本更简单。如果你需要从局域网内其他机器访问则不要勾选此项并务必设置好用户名密码。设置监听端口与 IP在 Web UI 设置页面你可以指定端口默认是 8080。记住这个端口后面代码里要用。“服务器域名”通常保持0.0.0.0即可表示监听所有网络接口。如果你只想本机访问可以改为127.0.0.1。设置用户名和密码强烈建议在 Web UI 设置页面的最上方设置一个用户名和密码。即使勾选了“绕过本地主机验证”从外部访问或者未来脚本调整时密码验证仍是重要的安全屏障。记下这个用户名和密码。应用并重启点击“应用”或“OK”qBittorrent 可能会提示需要重启。重启后WebUI 就生效了。你可以在浏览器里访问http://127.0.0.1:8080(端口换成你设置的) 来测试一下应该能看到和 qBittorrent 客户端类似的 Web 界面需要登录的话就用刚才设置的用户名密码。注意如果你将 qBittorrent 部署在 NAS、软路由或服务器上步骤类似但需要确保该设备的防火墙开放了对应的 WebUI 端口如 8080并且你知晓该设备的局域网 IP 地址。在代码中连接地址就要从127.0.0.1改为NAS的IP。2.2 Python 环境与库选择Python 环境没啥特殊要求3.6 以上版本都可以。我们主要需要一个库来方便地发送 HTTP 请求。虽然 Python 自带的urllib也能用但第三方库requests以其简洁优雅的 API 成为不二之选。安装非常简单pip install requests此外为了更优雅、更 Pythonic 地与 qBittorrent API 交互我强烈推荐使用一个第三方封装库qbittorrent-api。它把原始的 HTTP API 调用封装成了直观的类和方法大大降低了使用门槛并且能自动处理登录会话Cookie还有更好的错误处理。这是我们项目的“神器”。安装它pip install qbittorrent-api这个库的文档很完善我们后续的核心操作都将基于它。当然为了让你理解底层原理我也会简要提一下直接用requests如何实现。3. 核心 API 对接与封装一切就绪现在开始写代码。我们首先要解决的是如何让 Python 脚本“登录”到 qBittorrent 的 WebUI。3.1 认证登录获取管理门票qBittorrent 的 WebAPI 使用了基于 Cookie 的会话管理。我们需要先向登录接口发送凭证获得一个 Cookie后续的所有请求都要带上这个 Cookie 来表明身份。使用qbittorrent-api库这个过程简单到令人发指from qbittorrent import Client # 初始化客户端指定你的 qBittorrent WebUI 地址 qb Client(http://127.0.0.1:8080/) # 登录 try: qb.login(你的用户名, 你的密码) print(登录成功) except Exception as e: print(f登录失败: {e}) exit(1)如果登录成功qb这个客户端对象内部就已经维护好了登录状态。你可以通过qb.is_logged_in属性来检查。底层原理窥探如果不使用封装库用requests实现登录是这样的import requests session requests.Session() login_url http://127.0.0.1:8080/api/v2/auth/login # 注意这里的数据是 application/x-www-form-urlencoded 格式 data { username: 你的用户名, password: 你的密码 } resp session.post(login_url, datadata) if resp.text Ok.: print(登录成功) # 此时 session 会自动保存服务器返回的 Cookie else: print(登录失败, resp.text)可以看到封装库帮我们隐藏了接口路径、数据格式和会话管理的细节。3.2 关键 API 方法解析与封装登录之后我们就可以通过qb对象调用各种方法了。下面我挑几个最核心、最常用的功能来讲并分享一些封装技巧和注意事项。1. 添加下载任务qb.download_from_link这是最核心的功能。你可以添加磁力链、种子文件的 HTTP 链接甚至是种子文件的内容。# 添加一个磁力链接 magnet_link magnet:?xturn:btih:xxxx... try: qb.download_from_link(magnet_link) print(任务添加成功) except Exception as e: print(f添加失败: {e}) # 添加一个种子文件链接 torrent_url https://example.com/path/to/file.torrent qb.download_from_link(torrent_url) # 也可以直接读取本地的 .torrent 文件内容进行添加 with open(/path/to/your.torrent, rb) as f: torrent_content f.read() qb.download_from_file(torrent_content) # 注意这里是 download_from_file实操心得download_from_link方法默认会开始下载。如果你想添加后先暂停可以传入参数pauseTrue。例如qb.download_from_link(magnet_link, pauseTrue)。这在批量添加或需要人工检查后再开始的场景很有用。2. 获取任务列表与状态qb.torrents()这个方法返回一个字典列表每个字典代表一个下载任务包含了极其丰富的信息。# 获取所有任务 all_torrents qb.torrents() for torrent in all_torrents: print(f名称: {torrent[name]}) print(f哈希: {torrent[hash]}) # 每个任务的唯一标识 print(f进度: {torrent[progress] * 100:.2f}%) # progress是0-1的小数 print(f状态: {torrent[state]}) # 例如 ‘downloading’, ‘pausedDL’, ‘seeding’, ‘checking’ print(f下载速度: {torrent[dlspeed] / 1024:.2f} KB/s) print(f保存路径: {torrent[save_path]}) print(- * 30)你可以通过filter参数来筛选任务比如只获取正在下载的downloading qb.torrents(filterdownloading)其他 filter 选项还有‘completed’,‘paused’,‘active’,‘inactive’,‘resumed’等非常方便。3. 管理任务暂停、继续、删除有了任务的哈希值hash我们就可以精准控制它。task_hash 某个任务的hash值 # 暂停任务 qb.pause(task_hash) # 继续任务 qb.resume(task_hash) # 删除任务仅从列表中移除 qb.delete(task_hash) # 删除任务并删除文件慎用 qb.delete_permanently(task_hash)4. 设置任务属性分类、标签、保存路径自动化中对任务进行归类管理非常重要。task_hash 某个任务的hash值 # 为任务设置分类Category qb.set_category(task_hash, Movies) # 为任务添加标签Tags标签可以多个用逗号分隔 qb.add_tags(task_hash, [high_priority, chinese]) # 设置任务保存路径需要在 qBittorrent 设置中允许自动管理 qb.set_location(task_hash, /media/movies/)分类和标签是 qBittorrent 非常强大的功能结合其 RSS 下载器或我们的脚本可以实现诸如“自动将电影分类的任务下载到/volume1/video/movies/将剧集分类的下载到/volume1/video/tvshows/”这样的自动化规则。3.3 错误处理与连接稳健性网络请求总有可能失败。一个健壮的脚本必须考虑错误处理。请求超时与重试qbittorrent-api的Client在初始化时可以设置超时。from qbittorrent import Client import requests qb Client( http://127.0.0.1:8080/, timeout(10, 30) # (连接超时 读取超时) 单位秒 )对于重要的操作如添加任务你可以自己包装一个带重试的逻辑import time from requests.exceptions import RequestException def add_torrent_with_retry(qb_client, link, max_retries3): for i in range(max_retries): try: qb_client.download_from_link(link) return True except RequestException as e: print(f第{i1}次尝试失败: {e}) if i max_retries - 1: time.sleep(2) # 等待2秒后重试 else: print(f添加任务失败已重试{max_retries}次。) return False会话失效与重新登录长时间运行的脚本可能会遇到会话过期。一个简单的策略是在每次操作前检查登录状态或在捕获到特定的认证错误时重新登录。from qbittorrent import LoginFailed def safe_add_torrent(qb_client, link): if not qb_client.is_logged_in: try: qb_client.login(用户名, 密码) except LoginFailed: print(重新登录失败请检查凭证。) return try: qb_client.download_from_link(link) except Exception as e: # 如果是认证错误可以尝试重新登录一次再操作 if unauthorized in str(e).lower() or forbidden in str(e).lower(): print(会话可能失效尝试重新登录...) # 这里可以加入重新登录和重试的逻辑 else: raise e # 其他错误向上抛出4. 构建自动化流程从想法到实现掌握了核心的 API 调用我们就可以设计具体的自动化流程了。这里我提供两个最典型的场景作为案例你可以根据自己的需求组合和扩展。4.1 场景一RSS 订阅自动下载器很多 PT 站或资源站都提供 RSS 链接。我们可以用 Python 的feedparser库来解析 RSS然后结合 qBittorrent API 实现自动下载。步骤拆解获取并解析 RSS定期抓取 RSS 源解析出每条资源的信息标题、链接、发布时间。规则过滤根据标题中的关键词、体积、发布者等信息决定是否下载。去重判断避免重复下载已经添加过的任务。可以通过记录已处理过的资源唯一标识如 GUID或检查 qBittorrent 现有任务列表来实现。调用 API 添加任务。任务后续管理例如为不同 RSS 源的任务打上不同的标签。代码示例骨架import feedparser import time import re from qbittorrent import Client # 配置 RSS_URL 你的RSS链接 QB_URL http://127.0.0.1:8080/ QB_USER 用户名 QB_PASS 密码 KEYWORDS [1080p, BluRay, 特定小组名] # 需要包含的关键词 BLACKLIST [Sample, Trailer] # 需要排除的关键词 # 初始化客户端 qb Client(QB_URL) qb.login(QB_USER, QB_PASS) # 用于简单去重的集合持久化应该用文件或数据库 processed_guids set() def check_and_download(entry): 检查条目并决定是否下载 title entry.title link None # 1. 寻找磁力链或种子链接 # RSS 可能将链接放在不同字段 if hasattr(entry, links): for l in entry.links: if l.type application/x-bittorrent or magnet in l.href: link l.href break if not link and hasattr(entry, link): link entry.link # 回退到通用 link 字段 if not link or not link.startswith((magnet:, http)): print(f跳过 [{title}]未找到有效下载链接。) return False # 2. 关键词过滤 title_lower title.lower() # 必须包含所有所需关键词 if not all(kw.lower() in title_lower for kw in KEYWORDS): print(f跳过 [{title}]不符合关键词要求。) return False # 不能包含任何黑名单关键词 if any(bad.lower() in title_lower for bad in BLACKLIST): print(f跳过 [{title}]包含黑名单词汇。) return False # 3. 体积过滤示例解析标题中的体积如 ‘5.6 GB’ size_match re.search(r(\d\.?\d*)\s*(GB|GiB|MB|MiB), title, re.IGNORECASE) if size_match: size_num, unit float(size_match.group(1)), size_match.group(2).upper() if GB in unit or GIB in unit: size_gb size_num if GB in unit else size_num * 0.9313 # GiB转GB近似值 if size_gb 50: # 假设过滤掉大于50GB的文件 print(f跳过 [{title}]体积过大({size_gb:.1f}GB)。) return False # 4. 去重检查简单内存去重生产环境需持久化 if hasattr(entry, id): guid entry.id else: guid link # 用链接作为唯一标识 if guid in processed_guids: print(f跳过 [{title}]已处理过。) return False # 5. 添加下载任务 print(f符合条件正在添加: {title}) try: qb.download_from_link(link) # 可以添加分类或标签 # qb.set_category(某个hash, TV) # 注意添加后无法立即获取hash需要其他策略 processed_guids.add(guid) return True except Exception as e: print(f添加任务失败: {e}) return False # 主循环 def main_loop(): while True: print(f\n[{time.strftime(%Y-%m-%d %H:%M:%S)}] 开始检查 RSS...) feed feedparser.parse(RSS_URL) for entry in feed.entries: check_and_download(entry) print(f本轮检查完成等待下一轮。) time.sleep(300) # 等待5分钟300秒 if __name__ __main__: main_loop()4.2 场景二网页监控与资源抓取对于没有 RSS 的网站我们可以用requests和BeautifulSoup来抓取网页解析出下载链接。步骤拆解模拟请求使用requests获取目标网页 HTML注意处理可能需要的 headers如 User-Agent和 cookies。解析页面使用BeautifulSoup根据 HTML 结构定位包含资源链接的元素。链接提取与过滤从元素中提取出磁力链或种子页面的链接。有时需要二次请求种子页面才能拿到真正的下载链接。规则匹配和 RSS 场景类似根据页面文本标题、描述进行过滤。调用 API 添加任务。代码示例骨架以监控一个假想的论坛板块为例import requests from bs4 import BeautifulSoup import time import re from qbittorrent import Client # 配置 FORUM_URL https://example-forum.com/board-123.html QB_URL http://127.0.0.1:8080/ QB_USER 用户名 QB_PASS 密码 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } # 初始化 qb Client(QB_URL) qb.login(QB_USER, QB_PASS) session requests.Session() session.headers.update(HEADERS) # 存储已处理帖子ID防止重复 processed_posts set() def scrape_forum(): print(开始抓取论坛页面...) try: resp session.get(FORUM_URL, timeout10) resp.raise_for_status() except requests.RequestException as e: print(f获取页面失败: {e}) return soup BeautifulSoup(resp.text, html.parser) # 假设每个帖子标题在一个 classpost-title 的 a 标签里 post_links soup.find_all(a, class_post-title, hrefTrue) for link_tag in post_links: post_title link_tag.get_text(stripTrue) post_url link_tag[href] # 构造完整的帖子链接如果是相对路径 if not post_url.startswith(http): post_url requests.compat.urljoin(FORUM_URL, post_url) # 简单去重生产环境应用更精确的方法如帖子ID if post_url in processed_posts: continue # 关键词过滤示例只下载包含“完结”且是“某字幕组”的 if 完结 in post_title and 某字幕组 in post_title: print(f发现目标帖子: {post_title}) # 进入帖子详情页寻找磁力链 process_post_detail(post_url, post_title) processed_posts.add(post_url) time.sleep(1) # 礼貌性间隔避免请求过快 def process_post_detail(post_url, post_title): 处理帖子详情页提取下载链接 try: resp session.get(post_url, timeout10) resp.raise_for_status() except requests.RequestException as e: print(f获取帖子详情失败 [{post_title}]: {e}) return detail_soup BeautifulSoup(resp.text, html.parser) # 假设磁力链在 textarea 里或者是一个带有特定class的a标签 magnet_link None # 方法1找textarea textarea detail_soup.find(textarea, stringre.compile(rmagnet:\?)) if textarea: magnet_link textarea.get_text(stripTrue) # 方法2找包含magnet的链接 if not magnet_link: for a_tag in detail_soup.find_all(a, hrefTrue): if a_tag[href].startswith(magnet:): magnet_link a_tag[href] break if magnet_link: print(f 找到磁力链正在添加任务...) try: qb.download_from_link(magnet_link) print(f 任务添加成功: {post_title}) except Exception as e: print(f 添加任务失败: {e}) else: print(f 未在帖子中找到磁力链。) # 定时执行 while True: scrape_forum() print(f本轮抓取结束等待1小时后再次检查。) time.sleep(3600) # 等待1小时5. 进阶技巧与优化实践把基础功能跑通只是第一步要让这个自动化脚本稳定、可靠、易维护还需要一些进阶技巧。5.1 状态监控与通知集成脚本在后台默默运行我们怎么知道它干了啥有没有出错集成通知功能非常必要。日志记录使用 Python 内置的logging模块将运行信息、错误信息记录到文件方便排查。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(qb_auto_downloader.log), logging.StreamHandler() # 同时输出到控制台 ] ) logger logging.getLogger(__name__) logger.info(开始检查RSS...)消息推送成功添加任务或发生错误时推送到你的手机或聊天软件。Server酱 / PushDeer通过微信发送消息。只需向一个特定 URL 发送 HTTP 请求。Telegram Bot功能强大可以发送更结构化的消息。你需要先创建一个 Bot获取token和你的chat_id。import requests def send_telegram_message(message): token YOUR_BOT_TOKEN chat_id YOUR_CHAT_ID url fhttps://api.telegram.org/bot{token}/sendMessage data {chat_id: chat_id, text: message, parse_mode: Markdown} try: requests.post(url, datadata, timeout5) except Exception as e: logger.error(f发送Telegram通知失败: {e}) # 在添加任务成功后调用 send_telegram_message(f✅ 已添加新任务\n*{torrent_name}*)5.2 分类、标签与保存路径的自动化管理利用 qBittorrent 的分类和标签可以让下载管理井井有条。脚本可以在添加任务时根据规则自动设置。预定义分类先在 qBittorrent WebUI 的设置里创建好分类如Movies,TV-Shows,Music,Software并为每个分类设置好默认保存路径。脚本规则匹配在过滤资源时不仅判断是否下载还判断它属于哪个分类。def determine_category(title): title_lower title.lower() if any(x in title_lower for x in [.s01, .season, 季, ep]): return TV-Shows elif any(x in title_lower for x in [.bluray, .1080p, .2160p, 电影]): return Movies elif any(x in title_lower for x in [.flac, .album, 音乐]): return Music else: return Other # 默认分类添加任务时指定分类遗憾的是qbittorrent-api的download_from_link方法目前不支持直接指定分类。一个变通的方法是先添加任务不指定分类。立刻短暂等待后获取任务列表通过匹配标题或哈希如果 RSS 条目能提供种子哈希的话找到刚添加的任务。对该任务调用set_category方法。 这个过程有点绕而且有竞争条件风险如果同时添加多个任务。更稳健的做法是利用 qBittorrent 的“下载器”功能但这需要修改 qBittorrent 的配置。对于简单需求先添加后设置分类在大多数情况下是可行的。5.3 脚本的部署与常驻运行你不可能一直开着电脑和 IDE 来跑脚本。如何让它 7x24 小时运行Windows可以写一个.bat脚本然后用“任务计划程序”设置为开机启动或定时启动。或者将 Python 脚本打包成.exe使用pyinstaller然后将其注册为系统服务使用nssm。Linux/macOS最佳实践是使用Systemd或Supervisor来管理。Systemd 服务示例(/etc/systemd/system/qb-auto-dl.service)[Unit] DescriptionQBittorrent Auto Downloader Afternetwork.target [Service] Typesimple User你的用户名 WorkingDirectory/path/to/your/script ExecStart/usr/bin/python3 /path/to/your/script/auto_downloader.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target然后使用sudo systemctl enable --now qb-auto-dl.service启用并启动。Supervisor配置更灵活管理多个进程更方便适合有多个监控脚本的情况。6. 常见问题与排查实录在实际搭建和运行过程中你几乎一定会遇到下面这些问题。我把我的踩坑记录和解决方案分享给你。6.1 连接与认证问题问题LoginFailed错误或403 Forbidden。检查1确认 WebUI 已启用且端口正确。在浏览器访问http://ip:port测试。检查2确认用户名密码正确。注意 qBittorrent WebUI 的密码可能和客户端 GUI 的密码不同。检查3如果脚本和 qBittorrent 不在同一台机器确保关闭了“绕过本地主机验证”并且防火墙放行了端口。检查4如果 qBittorrent 版本很新v4.6检查是否启用了“启用跨站请求伪造 (CSRF) 保护”。如果启用需要确保你的请求头中包含正确的Referer。使用qbittorrent-api库通常会自动处理。问题连接超时。检查网络是否通畅qBittorrent 进程是否正常运行。尝试增加Client初始化时的timeout参数。6.2 任务添加失败问题问题添加磁力链成功但任务一直处于“元数据下载中”或“暂停”状态不开始下载。分析磁力链需要先通过 DHT 网络获取种子元信息Tracker 列表、文件结构。如果网络环境 DHT 被屏蔽或者该磁力链热度极低就可能卡住。解决尝试添加公共 Tracker 列表到任务中通过 WebUI 或 APIadd_trackers。优先使用种子文件.torrent链接稳定性远高于磁力链。检查 qBittorrent 的 DHT、PeX、本地用户发现等选项是否开启。问题添加任务时提示“无效的种子文件”或“不支持此链接”。检查链接是否确实是有效的磁力链以magnet:?xturn:btih:开头或种子文件直链。如果是网页链接需要先抓取到真正的.torrent文件下载地址。6.3 脚本逻辑与性能问题问题脚本运行一段时间后内存占用越来越高。分析可能是没有正确释放请求资源或者在循环中不断积累数据如processed_guids集合无限增长。解决使用with语句管理requests会话或响应对象。对于去重集合可以定期清理非常旧的条目或者使用小型数据库如 SQLite来持久化记录。确保你的解析器如 BeautifulSoup在处理完大量 HTML 后能被垃圾回收。问题规则过滤不准确误下载或漏下载。解决正则表达式和关键词匹配是门艺术。多测试使用更精确的匹配模式。例如匹配“1080p”时最好用正则r\b1080p\b来避免匹配到“1080p60”之类。考虑引入更复杂的规则引擎或者对标题进行分词后再匹配。问题如何获取刚添加任务的hash以便后续设置分类这是一个痛点。WebAPI 的/api/v2/torrents/add接口在添加成功后并不返回 hash。常见的变通方案是延迟匹配添加任务后等待几秒钟然后获取全部任务列表通过对比添加时间、保存路径或名称来找出最可能的新任务。不精确有风险。哈希预计算对于.torrent文件你可以用 Python 的bencode库读取并计算其 info_hash即任务 hash。这样在添加前你就知道了 hash。但对于磁力链此方法无效。利用保存路径为不同的监控源设置不同的临时保存路径添加后通过路径来筛选新任务。这需要你在 qBittorrent 中设置多个默认保存路径。6.4 安全与隐私考量不要将脚本和配置文件上传到公开的 GitHub 仓库尤其是里面包含你的 qBittorrent 登录密码、RSS 密钥、Telegram Bot Token 等敏感信息。使用配置文件如config.ini或.env文件并将其加入.gitignore。为 qBittorrent WebUI 设置强密码并考虑将其放在反向代理如 Nginx之后配置 HTTPS 和额外的 HTTP 基础认证增加一层安全防护。监控脚本的运行日志定期检查是否有异常添加任务的情况防止因规则错误或源污染导致下载了不想要的内容。最后这个项目的乐趣在于“创造自动化”。一开始可能只是为了自动下几部剧但随着你不断加入新的规则、集成新的通知方式、优化错误处理它会逐渐变成一个高度定制化、完全贴合你个人需求的数字管家。每当看到它又默默帮你下好了期待已久的资源那种成就感就是折腾技术的最大回报。
返回列表