海报文字识别到活动日历:Python+OCR提取事件信息并生成倒计时页面
2026/9/16 21:37:08 网站建设 项目流程

接到一个很典型的需求:手上有一张线下活动海报,上面写着“Sonya Saranphat Hong Kong FC - 20260808 SWEET HONEYMOON PARTY FAN MEETING”,需要把这行信息快速拆成日期、地点、活动类型,然后生成日历提醒和倒计时页面。手动整理一条没问题,但如果是几十张海报、频繁更新,就必须用程序处理。

这次就来跑通一条“海报文字识别 → 字段解析 → 结构化输出 → 日历/网页生成”的完整流程。文章会给出 Python 代码、批量处理思路和 FastAPI 接口示例,全程可以本地部署,OCR 部分用普通 CPU 也能跑,显卡只是可选项。整体不依赖云端,适合活动运营、粉丝组织、线下展会信息整理,以及任何需要从图片中抽取事件的场景。

1. 核心能力速览与适用边界

能力项说明
主要目标把活动海报图片转换为结构化 JSON、ICS 日历文件和 HTML 倒计时页面
输入素材活动海报 JPG/PNG/PDF 扫描件、截图
输出结果OCR 原始文本、结构化字段 JSON、.ics 日历文件、本地 HTML 页面
OCR 引擎PaddleOCR / Tesseract / 云 OCR(可替换),本文以 PaddleOCR 为例给出本地实现
硬件要求普通 CPU 可运行;OCR 批处理时启用 GPU 可缩短耗时
支持平台Windows / Linux / macOS
启动方式Python 脚本运行;FastAPI 服务方式
接口能力可通过 HTTP 上传图片并返回 JSON 字段
批量任务支持目录批量扫描,每次输出独立 JSON 和 ICS
关键依赖Python 3.9+、PaddleOCR、OpenCV、python-dateutil、ics、Jinja2、FastAPI
适合场景粉丝见面会、音乐会、展会、球赛等活动的日程整理与站点发布
不适合场景未授权的商业图片采集、涉及非公开个人信息的批量处理

先明确使用边界。OCR 只负责“读取图片里的文字”,不负责判断信息的真实性和授权状态。如果海报中包含人物肖像、主办方 Logo、非公开联系方式,本地识别用于个人日程没有问题;但要把识别结果、裁剪图片或重建页面发布到公网,必须确认获得了主办方或权属方的授权。涉及真实艺人和线下活动时,尤其要避免伪造、篡改、误导性宣传。建议只处理你自己有权处理的海报素材,批量抓取他人页面图片再转存,可能违反平台条款或版权规定。

2. 环境准备与项目初始化

2.1 创建虚拟环境

推荐使用虚拟环境隔离依赖,避免污染系统 Python。可以新建一个目录:

mkdir activity_parser cd activity_parser python -m venv venv

Windows 激活方式:

venv\Scripts\activate

Linux / macOS 激活方式:

source venv/bin/activate

2.2 安装依赖

PaddleOCR 依赖 PaddlePaddle 深度学习框架。PaddlePaddle 不同版本对应不同 CUDA 环境,建议先到 PaddlePaddle 官方安装页选择符合自己系统的安装命令,再安装 PaddleOCR。

CPU 环境的基础安装指令如下,实际版本号以官方为准:

pip install paddlepaddle pip install paddleocr pip install opencv-python pip install python-dateutil pip install ics pip install jinja2 pip install fastapi pip install uvicorn

如果网络条件允许,同时安装pytesseract作为备用 OCR 引擎:

pip install pytesseract

Tesseract 还需要系统级安装,不是只装 Python 包就能运行。Windows 需要到 UB Mannheim 的 Tesseract 安装包页面安装;Linux 可以用apt install tesseract-ocr tesseract-ocr-chi-sim。文章中不会把 Tesseract 作为主流程,因为 PaddleOCR 对中文海报文字识别开箱即用效果更稳,但你要知道存在这个备选。

2.3 准备目录结构

activity_parser/ ├── inputs/ # 海报原图 ├── ocr_output/ # OCR 原始结果 ├── json_output/ # 结构化 JSON ├── ics_output/ # 日历文件 ├── page_output/ # HTML 倒计时页面 ├── templates/ # Jinja2 模板 ├── ocr_engine.py ├── parser.py ├── batch_extract.py └── server.py

