3
0

start_run_ybm.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. """
  2. 药帮忙 (YBM) 平台采集入口
  3. 使用 commons1 调度框架 —— 从内部 API 拉取任务,由 YbmCrawler 执行采集,
  4. 拉取任务、心跳、回告全部走 commons。
  5. """
  6. import random
  7. import time
  8. import re
  9. from datetime import datetime
  10. from commons.scheduler import CrawlerScheduler
  11. from commons.Logger import get_spider_logger
  12. from commons.feishu_webhook import send_text, send_error_card
  13. from spiders.ybm.ybm_crawl import (
  14. YbmCrawler,
  15. PLATFORM_ID,
  16. YBM_DEVICE_ID,
  17. load_city_mapping,
  18. )
  19. logger = get_spider_logger("ybm_runner")
  20. PLATFORM_NAME = "药帮忙"
  21. IDLE_SECONDS_MIN = 180
  22. IDLE_SECONDS_MAX = 300
  23. def _report_progress(scheduler, task_id, is_finished, crawled_count=0,
  24. current_page=0, total_pages=0,
  25. exception_type=None, remark=None):
  26. """通过 commons 调度器 API 回告任务进度"""
  27. data = {
  28. "task_id": task_id,
  29. "platform": str(PLATFORM_ID),
  30. "username": scheduler.username,
  31. "current_page": current_page,
  32. "is_finished": is_finished,
  33. "total_pages": total_pages,
  34. }
  35. if crawled_count:
  36. data["crawled_count"] = crawled_count
  37. if exception_type is not None:
  38. data["exception_type"] = exception_type
  39. if remark:
  40. data["remark"] = remark
  41. try:
  42. scheduler.post_report(data)
  43. logger.info("任务回告成功: is_finished=%s, crawled_count=%s",
  44. is_finished, crawled_count)
  45. except Exception as e:
  46. logger.warning("任务回告失败: %s", e)
  47. def notify_result(task, crawl_count, success):
  48. """发送飞书通知(也走 commons.feishu_webhook)"""
  49. try:
  50. drug_name = task.get("product_name", "")
  51. spec = task.get("product_specs", "") or ""
  52. spec_items = [item.strip() for item in re.split(r"[|、,,\n\r]+", spec) if item.strip()]
  53. display_spec = "、".join(spec_items)
  54. notice_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  55. text_msg = (
  56. f"{notice_time} 通知:\n"
  57. f"平台: {PLATFORM_NAME}, 药品: {drug_name}, 品规: {display_spec}, 爬取数据: {crawl_count}条"
  58. )
  59. if success:
  60. send_text(text_msg)
  61. else:
  62. send_error_card(
  63. task_name=drug_name,
  64. err_msg=f"task_id={task.get('id')}, company_id={task.get('company_id')}, crawl_count={crawl_count}",
  65. mention_all=False,
  66. )
  67. except Exception as e:
  68. logger.warning("飞书通知发送失败:%s", e)
  69. def main_loop():
  70. """循环拉取任务并执行采集"""
  71. load_city_mapping()
  72. scheduler = CrawlerScheduler(YBM_DEVICE_ID, str(PLATFORM_ID))
  73. idle_seconds = random.randint(IDLE_SECONDS_MIN, IDLE_SECONDS_MAX)
  74. logger.info("药帮忙采集启动,每轮间隔 %s 秒", idle_seconds)
  75. while True:
  76. task_id = None
  77. try:
  78. scheduler.start() # 启动心跳守护线程(commons 内部管理)
  79. task = scheduler.get_task() or {}
  80. if not task:
  81. logger.info("%s 暂无任务,等待 %s 秒后重试", PLATFORM_NAME, idle_seconds)
  82. time.sleep(idle_seconds)
  83. continue
  84. task_id = task.get("id")
  85. logger.info("开始执行%s爬虫任务 task_id=%s", PLATFORM_NAME, task_id)
  86. crawler = YbmCrawler(task)
  87. crawl_count, is_success = crawler.run()
  88. is_finished = 1 if is_success else 0
  89. logger.info("%s爬虫任务执行完成 task_id=%s count=%s success=%s",
  90. PLATFORM_NAME, task_id, crawl_count, is_success)
  91. # 回告:通过 commons scheduler 上报结果
  92. _report_progress(
  93. scheduler, task_id,
  94. is_finished=is_finished,
  95. crawled_count=crawl_count,
  96. )
  97. # 飞书通知(也走 commons)
  98. notify_result(task, crawl_count, is_success)
  99. except Exception as e:
  100. logger.error("%s 爬虫任务执行失败: %s", PLATFORM_NAME, e, exc_info=True)
  101. # 异常也通过 commons 回告
  102. if task_id is not None:
  103. _report_progress(
  104. scheduler, task_id,
  105. is_finished=0,
  106. exception_type=type(e).__name__,
  107. remark=str(e)[:500],
  108. )
  109. time.sleep(idle_seconds)
  110. if __name__ == '__main__':
  111. main_loop()