Files
py-chan/converter.py
2026-08-11 16:18:44 +08:00

69 lines
2.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""把文章正文 HTML 片段转换为 Markdown。
文章正文是若干 ``<p>`` 段落,内部可能含 ``<br>``、``<strong>``、``<a>``、
``<img>`` 等。本模块递归地把行内节点转成 Markdown``<img>`` 通过
``resolve_image`` 回调决定最终链接(本地化或远程)。
"""
import re
from urllib.parse import urljoin
from bs4 import NavigableString, Tag
from config import BASE_URL
_WS_RE = re.compile(r"\s+")
def _abs(href: str) -> str:
"""把相对地址补成绝对 URL。"""
if not href:
return ""
return urljoin(BASE_URL + "/", href)
def _inline(node, resolve_image) -> str:
"""递归转换单个节点为 Markdown 文本。"""
if isinstance(node, NavigableString):
return str(node)
if not isinstance(node, Tag):
return ""
name = node.name
if name == "br":
return "\n"
inner = "".join(_inline(c, resolve_image) for c in node.children)
if name in ("strong", "b"):
s = inner.strip()
return f"**{s}**" if s else ""
if name in ("em", "i"):
s = inner.strip()
return f"*{s}*" if s else ""
if name == "a":
text = inner.strip()
href = _abs(node.get("href", ""))
return f"[{text}]({href})" if text else ""
if name == "img":
src = _abs(node.get("src", ""))
alt = (node.get("alt") or "").strip()
return resolve_image(src, alt)
# span / div / font / u 等容器:透传内容
return inner
def blocks_to_markdown(elements, resolve_image) -> str:
"""把文章正文的块级元素(``<p>`` 与块级 ``<img>`` 等,按文档顺序)转为 Markdown。
源页面的图表是 ``container-narrow`` 的直接子 ``<img>``,与 ``<p>`` 交替出现,
因此必须按顺序遍历而非只取 ``<p>``。块内空白折叠为单空格,块间用空行分隔。
"""
blocks: list[str] = []
for el in elements:
text = _WS_RE.sub(" ", _inline(el, resolve_image)).strip()
if text:
blocks.append(text)
return "\n\n".join(blocks)