bleach 全面教程:常用 API 串联与实战指南

56 阅读7分钟

大家好,我是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>
&#34;&#34;&#34;

# 默认清理:仅保留 bleach 内置白名单标签/属性,移除脚本/注释/危险标签
cleaned_html = bleach.clean(unsafe_html)
print(&#34;默认清理结果:\n&#34;, cleaned_html)

输出结果(所有危险内容被转义/移除):

<p>恶意文本</p>

<a href rel="noopener noreferrer">危险链接</a>

说明:

  • / 被完全移除(内容也无);
  • font-size: 20px 不在默认样式白名单中,被移除;
  • javascript: 伪协议的 href 被清空;
  • HTML 注释被移除;
  • 标签被转义为实体(如 <<)。
示例 2:自定义标签/属性白名单
# 自定义允许的标签:p、br、strong、em、a(常用评论区标签)
custom_tags = [&#34;p&#34;, &#34;br&#34;, &#34;strong&#34;, &#34;em&#34;, &#34;a&#34;]

# 自定义允许的属性:
# - a 标签允许 href、title、rel;
# - 所有标签允许 class 属性(字典格式:{标签: [属性列表]})
custom_attrs = {
    &#34;a&#34;: [&#34;href&#34;, &#34;title&#34;, &#34;rel&#34;],
    &#34;*&#34;: [&#34;class&#34;]  # &#34;*&#34; 表示所有允许的标签都可使用 class 属性
}

# 自定义允许的样式:color、font-size(仅 p 标签生效)
custom_styles = [&#34;color&#34;, &#34;font-size&#34;]

# 清理配置:移除不允许的标签(strip=True)
cleaned_html = bleach.clean(
    unsafe_html,
    tags=custom_tags,
    attributes=custom_attrs,
    styles=custom_styles,
    strip=True  # 移除不允许的标签(如 script/iframe),保留其内容(若有)
)
print(&#34;自定义白名单清理结果:\n&#34;, 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=&#34;nofollow&#34;)
    parse_email=True,    # 是否识别邮箱并转为 mailto: 链接(默认 True)
    skip_tags=None,      # 跳过指定标签内的文本(如 <pre> 内不解析链接)
    callbacks=None       # 自定义链接生成逻辑的回调函数
)

实战示例:

示例 1:基础链接化
# 含原始网址/邮箱的文本
raw_text = &#34;我的博客:https://example.com,邮箱:test@example.com&#34;

# 基础链接化
linked_text = bleach.linkify(raw_text)
print(&#34;基础链接化结果:\n&#34;, 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=&#34;nofollow&#34;(防 SEO 作弊)、target=&#34;_blank&#34;(新窗口打开):

linked_text = bleach.linkify(
    raw_text,
    attrs={
        &#34;rel&#34;: &#34;nofollow&#34;,
        &#34;target&#34;: &#34;_blank&#34;,
        &#34;class&#34;: &#34;link-style&#34;  # 自定义样式类
    }
)
print(&#34;自定义链接属性结果:\n&#34;, 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=[&#34;p&#34;, &#34;br&#34;, &#34;strong&#34;, &#34;em&#34;, &#34;a&#34;],
    attributes={
        &#34;a&#34;: [&#34;href&#34;, &#34;title&#34;, &#34;rel&#34;],
        &#34;*&#34;: [&#34;class&#34;]
    },
    styles=[&#34;color&#34;, &#34;font-size&#34;],
    strip=True
)

# 复用规则清理不同文本
text1 = &#34;hack<p class="comment">评论1</p>&#34;
text2 = &#34;<a href="https://example.com" rel="nofollow noopener noreferrer">链接</a>&#34;

print(&#34;清理文本1:&#34;, comment_sanitizer.sanitize(text1))
print(&#34;清理文本2:&#34;, 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) + [&#34;img&#34;]

# 扩展默认属性:给 a 标签加 'target' 属性
extended_attrs = bleach.ALLOWED_ATTRIBUTES.copy()
extended_attrs['a'].append('target')

# 扩展默认样式:加 'font-size'
extended_styles = list(bleach.ALLOWED_STYLES) + [&#34;font-size&#34;]

# 使用扩展后的白名单清理
cleaned = bleach.clean(
    &#34;<img src="test.jpg"><a href="" target="_blank" rel="noopener noreferrer">链接</a>&#34;,
    tags=extended_tags,
    attributes=extended_attrs,
    styles=extended_styles
)
print(&#34;扩展白名单清理结果:&#34;, cleaned)

输出结果

<img src="test.jpg"><a href="" target="_blank" rel="noopener noreferrer">链接</a>

2.5 组合用法:先清理再链接化

生产场景中,通常先清理 HTML 风险内容,再对文本中的链接进行格式化,避免链接包含危险内容。

实战示例(评论区处理):

def process_comment(comment_text):
    &#34;&#34;&#34;处理用户评论:先清理 HTML,再链接化&#34;&#34;&#34;
    # 步骤 1:清理危险 HTML
    cleaned = bleach.clean(
        comment_text,
        tags=[&#34;p&#34;, &#34;br&#34;, &#34;strong&#34;, &#34;em&#34;, &#34;a&#34;],
        attributes={&#34;a&#34;: [&#34;href&#34;, &#34;title&#34;, &#34;rel&#34;]},
        strip=True
    )
    # 步骤 2:链接化(添加 nofollow 防作弊)
    linked = bleach.linkify(
        cleaned,
        attrs={&#34;rel&#34;: &#34;nofollow&#34;, &#34;target&#34;: &#34;_blank&#34;}
    )
    return linked

# 测试:用户评论含危险标签 + 原始链接
user_comment = &#34;&#34;&#34;
alert(1)
<p>我的博客是 https://example.com,邮箱 test@example.com</p>
危险标签
&#34;&#34;&#34;

processed = process_comment(user_comment)
print(&#34;最终处理后的评论:\n&#34;, 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,满足「安全 + 易用 + 合规」需求:

需求:

  1. 允许用户使用基础格式标签(p、br、strong、em、a、img);
  2. 仅允许 img 标签的 src、alt 属性,且 src 仅允许 https 协议;
  3. 自动识别链接/邮箱并格式化,添加 nofollow/target="_blank";
  4. 移除所有危险标签/属性,保留内容;
  5. 禁止 javascript: 伪协议链接。

实现代码:

import bleach
from bleach.sanitizer import Sanitizer
from bleach.linkifier import Linkifier, CallbackBuilder

# 1. 定义自定义清理规则
ALLOWED_TAGS = [&#34;p&#34;, &#34;br&#34;, &#34;strong&#34;, &#34;em&#34;, &#34;a&#34;, &#34;img&#34;]
ALLOWED_ATTRIBUTES = {
    &#34;a&#34;: [&#34;href&#34;, &#34;title&#34;, &#34;rel&#34;, &#34;target&#34;],
    &#34;img&#34;: [&#34;src&#34;, &#34;alt&#34;],
    &#34;*&#34;: [&#34;class&#34;]  # 所有标签允许 class 属性
}
ALLOWED_STYLES = [&#34;color&#34;, &#34;font-size&#34;, &#34;font-weight&#34;]

# 2. 自定义链接规则:禁止 javascript: 伪协议,强制 https
def check_link(attrs, new=False):
    &#34;&#34;&#34;回调函数:过滤危险链接&#34;&#34;&#34;
    href = attrs.get(&#34;href&#34;, &#34;&#34;)
    # 禁止 javascript: 伪协议
    if href.lower().startswith(&#34;javascript:&#34;):
        return None
    # 强制 img 标签的 src 为 https
    if &#34;src&#34; in attrs and attrs[&#34;src&#34;].startswith(&#34;http:&#34;):
        attrs[&#34;src&#34;] = attrs[&#34;src&#34;].replace(&#34;http:&#34;, &#34;https:&#34;)
    return attrs

# 3. 初始化复用的清理器和链接器
sanitizer = Sanitizer(
    tags=ALLOWED_TAGS,
    attributes=ALLOWED_ATTRIBUTES,
    styles=ALLOWED_STYLES,
    strip=True
)

linkifier = Linkifier(
    callbacks=[CallbackBuilder(callback=check_link)],
    attrs={&#34;rel&#34;: &#34;nofollow&#34;, &#34;target&#34;: &#34;_blank&#34;}
)

# 4. 核心处理函数
def safe_process_comment(comment):
    # 步骤 1:清理 HTML
    cleaned = sanitizer.sanitize(comment)
    # 步骤 2:链接化(含自定义规则)
    linked = linkifier.linkify(cleaned)
    return linked

# 5. 测试
test_comment = &#34;&#34;&#34;
steal_cookie()
<p>
    我的博客:http://example.com,邮箱 test@example.com
    <img src="http://example.com/avatar.jpg" alt="头像">
    <a href="" rel="noopener noreferrer">危险链接</a>
</p>
&#34;&#34;&#34;

result = safe_process_comment(test_comment)
print(&#34;生产级处理结果:\n&#34;, 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>

四、注意事项与最佳实践

  1. 白名单最小化:仅允许业务必需的标签/属性(如评论区无需 iframe/script,直接禁用);
  2. 链接协议校验:强制链接使用 https,禁止 javascript:/vbscript: 等伪协议;
  3. 避免过度依赖:bleach 仅处理 HTML 层面的 XSS,业务层仍需校验输入(如长度、敏感词);
  4. 版本更新:及时升级 bleach,修复已知安全漏洞;
  5. 不要混用 strip=False 和危险标签:转义后的标签可能被前端解析,建议对危险场景使用 strip=True

五、总结

bleach 的核心价值是「安全清理 HTML」,其常用 API 可归纳为:

  • 基础清理:bleach.clean()(核心);
  • 链接格式化:bleach.linkify()
  • 规则复用:Sanitizer/Linkifier 类;
  • 白名单扩展:ALLOWED_TAGS/ALLOWED_ATTRIBUTES/ALLOWED_STYLES

通过「先清理后格式化」的组合,结合自定义白名单和链接校验,可满足绝大多数场景的 HTML 安全处理需求,是 Python 开发中防范 XSS 攻击的首选工具。