Files
study-tool/src/study_tool/capture.py
T

94 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""抓包:把页面 XHR/fetch 落成 data/capture-*.json。"""
import json
import os
import time
from study_tool import store
from study_tool.config import *
from study_tool.runtime import RUNS, log
# ------------------------------------------------------- 抓包(排查平台行为)
CAPTURES = {}
def capture_worker(key, cookies, minutes=10):
"""开一个有头浏览器,把这期间所有 /train/** 请求和响应记下来。
用于搞不清「某个状态到底由哪一发请求写成」:手动走一遍(点课程 → 播几秒 → 交评价),
再看抓到的接口和响应,就能定位该自动补哪一步。
"""
try:
from playwright.sync_api import sync_playwright
except Exception as e:
log(key, "抓包需要 playwright:%s" % e)
return
recs = CAPTURES.setdefault(key, [])
log(key, "抓包开始:浏览器里手动走一遍(点课程 → 播几秒 → 交评价),"
"完事点「停止抓包」,最多 %d 分钟" % minutes)
def on_resp(r):
try:
if r.request.resource_type not in ("xhr", "fetch"):
return
ct = (r.headers or {}).get("content-type", "")
body = ""
if "json" in ct:
try:
body = r.text()[:4000]
except Exception:
body = ""
recs.append({"t": time.strftime("%H:%M:%S"), "method": r.request.method,
"url": r.url, "req": (r.request.post_data or "")[:1000],
"status": r.status, "resp": body})
u = urlsplit(r.url)
log(key, " · %s %s%s → %s" % (r.request.method, u.netloc, u.path, r.status))
except Exception:
pass
try:
with sync_playwright() as p:
b = p.chromium.launch(headless=False)
ctx = b.new_context()
for c in cookies:
if "dgjy" in (c.get("domain") or ""):
try:
ctx.add_cookies([{"name": c["name"], "value": c.get("value") or "",
"domain": (c.get("domain") or "").lstrip("."),
"path": c.get("path") or "/"}])
except Exception:
pass
# 平台点课程/视频是 window.open 新开标签,必须把新页面也挂上监听,否则只抓到第一页
hooked = set()
def hook(page):
if id(page) in hooked:
return
hooked.add(id(page))
page.on("response", on_resp)
ctx.on("page", hook)
pg = ctx.new_page()
hook(pg)
# /training/ 根路径没有路由,会落到 404;直接开「我的培训」列表页
pg.goto(STUDY + "/training/myTrain", wait_until="domcontentloaded", timeout=60000)
t0 = time.time()
while time.time() - t0 < minutes * 60:
if (RUNS.get(key) or {}).get("capture_stop"):
break
pg.wait_for_timeout(1000)
except Exception as e:
log(key, "抓包异常:%s" % str(e)[:150])
finally:
path = ""
if recs:
try:
path = os.path.join(store.DATA_DIR,
"capture-%s-%s.json" % (key, time.strftime("%Y%m%d-%H%M%S")))
with open(path, "w", encoding="utf-8") as f:
json.dump(recs, f, ensure_ascii=False, indent=1)
except Exception as e:
log(key, "抓包结果保存失败:%s" % e)
set_state(key, capturing=False, capture_stop=False, capture_file=path)
log(key, "抓包结束:%d 条请求%s" % (len(recs), (",已存 " + path) if path else ""))