- “这图是不是糊了?”
- 以为是样式缺了?试试手动复制
- 差异在哪?想用对比工具一探究竟……
- 简单到不能再简单的代码,有问题吗?
- 最后的真相:viewBox vs viewbox,preserveAspectRatio vs preserveaspectratio
- 别急,问题其实出在 parser 上
- 正确做法:用 xml parser!
我有几个网页需要处理,想从中提取出 SVG 图单独保存。操作也很简单,网页保存为 .html
后,用 Python + BeautifulSoup 提取 <svg>
标签,保存为 .svg
文件。
一切看起来都很顺利,代码跑起来没报错,文件也生成了。但打开 SVG 一看,总感觉哪儿不太对劲。
“这图是不是糊了?”
那是一种怎么形容呢……“有点糊”的感觉。轮廓线条模糊了,缩放好像不太灵,反正就不像原网页中那么清晰了。

图中左侧是 BeautifulSoup 提取的结果,右侧是网页中原始的显示效果,看着还是非常明显。
以为是样式缺了?试试手动复制
起初我以为可能是 SVG 引用了网页外部的样式表,所以通过代码提取出来后,没法正确渲染。
那我就直接从 HTML 源码里把 <svg>
标签复制出来,保存成 .svg
文件。结果一打开:显示完全正常!
这说明:SVG 并没有依赖外部样式,问题出在提取方式上。
差异在哪?想用对比工具一探究竟……
想到这里,我打算用 Beyond Compare 这种工具来看看手动提取和代码提取的 SVG 文件有啥不同。
结果发现:属性的顺序被 BeautifulSoup 改了个遍,标签缩进也被重新排过了。
这下比对也不好看了,完全对不上。搞得我直接放弃用工具对比,只能开始回头怀疑自己的代码:
简单到不能再简单的代码,有问题吗?
下面是我最开始的代码,提取 SVG 并保存:
from bs4 import BeautifulSoup
with open('42e1.html', 'r', encoding='utf-8') as file:
html_content = file.read()
soup = BeautifulSoup(html_content)
svg = soup.find('svg')
if svg:
svg_string = svg.prettify()
with open('extracted_42e1.svg', 'w', encoding='utf-8') as f:
f.write(svg_string)
代码非常简单,难道是 .prettify()
的锅?它会不会在格式化的时候,修改了什么属性?
我试了几个 formatter
参数,也没解决问题,结果还是老样子。
最后的真相:viewBox vs viewbox,preserveAspectRatio vs preserveaspectratio
没辙了,我只能回到老老实实肉眼对比。
我皱着眉头盯着 Beyond Compare,看着它标红的地方……一开始我还没看出来。
结果,居然只是属性名变成了小写:
viewBox
→viewbox
preserveAspectRatio
→preserveaspectratio
嘴里飙出了一句国粹之后,我第一反应是直接暴力补丁:
svg_content = svg_content.replace("viewbox", "viewBox")
svg_content = svg_content.replace("preserveaspectratio", "preserveAspectRatio")
还一度想给 BeautifulSoup 提个 issue:你咋连大小写都搞不清?
别急,问题其实出在 parser 上
冷静之后,我想:不应该啊,BeautifulSoup 这么成熟的库,不可能犯这种低级错误。
于是去查了查文档——果然问题不在 BeautifulSoup 本身,而在于我用的解析器。
默认的 html.parser
是按照 HTML 规范设计的,它会把标签名和属性名全部转换为小写,这是符合 HTML 标准的。
但问题来了,SVG 是 XML 子集,属性名是区分大小写的。
换句话说,我在解析 XML 的时候用了 HTML 的工具,所以出问题了
正确做法:用 xml parser!
所以解决方法其实很简单,只需要:
soup = BeautifulSoup(html_content, "xml")
这样,viewBox
和 preserveAspectRatio
就能原封不动地保留下来,SVG 也就恢复正常了。结局也算是皆大欢喜了。
补一句:
-
如果专门要处理 SVG、MathML、RSS 等 XML 内容,请用
xml
或lxml-xml
。 -
如果要处理 HTML 页面,继续用
html.parser
是对的,因为使用 xml 解析可能会造成 html 部分的混乱。
以此留念,拒绝踩坑。