文章 / 公众号历史文章
『新认知』原来python也可以完整地调用VBA的功能驱动OFFICE自动化
之前我一直以为,python对OFFICE的自动化,相较VBA的话,是有限的,就像openpyxl那样,需要有人去不断地追加维护才行。 经过学习后,发现其实只要使用win32com这个库,就可以将VBA的代码直接移植到python上使用。功能上是和当前VBA的宿主相关,例如Excel版本越高,VBA
之前我一直以为,python对OFFICE的自动化,相较VBA的话,是有限的,就像openpyxl那样,需要有人去不断地追加维护才行。
经过学习后,发现其实只要使用win32com这个库,就可以将VBA的代码直接移植到python上使用。功能上是和当前VBA的宿主相关,例如Excel版本越高,VBA的功能越多,相应的win32com这个库也可以自动增加这些功能,本质上win32com只是一个桥梁,使用python可以驱动COM组件,COM组件暴露多少功能,它就能实现多少。
不过按童大谦老师的【代替VBA!用python轻松实现Excel编程】一书里说的,极个别接口还是有些异常的。
我专门测试了一下,拿Excel比较新的接口如PowerQuery。实测还是正常可以获取的,代码如下(xlwings本质底层用的是win32com):

所以,如果python自动化的是COM组件,应该使用win32com,更科学。而不是使用pythonnet,后者是对.NET的非托管的dll起作用。如果硬着来,效果比较差,例如以下的代码,需要使用反映的方式才能操作COM组件。

那有哪些软件是COM组件暴露,哪些不是?这个可以用一段小命令,调出一个COM浏览器,不过更快的应该直接问GPT4,哈哈。
from win32com.client import combrowsecombrowse.main()
因为我本机是OFFICE365,所以这里就直接获取到最新COM组件Excel16。

当然如果要使用C#来实现这些新的接口,如果没有引用Excel16的dll,就没有智能提示,只能用动态类型来处理,也一样可以的。
static void Main(string[] args) { Excel.Application app = (Excel.Application)Marshal.GetActiveObject("Excel.Application"); dynamic wkb = app.ActiveWorkbook; Console.WriteLine(wkb.Queries.Item[1].Name); }
除了OFFICE软件,笔者用到的还有sqlserver的对象模型自动化,它有两个模型SMO和AMO。其中SMO可能比较旧的技术,还是COM组件的,但AMO分析模型,就已经变成是.NET的托管模型了。
import win32com.client
# 创建 SQL Server 对象smo = win32com.client.Dispatch('Microsoft.SqlServer.Management.Smo.Server')
# 连接到 SQL Server 实例server = smo('localhost') # 替换为你的服务器名称
# 获取数据库列表databases = server.Databasesfor db in databases: print(f'Database Name: {db.Name}')
所以后者需要用pythonnet的方案了。
import clrfrom System import Stringfrom System.Reflection import BindingFlags
# 添加对 Microsoft.AnalysisServices.Tabular 的引用dll_path = r'C:\Path\To\Your\Microsoft.AnalysisServices.Tabular.dll'clr.AddReference(dll_path)
# 导入 Microsoft.AnalysisServices.Tabular 命名空间from Microsoft.AnalysisServices.Tabular import Server, Database
# 创建并连接到 Analysis Services 服务器实例server = Server()server.Connect('localhost') # 替换为你的服务器地址
# 获取数据库对象database = server.Databases['YourDatabaseName'] # 替换为你的数据库名称
# 打印数据库信息print(f'Database Name: {database.Name}')print(f'Database Compatibility Level: {database.CompatibilityLevel}')
# 列出所有表for table in database.Model.Tables: print(f'Table: {table.Name}')