
本文介绍如何使用 Python(zipfile + lxml)直接解析 Excel .xlsx 文件的 Drawing XML,高效提取形状的文本内容、填充色(solid fill)及边框色(line color),规避 xlwings/pywin32 的性能开销,且无需 openpyxl 支持。
本文介绍如何使用 python(zipfile + lxml)直接解析 excel `.xlsx` 文件的 drawing xml,高效提取形状的文本内容、填充色(solid fill)及边框色(line color),规避 xlwings/pywin32 的性能开销,且无需 openpyxl 支持。
Excel 的 .xlsx 文件本质上是 ZIP 压缩包,其中每个工作表对应的图形信息存储在 xl/drawings/drawing*.xml 文件中。这些 XML 遵循 Office Open XML 标准,包含
要同时提取文本与颜色,需对原始代码进行两处关键增强:
- 扩展 XPath 表达式,精准定位文本内容、填充色值(a:solidFill/a:srgbClr/@val)和边框色值(a:ln/a:solidFill/a:srgbClr/@val);
- 结构化组织结果,将每个形状的文本、几何类型(如 ellipse)、填充色和边框色统一成可读格式(如字典或元组)。
以下是完整、健壮的实现示例:
from zipfile import ZipFile
from lxml import etree
def extract_shapes_with_color(xlsx_path):
ns = {
"xdr": "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main",
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
}
shapes_data = []
with ZipFile(xlsx_path) as z:
# 定位所有 drawing XML 文件(如 drawing1.xml, drawing2.xml)
drawing_files = [f for f in z.filelist if "drawings/drawing" in f.filename and f.filename.endswith(".xml")]
for drw in drawing_files:
with z.open(drw.filename) as f:
tree = etree.fromstring(f.read())
# 提取文本:仅针对有 spPr 且含 txBody 的形状(即含文字的形状)
texts = tree.xpath('//xdr:txBody[preceding-sibling::xdr:spPr]/a:p/a:r/a:t/text()', namespaces=ns)
# 提取样式属性:几何类型、填充色、边框色(按顺序匹配)
# 注意:XPath 返回扁平列表,需按语义分组(每形状最多 1 个几何 + 1 个填充 + 1 个边框)
shape_attrs = tree.xpath(
'//xdr:spPr/a:prstGeom/@prst | '
'//xdr:spPr/a:solidFill/a:srgbClr/@val | '
'//xdr:spPr/a:ln/a:solidFill/a:srgbClr/@val',
namespaces=ns
)
# 按形状分组:每个 <sp> 对应一组属性,但 XPath 不自动分组 → 需结合节点层级更稳健的方式
# 更推荐:遍历每个 <sp>,分别提取其内部属性(如下所示)
sp_nodes = tree.xpath('//xdr:sp', namespaces=ns)
for i, sp in enumerate(sp_nodes):
# 获取文本(若存在)
text = ""
t_node = sp.xpath('.//xdr:txBody/a:p/a:r/a:t/text()', namespaces=ns)
if t_node:
text = "".join(t_node).strip()
# 获取几何类型
geom = sp.xpath('.//a:prstGeom/@prst', namespaces=ns)
geometry = geom[0] if geom else "custom"
# 获取填充色(solid fill)
fill = sp.xpath('.//a:solidFill/a:srgbClr/@val', namespaces=ns)
fill_color = fill[0] if fill else None
# 获取边框色(line solid fill)
line_fill = sp.xpath('.//a:ln/a:solidFill/a:srgbClr/@val', namespaces=ns)
line_color = line_fill[0] if line_fill else None
shapes_data.append({
"text": text,
"geometry": geometry,
"fill_color": fill_color, # 6位十六进制 RGB,如 "729fcf"
"line_color": line_color # 如 "3465a4"
})
return shapes_data
# 使用示例
if __name__ == "__main__":
result = extract_shapes_with_color("/home/luis/tmp/shapes.xlsx")
for i, shape in enumerate(result, 1):
print(f"Shape {i}: '{shape['text']}' | {shape['geometry']} | Fill: #{shape['fill_color']} | Line: #{shape['line_color']}")</sp></sp>
注意事项与最佳实践:
- ✅ 颜色格式说明:@val 属性返回的是小写、无 # 前缀的 6 位十六进制 RGB 值(如 "ff0000" 表示红色),使用时可直接拼接 # 构成标准 CSS 颜色。
- ⚠️ 空值处理:并非所有形状都有填充或边框,xpath() 可能返回空列表,务必用 if xxx else None 防止索引错误。
- ? 调试技巧:解压 .xlsx 后手动打开 xl/drawings/drawing1.xml,用浏览器或 VS Code 的 XML 工具查看结构;右键元素 → “Copy XPath” 可快速生成路径,再精简为通用表达式(如将 /xdr:wsDr/xdr:twoCellAnchor[2]/xdr:sp/xdr:spPr/... 简化为 //xdr:sp//a:solidFill/a:srgbClr/@val)。
- ? 不支持渐变/图案填充:本方案仅覆盖 a:solidFill;若需处理 a:gradFill 或 a:pattFill,需扩展 XPath 并解析对应子节点(如 a:gradFill/a:gsLst/a:gs/@pos 和 a:gs/a:srgbClr/@val)。
该方法完全基于标准库与轻量级 lxml,毫秒级完成百页 Excel 的形状批量解析,是自动化报表分析、模板校验与 UI 一致性检查的理想技术选型。











