"""把文章正文 HTML 片段转换为 Markdown。 文章正文是若干 ``

`` 段落,内部可能含 ``
``、````、````、 ```` 等。本模块递归地把行内节点转成 Markdown,```` 通过 ``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: """把文章正文的块级元素(``

`` 与块级 ```` 等,按文档顺序)转为 Markdown。 源页面的图表是 ``container-narrow`` 的直接子 ````,与 ``

`` 交替出现, 因此必须按顺序遍历而非只取 ``

``。块内空白折叠为单空格,块间用空行分隔。 """ 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)