提前创建目录,避免运行时找不到路径:

mkdir inputs ocr_output json_output ics_output page_output templates

这一步没有特殊门槛。PaddleOCR 第一次运行时需要下载检测、识别模型,需要保持网络畅通;下载完模型会缓存在本地,后续离线也能跑。

3. 单张海报文字识别(OCR)

3.1 OCR 引擎封装

写一个ocr_engine.py,把 PaddleOCR 初始化成单例。这样在批量任务中不会重复加载模型,降低内存占用。

import os from pathlib import Path try: from paddleocr import PaddleOCR except ImportError: PaddleOCR = None OCR_ENGINE = None OUTPUT_DIR = Path("ocr_output") OUTPUT_DIR.mkdir(exist_ok=True) def get_ocr_engine(): global OCR_ENGINE if OCR_ENGINE is None: if PaddleOCR is None: raise RuntimeError("PaddleOCR 未安装,请先安装 paddleocr") OCR_ENGINE = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False) return OCR_ENGINE def ocr_image(image_path: str) -> list[str]: engine = get_ocr_engine() result = engine.ocr(image_path, cls=True) lines = [] # 不同版本 PaddleOCR 返回结构略有差异,这里做兼容处理 for page in result: if not page: continue for line in page: try: # 常见版本: [[box], (文本, 置信度)] text = line[1][0] confidence = float(line[1][1]) lines.append({"text": text, "confidence": confidence}) except Exception: continue # 把 OCR 原始文本落盘,方便排查 stem = Path(image_path).stem with open(OUTPUT_DIR / f"{stem}_ocr.json", "w", encoding="utf-8") as fp: json.dump(lines, fp, ensure_ascii=False, indent=2) return lines

OCR 识别完成后,原始文本按行保存到ocr_output/目录。这个步骤的意义是保留中间结果。后续字段解析如果出错,可以回到这一步检查是 OCR 没读出来,还是正则规则没覆盖到。

3.2 海报样例

假设一张海报上的核心文案是:

Sonya Saranphat Hong Kong FC 20260808 SWEET HONEYMOON PARTY FAN MEETING

实际情况中,海报还会带主办方、场馆地址、开票时间、嘉宾信息,OCR 会把这堆文字混在一起输出,顺序不一定和视觉一致。因此要做字段解析,而不是直接对一行字符串做切片。

3.3 验证 OCR 是否成功

运行以下命令测试单张图片:

python - <<'PY' from ocr_engine import ocr_image lines = ocr_image("inputs/fan_meeting.png") for item in lines: print(item["text"], round(item["confidence"], 4)) PY

判断标准:

  • 八位日期20260808能原样输出;
  • 英文地名、活动名基本完整;
  • 中文文字没有大面积乱码。

如果 OCR 结果出现大量空白,先检查图片亮度和对比度;如果文字扭曲,需要做透视矫正后再识别。

4. 字段解析与结构化输出

OCR 只是把图片变成文本。接下来要把“散装文本”变成带语义的字段:活动名称、地点、日期、活动类型、组织方。

4.1 用正则提取字段

写一个parser.py,先定义关键词表,再用正则匹配日期规律。这种方案不完美,但对于命名规则相对统一的活动海报,够用。

