first commit

This commit is contained in:
2026-08-11 16:18:44 +08:00
commit 5cc322a598
231 changed files with 33294 additions and 0 deletions

86
writer.py Normal file
View 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"![{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")