backfill_weixin.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. """补下载微信公众号帖子的【完整正文 + 内文图】到本地,写回 app.db。
  2. 微信搜索只回标题+封面,正文没下;分类只看标题会判不准。本脚本对库里所有微信帖逐个拉详情
  3. (wei_xin/detail,实测不需 token)→ 下全部内文图到 data/search/weixin/<hash>/img_*.jpg
  4. → 把 body_text + images 写回 search_results.extra。之后微信就和小红书一样是「完整内容」。
  5. 限流:详情接口 10~12s 随机一次(图片下载走 CDN,不计入闸)。已补过的(extra 有 body_text)跳过,可重跑。
  6. 用法:PYTHONPATH=. CK_ENV_FILE=.env python -m acquisition.backfill_weixin
  7. """
  8. from __future__ import annotations
  9. import hashlib
  10. import time
  11. from pathlib import Path
  12. from acquisition import store
  13. from acquisition.crawler import RateLimiter
  14. from acquisition.search import fetch_weixin_detail
  15. from core.config import Settings
  16. from creation_knowledge.integrations.video_extract import _default_download
  17. ROOT = Path(__file__).resolve().parent.parent
  18. DATA = ROOT / "data"
  19. GATE_MIN, GATE_MAX = 10.0, 12.0 # 微信详情接口两次调用随机间隔(秒)
  20. def _dl(url: str, dst: Path, public: str):
  21. try:
  22. dst.parent.mkdir(parents=True, exist_ok=True)
  23. dst.write_bytes(_default_download(url, "weixin"))
  24. return public
  25. except Exception:
  26. return None
  27. def main() -> None:
  28. settings = Settings.from_env()
  29. conn = store.connect()
  30. urls = store.weixin_urls_missing_body(conn)
  31. if not urls:
  32. print("微信帖正文都已补过。"); conn.close(); return
  33. gate = RateLimiter(min_interval_seconds=GATE_MIN, max_interval_seconds=GATE_MAX)
  34. total, ok, fail, img_total = len(urls), 0, 0, 0
  35. print(f"待补正文的微信帖 {total} 个(详情 {GATE_MIN}~{GATE_MAX}s 一次)...")
  36. for i, url in enumerate(urls, 1):
  37. try:
  38. body, img_urls = fetch_weixin_detail(url, settings=settings, rate_limiter=gate)
  39. except Exception as exc:
  40. fail += 1
  41. print(f" [{i}/{total}] 详情失败: {str(exc)[:50]}")
  42. continue
  43. h = hashlib.md5(url.encode()).hexdigest()[:16]
  44. base, pub = DATA / "search" / "weixin" / h, f"/data/search/weixin/{h}"
  45. imgs = []
  46. for j, u in enumerate(img_urls):
  47. local = _dl(u, base / f"img_{j}.jpg", f"{pub}/img_{j}.jpg")
  48. if local:
  49. imgs.append(local)
  50. store.update_post_content(conn, url, body, imgs)
  51. ok += 1
  52. img_total += len(imgs)
  53. if i % 10 == 0 or i == total:
  54. print(f" [{i}/{total}] 正文 {len(body)} 字 / 图 {len(imgs)} 张(累计 {ok} 成 / {fail} 败 / {img_total} 图)")
  55. conn.close()
  56. print(f"完成:{ok} 帖补到正文,{fail} 失败,共下 {img_total} 张内文图。")
  57. if __name__ == "__main__":
  58. main()