import json import re from datetime import datetime from pathlib import Path from zoneinfo import ZoneInfo OUTPUT_DIR = Path("json_output") OUTPUT_DIR.mkdir(exist_ok=True) def parse_event_from_ocr_text(text: str) -> dict: upper_text = text.upper() # 1. 八位日期:20260808 date_match = re.search(r"(20\d{2})[^\d]?(\d{2})[^\d]?(\d{2})", text) event_date = None if date_match: year = int(date_match.group(1)) month = int(date_match.group(2)) day = int(date_match.group(3)) event_date = datetime(year, month, day).date().isoformat() # 2. 活动地点:规则匹配 "Hong Kong" 等常见城市 location = None for keyword in ["Hong Kong", "HONG KONG", "Bangkok", "Taipei", "Singapore"]: if keyword in upper_text: location = keyword.title() break # 3. 活动类型:关键词匹配 event_type = None if "FAN MEET" in upper_text or "FANMEET" in upper_text: event_type = "Fan Meeting" elif "CONCERT" in upper_text: event_type = "Concert" elif "EXHIBITION" in upper_text: event_type = "Exhibition" # 4. 活动名称:取一个尽量可读的文本 event_name_match = re.search(r"SWEET[ A-Z0-9]*", upper_text) event_name = None if event_name_match: start = event_name_match.start() # 尽量还原原始大小写 raw_name = text[start:start + len(event_name_match.group(0))] event_name = raw_name.strip() # 5. 主要人物 / 组织名:这里只做最小实现 # 实际项目会维护关键词表 entity_keywords = ["Sonya Saranphat"] mentions = [] for entity in entity_keywords: if entity.lower() in text.lower(): mentions.append(entity) return { "event_name": event_name, "event_date": event_date, "location": location, "event_type": event_type, "mentioned_entities": mentions, "raw_text": text.strip(), } def parse_ocr_result(lines: list[dict]) -> dict: text = "\n".join([item["text"] for item in lines if item.get("text")]) return parse_event_from_ocr_text(text) def save_result(result: dict, input_name: str): stem = Path(input_name).stem output_file = OUTPUT_DIR / f"{stem}.json" with open(output_file, "w", encoding="utf-8") as fp: json.dump(result, fp, ensure_ascii=False, indent=2) return output_file

这段代码针对“海报核心文案是标准英文字段”的场景。如果活动名称完全是小写或包含中文,正则规则需要扩展。最稳妥的做法不是用一条超长正则匹配所有情况,而是做规则组合:

  • 先找特殊日期编码,比如202608082026-08-082026/08/0808 AUG 2026
  • 再找地名关键词表;
  • 然后用事件类型关键词表分类。

结构上把这三张表抽出来,后续新海报只扩展表,不重写解析逻辑。

4.2 单张海报主流程

把 OCR 和解析串起来,执行下面命令:

python - <<'PY' from ocr_engine import ocr_image from parser import parse_ocr_result, save_result image = "inputs/fan_meeting.png" lines = ocr_image(image) result = parse_ocr_result(lines) print(result) print(save_result(result, image)) PY

预期 JSON 输出:

{ "event_name": "SWEET HONEYMOON PARTY", "event_date": "2026-08-08", "location": "Hong Kong", "event_type": "Fan Meeting", "mentioned_entities": ["Sonya Saranphat"], "raw_text": "Sonya Saranphat Hong Kong FC\n20260808\nSWEET HONEYMOON PARTY FAN MEETING" }

这就是结构化输出。只要 OCR 文本里有这些关键字,解析器就能建立可查询的事件库。

5. 生成日历与倒计时页面

结构化 JSON 还只是一个中间产物。日常使用中,用户需要两种落地形式:能被手机日历直接导入的 ICS 文件,以及能在浏览器里打开的倒计时页面。

5.1 生成 ICS 日历事件

使用ics库创建日历事件。关键点是要指定时区。20260808这样的纯日期是否表示当地时间,需要依赖活动举办地。在此以 Hong Kong 所在的Asia/Hong_Kong为例。

from pathlib import Path from ics import Calendar, Event from zoneinfo import ZoneInfo from datetime import datetime ICS_DIR = Path("ics_output") ICS_DIR.mkdir(exist_ok=True) def generate_ics(event_result: dict, input_stem: str = "event"): cal = Calendar() ev = Event() event_name = event_result.get("event_name") or "Event" ev.name = event_name event_date = event_result.get("event_date") if event_date: # 默认按活动当地时区解释日期,缺少开始时间时按 00:00 占位 tz = ZoneInfo("Asia/Hong_Kong") local_dt = datetime.fromisoformat(event_date + "T00:00:00").replace(tzinfo=tz) ev.begin = local_dt # 这里不设置 duration,让日历自动判断为全天事件的样式 # 如果后续能识别到开始时间,再补充 ev.begin 的时分秒 location = event_result.get("location") if location: ev.location = location event_type = event_result.get("event_type") if event_type: ev.description = f"Type: {event_type}" if event_result.get("mentioned_entities"): ev.description += " | Entity: " + ", ".join(event_result["mentioned_entities"]) cal.events.add(ev) output_file = ICS_DIR / f"{input_stem}.ics" with open(output_file, "w", encoding="utf-8") as fp: fp.write(str(cal)) return output_file

