"""产物输出:图片本地化、TS 数据文件、JSON、Markdown 归档。""" import re from pathlib import Path from config import CHAN_IMG_DIR, CHAN_IMG_URL_PREFIX, CHAN_DATA_FILE from fetcher import fetch_bytes def _guess_ext(url: str) -> str: m = re.search(r"\.(png|jpe?g|gif|webp|bmp|svg)", url, re.I) return "." + m.group(1).lower() if m else ".png" def remote_image_resolver(src: str, alt: str) -> str: """不下载图片,正文保留远程链接。""" return f"![{alt}]({src})" def make_image_resolver(p_num: int, refresh: bool = False): """返回一个闭包:把图片下载到 Vue 项目的 public 目录,返回可访问 URL。 下载失败时回退到远程链接,保证正文不会因单张图片而中断。 """ CHAN_IMG_DIR.mkdir(parents=True, exist_ok=True) seq = 0 def resolve(src: str, alt: str) -> str: nonlocal seq seq += 1 ext = _guess_ext(src) fname = f"p{p_num:03d}_{seq:02d}{ext}" dest = CHAN_IMG_DIR / fname if refresh or not dest.exists(): try: dest.write_bytes(fetch_bytes(src)) except Exception as e: # noqa: BLE001 print(f" [img] 下载失败,使用远程链接:{src}({e})") return f"![{alt}]({src})" url = f"{CHAN_IMG_URL_PREFIX}/{fname}" return f"![{alt}]({url})" return resolve def _ts_escape(s: str) -> str: """转义模板字符串中的特殊字符:反斜杠、反引号、${。""" return s.replace("\\", "\\\\").replace("`", "\\`").replace("${", "\\${") def _ts_str(s: str) -> str: return "`" + _ts_escape(s) + "`" def write_ts_file(articles: list[dict], path: Path, stamp: str) -> None: """生成 ``chanlun.ts``,导出 ``chanlunLessons: Article[]``。""" lines: list[str] = [] lines.append("import type { Article } from '@/types/article'") lines.append("") lines.append("/**") lines.append(" * 缠论《教你炒股票》系列原文") lines.append(f" * 共 {len(articles)} 课 · 抓取自 https://www.kline8.com/chanlun") lines.append(f" * 由 py-chan 爬虫自动生成 · {stamp}") lines.append(" */") lines.append("export const chanlunLessons: Article[] = [") for a in articles: lines.append(" {") lines.append(f" id: {_ts_str(a['id'])},") lines.append(f" slug: {_ts_str(a['slug'])},") lines.append(" category: 'chanlun',") lines.append(f" title: {_ts_str(a['title'])},") lines.append(f" subtitle: {_ts_str(a['subtitle'])},") lines.append(f" lesson: {a['lesson']},") lines.append(f" date: {_ts_str(a['date'])},") tags = ", ".join(_ts_str(t) for t in a["tags"]) lines.append(f" tags: [{tags}],") lines.append(f" excerpt: {_ts_str(a['excerpt'])},") lines.append(f" readingTime: {a['readingTime']},") lines.append(f" content: {_ts_str(a['content'])},") lines.append(" },") lines.append("]") lines.append("") path.parent.mkdir(parents=True, exist_ok=True) path.write_text("\n".join(lines), encoding="utf-8")