通过Python运行Excel宏?

71
我想通过Python运行宏,但我不确定如何使其工作... 到目前为止,我已经有了以下代码,但它没有起作用。
import win32com.client
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename="C:\test.xlsm",ReadOnly=1)
xl.Application.Run("macrohere")
xl.Workbooks(1).Close(SaveChanges=0)
xl.Application.Quit()
xl=0

我得到了以下追踪回溯:

Traceback (most recent call last):
  File "C:\test.py", line 4, in <module>
    xl.Application.Run("macrohere")
  File "<COMObject <unknown>>", line 14, in Run
  File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 282, in _ApplyTypes_
    result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
com_error: (-2147352567, 'Exception occurred.', (0, u'Microsoft Excel', u"Cannot run the macro 'macrohere'. The macro may not be available in this workbook or all macros may be disabled.", u'xlmain11.chm', 0, -2146827284), None)

编辑

import win32com.client
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename="C:\test.xlsm",ReadOnly=1)
try:
    xl.Application.Run("test.xlsm!testmacro.testmacro")
    # It does run like this... but we get the following error:
    # Traceback (most recent call last):
        # File "C:\test.py", line 7, in <module>
        # xl.Workbooks(1).Close(SaveChanges=0)
        # File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 192, in __call__
        # return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
    # com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147352565), None)
except:
    # Except isn't catching the above error... :(
    xl.Workbooks(1).Close(SaveChanges=0)
    xl.Application.Quit()
    xl=0

1
也许不要在模块和方法中使用相同的名称。 - Tim Williams
宏定义是什么?它是像Public sub testMacro还是Private这样声明的? - enderland
你需要从Python运行宏吗?其实完全可以直接用Python完成整个任务。此外,应使用del xl这样的方式来清除对对象的引用,而不是xl=0 - Aleksander Lidtke
@TimWilliams 能否详细说明一下? - Ryflex
@enderland,不是的,它只是Sub testMacro - Ryflex
显示剩余2条评论
9个回答

93

我认为错误与您调用的宏有关,请尝试以下代码:

代码

import os, os.path
import win32com.client

if os.path.exists("excelsheet.xlsm"):
    xl=win32com.client.Dispatch("Excel.Application")
    xl.Workbooks.Open(os.path.abspath("excelsheet.xlsm"), ReadOnly=1)
    xl.Application.Run("excelsheet.xlsm!modulename.macroname")
##    xl.Application.Save() # if you want to save then uncomment this line and change delete the ", ReadOnly=1" part from the open function.
    xl.Application.Quit() # Comment this out if your excel script closes
    del xl

4
这是使用win32com在后台启动Excel,因此在Linux上无法工作。 - cole
2
如果我的宏保存在另一个名为.xlsm的文件中,而我需要运行的文件是另一个xls文件,我该怎么办? - Nimish Bansal
@NimishBansal,你找到解决方案了吗?我也有类似的问题。一种方法是打开xlsxlsm文件,就像手动打开Excel文档一样...但我还没有尝试过。 - alpha_989
2
@cole在Linux上运行VBA/.xlsm不是完全不可能的吗? - 3pitt
1
好的。绝对行不通 :) 如果我没记错的话,有人问过它在Linux上是否可行...否则我不知道我为什么会写那个。 - cole
显示剩余2条评论

13

我对SMNALLY的代码进行了一些修改,以使其可以在Python 3.5.2中运行。 这是我的结果:

    #Import the following library to make use of the DispatchEx to run the macro
    import win32com.client as wincl

    def runMacro():

        if os.path.exists("C:\\Users\\Dev\\Desktop\\Development\\completed_apps\\My_Macr_Generates_Data.xlsm"):

        # DispatchEx is required in the newest versions of Python.
        excel_macro = wincl.DispatchEx("Excel.application")
        excel_path = os.path.expanduser("C:\\Users\\Dev\\Desktop\\Development\\completed_apps\\My_Macr_Generates_Data.xlsm")
        workbook = excel_macro.Workbooks.Open(Filename = excel_path, ReadOnly =1)
        excel_macro.Application.Run\
            ("ThisWorkbook.Template2G")
        #Save the results in case you have generated data
        workbook.Save()
        excel_macro.Application.Quit()  
        del excel_macro

1
在Python 3.6上测试过,运行得非常好。我的情况下,“ThisWorkbook.Template2G”无法正常工作,直接使用宏名称则可以。例如:excel_macro.Application.Run("Template2G") - YoungSheldon

4

这是一个带有空格的xlsm文件,请注意。

file = 'file with spaces.xlsm'
excel_macro.Application.Run('\'' + file + '\'' + "!Module1.Macro1")