生成的 ICS 文件可以导入 Google Calendar、Apple Calendar、Outlook 和大多数手机日历应用。这里把开始时间设为活动当天零点,严格来说只是占位。如果你的海报里有明确的开始时间,比如Door Open 18:00,需要额外解析时分,再把ev.begin改成16:00:00真正的时间。

5.2 生成倒计时 HTML 页面

用 Jinja2 写一个模板,输出活动名称、日期、地点和倒计时脚本。倒计时脚本在浏览器本地运行,不依赖后端。

先创建模板文件templates/event_page.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ event_name }}</title> <style> body { font-family: Arial, sans-serif; max-width: 640px; margin: 40px auto; padding: 16px; } .info { margin: 12px 0; font-size: 18px; } #countdown { font-size: 28px; font-weight: bold; color: #b45a23; } </style> </head> <body> <h1>{{ event_name }}</h1> <div class="info">日期:{{ event_date }}</div> <div class="info">地点:{{ location }}</div> <div class="info">类型:{{ event_type }}</div> <h2>倒计时</h2> <div id="countdown">--</div> <p><a href="{{ ics_filename }}">下载日历文件</a></p> <script> const eventDate = new Date("{{ event_date }}T00:00:00+08:00").getTime(); function updateCountdown() { const now = Date.now(); const diff = eventDate - now; if (diff <= 0) { document.getElementById("countdown").textContent = "活动进行中,祝顺利!"; return; } const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff / (1000 * 60 * 60)) % 24); const minutes = Math.floor((diff / (1000 * 60)) % 60); const seconds = Math.floor((diff / 1000) % 60); document.getElementById("countdown").textContent = days + " 天 " + hours + " 小时 " + minutes + " 分 " + seconds + " 秒"; } updateCountdown(); setInterval(updateCountdown, 1000); </script> </body> </html>

用 Jinja2 渲染。注意时区:如果使用东八区时间,模板里写+08:00。真实活动需要根据举办地选择时区,否则手机会提醒错时间。

from pathlib import Path from zoneinfo import ZoneInfo from datetime import datetime from jinja2 import Environment, FileSystemLoader PAGE_DIR = Path("page_output") PAGE_DIR.mkdir(exist_ok=True) env = Environment(loader=FileSystemLoader("templates")) template = env.get_template("event_page.html") def generate_page(event_result: dict, ics_filename: str, input_stem: str = "event"): event_name = event_result.get("event_name") or "Event" # 方便浏览器读取的 ISO 日期字符串,示例只处理日期,不带具体时间 event_date = event_result.get("event_date") local_tz = ZoneInfo("Asia/Hong_Kong") display_time = None if event_date: dt = datetime.fromisoformat(event_date).replace(tzinfo=local_tz) display_time = dt.isoformat() html = template.render( event_name=event_name, event_date=event_result.get("event_date"), location=event_result.get("location") or "待定", event_type=event_result.get("event_type") or "未分类", ics_filename=ics_filename, ) page_file = PAGE_DIR / f"{input_stem}.html" page_file.write_text(html, encoding="utf-8") return page_file

最后,用一个脚本串联完整流程。

python - <<'PY' from ocr_engine import ocr_image from parser import parse_ocr_result, save_result from ics_writer import generate_ics from page_writer import generate_page image = "inputs/fan_meeting.png" stem = image.replace("/", "_").replace(".png", "").replace(".jpg", "") lines = ocr_image(image) result = parse_ocr_result(lines) save_result(result, image) ics_file = generate_ics(result, stem) page_file = generate_page(result, ics_file.name, stem) print("JSON:", result) print("ICS:", ics_file) print("HTML:", page_file) PY

打开生成的 HTML 页面,看到活动名称和倒计时数字,说明整条流程已经跑通。

6. 批量任务与 API 接口

6.1 批量处理目录下所有海报

批量任务要考虑几点:输入文件格式、循环顺序、失败隔离、重复处理。最省事的方式是用Path.rglob遍历inputs目录下所有图片。每一张图片独立处理,某一张失败不能中断整个目录。

