87 lines
3.1 KiB
Python
87 lines
3.1 KiB
Python
"""产物输出:图片本地化、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""
|
||
|
||
|
||
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""
|
||
url = f"{CHAN_IMG_URL_PREFIX}/{fname}"
|
||
return f""
|
||
|
||
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")
|