"""极简 HTML→纯文本提取(纯标准库,零第三方依赖,避免引入许可不明的解析库)。 不追求完美排版,目标是从网页中抽取可读正文供模型摘要。会丢弃 script/style/ noscript 等非正文标签,折叠多余空白。 """ from html.parser import HTMLParser _SKIP_TAGS = {"script", "style", "noscript", "template", "svg", "head"} _BLOCK_TAGS = { "p", "div", "br", "li", "ul", "ol", "tr", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "header", "footer", } class _Extractor(HTMLParser): def __init__(self): super().__init__(convert_charrefs=True) self._parts: list[str] = [] self._skip_depth = 0 def handle_starttag(self, tag, attrs): if tag in _SKIP_TAGS: self._skip_depth += 1 elif tag in _BLOCK_TAGS: self._parts.append("\n") def handle_endtag(self, tag): if tag in _SKIP_TAGS and self._skip_depth > 0: self._skip_depth -= 1 elif tag in _BLOCK_TAGS: self._parts.append("\n") def handle_data(self, data): if self._skip_depth == 0 and data: self._parts.append(data) def text(self) -> str: raw = "".join(self._parts) # 折叠空白:去掉行内多余空格,压缩连续空行 lines = [ " ".join(line.split()) for line in raw.splitlines() ] out: list[str] = [] blank = False for line in lines: if line: out.append(line) blank = False elif not blank: out.append("") blank = True return "\n".join(out).strip() def html_to_text(html: str, max_chars: int = 6000) -> str: """把 HTML 转为纯文本并截断到 max_chars。解析失败时退化为原文截断。""" if not html: return "" try: parser = _Extractor() parser.feed(html) text = parser.text() except Exception: text = html if len(text) > max_chars: text = text[:max_chars] + "…" return text