from pathlib import Path import traceback from ocr_engine import ocr_image from parser import parse_ocr_result, save_result from ics_writer import generate_ics from page_writer import generate_page IMAGE_SUFFIX = {".jpg", ".jpeg", ".png", ".bmp", ".webp"} def run_batch(input_dir: str = "inputs"): input_dir = Path(input_dir) success = [] failed = [] for image_path in sorted(input_dir.rglob("*")): if image_path.suffix.lower() not in IMAGE_SUFFIX: continue stem = image_path.stem print("开始处理:", image_path) try: lines = ocr_image(str(image_path)) result = parse_ocr_result(lines) save_result(result, str(image_path)) ics_file = generate_ics(result, stem) page_file = generate_page(result, ics_file.name, stem) success.append({ "file": str(image_path), "json": str(save_result(result, str(image_path))), "ics": str(ics_file), "html": str(page_file), }) except Exception: failed.append(str(image_path)) traceback.print_exc() print("成功:", len(success), "失败:", len(failed)) return {"success": success, "failed": failed} if __name__ == "__main__": run_batch()

判断批量任务是否成功的标准:json_outputics_outputpage_output三个目录中有对应文件生成;失败列表为空,或失败原因都是同一类问题。

6.2 FastAPI 上传接口

把批处理服务化,可以让其他人员通过网页上传一张海报,也可以让内部工具批量提交。下面是一个最小 FastAPI 服务,上传文件后立刻执行“识别 + 解析 + 生成 json”。

import tempfile from pathlib import Path from fastapi import FastAPI, File, UploadFile, HTTPException from ocr_engine import ocr_image from parser import parse_ocr_result app = FastAPI() @app.post("/api/extract") async def extract_activity(file: UploadFile = File(...)): suffix = Path(file.filename).suffix.lower() if suffix not in {".jpg", ".jpeg", ".png", ".bmp", ".webp"}: raise HTTPException(status_code=400, detail="不支持的图片格式") content = await file.read() if not content: raise HTTPException(status_code=400, detail="上传内容为空") with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: tmp.write(content) tmp_path = tmp.name try: lines = ocr_image(tmp_path) result = parse_ocr_result(lines) return result finally: Path(tmp_path).unlink(missing_ok=True)

启动接口服务:

uvicorn server:app --host 127.0.0.1 --port 8000

调用测试:

curl -X POST http://127.0.0.1:8000/api/extract \ -F "file=@inputs/fan_meeting.png"

返回示例:

{ "event_name": "SWEET HONEYMOON PARTY", "event_date": "2026-08-08", "location": "Hong Kong", "event_type": "Fan Meeting", "mentioned_entities": ["Sonya Saranphat"], "raw_text": "Sonya Saranphat Hong Kong FC\n20260808\nSWEET HONEYMOON PARTY FAN MEETING" }

接口跑通后,就可以把它接到后台管理系统、桌面小工具,或者其他需要“传图 → 得到结构化事件”的业务中。生产环境使用接口服务时,建议加访问控制。最简单的方式是设置 API Token 或只允许内网访问,不要把服务直接暴露到公网。

7. 资源占用与性能观察

这篇流程不需要大显存显卡。PaddleOCR 的模型较小,CPU 模式就能完成识别。资源占用主要取决于图片分辨率和批量并发数。

实际观察的维度:

  • 启动阶段:PaddleOCR 第一次初始化会加载检测模型和识别模型,此时内存占用升高,但只发生一次。
  • 单张识别:长边 2000 像素的普通海报,CPU 识别通常在秒级到十几秒之间,具体视 CPU 和图片复杂度而定。
  • 批量任务:如果串行处理,内存稳定但耗时长;如果多用线程池并发处理,需要控制ThreadPoolExecutor(max_workers),因为 OCR 引擎和 PaddleOCR 内部并不是绝对的线程安全。
  • GPU 加速:PaddlePaddle 的 GPU 版本可以缩短识别时间,但需要额外安装 CUDA 环境。粉丝海报这种偏文字图,CPU 已经能跑,没必要一上来就配置 GPU。

减少资源占用的常用手段是预处理图片。识别前先用 OpenCV 转灰度、增强对比度,可以降低无效像素数量:

import cv2 def preprocess_image(image_path: str): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) resized = cv2.resize(gray, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC) _, thresh = cv2.threshold(resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) return thresh

