3
0

start_run_yaoex.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. """
  2. 壹药城 (Yaoex) 平台采集入口
  3. 使用 commons1 调度框架 —— 从内部 API 拉取任务,由 YaoexCrawler / YaoexSnapshotCrawl 执行采集。
  4. """
  5. import random
  6. import re
  7. import time
  8. from datetime import datetime
  9. import requests
  10. from commons.Logger import get_spider_logger
  11. from commons.feishu_webhook import send_text, send_error_card
  12. from spiders.yaoex.yaoex_crawl import (
  13. YaoexCrawler,
  14. PLATFORM_ID,
  15. YYC_DEVICE_ID,
  16. TASK_PULL_URL,
  17. TASK_HEARTBEAT_URL,
  18. TASK_API_TOKEN,
  19. )
  20. from spiders.yaoex.yaoex_snapshot_crawl import YaoexSnapshotCrawl
  21. logger = get_spider_logger("yaoex_runner")
  22. PLATFORM_NAME = "壹药城"
  23. IDLE_SECONDS_MIN = 180
  24. IDLE_SECONDS_MAX = 300
  25. def pull_task():
  26. """从内部调度 API 拉取任务"""
  27. headers = {'X-Crawler-Token': TASK_API_TOKEN}
  28. params = {'platform': PLATFORM_ID, 'username': YYC_DEVICE_ID}
  29. try:
  30. response = requests.get(TASK_PULL_URL, params=params, headers=headers, timeout=15)
  31. result = response.json()
  32. logger.info("拉取任务 API 返回: %s", result)
  33. if result.get('code') == 'success':
  34. task = result.get('data', {}).get('task') or {}
  35. if task and task.get("id"):
  36. logger.info("拉取任务成功 platform=%s task_id=%s", PLATFORM_ID, task.get("id"))
  37. return task
  38. else:
  39. logger.info("拉取任务成功但无有效任务(task=%s),视为暂无任务", task)
  40. return {}
  41. else:
  42. logger.warning("拉取任务 API 返回非 success: code=%s, msg=%s",
  43. result.get('code'), result.get('msg'))
  44. return {}
  45. except Exception as e:
  46. logger.error("拉取任务 API 请求失败: %s", e)
  47. return {}
  48. def send_heartbeat():
  49. """向调度 API 上报心跳,告知服务器爬虫在线"""
  50. headers = {'X-Crawler-Token': TASK_API_TOKEN}
  51. payload = {"platform": str(PLATFORM_ID), "username": YYC_DEVICE_ID}
  52. try:
  53. resp = requests.post(TASK_HEARTBEAT_URL, json=payload, headers=headers, timeout=10)
  54. logger.debug("心跳上报: HTTP %s, %s", resp.status_code, resp.text[:200])
  55. except Exception as e:
  56. logger.warning("心跳上报失败: %s", e)
  57. def notify_result(task, crawl_count, success):
  58. """发送飞书通知"""
  59. try:
  60. drug_name = task.get("product_name", "")
  61. spec = task.get("product_specs", "") or ""
  62. spec_items = [item.strip() for item in re.split(r"[|、,,\n\r]+", spec) if item.strip()]
  63. display_spec = "、".join(spec_items)
  64. notice_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  65. text_msg = (
  66. f"{notice_time} 通知:\n"
  67. f"平台: {PLATFORM_NAME}, 药品: {drug_name}, 品规: {display_spec}, 爬取数据: {crawl_count}条"
  68. )
  69. if success:
  70. send_text(text_msg)
  71. else:
  72. send_error_card(
  73. task_name=drug_name,
  74. err_msg=f"task_id={task.get('id')}, company_id={task.get('company_id')}, crawl_count={crawl_count}",
  75. mention_all=False,
  76. )
  77. except Exception as e:
  78. logger.warning("飞书通知发送失败: %s", e)
  79. def main_loop():
  80. """循环拉取任务并执行采集"""
  81. idle_seconds = random.randint(IDLE_SECONDS_MIN, IDLE_SECONDS_MAX)
  82. logger.info("壹药城采集启动,每轮间隔 %s 秒", idle_seconds)
  83. while True:
  84. try:
  85. # 先上报心跳,告诉服务器爬虫在线
  86. send_heartbeat()
  87. task = pull_task()
  88. if not task:
  89. logger.info("%s 暂无任务,等待 %s 秒后重试", PLATFORM_NAME, idle_seconds)
  90. time.sleep(idle_seconds)
  91. continue
  92. # 根据 snapshot_collect_status 选择爬虫类型
  93. snapshot_status = task.get("snapshot_collect_status", 1)
  94. if snapshot_status == 0:
  95. spider_cls = YaoexSnapshotCrawl
  96. logger.info("snapshot_collect_status=%s → 启用快照模式 YaoexSnapshotCrawl", snapshot_status)
  97. else:
  98. spider_cls = YaoexCrawler
  99. logger.info("snapshot_collect_status=%s → 启用接口模式 YaoexCrawler", snapshot_status)
  100. logger.info("开始执行%s爬虫任务 task_id=%s", PLATFORM_NAME, task.get("id"))
  101. crawler = spider_cls(task)
  102. crawl_count, is_success = crawler.run()
  103. logger.info("%s爬虫任务执行完成 task_id=%s count=%s success=%s",
  104. PLATFORM_NAME, task.get("id"), crawl_count, is_success)
  105. notify_result(task, crawl_count, is_success)
  106. except Exception as e:
  107. logger.error("%s 爬虫任务执行失败: %s", PLATFORM_NAME, e, exc_info=True)
  108. time.sleep(idle_seconds)
  109. if __name__ == '__main__':
  110. main_loop()