first commit
This commit is contained in:
86
writer.py
Normal file
86
writer.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""产物输出:图片本地化、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")
|
||||
Reference in New Issue
Block a user