runner.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. """编排入口:按步骤跑完整采集流程。"""
  2. from __future__ import annotations
  3. from playwright.sync_api import sync_playwright
  4. from config import settings
  5. from collectors.douyin.auth import click_login_if_needed, wait_until_logged_in
  6. from collectors.douyin.comments import collect_comments_and_save
  7. from collectors.douyin.common import SkipVideoError, human_pause, run_step
  8. from collectors.douyin.navigate import go_to_next_video
  9. from collectors.douyin.search import (
  10. click_video_filter,
  11. open_first_video,
  12. search_keyword,
  13. )
  14. from collectors.douyin.video import collect_and_save_video_meta
  15. def run() -> None:
  16. settings.BROWSER_DATA_DIR.mkdir(parents=True, exist_ok=True)
  17. keyword = settings.SEARCH_KEYWORD
  18. limit = settings.COMMENT_LIMIT
  19. video_limit = settings.VIDEO_LIMIT
  20. max_attempts = max(video_limit * 3, video_limit + 5)
  21. with sync_playwright() as p:
  22. context = p.chromium.launch_persistent_context(
  23. user_data_dir=str(settings.BROWSER_DATA_DIR),
  24. headless=False,
  25. viewport={"width": 1280, "height": 800},
  26. locale="zh-CN",
  27. args=["--disable-blink-features=AutomationControlled"],
  28. )
  29. page = context.pages[0] if context.pages else context.new_page()
  30. page.goto(settings.DOUYIN_URL, wait_until="domcontentloaded")
  31. print(f"已打开: {page.url}")
  32. human_pause(2.0, 3.5)
  33. run_step("点击登录", lambda: click_login_if_needed(page))
  34. try:
  35. wait_until_logged_in(page)
  36. except Exception as exc:
  37. print(f"[等待登录] 超时或失败: {exc}")
  38. print("登录未完成,后续步骤将跳过;浏览器保持打开,可手动继续。")
  39. else:
  40. human_pause(2.0, 4.0)
  41. if not run_step("搜索关键词", lambda: search_keyword(page, keyword)):
  42. pass
  43. elif not run_step("点击视频筛选", lambda: click_video_filter(page, keyword)):
  44. pass
  45. elif not run_step("打开第一个视频", lambda: open_first_video(page)):
  46. pass
  47. else:
  48. human_pause(2.0, 4.0)
  49. prev_video_id = ""
  50. success = 0
  51. attempt = 0
  52. need_switch = False
  53. while success < video_limit and attempt < max_attempts:
  54. attempt += 1
  55. print("=" * 48)
  56. print(
  57. f"尝试采集第 {attempt} 个候选 "
  58. f"(已成功 {success}/{video_limit})"
  59. )
  60. print("=" * 48)
  61. if need_switch:
  62. switched = {"id": ""}
  63. def _switch() -> None:
  64. switched["id"] = go_to_next_video(page, prev_video_id)
  65. if not run_step("切换到下一条搜索视频", _switch):
  66. print("无法继续在搜索结果中切视频,结束采集。")
  67. break
  68. if switched["id"]:
  69. prev_video_id = switched["id"]
  70. human_pause(2.0, 3.5)
  71. need_switch = True
  72. video_meta_box: dict = {}
  73. try:
  74. print("[获取作者信息/AI总结并入库] 开始")
  75. video_meta_box["meta"] = collect_and_save_video_meta(
  76. page, keyword, limit
  77. )
  78. print("[获取作者信息/AI总结并入库] 完成")
  79. except SkipVideoError as exc:
  80. print(f"[跳过视频] {exc}")
  81. human_pause(1.0, 2.0)
  82. continue
  83. except Exception as exc:
  84. print(f"[获取作者信息/AI总结并入库] 失败: {exc}")
  85. print("准备重试一次...")
  86. human_pause(2.0, 4.0)
  87. try:
  88. video_meta_box["meta"] = collect_and_save_video_meta(
  89. page, keyword, limit
  90. )
  91. print("[获取作者信息/AI总结并入库] 完成")
  92. except SkipVideoError as exc2:
  93. print(f"[跳过视频] {exc2}")
  94. continue
  95. except Exception as exc2:
  96. print(f"[获取作者信息/AI总结并入库] 重试仍失败: {exc2}")
  97. continue
  98. prev_video_id = video_meta_box["meta"].get("video_id") or prev_video_id
  99. human_pause(1.5, 2.5)
  100. try:
  101. print("[打开评论区并按序采集入库] 开始")
  102. collect_comments_and_save(
  103. page, video_meta_box["meta"], limit
  104. )
  105. print("[打开评论区并按序采集入库] 完成")
  106. success += 1
  107. except SkipVideoError as exc:
  108. print(f"[跳过视频] {exc}")
  109. except Exception as exc:
  110. print(f"[打开评论区并按序采集入库] 失败: {exc}")
  111. print("准备重试一次...")
  112. human_pause(2.0, 4.0)
  113. try:
  114. collect_comments_and_save(
  115. page, video_meta_box["meta"], limit
  116. )
  117. print("[打开评论区并按序采集入库] 完成")
  118. success += 1
  119. except SkipVideoError as exc2:
  120. print(f"[跳过视频] {exc2}")
  121. except Exception as exc2:
  122. print(f"[打开评论区并按序采集入库] 重试仍失败: {exc2}")
  123. human_pause(1.5, 3.0)
  124. print(f"多视频采集结束:成功 {success}/{video_limit},尝试 {attempt} 次。")
  125. print("流程结束(失败步骤已跳过)。浏览器保持打开,关闭窗口或按 Ctrl+C 结束。")
  126. try:
  127. page.wait_for_event("close")
  128. except Exception:
  129. pass
  130. context.close()