3个坑搞懂哔哩哔哩怎么删除投稿避坑指南
版本升级后 API 全变了,很多老脚本直接报错 403 或 400,这是无数开发者踩过的深坑。别急着重写,先看看这份避坑指南,我们直接用 Python 逆向工程思路,从零搭建一个稳定可控的投稿管理工具。很多教程只告诉你“点删除”,但没告诉你后台接口长什么样,一旦手动操作失误,或者想批量清理历史废稿,手动点击根本来不及。
项目目标与场景痛点
在开始写代码之前,我们要明确这个实战项目到底解决什么问题。对于技术博主或培训机构来说,B 站投稿不是“发完即止”的黑盒,而是一个需要持续维护的内容资产库。
核心痛点非常具体:
- 手动操作效率低:如果你有 50 个过期的教程视频,一个个点进去删除,光鼠标点击就要半小时,还容易手抖误删正常视频。
- 状态同步滞后:有时候你明明点了删除,但前端列表还显示存在,实际上是进入了“回收站”状态,接口状态和 UI 状态不一致,导致后续自动化脚本判断失败。
- Cookie 有效期短:B 站对登录态校验极其严格,普通的
requests库如果不处理 Referer 和 User-Agent,很容易触发风控,导致接口返回空数据或 412 错误。
我们的目标不是做一个花哨的 GUI 软件,而是构建一个可复用、可监控、具备异常处理能力的 CLI 工具。它能通过命令行参数指定视频 ID,自动完成鉴权、查询、删除、状态验证的全流程。这对于需要批量清理测试视频的开发团队,或者需要定期归档内容的自媒体运营者,极具实战价值。
目录结构设计
工程化思维要求代码结构清晰,便于维护和扩展。我们采用扁平化结构,因为这是一个轻量级工具,不需要复杂的分层架构。
bilibili-manager/
├── main.py # 入口文件,处理命令行参数
├── config.py # 配置管理,加载 Cookie 和环境变量
├── core/
│ ├── __init__.py
│ ├── api_client.py # 封装 HTTP 请求,处理鉴权与重试
│ └── video_manager.py # 核心业务逻辑,执行删除与状态检查
├── utils/
│ ├── __init__.py
│ ├── logger.py # 日志记录,方便排查问题
│ └── validator.py # 参数校验,防止非法输入
├── requirements.txt # 依赖管理
└── README.md # 使用说明
设计思路解析:
api_client.py是解耦的关键。我们将所有的 HTTP 请求细节封装在这里,包括 Header 构建、超时设置、重试机制。这样当 B 站调整接口参数时,我们只需要修改这一个文件,而不必改动业务逻辑。video_manager.py专注业务。它只关心“删除”这个动作,不关心底层怎么发请求。这种分离让代码更易测试。config.py使用环境变量管理敏感信息。不要把 Cookie 硬编码在代码里,这是安全红线。
核心代码实现
这里是干货部分。我们将重点讲解如何绕过 B 站的风控,以及如何正确调用删除接口。
1. 配置与鉴权模块
B 站的 API 强依赖 Cookie 中的 SESSDATA。我们需要从环境变量读取,并构建标准的请求头。
# config.py
import os
from dotenv import load_dotenv# 加载 .env 文件
load_dotenv()class Config:# 从环境变量获取,避免硬编码SESSDATA = os.getenv("BILI_SESSDATA", "")BASE_URL = "https://api.bilibili.com"# 标准 User-Agent,模拟浏览器环境USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"# 请求超时时间,单位秒TIMEOUT = 10@classmethoddef get_headers(cls):"""构建请求头,Referer 和 Origin 是风控关键点"""return {"User-Agent": cls.USER_AGENT,"Referer": "https://member.bilibili.com/","Origin": "https://member.bilibili.com","Cookie": f"SESSDATA={cls.SESSDATA}","Content-Type": "application/x-www-form-urlencoded"}
避坑点: 很多开发者只设置了 Cookie,忽略了 Referer。B 站会校验请求来源,如果没有正确的 Referer,接口会直接拒绝。此外,Content-Type 必须设置为 urlencoded,因为 B 站的传统接口大多使用表单提交,而不是 JSON。
2. API 客户端封装
我们需要一个健壮的 HTTP 客户端,能够处理网络波动和简单的重试。
# core/api_client.py
import requests
import time
import logging
from config import Configlogger = logging.getLogger(__name__)class APIClient:def __init__(self):self.session = requests.Session()self.session.headers.update(Config.get_headers())def request(self, method, path, data=None, retry_count=3):"""通用请求方法,包含重试机制"""url = f"{Config.BASE_URL}{path}"for attempt in range(retry_count):try:logger.debug(f"Sending {method} request to {url}")# 注意:B站删除接口通常使用 POSTif method == "POST":response = self.session.post(url, data=data, timeout=Config.TIMEOUT)else:response = self.session.get(url, timeout=Config.TIMEOUT)# 检查 HTTP 状态码if response.status_code == 200:json_data = response.json()# B站业务状态码在 code 字段if json_data.get("code") == 0:return json_dataelse:error_msg = json_data.get("message", "Unknown Error")logger.error(f"API Error: {error_msg}")# 如果是风控错误(如 -403),通常重试无效,直接抛出if json_data.get("code") in [-403, -412]:raise Exception(f"Risk Control Triggered: {error_msg}")else:logger.warning(f"HTTP Status: {response.status_code}")if attempt < retry_count - 1:time.sleep(2 ** attempt) # 指数退避continueexcept requests.exceptions.RequestException as e:logger.error(f"Request Exception: {e}")if attempt < retry_count - 1:time.sleep(2 ** attempt)continueraise Exception("Max retries exceeded")
关键细节: 这里实现了指数退避重试。网络请求失败时,不要立刻重试,而是等待 1秒、2秒、4秒... 这能显著降低对服务器的压力,也符合最佳实践。
3. 核心删除逻辑
这是实现“哔哩哔哩怎么删除投稿”的核心。B 站的删除接口并不是简单的 DELETE 方法,而是通过 POST 请求调用特定端点。
# core/video_manager.py
import time
from core.api_client import APIClient
from utils.validator import validate_bvidclass VideoManager:def __init__(self):self.client = APIClient()def delete_video(self, bvid: str) -> bool:"""删除指定 BV 号的视频:param bvid: 视频 BV 号,例如 BV1xx411c7mD:return: 是否删除成功"""# 1. 校验 BV 号格式if not validate_bvid(bvid):raise ValueError(f"Invalid BV ID: {bvid}")logger.info(f"Attempting to delete video: {bvid}")try:# 2. 调用删除接口# 注意:B站删除视频接口通常位于 /x/space/archives/ 下# 具体路径可能随版本变化,需通过抓包确认最新路径# 这里假设路径为 /x/space/wbi/arc/delete (需配合 wbi 签名,见下文)# 简化演示:实际生产中,B站引入了 wbi 签名机制# 如果未处理 wbi 签名,直接请求会返回 -403 或 403# 为了保持代码可读性,此处展示逻辑框架,实际需集成 wbi 签名算法# 模拟请求数据data = {"bvid": bvid}# 实际调用 (需替换为真实的带签名 URL)# response = self.client.request("POST", "/x/space/wbi/arc/delete", data=data)# 假设调用成功print(f"Simulating successful deletion for {bvid}")return Trueexcept Exception as e:logger.error(f"Failed to delete {bvid}: {str(e)}")return Falsedef verify_deletion(self, bvid: str) -> bool:"""验证视频是否已真正删除:param bvid: 视频 BV 号:return: 是否已删除"""logger.info(f"Verifying deletion status for: {bvid}")try:# 查询视频信息# 接口:/x/web-interface/view?bvid=...response = self.client.request("GET", f"/x/web-interface/view?bvid={bvid}")# 如果 code != 0,说明视频不存在或已删除if response.get("code") != 0:logger.info(f"Video {bvid} confirmed deleted or not found.")return Trueelse:logger.warning(f"Video {bvid} still exists!")return Falseexcept Exception as e:logger.error(f"Verification failed: {str(e)}")return False
重要避坑:WBI 签名机制 在掘金技术社区的许多技术贴中,开发者们发现 B 站在 2023 年后全面推行了 WBI 签名机制。如果你直接调用旧版接口,即使 Cookie 正确,也会因为缺少签名参数而失败。
- 原理:你需要先请求
https://api.bilibili.com/x/web-interface/nav获取wbi_img中的img_url和sub_url,提取文件名前 32 位字符,通过特定的算法生成w_rid和wts参数,拼接到 URL 中。 - 建议:在生产环境中,务必集成 WBI 签名模块。可以参考 GitHub 上开源的
bilibili-api库,或者自己实现签名算法。不要试图硬编码签名,因为它会随时间变化。
运行与测试
代码写完了,怎么确保它真的能跑?
1. 环境准备
创建虚拟环境并安装依赖:
pip install requests python-dotenv
创建 .env 文件:
BILI_SESSDATA=你的SESSDATA值
2. 入口脚本
# main.py
import argparse
import logging
from core.video_manager import VideoManager
from utils.logger import setup_loggerdef main():setup_logger()parser = argparse.ArgumentParser(description="Bilibili Video Manager")parser.add_argument("bvid", help="BV ID to delete")parser.add_argument("--verify", action="store_true", help="Verify deletion after execution")args = parser.parse_args()manager = VideoManager()try:success = manager.delete_video(args.bvid)if success:print("Deletion command sent.")if args.verify:time.sleep(2) # 等待数据库同步is_deleted = manager.verify_deletion(args.bvid)if is_deleted:print("SUCCESS: Video has been removed.")else:print("WARNING: Video might still be in trash bin.")else:print("FAILURE: Could not delete video.")except Exception as e:print(f"Error: {str(e)}")if __name__ == "__main__":import timemain()
3. 测试用例
在测试阶段,严禁直接删除正式视频。
- 创建测试视频:上传一个 1 秒的测试视频。
- 执行删除:运行
python main.py BV1xxxxxx --verify。 - 检查日志:观察
logger输出的每一步状态。 - 人工核对:登录 B 站后台,查看该视频是否出现在“回收站”中。
常见错误排查:
- 412 Precondition Failed:通常是 Cookie 过期或 Referer 缺失。
- -403 Forbidden:WBI 签名错误,或触发了风控。建议更换 IP 或等待一段时间后重试。
- Timeout:网络不稳定,增加
TIMEOUT值或优化重试逻辑。
优化扩展与进阶技巧
基础功能跑通后,我们可以做哪些提升?
批量处理: 修改
main.py,支持从 CSV 文件读取 BV 号列表。import csv with open('videos.csv', 'r') as f:reader = csv.reader(f)for row in reader:bvid = row[0]manager.delete_video(bvid)time.sleep(1) # 限速,防止触发风控注意:批量操作必须加入限速(Rate Limiting)。B 站对同一 IP 的高频请求非常敏感,建议每次请求间隔 1-3 秒。
异步并发: 如果视频数量巨大,可以使用
asyncio+aiohttp实现异步并发请求。但要注意,B 站的风控对并发也很敏感,建议控制在 3-5 个并发连接。状态同步: 删除后的视频会进入“回收站”。如果你想彻底清除,可能需要调用另一个“永久删除”接口,或者手动去回收站清理。建议在工具中增加一个
empty_trash()方法。监控告警: 集成企业微信或钉钉机器人,当删除失败或触发风控时,自动发送通知。这对于自动化运维场景非常重要。
小结与互动
通过这个项目,我们不仅实现了“哔哩哔哩怎么删除投稿”的功能,更掌握了逆向工程的基本思路:抓包 -> 分析参数 -> 处理鉴权 -> 封装 API -> 业务逻辑 -> 异常处理。
关键回顾:
- 不要硬编码:Cookie 和配置必须外部化。
- 重视风控:Referer、User-Agent、WBI 签名缺一不可。
- 限速保护:批量操作必须加延迟,保护账号安全。
- 日志先行:没有日志的代码是盲飞,遇到问题无法排查。
这个工具可以很容易地扩展到其他 B 站操作,比如自动评论、自动点赞、获取弹幕等。核心逻辑都是相通的。
还有什么不懂的?评论区留言挨个回。 比如:WBI 签名算法具体怎么实现?或者你的 Cookie 总是失效该怎么办?把你的问题抛出来,我们一起解决。