B站api调用、python实现随机换直播间的设计
b站直播间自动换直播脚本:
脚本仅限本机、本人、自用;
脚本中的--remote-debugging-port开启的调试端口禁止被局域网/公网其他机器访问;
在cmd里打开bilibili软件
start "" "你的安装路径\哔哩哔哩.exe" --remote-debugging-port=9222
需要调用的库
import json, time, random, msvcrt, os
import requests, websocket
以下为全部代码
-- coding: utf-8 --
"""B站客户端 一键随机换房 v7 —— 支持 P 键暂停/继续(停在当前直播间)"""
import json, time, random, msvcrt, os
import requests, websocket
==================== 配置区(请根据实际环境修改) ====================
CDP 调试端口,需与启动客户端时的 --remote-debugging-port 一致
可通过环境变量 BILI_CDP_PORT 覆盖,否则默认 9222
CDP_PORT = int(os.environ.get("BILI_CDP_PORT", 9222))
B站客户端可执行文件完整路径
可通过环境变量 BILIBILI_EXE_PATH 覆盖,否则使用下方默认值(请改为你的实际路径!)
BILIBILI_EXE_PATH = os.environ.get(
"BILIBILI_EXE_PATH",
r"F:\Program Files\bilibili\哔哩哔哩.exe" # <-- 如果环境变量未设置,请修改此路径为你的实际安装路径
)
======================================================================
DESKTOP_PREFIX = True
BASE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
SESS = requests.Session()
def room_url(rid):
return f"https://live.bilibili.com/desktop/{rid}" if DESKTOP_PREFIX
else f"https://live.bilibili.com/{rid}"
def get_targets():
try:
return SESS.get(f"http://127.0.0.1:{CDP_PORT}/json", timeout=2).json()
except requests.exceptions.ConnectionError:
return None
def cdp_navigate(ws_url, url):
try:
ws = websocket.create_connection(ws_url, timeout=6)
ws.send(json.dumps({"id": 1, "method": "Page.navigate", "params": {"url": url}}))
ws.recv(); ws.close()
return True
except Exception:
return False
def cdp_call(ws_url, method, params=None):
try:
ws = websocket.create_connection(ws_url, timeout=8)
msg = {"id": 1, "method": method}
if params: msg["params"] = params
ws.send(json.dumps(msg))
deadline = time.time() + 8
while time.time() < deadline:
data = json.loads(ws.recv())
if data.get("id") == 1:
ws.close(); return data.get("result", {})
ws.close()
except Exception:
pass
return {}
---------- Cookie ----------
def extract_cookies():
ts = get_targets() or []
t = next((x for x in ts if "bilibili.com" in (x.get("url") or "") and x.get("webSocketDebuggerUrl")), None)
or next((x for x in ts if x.get("type") == "page" and x.get("webSocketDebuggerUrl")), None)
if not t: return None
res = cdp_call(t["webSocketDebuggerUrl"], "Network.getAllCookies")
bili = [c for c in res.get("cookies", []) if "bilibili.com" in c.get("domain", "")]
if not bili: return None
jar = requests.cookies.RequestsCookieJar()
for c in bili:
jar.set(c["name"], c["value"], domain=c.get("domain"), path=c.get("path", "/"))
return jar
def setup_session():
print(" · 从客户端提取登录 Cookie...")
jar = extract_cookies()
SESS.headers.update({"User-Agent": BASE_UA, "Referer": "https://live.bilibili.com/"})
if jar and len(jar) > 0:
SESS.cookies = jar
has_sess = any(k in jar.keys() for k in ("SESSDATA", "bili_jct", "DedeUserID"))
print(f" OK {len(jar)} 个 cookie,登录态: {'有(SESSDATA)' if has_sess else '无'}")
return True
print(" ! 未提取到 cookie"); return False
---------- 开播核验(宽松) ----------
def check_live(room_id):
try:
d = SESS.get("https://api.live.bilibili.com/room/v1/Room/get_info",
params={"room_id": room_id}, timeout=5).json()
if d.get("code") == 0:
data = d["data"]
return (data.get("live_status") == 1), data.get("title", "?"), data.get("online", 0)
return None, "?", 0
except Exception:
return None, "?", 0
---------- 房间源 ----------
def src_following_online():
out = []
try:
d = SESS.get("https://api.live.bilibili.com/xlive/web-ucenter/user/following",
params={"page": 1, "pagesize": 30, "order_type": "attention"}, timeout=5).json()
for x in ((d.get("data") or {}).get("list") or []):
if x.get("live_status") == 1:
out.append((x["roomid"], x.get("uname", "?"), x.get("title", "?"), x.get("online", 0)))
except Exception as e:
print(" [关注列表 fail]", e)
return out
def src_area_getlist():
out = []
for parent in (1, 9, 2, 3, 6, 0):
for page in (1, 2):
try:
d = SESS.get("https://api.live.bilibili.com/room/v1/Area/getList",
params={"platform": "web", "parent_area_id": parent, "area_id": 0,
"page": page, "page_size": 30, "sort_type": "online"}, timeout=5).json()
for x in (d.get("data") or []):
if x.get("roomid"):
out.append((x["roomid"], x.get("uname", "?"), x.get("title", "?"), x.get("online", 0)))
except Exception:
pass
return out
def fetch_live_rooms():
rooms = src_following_online()
if len(rooms) < 20:
rooms += src_area_getlist()
print(" · 逐个核验开播状态...")
final, seen = [], set()
for rid, name, title, online in rooms:
if rid in seen: continue
seen.add(rid)
status, t, on = check_live(rid)
if status is False: continue
final.append((rid, name, t or title, on or online))
if len(final) >= 50: break
return final
---------- target 锁定 ----------
LOCKED_WS = None # 锁定后可见窗口的 webSocketDebuggerUrl
BROADCAST_MODE = False
def list_targets():
ts = get_targets()
if ts is None:
print(" X 连不上 9222"); return []
print(f"\n [调试口下共 {len(ts)} 个 target]😊
for i, t in enumerate(ts):
print(f" #{i} type={t.get('type'):8} title={(t.get('title') or '')[:24]:24} url={(t.get('url') or '')[:60]}")
def score(t):
u = t.get("url") or ""; s = 0
if t.get("type") == "page": s += 10
if "/desktop/" in u and "live.bilibili.com" in u: s += 50
elif "live.bilibili.com" in u: s += 20
if "sw.js" in u or "user-guide" in u: s -= 40
return s
cand = [t for t in ts if t.get("webSocketDebuggerUrl")]
cand.sort(key=score, reverse=True)
return cand
def navigate_visible(room_id):
global LOCKED_WS
url = room_url(room_id)
if LOCKED_WS:
return cdp_navigate(LOCKED_WS, url)
cands = list_targets()
if not cands: return False
return cdp_navigate(cands[0]["webSocketDebuggerUrl"], url)
def navigate_all_probe(room_id):
cands = [t for t in (get_targets() or []) if t.get("type") == "page" and t.get("webSocketDebuggerUrl")]
url = room_url(room_id)
for t in cands:
cdp_navigate(t["webSocketDebuggerUrl"], url)
return len(cands) > 0
def do_navigate(room_id):
return navigate_all_probe(room_id) if BROADCAST_MODE else navigate_visible(room_id)
---------- 等待: 支持 P 暂停/继续, 任意键立刻换 ----------
def wait_with_pause(sec):
remaining = sec
paused = False
last_print = -1
while True:
if msvcrt.kbhit():
ch = msvcrt.getch()
if ch in (b'\x00', b'\xe0'):
try: msvcrt.getch()
except Exception: pass
continue
key = ch.decode('gbk', errors='ignore').lower()
if key == 'p':
paused = not paused
if paused:
print("\n ⏸ 已暂停自动换房 —— 停在当前直播间。再按 P 继续。")
else:
print(f"\n ▶ 已继续 —— 还剩约 {int(remaining)} 秒后自动换下一个。")
last_print = -1
continue
else:
if paused:
print(" >> 按键: 解除暂停并立刻换下一个")
return 'next'
print(" >> 按键,立刻换下一个")
return 'next'
if not paused:
remaining -= 0.2
if remaining <= 0:
return 'timeout'
cur = int(remaining // 5)
if cur != last_print and remaining > 0:
last_print = cur
time.sleep(0.2)
---------- 自检 + 定位可见窗口 ----------
def self_check_and_locate():
global LOCKED_WS, BROADCAST_MODE
print("="56); print(" 第1步 自检 + 定位你眼前的直播窗口"); print("="56)
cands = list_targets()
if not cands:
print(' X 请用此命令启动客户端(先完全退出)😂
print(f' start "" "{BILIBILI_EXE_PATH}" --remote-debugging-port={CDP_PORT}')
return False
test_rid = 5440
print("\n >> 试探首选 target -> 测试房间", test_rid)
navigate_visible(test_rid)
ans = input(" 你眼前的客户端画面变了吗? y/n: ").strip().lower()
if ans == "y":
cands = list_targets()
if cands:
LOCKED_WS = cands[0]["webSocketDebuggerUrl"]
print(" V 锁定可见窗口成功!\n")
return True
print(" >> 首选没命中,广播所有 page 型 target...")
navigate_all_probe(test_rid)
ans2 = input(" 这次画面变了吗? y/n: ").strip().lower()
if ans2 == "y":
BROADCAST_MODE = True
print(" V 广播模式可用!\n")
return True
print(" X 都没命中。请完全退出客户端后用此命令重启:")
print(f' start "" "{BILIBILI_EXE_PATH}" --remote-debugging-port={CDP_PORT} --remote-allow-origins=*')
return False
---------- 主循环 ----------
def main():
if not self_check_and_locate():
input("\n处理好后按回车重试..."); return main()
print("="56); print(" 第2步 建立会话(提取Cookie)"); print("="56)
setup_session()
print("\n" + "="56); print(" 第3步 拉取【正在开播】的房间池"); print("="56)
rooms = fetch_live_rooms()
if not rooms:
extra = input(" ! 没拉到。手动输入几个在播房间号(空格分隔): ").strip()
rooms = [(int(r), "手动", "", 0) for r in extra.split() if r.isdigit()]
if not rooms:
print(" 没有可用房间,退出。"); return
random.shuffle(rooms)
print(f" V 可用在播房间: {len(rooms)} 个\n")
for rid, n, t, on in rooms[:10]:
print(f" {rid} 人气{on} {n} | {(t or '')[:18]}")
print("\n" + "="56)
print(" 开始! 操作说明:")
print(" 【任意键】= 立刻换下一个")
print(" 【P 键】 = 暂停/继续 自动换房(停在当前直播间)")
print(" 【Ctrl+C】= 彻底退出脚本")
print("="56 + "\n")
i = 0
while True:
if i > 0 and i % len(rooms) == 0:
print("\n [] 一轮看完,重新拉取...")
new = fetch_live_rooms()
if new:
rooms = new; random.shuffle(rooms)
print(f" [] 刷新为 {len(rooms)} 个\n")
rid, name, title, online = rooms[i % len(rooms)]
status, lt, on = check_live(rid)
if status is False:
print(f" -- 跳过 {rid} ({name}):已下播"); i += 1; continue
if not do_navigate(rid):
print(" [!] 无可导航 target,5秒后重试"); time.sleep(5); continue
print(f" >> [{i+1}] 进入 {rid} 人气{on or online} ({name}) | {(lt or title or '')[:22]}")
sec = random.randint(20, 45)
print(f" 观看约 {sec} 秒 | 任意键=立刻换 / P=暂停停在这里 ...")
result = wait_with_pause(sec)
i += 1
if name == "main":
try:
main()
except KeyboardInterrupt:
print("\n\n已停止。下次见~")

浙公网安备 33010602011771号