如何使用openpyxl为Excel工作簿添加自定义属性同步至SharePoint
你用VBA操作的ContentTypeProperties属于OOXML格式定义的自定义文档属性,和wb.properties下暴露的核心文档属性(标题、作者、主题等固定字段)是完全独立的两套存储体系:
- 你之前用
setattr、修改__dict__的写法只是给Python对象临时加了实例属性,根本不会被写入xlsx文件结构,自然无法同步到SharePoint。 - 3.2.0b1测试版的自定义属性API还在调整,你遇到的
add方法不存在是因为该方法最终没有合入正式发布版本,不需要继续在beta版上调试。
方案1:openpyxl 3.1.0+ 正式版实现(推荐)
3.1.0及之后的正式版已经完整支持自定义文档属性读写,不需要调用不存在的add方法,直接向custom_doc_props列表追加CustomProperty对象即可:
from openpyxl import load_workbook from openpyxl.packaging.custom import CustomProperty # 加载已有工作簿 wb = load_workbook(r"你的目标文件路径.xlsx") # 可选:先移除同名旧属性,避免重复存储 wb.custom_doc_props = [ prop for prop in wb.custom_doc_props if prop.name not in ("Project Title", "Project Number") ] # 新增/修改自定义属性,完全支持带空格的属性名 wb.custom_doc_props.append(CustomProperty(name="Project Title", value="hello")) wb.custom_doc_props.append(CustomProperty(name="Project Number", value=12)) # 保存修改 wb.save(r"修改后文件保存路径.xlsx")
类型说明:属性值会自动匹配SharePoint字段类型:传str存为文本、传int/float存为数值、传bool存为是/否、传datetime对象存为日期时间,和VBA修改的行为完全一致,保存后的文件在SharePoint同步目录下会自动同步属性值。
方案2:openpyxl 3.0.x 旧版本兼容方案
如果受环境限制无法升级openpyxl版本,可以直接操作xlsx压缩包内的自定义属性底层XML文件实现需求,不需要依赖库的上层封装:
from openpyxl import load_workbook from openpyxl.xml.functions import fromstring, tostring import xml.etree.ElementTree as ET wb = load_workbook(r"你的目标文件路径.xlsx") # 注册自定义属性XML命名空间 CUSTOM_PROP_NS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" VALUE_TYPE_NS = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" ET.register_namespace("", CUSTOM_PROP_NS) ET.register_namespace("vt", VALUE_TYPE_NS) # 读取已有自定义属性配置,不存在则新建根节点 if "docProps/custom.xml" in wb._archive.namelist(): custom_props_root = fromstring(wb._archive.read("docProps/custom.xml")) else: custom_props_root = ET.Element(f"{{{CUSTOM_PROP_NS}}}Properties") def set_custom_prop(prop_name: str, prop_value): # 先删除已存在的同名属性 for exist_prop in custom_props_root.findall(f"{{{CUSTOM_PROP_NS}}}property[@name='{prop_name}']"): custom_props_root.remove(exist_prop) # 生成新属性ID(从2开始计数,ID1为系统预留) exist_pids = [int(p.get("pid")) for p in custom_props_root.findall(f"{{{CUSTOM_PROP_NS}}}property")] new_pid = str(max(exist_pids + [1]) + 1) # 写入属性节点 prop_node = ET.SubElement( custom_props_root, f"{{{CUSTOM_PROP_NS}}}property", {"fmtid": "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", "pid": new_pid, "name": prop_name} ) # 匹配值类型写入节点 if isinstance(prop_value, str): val_node = ET.SubElement(prop_node, f"{{{VALUE_TYPE_NS}}}lpwstr") val_node.text = prop_value elif isinstance(prop_value, (int, float)): val_node = ET.SubElement(prop_node, f"{{{VALUE_TYPE_NS}}}r8") val_node.text = str(prop_value) elif isinstance(prop_value, bool): val_node = ET.SubElement(prop_node, f"{{{VALUE_TYPE_NS}}}bool") val_node.text = "true" if prop_value else "false" # 调用方法设置目标属性 set_custom_prop("Project Title", "hello") set_custom_prop("Project Number", 12) # 将修改后的XML写回工作簿包 wb._archive.writestr( "docProps/custom.xml", tostring(custom_props_root, encoding="UTF-8", xml_declaration=True) ) wb.save(r"修改后文件保存路径.xlsx")
其他说明
- xlsxwriter作为只写模式的库,本身不支持读取修改已有文件,不适合当前编辑存量文件的场景。
- 不要尝试修改核心属性
wb.properties来实现自定义属性需求,核心属性是OOXML标准定义的固定字段,即使强行给Python对象添加属性,也不会被序列化写入文件。
内容的提问来源于stack exchange,提问作者likethevegetable
相关产品推荐
相关产品推荐

