Phase 2: 轉換管線 — 五個本地轉換器 + convert.py 統一入口 + selfcheck

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LittleYellow
2026-07-14 20:51:17 +08:00
parent 59c16ea393
commit 8d58436f05
9 changed files with 673 additions and 0 deletions

53
tools/convert/from_web.py Normal file
View File

@@ -0,0 +1,53 @@
"""網頁 URL → 完整 HTML 快照 + Markdown 正文萃取trafilatura全程本地處理
可獨立執行python tools/convert/from_web.py <url> [-o out.md] [--snapshot out.html] [--dry-run]
"""
import argparse
import pathlib
import trafilatura
def fetch(url):
"""抓取 URL回傳完整 HTML 字串。失敗時拋 RuntimeError。"""
html = trafilatura.fetch_url(url)
if not html:
raise RuntimeError(f"抓取失敗:{url}")
return html
def extract(html):
"""HTML → Markdown 正文。回傳 (markdown, status)。萃取不到正文時 status='empty'"""
md = trafilatura.extract(
html, output_format="markdown", include_tables=True, include_links=True
)
if not md or not md.strip():
return "", "empty"
return md.strip(), "ok"
def main(argv=None):
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("url")
ap.add_argument("-o", "--output")
ap.add_argument("--snapshot", help="HTML 快照輸出路徑")
ap.add_argument("--dry-run", action="store_true")
a = ap.parse_args(argv)
html = fetch(a.url)
md, status = extract(html)
if a.dry_run:
print(f"[dry-run] {a.url}: status={status}, snapshot {len(html)} chars, "
f"markdown {len(md)} chars")
return
if status != "ok":
print(f"empty: {a.url} 萃取不到正文")
raise SystemExit(2)
if a.snapshot:
pathlib.Path(a.snapshot).write_text(html, encoding="utf-8")
out = pathlib.Path(a.output) if a.output else pathlib.Path("page.md")
out.write_text(md, encoding="utf-8")
print(out)
if __name__ == "__main__":
main()