Python如何处理UTF-8 BOM编码标记

 更新时间:2026年09月18日 08:21:48   作者:detayun  
UTF-8 BOM(b'\xef\xbb\xbf')是Windows记事本等工具添加的编码标记,非标准UTF-8组成部分,常文件解析中类型判断失败,下面我们就来看看如何正确处理它吧

前言

在爬虫、文件解析、HTTP响应处理场景里,经常会遇到一种诡异问题:肉眼看文本完全正常,但程序判断逻辑莫名失效。典型案例:HTML页面明明是标准文档,却因为开头多出3个字节,被误判为非HTML类型。罪魁祸首就是 UTF-8 BOM

很多开发者有误区:UTF-8本身不需要BOM。UTF-8 BOM只是微软体系(Windows记事本等)为了标记编码加入的约定,并不是UTF-8标准强制要求。它的二进制为 b'\xef\xbb\xbf',占3字节,放在文件最前面。

什么是 UTF-8 BOM

  • BOM:Byte Order Mark,字节序标记。原本是为UTF-16、UTF-32区分大小端设计。
  • UTF-8 不存在字节序问题,但 Windows 记事本保存UTF-8文件时,默认会写入 0xEF 0xBB 0xBF 前缀,也就是UTF-8 BOM。
  • 这3个字节不是正文内容,只是编码标记。但在二进制读取时,会原样读到内存,干扰解析逻辑。

举个例子:

# 带BOM的HTML原始二进制
data = b'\xef\xbb\xbf<!DOCTYPE html><html>...</html>'
print(data.lstrip()) 
# 输出还是 b'\xef\xbb\xbf<!DOCTYPE html>...'
# lstrip() 只能删除空白字符,不会移除BOM字节!

这就是我在HTTP文件类型检测函数踩的坑:Content-Type 返回text/html,但正文头部带BOM,前缀匹配直接失败,识别成txt。

Python 处理 UTF-8 BOM 的几种方案

方案1:二进制预处理(推荐,爬虫场景首选)

先判断是否以BOM字节开头,如果是直接切片去掉前3字节。适合直接读取bytes的场景,比如requests响应原始内容。

def strip_utf8_bom(data: bytes) -> bytes:
    if data.startswith(b'\xef\xbb\xbf'):
        return data[3:]
    return data

raw = b'\xef\xbb\xbfhello world'
res = strip_utf8_bom(raw)
print(res) # b'hello world'

适合我们之前写的detect_file_type文件类型判断逻辑,二进制阶段清除BOM,不需要解码,性能更好,也避免解码异常。

方案2:使用utf-8-sig编码(文本读取首选)

Python内置编码 utf-8-sig,会自动识别并丢弃UTF-8 BOM,不需要手动写切片逻辑。

# 读取本地文件
with open("test.html", "r", encoding="utf-8-sig") as f:
    text = f.read()

# bytes转字符串时使用
raw_bytes = b'\xef\xbb\xbf<!DOCTYPE html>'
text = raw_bytes.decode("utf-8-sig")
print(text)

注意区分:

  • utf-8:不会自动移除BOM,BOM会变成 \ufeff 字符留在文本里;
  • utf-8-sig:自动移除BOM,得到干净文本。

方案3:字符串层面清除 \ufeff

如果已经解码成字符串,BOM会变成Unicode零宽字符 \ufeff,可以直接替换:

text_with_bom = "\ufeff<!DOCTYPE html>"
clean_text = text_with_bom.replace("\ufeff", "")
print(clean_text)

缺点:会扫描全文替换,只适合小文本;大文件/二进制场景不推荐。

实战场景:爬虫HTTP响应中的BOM

很多网站返回HTML时会带上UTF-8 BOM。用requests拿到response.content是原始bytes。
如果直接用原始字节做魔数、标签前缀判断,BOM会干扰匹配。

就像前面文件类型检测代码,处理顺序建议:二进制先剔除BOM,再去除空白,再做前缀匹配

def strip_bom_and_whitespace(data: bytes) -> bytes:
    # 第一步移除UTF8 BOM
    if data.startswith(b'\xef\xbb\xbf'):
        data = data[3:]
    # 第二步去除前置空白:空格、换行、回车、tab
    return data.lstrip(b' \r\n\t')

# 测试BOM+换行场景
html_bin = b'\xef\xbb\xbf\n  <html><body></body></html>'
clean_bin = strip_bom_and_whitespace(html_bin)
print(clean_bin.startswith(b'<html')) # True

