繁体   English   中英

使用 Python 更新 Excel 电子表格中的链接

[英]Update Links in for Excel Spreadsheet Using Python

我正在 Python 中运行模拟,生成的输出需要由建模者在其 excel 工作簿中直接使用。 我已经生成了将我的数据直接输出到他们的 excel 电子表格模板中的代码。 我生成的用于将数据直接输出到他们的模板的代码很好,但我遇到的问题是建模者有一系列“链接”在一起的工作簿。 如果我将数据插入到他们的电子表格中,则指向该工作簿的链接不会更新,除非用户实际打开工作簿以“编辑链接”->“更新值”。 如果只有一个工作簿,则用户可以毫无问题地打开该工作簿。 实际上,将有 100 多个工作簿需要更新链接。 不幸的是,我无法改变建模者链接工作簿的方法——我唯一能做的就是适应他们的方法。

我的目标是创建一个 Python 解决方案,使我能够 1) 生成模拟数据,2) 将生成的数据插入建模者的工作簿,以及 3) 更新工作簿之间的所有链接。 最终,为了精简,我希望能够在一个端到端的 python 程序中完成所有三项工作。 我已经解决了 (1) 和 (2),并且我有一个几乎有效的 (3) 解决方案。 我生成了以下功能脚本:

from win32com.client import Dispatch
import pandas as pd
from openpyxl import load_workbook
import os
import time

def run_macro(workbook_name, vba_sub, com_instance):
    wb = com_instance.workbooks.open(workbook_name)
    wb.RefreshAll()
    xl_module = wb.VBProject.VBComponents.Add(1)
    xl_module.CodeModule.AddFromString(vba_sub.strip())
    com_instance.Application.Run('UpdateLinkValues')
    wb.Save()
    wb.Close()

    return True

def main():
    dir_root  = ("C:\\Model_Spreadsheets")

    vba_sub = \
        '''
        sub UpdateLinkValues()
            Application.AskToUpdateLinks = False
            ActiveWorkbook.UpdateLink Name:=ActiveWorkbook.LinkSources
        end sub
        '''

    xl_app = Dispatch("Excel.Application")
    xl_app.Visible = False
    xl_app.DisplayAlerts = False

    for root, dirs, files in os.walk(dir_root):
        for fn in files:
            if fn.endswith(".xlsx") and fn[0] is not "~":
                run_macro(os.path.join(root, fn), vba_sub, xl_app)
    xl_app.Quit()


if __name__ == "__main__":
    main()

该脚本非常接近我正在寻找的正确解决方案,但我似乎“随机”遇到了 VBA 错误:

run-time error '1004' method 'updatelink' method of object '_workbook' failed

每次我尝试运行这个脚本时都会出现这个错误,但它不会每次都出现在同一个工作簿上——有时,它出现在第一个工作簿上,有时出现在 15 日,等等......

我可以选择在 VBA 中进行调试,我可以继续处理下一个工作簿的唯一方法是将宏更改为

sub UpdateLinkValues()
    Application.AskToUpdateLinks = False
end sub

如果我运行这个宏并退出调试,程序将继续运行,直到再次遇到相同的错误。 我的第一个想法是,在我打开工作簿和尝试运行宏之间可能存在时间问题。 我发现的一种解决方法是我可以更改宏和应用程序可见性:

vba_sub = \
    '''
    sub UpdateLinkValues()
        Application.AskToUpdateLinks = False
    end sub
    '''

xl_app.Visible = True

这工作正常,但我不喜欢打开和关闭每个工作簿,因为这需要很长时间。 我的问题是,有谁知道为什么会出现这个运行时错误——有解决方案吗? 或者,有没有人知道如何将 Python 中的这个运行时错误作为异常拦截? 如果我可以将此错误作为 python 中的异常拦截,那么我可以使用我的替代解决方案来处理这些细节工作簿。

提前致谢!

考虑让 Python 使用您初始化的 COM 对象(即xl_appwb对象)直接运行UpdateLink方法。 无需在每个工作簿中构建宏然后调用它。

UpdateLink()下面包含在try/except/finally块中,以防工作簿没有链接,因为LinkSources将返回一个Empty值,引发 COM 异常,即您收到的错误:

对象“_workbook”的运行时错误“1004”方法“updatelink”方法失败

还要确保在使用后取消初始化对象(VBA 中的最佳实践: Set wb = Nothing )以释放 CPU 资源,否则它们将作为后台进程保留,直到垃圾回收。

def run_macro(workbook_name, com_instance):
    wb = com_instance.workbooks.open(workbook_name)
    com_instance.AskToUpdateLinks = False
    try:
       wb.UpdateLink(Name=wb.LinkSources())

    except Exception as e:
       print(e)

   finally:
       wb.Close(True)
       wb = None    
    return True

def main():
    dir_root  = ("C:\\Model_Spreadsheets")

    xl_app = Dispatch("Excel.Application")
    xl_app.Visible = False
    xl_app.DisplayAlerts = False

    for root, dirs, files in os.walk(dir_root):
        for fn in files:
            if fn.endswith(".xlsx") and fn[0] is not "~":
                run_macro(os.path.join(root, fn), xl_app)
    xl_app.Quit()
    xl = None

另外 - 尽管 VBA 默认随 Excel 和 MS Office 应用程序一起提供,但它实际上是一个单独的组件。 要检查,在 VBA IDE 中的 Tools \\ References 下,您将看到 VBA 是第一个检查的项目,没有内置。 事实上,VBA 所做的正是您在 Python 中所做的:为 Excel 对象库创建一个 COM 接口。 所以在某种意义上,VBA 与 Excel 和 Python 的关系一样!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM