first commit
This commit is contained in:
68
converter.py
Normal file
68
converter.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""把文章正文 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)
|
||||
Reference in New Issue
Block a user