3

我怀疑您还没有授权您的Excel安装程序运行来自自动化Excel的宏。这是默认的安装安全保护措施。要更改此设置:

  1. 文件 > 选项 > 信任中心
  2. 单击“信任中心设置...”按钮
  3. 宏设置 > 选中启用所有宏

下面的“启用VBA模型”复选框是否被选中?另外,在之前的问题中,您是否尝试从Python生成宏,并且已经检查过您打开的xlsm文件中是否真的包含该宏? - Zeugma

1

SMNALLY的代码变体,如果您已经打开Excel,则不会退出:

import os, os.path
import win32com.client
    
if os.path.exists("excelsheet.xlsm"):
    xl=win32com.client.Dispatch("Excel.Application")
    wb = xl.Workbooks.Open(os.path.abspath("excelsheet.xlsm"), ReadOnly=1) #create a workbook object
    xl.Application.Run("excelsheet.xlsm!modulename.macroname")
    wb.Close(False) #close the work sheet object rather than quitting excel
    del wb
    del xl

如果我在Module1中有多个子程序,我是每次都要包含每个名称吗? - FabioSpaghetti

1

嗯,我在那个部分遇到了一些麻烦(是的,还在继续 xD):

xl.Application.Run("excelsheet.xlsm!macroname.macroname")

因为我不常使用Excel(与VB或宏一样,但我需要用Python使用Femap),所以最终通过检查宏列表解决了它: 开发人员->宏: 在那里我看到:这个“ macroname.macroname ”应该像“ sheet_name.macroname ”那样在“宏”列表中。 (我花了大约30分钟到1个小时来解决它,因此对于像我这样的Excel新手可能会有所帮助)xD

这不是必要的。macroname 不一定与特定工作表相关联。但是您是否正在尝试在名为 sheet_name 的特定工作表上运行 macroname - alpha_989

0

我正在使用 Windows 22H2 Build 22621.1194 64 位操作系统

Python 版本为 3.11.2

Microsoft® Excel® for Microsoft 365 MSO (版本号为 2302 Build 16.0.16130.20186) 64 位

我需要运行 python -m pip install pywin32 来安装 win32com.client

Python 代码

import time
import win32com.client
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename=r"C:\Users\david\Desktop\xl_HW.xlsm",ReadOnly=1)
xl.Application.Run("sbHelloWorld")
xl.Workbooks(1).Close(SaveChanges=0)
xl.Application.Quit()
xl=0
time.sleep(10)

不需要模块名称,因为子程序已声明为Public

VBA中的例程为 -

Public Sub sbHelloWorld()
    MsgBox "Hello World"
End Sub

0

我尝试了win32com和xlwings的方式,但都没有成功。我使用PyCharm,但在win32com的自动完成中没有看到.WorkBook选项。

当我尝试将工作簿传递为变量时,我遇到了-2147352567错误。

然后,我找到了一种使用vba shell运行Python脚本的方法。在完成所有操作后,在你正在使用的XLS文件上写入一些东西,这样Excel就知道是时候运行VBA宏了。

但是vba Application.wait函数会占用100%的CPU,这很奇怪。有些人说使用Windows Sleep函数可以解决这个问题。

 Import xlsxwriter


 Shell "C:\xxxxx\python.exe 
 C:/Users/xxxxx/pythonscript.py"

 exitLoop = 0

等待 Python 完成它的工作。

  Do

  waitTime = TimeSerial(Hour(Now), Minute(Now), Second(Now) + 30)
  Application.Wait waitTime
  Set wb2 = Workbooks.Open("D:\xxxxx.xlsx", ReadOnly:=True)
  exitLoop = wb2.Worksheets("blablabla").Cells(50, 1)
  wb2.Close exitLoop

  Loop While exitLoop <> 1




  Call VbaScript

-2
对于Python 3.7或更高版本(2018-10-10),我必须结合@Alejandro BR和SMNALLY的答案,因为@Alejandro忘记定义wincl。
import os, os.path
import win32com.client
if os.path.exists('C:/Users/jz/Desktop/test.xlsm'):
    excel_macro = win32com.client.DispatchEx("Excel.Application") # DispatchEx is required in the newest versions of Python.
    excel_path = os.path.expanduser('C:/Users/jz/Desktop/test.xlsm')
    workbook = excel_macro.Workbooks.Open(Filename = excel_path, ReadOnly =1)
    excel_macro.Application.Run("test.xlsm!Module1.Macro1") # update Module1 with your module, Macro1 with your macro
    workbook.Save()
    excel_macro.Application.Quit()  
    del excel_macro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接