当前位置: 首页 > ops >正文

【知识】Elsevier论文接收后的后续流程

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn]

如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~

目录

整体流程

每步详细

监听脚本


整体流程

每步详细

1、接受后,会自动进入到发表流程,会收到邮件提醒。

2、(等流转到了再更新)

监听脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-import os
import re
import time
import logging
import requests
from typing import Dict, List, Tuple, Any, Optional
from email.utils import formatdate  # 保留导入以便需要时扩展
from bs4 import BeautifulSoup# ---------------- 固定配置 ----------------
URL = "https://authors.elsevier.com/tracking/article/details.do?aid=<>&jid=<>&surname=<>"
INTERVAL_SECONDS = 3600  # 间隔一小时
REQUEST_TIMEOUT = 20
MAX_RETRIES = 3
# -----------------------------------------# -------- 通知接口 --------
NOTIFY_URL = "http://14.103.144.178:7790/send/friend"
TARGET_ID = ["wxid_043", "wxid_lg"]
API_KEY = "xxxx"def do_send_notification(target_id: str, message: str) -> None:"""发送通知(单个目标)。使用 params 以确保正确编码。"""try:params = {"target": target_id, "key": API_KEY, "msg": message}resp = requests.get(NOTIFY_URL, params=params, timeout=10)if resp.status_code == 200:print(f"[通知] 已发送成功 → {target_id}")else:print(f"[通知] 发送失败 → {target_id},状态码: {resp.status_code},响应: {resp.text[:200]}")except Exception as e:print(f"[通知] 请求发送失败 → {target_id}: {e}")def send_notification(message: str) -> None:"""批量发送通知"""if isinstance(TARGET_ID, list):for tid in TARGET_ID:do_send_notification(tid, message)time.sleep(1)else:do_send_notification(TARGET_ID, message)
# ---------------------------------------------------------------HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36","Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
}logging.basicConfig(level=logging.INFO,format="%(asctime)s %(levelname)s: %(message)s"
)_stop = False
def _handle_signal(signum, frame):global _stoplogging.info("Received signal %s; preparing to stop after current cycle...", signum)_stop = Truetry:import signalfor sig in (signal.SIGINT, signal.SIGTERM):signal.signal(sig, _handle_signal)
except Exception:passdef _norm_text(s: str) -> str:if not s: return ""import re as _rereturn _re.sub(r"\s+", " ", str(s)).strip()def fetch_html(url: str, retries: int = MAX_RETRIES, timeout: int = REQUEST_TIMEOUT) -> str:last_exc = Nonefor i in range(retries):try:with requests.Session() as s:s.headers.update(HEADERS)resp = s.get(url, timeout=timeout, allow_redirects=True)resp.raise_for_status()return resp.textexcept Exception as e:last_exc = elogging.warning("Fetch attempt %s failed: %s", i+1, e)time.sleep(2 * (i + 1))raise RuntimeError(f"Failed to fetch page after {retries} attempts: {last_exc}")def parse_snapshot(html: str) -> Dict[str, Any]:"""返回包含三项信息的快照:- lastUpdatedDate: str- statusComment: str- productionEvents: List[Dict[str, str]] (键:date, event)"""soup = BeautifulSoup(html, "html.parser")# 1) lastUpdatedDatelast_updated = ""el = soup.select_one("#lastUpdatedDate")if el:last_updated = _norm_text(el.get_text())else:candidates = soup.find_all(string=re.compile(r"Last update", re.I))if candidates:node = candidates[0].parentlast_updated = _norm_text(node.get_text())# 2) Status commentstatus_comment = ""label = soup.find(string=re.compile(r"Status comment", re.I))if label:container = label.parentdd = container.find_next(["dd","p","span","div"])status_comment = _norm_text((dd.get_text() if dd else container.get_text()).replace(str(label), ""))if not status_comment: status_comment = _norm_text(container.find_next(string=True) or "")else:possible = soup.find_all(string=re.compile(r"(Status comment|status:|status\s+comment)", re.I))if possible: status_comment = _norm_text(possible[0])# 3) Production eventsproduction_events: List[Dict[str, str]] = []head = soup.find(string=re.compile(r"Production events", re.I))if head:sec = head.parenttable = sec.find_next("table")if table:rows = table.find_all("tr")for r in rows:cols = [ _norm_text(c.get_text()) for c in r.find_all(["td","th"]) ]if len(cols) >= 2:date, event = cols[0], cols[1]if re.match(r"(?i)date", date) and re.match(r"(?i)event", event): continueif date or event: production_events.append({"date": date, "event": event})else:ul = sec.find_next("ul")if ul:for li in ul.find_all("li"):txt = _norm_text(li.get_text())m = re.match(r"^(\d{1,4}[-/]\d{1,2}[-/]\d{1,2}).*?[—-]\s*(.+)$", txt)if m: production_events.append({"date": m.group(1), "event": m.group(2)})else: production_events.append({"date": "", "event": txt})return {"lastUpdatedDate": last_updated,"statusComment": status_comment,"productionEvents": production_events,}def diff_snapshots(old: Optional[Dict[str, Any]], new: Dict[str, Any]) -> Tuple[bool, str]:if not old:return False, "Baseline initialized."  # 启动即基线,不通知changes = []if old.get("lastUpdatedDate") != new.get("lastUpdatedDate"):changes.append(f"• lastUpdatedDate: '{old.get('lastUpdatedDate')}' → '{new.get('lastUpdatedDate')}'")if _norm_text(old.get("statusComment","")) != _norm_text(new.get("statusComment","")):changes.append(f"• Status comment changed:\n    OLD: {old.get('statusComment')}\n    NEW: {new.get('statusComment')}")old_events = old.get("productionEvents", [])new_events = new.get("productionEvents", [])if old_events != new_events:old_set = {(e.get("date",""), e.get("event","")) for e in old_events}new_set = {(e.get("date",""), e.get("event","")) for e in new_events}added = new_set - old_setremoved = old_set - new_setif added: changes.append("• Production events — ADDED:\n  " + "\n  ".join([f"{d} — {ev}" for d,ev in added]))if removed: changes.append("• Production events — REMOVED:\n  " + "\n  ".join([f"{d} — {ev}" for d,ev in removed]))if not added and not removed: changes.append("• Production events changed order/content.")if not changes: return False, "No change detected."return True, "\n".join(changes)def format_snapshot_for_message(snap: Dict[str, Any], url: str) -> str:lines = [f"URL: {url}",f"LastUpdatedDate: {snap.get('lastUpdatedDate','')}",f"Status comment: {snap.get('statusComment','')}","Production events:"]events = snap.get("productionEvents") or []if not events:lines.append("  (none)")else:for e in events: lines.append(f"  - {e.get('date','')} — {e.get('event','')}")return "\n".join(lines)def run_daemon():prev_snap: Optional[Dict[str, Any]] = Nonebackoff = 300  # 错误时等待 5 分钟再试while not _stop:try:html = fetch_html(URL)new_snap = parse_snapshot(html)changed, diff_msg = diff_snapshots(prev_snap, new_snap)if prev_snap is None:logging.info("初始化成功:\n%s", format_snapshot_for_message(new_snap, URL))elif changed:snapshot_text = format_snapshot_for_message(new_snap, URL)message = f"{diff_msg}\n\n状态已更新:\n{snapshot_text}"send_notification(message)logging.info("通知已发送.")else:logging.info("状态无变化.")prev_snap = new_snap# 等待固定间隔for _ in range(int(INTERVAL_SECONDS)):if _stop: breaktime.sleep(1)except Exception as e:logging.exception("Cycle failed: %s", e)# 失败则短暂退避后重试for _ in range(backoff):if _stop: breaktime.sleep(1)if __name__ == "__main__":run_daemon()