常见踩坑点总结

  1. bytes.lstrip() 不能自动删除BOM,BOM不属于空白字符;
  2. utf-8utf-8-sig 是两个编码,打开文件选错就会残留\ufeff
  3. 不要盲目全局替换\ufeff,极少数业务场景下这个字符是业务正文,直接替换会破坏内容;
  4. 爬虫文件类型识别,优先在bytes阶段处理BOM,不要等到解码后处理,避免不必要的解码开销;
  5. UTF-8 BOM在Linux服务端极少出现,大多是Windows生成的文件或者Windows服务器输出。

什么时候保留BOM,什么时候去掉?

  • 需要移除:网页解析、JSON解析、XML解析、文件类型识别、文本对比。
  • 保留BOM:需要兼容Windows记事本,输出文件要让记事本默认识别为UTF-8时,可以手动写入BOM。

写入带BOM的UTF8文件示例:

content = "测试文本"
with open("out.txt", "wb") as fp:
    fp.write(b'\xef\xbb\xbf')
    fp.write(content.encode("utf-8"))

小结

UTF-8 BOM只是历史遗留标记,不是UTF-8标准一部分。在Python里分两种场景处理:

  1. 操作bytes原始二进制:判断前缀 b'\xef\xbb\xbf' 切片去除;
  2. 读取文本字符串:直接使用utf-8-sig编码自动处理。

在爬虫、文件检测这类底层二进制判断场景,推荐bytes预处理,可以避免解码带来的性能损耗与编码异常,也是我在HTTP文件类型识别函数里最终采用的方案。

到此这篇关于Python如何处理UTF-8 BOM编码标记的文章就介绍到这了,更多相关Python处理UTF-8 BOM内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python 字典常用方法超详细梳理总结

    python 字典常用方法超详细梳理总结

    这篇文章主要介绍了Python数据类型字典dictionary,字典是另一种可变容器模型,且可存储任意类型对象。本篇文字将详细讲述字典的常用方法,需要的可以参考一下
    2022-03-03
  • python3.6.8 + pycharm + PyQt5 环境搭建的图文教程

    python3.6.8 + pycharm + PyQt5 环境搭建的图文教程

    这篇文章主要介绍了python3.6.8 + pycharm + PyQt5 环境搭建,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • 用python 批量操作redis数据库

    用python 批量操作redis数据库

    这篇文章主要介绍了如何用python 批量操作redis数据库,帮助大家更好的理解和学习使用python,感兴趣的朋友可以了解下
    2021-03-03
  • python中xlrd模块的使用详解

    python中xlrd模块的使用详解

    这篇文章主要介绍了python中xlrd模块的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 时间序列预测中的数据滑窗操作实例(python实现)

    时间序列预测中的数据滑窗操作实例(python实现)

    滑动窗口操作非常普遍,非常有用,它们也很容易在Python中实现,下面这篇文章主要给大家介绍了关于时间序列预测中的数据滑窗操作python实现的相关资料,需要的朋友可以参考下
    2022-03-03
  • opencv 傅里叶变换的实现

    opencv 傅里叶变换的实现

    本文主要介绍了opencv 傅里叶变换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>
    2022-06-06
  • python输出后面多一个None问题

    python输出后面多一个None问题

    在Python中,函数如果没有显式指定返回值,会默认返回`None`,例如,计算一个数的平方根并输出,如果没有处理`None`,会输出结果后跟`None`
    2024-11-11
  • Python反射操作对象属性方法详解

    Python反射操作对象属性方法详解

    这篇文章主要介绍了Python反射操作对象属性方法详解,在Python面对对象中,通过字符串的形式去操作对象的属性方法就称之为反射(在Python中一切事物都是可以为对象),需要的朋友可以参考下
    2023-08-08
  • Python系统交互库解析过程

    Python系统交互库解析过程

    本文介绍了Python中os、sys、subprocess等模块及paramiko、Fabric、socket、requests等库的核心功能,涵盖文件操作、系统交互、进程管理、网络通信、数据处理等场景,并强调了安全使用YAML等格式的注意事项
    2025-07-07
  • 如何使用python的subprocess执行命令、交互、等待、是否结束及解析JSON结果

    如何使用python的subprocess执行命令、交互、等待、是否结束及解析JSON结果

    这篇文章主要给大家介绍了关于如何使用python的subprocess执行命令、交互、等待、是否结束及解析JSON结果的相关资料,subprocess模块提供了一种简单的方法来创建和管理子进程,它可以让我们在Python程序中执行外部命令,获取命令的输出和错误信息,需要的朋友可以参考下
    2023-12-12

最新评论