趋近智
Assistant
在线
趋近智
从大型爬取(如Common Crawl)获取的网页通常以HTML编码,它不仅包含主要文本内容,还包括结构标记 (token)、样式信息(CSS)、交互元素(JavaScript)、导航菜单、广告、页眉、页脚以及其他常被称为“样板内容”的周边文本。将这些无关材料留在训练数据中可能引入噪音,稀释高质量文本的有效信息,并使模型对自然语言结构的理解产生偏差。因此,有效移除标记和样板内容是为LLM预训练 (pre-training)准备网页数据的标准步骤。
处理HTML文档的第一步是解析其结构。原始HTML是文本字符串,但库可以将其转换为结构化表示,通常是树形结构,便于浏览和处理。Python为此提供了几个优秀的库。Beautiful Soup 因其灵活性和易用性而广泛使用,尤其擅长处理网页上常见的非标准或格式错误的HTML。lxml 是另一个功能强大的选择,以其速度著称,常被用作 Beautiful Soup 的后端解析器。
这是一个使用 Beautiful Soup 解析HTML字符串并提取所有文本内容的简单示例,去除了标签:
from bs4 import BeautifulSoup
import torch # PyTorch集成未来的占位符
# 示例HTML内容(实际情况通常复杂得多)
html_doc = """
<html><head><title>Sample Page</title>
<style>body {font-size: 12px;}</style></head>
<body>
<header><h1>Main Title</h1><nav><a>Home</a> <a>About</a></nav></header>
<main>
<p>This is the primary article content we want to keep.
It discusses important topics.</p>
<p>Another paragraph of useful information.</p>
<script>console.log('Some script');</script>
</main>
<footer><p>Copyright 2023. Some footer links.</p></footer>
</body></html>
"""
soup = BeautifulSoup(html_doc, 'lxml') # 使用lxml解析器
# 获取所有文本,去除标签
all_text = soup.get_text(separator=' ', strip=True)
print("所有文本(包含样板内容):")
print(all_text)
# 输出:Sample Page Main Title Home About 这是我们希望保留的主要文章
# 内容。它讨论了重要主题。另一个有用的信息段落。console.log('Some script'); 版权所有
# 2023。一些页脚链接。
# 尝试只获取主要内容文本(简单示例)
main_content_tag = soup.find('main')
if main_content_tag:
main_text = main_content_tag.get_text(separator=' ', strip=True)
print("\n主要内容文本(简单提取):")
print(main_text)
# 输出:这是我们希望保留的主要文章内容。它
# 讨论了重要主题。另一个有用的信息段落。console.log('Some script');
else:
print("\n未找到'main'标签。")
正如示例所示,简单地对整个文档调用 get_text() 通常会包含来自页眉、页脚以及(如果包含文本节点)脚本的不必要文本。虽然查找像 <main> 这样的特定标签会有所帮助,但这依赖于语义化HTML的使用,而这在不同网站之间并不总是一致的。还要注意到,上述简单提取仍然包含了 <main> 中 <script> 标签的内容。
一个常见的起点是使用基于规则的方法。这包括识别与样板内容相关联的HTML标签,并在提取文本之前将其及其内容完全移除。常见目标包括:
<script>: 包含JavaScript代码。<style>: 包含CSS样式规则。<nav>: 通常包含网站导航链接。<header>: 通常包含徽标、网站标题和顶部导航。<footer>: 通常包含版权声明、联系信息和底部链接。<aside>: 表示侧边栏或与主要内容略微相关的部分。<iframe> 标签)。我们可以使用 Beautiful Soup 的 decompose() 方法在文本提取之前从解析树中移除这些元素。
from bs4 import BeautifulSoup
import torch # 占位符
html_doc = """
<html><head><title>Sample Page</title>
<style>body {font-size: 12px;}</style></head>
<body>
<header><h1>Main Title</h1><nav><a>Home</a> <a>About</a></nav></header>
<main>
<p>This is the primary article content we want to keep.
It discusses important topics.</p>
<div class="sidebar"><p>Related Links</p></div>
<p>Another paragraph of useful information.</p>
<script>console.log('Some script');</script>
</main>
<footer><p>Copyright 2023. Some footer links.</p></footer>
</body></html>
"""
soup = BeautifulSoup(html_doc, 'lxml')
# 要删除的标签
tags_to_remove = ['script', 'style', 'nav', 'header', 'footer', 'aside']
# 也删除通过常见样板内容类/ID识别的元素(示例)
selectors_to_remove = ['.sidebar', '#ads']
for tag_name in tags_to_remove:
for tag in soup.find_all(tag_name):
tag.decompose() # 删除标签及其内容
for selector in selectors_to_remove:
for tag in soup.select(selector): # 使用CSS选择器
tag.decompose()
# 从修改后的BeautifulSoup对象中提取文本
cleaned_text = soup.get_text(separator=' ', strip=True)
print("基于规则删除后的文本:")
print(cleaned_text)
# 输出:示例页面 这是我们希望保留的主要文章内容。
# 它讨论了重要主题。另一个有用的信息段落。
尽管基于规则的方法在移除显而易见的样板内容方面很有效,但它们也有限制。它们可能很脆弱;网站内容结构各异,依赖 <main> 等特定标签或 sidebar 等类名并非普遍可靠。如果例如,图的标题放在 <footer> 标签内,激进的移除也可能丢弃有用信息。在多样化的网络上维护全面的规则是一项挑战。
更高级的技术侧重于识别页面的主要内容块,而不仅仅是删除已知的样板标签。这些算法通常基于以下原则运作:主要内容在其部分内的文本密度通常相对于HTML标记 (token)量更高,而导航菜单或页脚包含许多链接(标记)但连续文本较少。
像 trafilatura 这样的库是专门为此任务设计的,通常包含关于文本密度、链接密度、标签模式和文档结构的启发式规则。它们旨在稳定地从新闻页面、博客和其他网页文档中提取核心文章文本,同时丢弃周围的样板内容。
使用 trafilatura 通常很简单:
import trafilatura
import torch # 占位符
# 假设 'html_doc' 包含先前示例中的原始HTML字符串
# 对于trafilatura,通常最好直接为其提供原始HTML字符串
# 第二个HTML文档的示例
html_doc = """
<html><head><title>Sample Page</title><style>body {font-size: 12px;}</style></head>
<body>
<header><h1>Main Title</h1><nav><a>Home</a> <a>About</a></nav></header>
<main>
<p>This is the primary article content we want to keep. It discusses important topics.</p>
<div class="sidebar"><p>Related Links</p></div>
<p>Another paragraph of useful information.</p>
<script>console.log('Some script');</script>
</main>
<footer><p>Copyright 2023. Some footer links.</p></footer>
</body></html>
"""
# 对于LLM数据,通常将 include_comments=False
# favor_recall=True 可能会包含稍多的文本,可能是一些边缘的
# 样板内容
# favor_precision=True 更严格,可能会遗漏一些主要内容
extracted_text = trafilatura.extract(
html_doc,
include_comments=False,
include_tables=True
)
print("Trafilatura提取的文本:")
if extracted_text:
print(extracted_text)
else:
print("Trafilatura未能提取到主要内容。")
# 预期输出(可能因库版本/启发式规则略有不同):
# 主标题 # 如果H1标题靠近主要文本,Trafilatura有时会包含它
# 主要文本
# 这是我们希望保留的主要文章内容。它讨论了重要主题。
# 另一个有用的信息段落。
这些工具在准确性和大规模处理的计算效率之间通常提供了很好的平衡。它们封装了从分析大量网页中获得的复杂启发式规则,省去了您自行开发和维护复杂规则集的麻烦。
trafilatura 等工具无法看到这些动态加载的内容。处理此类网站需要使用无头浏览器(例如,使用 Selenium、Playwright 或 Puppeteer 等工具)渲染页面,这会大幅增加处理时间和资源消耗。对于处理数十亿网页来说,这通常不切实际,这也是Common Crawl等来源的数据集可能低估高度动态网站内容的原因之一。PyPDF2 或 Apache Tika),然后才能进行文本清洗。trafilatura 这样的库通常具有一定的语言感知能力,但性能可能有所不同。样板内容和标记 (token)移除通常在数据处理流程的早期进行,通常是在获取原始HTML内容之后,以及在计算开销更大的步骤(如近似重复检测或质量过滤)之前。通过首先移除不相关的标记和文本,后续步骤可以在更小、更干净的数据上操作,从而提高效率和效用。例如,在提取的主要内容上计算文本质量分数或检测重复项比在包含导航链接和广告的完整HTML源上更有意义。这一步,无论是基于规则还是使用专用库,都应设计为可扩展的,能够在像Apache Spark或Dask这样的分布式框架中并行处理文档,正如本课程后面将讨论的。