You've already forked pruchase_jhw
新闻
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import json
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from src.tasks import surugaya_auth
|
||||
from src.tasks.base import BasePurchaseTask
|
||||
from src.config.accounts import get_account_config
|
||||
from src.parsers.surugaya_news import collect_all_news
|
||||
from src.task_queue.redis_manager import get_redis_client
|
||||
|
||||
# 「お知らせ一覧」页面地址。
|
||||
NEWS_URL = "https://www.suruga-ya.jp/pcmypage/action_news"
|
||||
|
||||
|
||||
class SurugayaNewsMonitorTask(BasePurchaseTask):
|
||||
"""
|
||||
骏河屋「お知らせ一覧」通知抓取任务。
|
||||
|
||||
入参约定:
|
||||
- topic: surugaya_news
|
||||
- purchase_account: 抓取通知所属账号
|
||||
- payload: 无额外字段
|
||||
|
||||
行为:打开 action_news 页面(必要时自动登录),解析首页最新通知,
|
||||
按 data_id 去重写入 Redis Hash(field=data_id),供中心 API 读取。
|
||||
"""
|
||||
|
||||
async def execute(self) -> dict[str, Any]:
|
||||
self.report_event = "surugaya_news"
|
||||
self.is_report = False
|
||||
|
||||
logger.debug(f"[任务 {self.task_id}] 骏河屋通知抓取工作流已启动。")
|
||||
if not self.page:
|
||||
raise ValueError("页面对象未初始化,无法执行通知抓取任务。")
|
||||
|
||||
page = self.page
|
||||
|
||||
await page.goto(NEWS_URL, wait_until="domcontentloaded", timeout=30000)
|
||||
|
||||
# 登录兜底:长时间运行登录态可能过期,未登录则自动登录后重新进入。
|
||||
if not await self._ensure_login(page):
|
||||
raise RuntimeError(f"账号 '{self.account_id}' 登录失败,无法抓取通知。")
|
||||
|
||||
news_items = await collect_all_news(page)
|
||||
logger.info(f"[任务 {self.task_id}] 账号 '{self.account_id}' 翻页解析到 {len(news_items)} 条通知。")
|
||||
|
||||
written = await self._save_news_to_redis(news_items)
|
||||
|
||||
return {
|
||||
"account_id": self.account_id,
|
||||
"parsed_count": len(news_items),
|
||||
"written_count": written,
|
||||
}
|
||||
|
||||
async def _notify_task_failed(self) -> None:
|
||||
"""通知抓取是每 5 分钟运行的常规轮询,单次失败下轮即重试,不发失败告警邮件,
|
||||
避免登录抖动等偶发问题造成告警刷屏(失败现场截图仍由基类保留)。"""
|
||||
logger.warning(
|
||||
f"[任务 {self.task_id}] 通知抓取失败(不告警,等待下一轮重试): {self.error_message}"
|
||||
)
|
||||
|
||||
async def _ensure_login(self, page) -> bool:
|
||||
log_prefix = f"[任务 {self.task_id}] "
|
||||
if await surugaya_auth.check_login_status(page, log_prefix=log_prefix):
|
||||
return True
|
||||
|
||||
logger.warning(f"{log_prefix}账号 '{self.account_id}' 未登录,执行登录流程。")
|
||||
account_config = get_account_config("surugaya", self.account_id)
|
||||
if not account_config:
|
||||
raise ValueError(f"未找到骏河屋账号配置: {self.account_id}")
|
||||
|
||||
await surugaya_auth.surugaya_login(page, account_config, log_prefix=log_prefix)
|
||||
await page.goto(NEWS_URL, wait_until="domcontentloaded", timeout=30000)
|
||||
return await surugaya_auth.check_login_status(page, log_prefix=log_prefix)
|
||||
|
||||
async def _save_news_to_redis(self, news_items: list[dict[str, Any]]) -> int:
|
||||
"""按 data_id 作为 field 写入 Redis Hash;返回写入条数。"""
|
||||
if not news_items:
|
||||
return 0
|
||||
|
||||
redis_client = get_redis_client()
|
||||
hash_key = self.settings.redis_news_hash_key
|
||||
fetched_at = int(time.time())
|
||||
|
||||
mapping = {
|
||||
item["data_id"]: json.dumps(
|
||||
{**item, "account_id": self.account_id, "fetched_at": fetched_at},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
for item in news_items
|
||||
}
|
||||
await redis_client.hset(hash_key, mapping=mapping)
|
||||
logger.info(
|
||||
f"[任务 {self.task_id}] 已将 {len(mapping)} 条通知写入 Redis Hash '{hash_key}'。"
|
||||
)
|
||||
return len(mapping)
|
||||
Reference in New Issue
Block a user