大家好,我是jobleap.cn的小九。 bleach 是 Python 生态中专注于安全清理 HTML 文本、防范 XSS(跨站脚本攻击)的核心库,同时支持自动识别文本中的链接并格式化。本文将系统梳理 bleach 的所有常用 API,通过「基础用法 → 进阶定制 → 综合实战」的逻辑串联,帮助你掌握从简单清理到生产级应用的全流程。
一、环境准备
首先安装 bleach 库(推荐使用最新稳定版):
pip install bleach # 目前最新稳定版为 6.1.0
二、核心 API 详解与实战
bleach 的核心能力围绕「HTML 清理」和「链接格式化」展开,以下是所有高频 API 的用法拆解。
2.1 基础 HTML 清理:bleach.clean()
clean() 是 bleach 最核心的 API,用于清理非受信 HTML 文本,仅保留白名单内的标签/属性/样式,默认转义或移除危险内容。
函数签名(关键参数):
bleach.clean(
text, # 待清理的 HTML 文本(必传)
tags=bleach.ALLOWED_TAGS, # 允许保留的 HTML 标签列表
attributes=bleach.ALLOWED_ATTRIBUTES, # 允许保留的标签属性(字典/列表)
styles=bleach.ALLOWED_STYLES, # 允许保留的 CSS 样式属性
strip=False, # 对不允许的标签:True=移除标签(保留内容),False=转义标签
strip_comments=True # 是否移除 HTML 注释(默认 True,推荐保持)
)
实战示例:
示例 1:默认清理(防 XSS 基础)
import bleach
# 模拟用户提交的危险 HTML(含 XSS 脚本、危险标签)
unsafe_html = """
alert('XSS 攻击')
<p>恶意文本</p>
<a href="" rel="noopener noreferrer">危险链接</a>
"""
# 默认清理:仅保留 bleach 内置白名单标签/属性,移除脚本/注释/危险标签
cleaned_html = bleach.clean(unsafe_html)
print("默认清理结果:\n", cleaned_html)
输出结果(所有危险内容被转义/移除):
<p>恶意文本</p>
<a href rel="noopener noreferrer">危险链接</a>
说明:
/被完全移除(内容也无);font-size: 20px不在默认样式白名单中,被移除;javascript:伪协议的 href 被清空;- HTML 注释被移除;
- 标签被转义为实体(如
<→<)。
示例 2:自定义标签/属性白名单
# 自定义允许的标签:p、br、strong、em、a(常用评论区标签)
custom_tags = ["p", "br", "strong", "em", "a"]
# 自定义允许的属性:
# - a 标签允许 href、title、rel;
# - 所有标签允许 class 属性(字典格式:{标签: [属性列表]})
custom_attrs = {
"a": ["href", "title", "rel"],
"*": ["class"] # "*" 表示所有允许的标签都可使用 class 属性
}
# 自定义允许的样式:color、font-size(仅 p 标签生效)
custom_styles = ["color", "font-size"]
# 清理配置:移除不允许的标签(strip=True)
cleaned_html = bleach.clean(
unsafe_html,
tags=custom_tags,
attributes=custom_attrs,
styles=custom_styles,
strip=True # 移除不允许的标签(如 script/iframe),保留其内容(若有)
)
print("自定义白名单清理结果:\n", cleaned_html)
输出结果(仅保留允许的标签/属性/样式):
<p>恶意文本</p>
<a href rel="noopener noreferrer">危险链接</a>
2.2 链接自动格式化:bleach.linkify()
linkify() 用于自动识别文本中的「网址/邮箱」,并将其转换为 <a rel="noopener noreferrer"> 标签,常与 clean() 配合使用(先清理再链接化)。
函数签名(关键参数):
bleach.linkify(
text, # 待处理的文本(可含 HTML)
attrs=None, # 为生成的 <a rel="noopener noreferrer"> 标签添加自定义属性(如 rel="nofollow")
parse_email=True, # 是否识别邮箱并转为 mailto: 链接(默认 True)
skip_tags=None, # 跳过指定标签内的文本(如 <pre> 内不解析链接)
callbacks=None # 自定义链接生成逻辑的回调函数
)
实战示例:
示例 1:基础链接化
# 含原始网址/邮箱的文本
raw_text = "我的博客:https://example.com,邮箱:test@example.com"
# 基础链接化
linked_text = bleach.linkify(raw_text)
print("基础链接化结果:\n", linked_text)
输出结果:
我的博客:<a href="https://example.com" rel="noopener noreferrer">https://example.com</a>,邮箱:<a href="" rel="noopener noreferrer">test@example.com</a>
示例 2:自定义链接属性(SEO/安全优化)
为生成的 <a rel="noopener noreferrer"> 标签添加 rel="nofollow"(防 SEO 作弊)、target="_blank"(新窗口打开):
linked_text = bleach.linkify(
raw_text,
attrs={
"rel": "nofollow",
"target": "_blank",
"class": "link-style" # 自定义样式类
}
)
print("自定义链接属性结果:\n", linked_text)
输出结果:
我的博客:<a href="https://example.com" rel="nofollow noopener noreferrer" target="_blank" class="link-style">https://example.com</a>,邮箱:<a href="" rel="nofollow noopener noreferrer" target="_blank" class="link-style">test@example.com</a>
2.3 复用清理规则:bleach.sanitizer.Sanitizer 类
如果需要多次使用相同的清理规则(如全站统一评论清理规则),直接用 clean() 会重复传参,此时推荐使用 Sanitizer 类封装规则,提升代码复用性。
实战示例:
from bleach.sanitizer import Sanitizer
# 封装通用清理规则(复用 2.1 示例 2 的配置)
comment_sanitizer = Sanitizer(
tags=["p", "br", "strong", "em", "a"],
attributes={
"a": ["href", "title", "rel"],
"*": ["class"]
},
styles=["color", "font-size"],
strip=True
)
# 复用规则清理不同文本
text1 = "hack<p class="comment">评论1</p>"
text2 = "<a href="https://example.com" rel="nofollow noopener noreferrer">链接</a>"
print("清理文本1:", comment_sanitizer.sanitize(text1))
print("清理文本2:", comment_sanitizer.sanitize(text2))
输出结果:
清理文本1: <p class="comment">评论1</p>
清理文本2: <a href="https://example.com" rel="nofollow noopener noreferrer">链接</a>
2.4 内置白名单常量:ALLOWED_TAGS/ALLOWED_ATTRIBUTES/ALLOWED_STYLES
bleach 内置了默认的安全白名单,可直接使用或扩展,避免重复定义:
| 常量 | 说明 | 默认值示例 |
|---|---|---|
bleach.ALLOWED_TAGS | 默认允许的标签 | ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'p'] |
bleach.ALLOWED_ATTRIBUTES | 默认允许的属性 | {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']} |
bleach.ALLOWED_STYLES | 默认允许的样式 | ['color', 'background-color', 'font-weight', 'font-style'] |
扩展默认白名单示例:
# 扩展默认标签:在原有基础上加 'img' 标签
extended_tags = list(bleach.ALLOWED_TAGS) + ["img"]
# 扩展默认属性:给 a 标签加 'target' 属性
extended_attrs = bleach.ALLOWED_ATTRIBUTES.copy()
extended_attrs['a'].append('target')
# 扩展默认样式:加 'font-size'
extended_styles = list(bleach.ALLOWED_STYLES) + ["font-size"]
# 使用扩展后的白名单清理
cleaned = bleach.clean(
"<img src="test.jpg"><a href="" target="_blank" rel="noopener noreferrer">链接</a>",
tags=extended_tags,
attributes=extended_attrs,
styles=extended_styles
)
print("扩展白名单清理结果:", cleaned)
输出结果:
<img src="test.jpg"><a href="" target="_blank" rel="noopener noreferrer">链接</a>
2.5 组合用法:先清理再链接化
生产场景中,通常先清理 HTML 风险内容,再对文本中的链接进行格式化,避免链接包含危险内容。
实战示例(评论区处理):
def process_comment(comment_text):
"""处理用户评论:先清理 HTML,再链接化"""
# 步骤 1:清理危险 HTML
cleaned = bleach.clean(
comment_text,
tags=["p", "br", "strong", "em", "a"],
attributes={"a": ["href", "title", "rel"]},
strip=True
)
# 步骤 2:链接化(添加 nofollow 防作弊)
linked = bleach.linkify(
cleaned,
attrs={"rel": "nofollow", "target": "_blank"}
)
return linked
# 测试:用户评论含危险标签 + 原始链接
user_comment = """
alert(1)
<p>我的博客是 https://example.com,邮箱 test@example.com</p>
危险标签
"""
processed = process_comment(user_comment)
print("最终处理后的评论:\n", processed)
输出结果:
<p>我的博客是 <a href="https://example.com" rel="nofollow noopener noreferrer" target="_blank">https://example.com</a>,邮箱 <a href="" rel="nofollow noopener noreferrer" target="_blank">test@example.com</a></p>
危险标签
三、综合实战:评论区 HTML 安全处理
以下是生产级别的评论区处理示例,整合 bleach 所有核心 API,满足「安全 + 易用 + 合规」需求:
需求:
- 允许用户使用基础格式标签(p、br、strong、em、a、img);
- 仅允许 img 标签的 src、alt 属性,且 src 仅允许 https 协议;
- 自动识别链接/邮箱并格式化,添加 nofollow/target="_blank";
- 移除所有危险标签/属性,保留内容;
- 禁止 javascript: 伪协议链接。
实现代码:
import bleach
from bleach.sanitizer import Sanitizer
from bleach.linkifier import Linkifier, CallbackBuilder
# 1. 定义自定义清理规则
ALLOWED_TAGS = ["p", "br", "strong", "em", "a", "img"]
ALLOWED_ATTRIBUTES = {
"a": ["href", "title", "rel", "target"],
"img": ["src", "alt"],
"*": ["class"] # 所有标签允许 class 属性
}
ALLOWED_STYLES = ["color", "font-size", "font-weight"]
# 2. 自定义链接规则:禁止 javascript: 伪协议,强制 https
def check_link(attrs, new=False):
"""回调函数:过滤危险链接"""
href = attrs.get("href", "")
# 禁止 javascript: 伪协议
if href.lower().startswith("javascript:"):
return None
# 强制 img 标签的 src 为 https
if "src" in attrs and attrs["src"].startswith("http:"):
attrs["src"] = attrs["src"].replace("http:", "https:")
return attrs
# 3. 初始化复用的清理器和链接器
sanitizer = Sanitizer(
tags=ALLOWED_TAGS,
attributes=ALLOWED_ATTRIBUTES,
styles=ALLOWED_STYLES,
strip=True
)
linkifier = Linkifier(
callbacks=[CallbackBuilder(callback=check_link)],
attrs={"rel": "nofollow", "target": "_blank"}
)
# 4. 核心处理函数
def safe_process_comment(comment):
# 步骤 1:清理 HTML
cleaned = sanitizer.sanitize(comment)
# 步骤 2:链接化(含自定义规则)
linked = linkifier.linkify(cleaned)
return linked
# 5. 测试
test_comment = """
steal_cookie()
<p>
我的博客:http://example.com,邮箱 test@example.com
<img src="http://example.com/avatar.jpg" alt="头像">
<a href="" rel="noopener noreferrer">危险链接</a>
</p>
"""
result = safe_process_comment(test_comment)
print("生产级处理结果:\n", result)
输出结果:
<p>
我的博客:<a href="https://example.com" rel="nofollow noopener noreferrer" target="_blank">https://example.com</a>,邮箱 <a href="" rel="nofollow noopener noreferrer" target="_blank">test@example.com</a>
<img src="https://example.com/avatar.jpg" alt="头像">
</p>
四、注意事项与最佳实践
- 白名单最小化:仅允许业务必需的标签/属性(如评论区无需
iframe/script,直接禁用); - 链接协议校验:强制链接使用
https,禁止javascript:/vbscript:等伪协议; - 避免过度依赖:bleach 仅处理 HTML 层面的 XSS,业务层仍需校验输入(如长度、敏感词);
- 版本更新:及时升级 bleach,修复已知安全漏洞;
- 不要混用 strip=False 和危险标签:转义后的标签可能被前端解析,建议对危险场景使用
strip=True。
五、总结
bleach 的核心价值是「安全清理 HTML」,其常用 API 可归纳为:
- 基础清理:
bleach.clean()(核心); - 链接格式化:
bleach.linkify(); - 规则复用:
Sanitizer/Linkifier类; - 白名单扩展:
ALLOWED_TAGS/ALLOWED_ATTRIBUTES/ALLOWED_STYLES。
通过「先清理后格式化」的组合,结合自定义白名单和链接校验,可满足绝大多数场景的 HTML 安全处理需求,是 Python 开发中防范 XSS 攻击的首选工具。