http://www.xdnf.cn/news/18483.html

相关文章:

  • 可预约体验 | 一句话生成全栈应用,网易CodeWave智能开发能力全新升级!
  • TDengine IDMP 应用场景:工业锅炉监控
  • 资深产品经理个人能力提升方向:如何系统化进阶与考证规划
  • Maven快速入门
  • Day26 树的层序遍历 哈希表 排序算法 内核链表
  • 数据库服务语句应用
  • 【机器学习深度学习】多模态典型任务与应用全景
  • 深入理解Java多线程:状态、安全、同步与通信
  • Trae 编辑器在 Python 环境缺少 Pylance,怎么解决
  • 服务器支持IPv6吗?如何让服务器支持IPv6
  • 爬楼梯变式
  • Unreal Engine ATriggerVolume
  • [TG开发]部署机器人
  • Unreal Engine AActor
  • 【typenum】 22 类型级别二进制对数运算(Logarithm2)
  • 【Java SE】深入理解继承与多态
  • openstack的novnc兼容问题
  • GitCode 疑难问题诊疗:全面指南与解决方案
  • 94. 城市间货物运输 I, Bellman_ford 算法, Bellman_ford 队列优化算法
  • 智慧工厂烟雾检测:全场景覆盖与精准防控
  • Java基础 8.22
  • 2-3.Python 编码基础 - 类型检测与类型转换
  • 集成电路学习:什么是SVM支持向量机
  • AI 大模型 “进化史”:从参数竞赛到场景落地,技术突破藏着哪些逻辑?
  • Unreal Engine UFloatingPawnMovement
  • 【ECharts】2. ECharts 性能优化
  • kafka的rebalance机制是什么
  • CentOS 10安装Ollama
  • 12-Linux系统用户管理及基础权限
  • 机试备考笔记 18/31