"""极简 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