预处理不一定能提升所有图片的准确度。文字底色复杂、带强烈艺术字的海报,反而可能因为二值化丢失信息。建议保留原图和预处理两套流程,对比后选择更稳的方案。

8. 常见问题与排查方法

问题现象可能原因排查方式解决方案
PaddleOCR 初始化时报错PaddlePaddle 版本不匹配查看错误堆栈,检查 paddle 版本按 PaddlePaddle 官方安装页重装对应版本
第一次运行需要下载模型却卡住网络不稳定检查模型缓存目录,稍后重试配置镜像或手动放置模型文件到缓存目录
识别结果为空图片亮度低、对比度低、文字过小用 OpenCV 预览预处理后的图片增强对比度、放大图片、去倾斜后再识别
OCR 输出中文乱码图片有非常规字体或字符集识别受限检查 OCR 日志中是否加载了中文识别模型确保lang="ch",必要时补充字体样本
日期解析失败海报日期不是20260808格式打印 OCR 文本,确认日期实际写法增加正则分支,支持/-分隔
生成的 ICS 导入日历后时间差一天时区没有设置,或 UTC 转换错误打开 ICS 文件查看 VTIMEZONE 和 DTSTART确保使用ZoneInfo并保持本地时区语义
批量任务只处理了第一张图遍历逻辑只匹配了固定文件名检查目录循环后缀过滤使用rglob和完整后缀集合
API 上传后返回 413请求体超过服务器限制查看 uvicorn 日志在调用端限制文件大小,或在服务前增加反向代理限制
接口调用超时OCR 本身耗时较长单张图片测试响应时间增大 HTTP 超时,或改为异步任务队列
输出结果不稳定不同海报规则差异大对多条海报回放 OCR 文本建立“字段关键词表 + 正则”双通道,维持回归样例

其中最容易忽略的是时区问题。解析出20260808并不等于可以在日历里直接盲写2026-08-08 UTC。活动日期应该绑定到举办地时区。建议程序内部统一保存“原始本地时间字符串 + 时区名”,生成 ICS 时做一次正确的replace(tzinfo=...),生成页面时再用浏览器本地时间计算倒计时。不要把两种语义混在一个字段里。

9. 最佳实践与总结

这套流程最适合作为“活动信息本地整理工具”的第一步。开发时不建议一开始就追求识别所有海报,先准备一套包含 10 张不同风格海报的测试集,每张都要输出 JSON 字段,然后把失败的案例逐步补进正则或关键词表。

几个实用建议:

  • 保留 OCR 原始文本。不要只存最终 JSON,否则字段解析规则迭代时需要重新跑全部图片。
  • 输入文件与输出文件分目录管理。inputs只放原始图片,json_outputics_outputpage_output单独建目录,方便一键清理。
  • 批量任务加日志。每张图片处理时间、成功状态、输出路径都要写入日志文件,出现中断时可以断点续跑。
  • 批量任务加失败重试机制。OCR 偶尔会因为下载模型、临时文件读写错误失败,重试两次可以明显降低失败率。
  • API 服务要限制访问范围。如果只是本地内部用,监听127.0.0.1就够了;如果需要局域网访问,再加 Token 鉴权。
  • 涉及真实人物、品牌、主办方的海报,发布前必须确认授权。本地解析不构成侵权,但把海报剪裁、转存、上传到公网平台需要另行判断。

如果你第一次跑通,优先验证三件事:单张海报能否稳定识别出20260808这八位日期;能否把英文活动名完整归入event_name;ICS 文件导入手机日历后,活动日期是否和海报一致。这三步通过后,再往下加批量任务和 API 服务。

继续扩展的方向也很明确:把结构化 JSON 存入 SQLite 或 PostgreSQL,做一个简易的活动日历管理后端;增加门票状态字段,处理后端通知;接入手机日历订阅链接,让粉丝看到更新后能一键订阅。核心逻辑就是这里“图片文本 → 结构化事件 → 日历/页面”这条管线,后续只是换入口和出口。

如果只是偶尔整理几张活动海报,直接用脚本就行,不需要写 API;如果要把能力开放给团队或重复使用的工具,再上 FastAPI。建议先把这条流程的代码和测试样例保存下来,后续遇到新的活动海报,直接丢进inputs目录跑一次批量任务,数据就整理好了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询