first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
阶段2: 逐页整页截图爬取。
|
||||
从 SQLite 读取所有 level2 叶子(url + screenshot_path), 依次:
|
||||
随机 5-15s 延时 -> 导航到 url -> 等待加载 -> 整页滚动拼接截图 -> 保存到树形目录
|
||||
支持断点续跑(已存在截图文件则跳过)。进度写 db/crawl_progress.log
|
||||
用法: python3 crawl_shots.py [start_index]
|
||||
"""
|
||||
import os, sys, json, time, random, sqlite3, urllib.request
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from capture_full import capture, ev
|
||||
|
||||
DAEMON = "http://127.0.0.1:10086/command"
|
||||
SESSION = "2haohr"
|
||||
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DB = os.path.join(BASE, "db", "research.db")
|
||||
LOG = os.path.join(BASE, "db", "crawl_progress.log")
|
||||
|
||||
def navigate(url):
|
||||
body = json.dumps({"action": "navigate", "args": {"url": url, "newTab": False}, "session": SESSION}).encode()
|
||||
req = urllib.request.Request(DAEMON, data=body, headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req, timeout=45) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
def wait_ready(max_wait=8):
|
||||
"""等待页面加载完成(readyState + 主内容出现)"""
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < max_wait:
|
||||
try:
|
||||
rs = ev("(()=>document.readyState)()", SESSION)
|
||||
if rs == "complete":
|
||||
time.sleep(1.2)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.6)
|
||||
return False
|
||||
|
||||
def log(msg):
|
||||
line = time.strftime("%H:%M:%S ") + msg
|
||||
print(line, flush=True)
|
||||
with open(LOG, "a") as f:
|
||||
f.write(line + "\n")
|
||||
|
||||
def main():
|
||||
start = int(sys.argv[1]) if len(sys.argv) > 1 else 0
|
||||
conn = sqlite3.connect(DB); conn.row_factory = sqlite3.Row
|
||||
rows = conn.execute(
|
||||
"SELECT node_key,name,full_path,url,screenshot_path FROM features WHERE level=2 ORDER BY sort_order, id"
|
||||
).fetchall()
|
||||
# 稳定顺序: 按 full_path
|
||||
rows = sorted(rows, key=lambda r: r["full_path"] or "")
|
||||
conn.close()
|
||||
total = len(rows)
|
||||
log(f"=== crawl start, total={total}, start_index={start} ===")
|
||||
done = 0; skipped = 0; failed = 0
|
||||
for i, r in enumerate(rows):
|
||||
if i < start:
|
||||
continue
|
||||
rel = r["screenshot_path"]
|
||||
out = os.path.join(BASE, rel)
|
||||
if os.path.exists(out) and os.path.getsize(out) > 3000:
|
||||
skipped += 1
|
||||
continue
|
||||
delay = random.uniform(5, 15)
|
||||
log(f"[{i+1}/{total}] wait {delay:.1f}s -> {r['full_path']}")
|
||||
time.sleep(delay)
|
||||
try:
|
||||
navigate(r["url"])
|
||||
wait_ready()
|
||||
time.sleep(random.uniform(0.8, 2.0)) # 额外抖动
|
||||
res = capture(SESSION, out, 16)
|
||||
done += 1
|
||||
log(f" OK {r['name']} -> {rel}")
|
||||
except Exception as e:
|
||||
failed += 1
|
||||
log(f" FAIL {r['name']} url={r['url']} err={str(e)[:150]}")
|
||||
log(f"=== DONE done={done} skipped={skipped} failed={failed} total={total} ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user