【问题标题】:Error while working with excel using python使用 python 处理 excel 时出错
【发布时间】:2010-09-15 13:26:58
【问题描述】:

当我的脚本同时更新一个 excel 时,如果我要手动使用另一个 excel 进行任何其他工作,我正在使用 dispatch

     from win32com.client import Dispatch

     excel    = Dispatch('Excel.Application')
excel.Visible   = True 

file_name="file_name.xls"
workbook = excel.Workbooks.Open(file_name)
workBook  = excel.ActiveWorkbook
sheet=workbook.Sheets(sheetno)

我收到这样的错误 (, com_error(-2147418111, '调用被被调用者拒绝', None, None)

有什么办法可以克服它..我可以更新另一个excel而不会出错..

【问题讨论】:

  • 包括整个回溯,这样我们就可以看到是哪一行导致了错误。一些 cmets:首先,您有两个变量 workbook 和 workBook,它们仅相差一个大写字母。可能不是一个好主意。其次,没有理由获取 ActiveWorkbook,因为您已经在上一行引用了它(打开的工作簿立即处于活动状态)。第三,您没有为文件名指定路径,因此如果您的 python 代码和 excel 工作表位于不同的目录中,则打开将失败。最后,也许 sheetno 超出了范围。 Excel 将工作表保存在一个从 1 开始的数组中。

标签: python excel pywin32 win32com


【解决方案1】:

我最近遇到了同样的问题。虽然听起来可能有多个根本原因,但我的情况正在发生,因为 Python 进行后续调用太快,Excel 跟不上,尤其是在外部查询刷新时。我通过在我的大多数呼叫之间插入time.sleep() 并为任何特别长的呼叫(通常在 7-15 秒之间)增加 sleep 参数,解决了这个间歇性的“呼叫被被呼叫者拒绝”错误。这使 Excel 有时间在 Python 发出其他命令之前完成每个命令。

【讨论】:

    【解决方案2】:

    出现此错误的原因是,如果您调用的 COM 对象已经在处理另一个操作,它将拒绝外部调用。没有对调用进行异步处理,并且行为可能看起来是随机的。

    根据操作,您将看到 pythoncom.com_error 或 pywintypes.com_error。解决此问题的一种简单(如果不太优雅)的方法是使用 try-except 将调用包装到 COM 对象中,如果遇到这些访问错误之一,请重试调用。

    有关背景信息,请参阅 Mark Hammond 和 Andy Robinson 撰写的 chapter 12 excerpt from Python Programming on Win32 的“错误处理”部分(O'Reilly 2000)。

    Siew Kam Onn's blog post "Python programming with Excel, how to overcome COM_error from the makepy generated python file" 中还有一些专门关于 Excel 的有用信息。

    【讨论】:

      【解决方案3】:

      我一直在为同样的问题苦苦挣扎,但现在我已经找到了一个对我有用的解决方案。

      我创建了一个类 ComWrapper,我将 Excel COM 对象包装在其中。它会自动包装每个嵌套对象并在 ComWrapper 中调用,并在它们用作函数调用的参数或对包装对象的赋值时将它们解包。包装器通过捕获“呼叫被被呼叫者拒绝”异常并重试呼叫直到达到顶部定义的超时来工作。如果达到超时,异常最终会被抛出到包装对象之外。

      对包装对象的函数调用由函数 _com_call_wrapper 自动包装,这就是神奇之处。

      要使其工作,只需使用 ComWrapper 包装来自 Dispatch 的 com 对象,然后像往常一样使用它,就像在代码底部一样。如有问题请评论。

      import win32com.client
      from pywintypes import com_error
      import time
      import logging
      
      _DELAY = 0.05  # seconds
      _TIMEOUT = 60.0  # seconds
      
      
      def _com_call_wrapper(f, *args, **kwargs):
          """
          COMWrapper support function. 
          Repeats calls when 'Call was rejected by callee.' exception occurs.
          """
          # Unwrap inputs
          args = [arg._wrapped_object if isinstance(arg, ComWrapper) else arg for arg in args]
          kwargs = dict([(key, value._wrapped_object)
                         if isinstance(value, ComWrapper)
                         else (key, value)
                         for key, value in dict(kwargs).items()])
      
          start_time = None
          while True:
              try:
                  result = f(*args, **kwargs)
              except com_error as e:
                  if e.strerror == 'Call was rejected by callee.':
                      if start_time is None:
                          start_time = time.time()
                          logging.warning('Call was rejected by callee.')
      
                      elif time.time() - start_time >= _TIMEOUT:
                          raise
      
                      time.sleep(_DELAY)
                      continue
      
                  raise
      
              break
      
          if isinstance(result, win32com.client.CDispatch) or callable(result):
              return ComWrapper(result)
          return result
      
      
      class ComWrapper(object):
          """
          Class to wrap COM objects to repeat calls when 'Call was rejected by callee.' exception occurs.
          """
      
          def __init__(self, wrapped_object):
              assert isinstance(wrapped_object, win32com.client.CDispatch) or callable(wrapped_object)
              self.__dict__['_wrapped_object'] = wrapped_object
      
          def __getattr__(self, item):
              return _com_call_wrapper(self._wrapped_object.__getattr__, item)
      
          def __getitem__(self, item):
              return _com_call_wrapper(self._wrapped_object.__getitem__, item)
      
          def __setattr__(self, key, value):
              _com_call_wrapper(self._wrapped_object.__setattr__, key, value)
      
          def __setitem__(self, key, value):
              _com_call_wrapper(self._wrapped_object.__setitem__, key, value)
      
          def __call__(self, *args, **kwargs):
              return _com_call_wrapper(self._wrapped_object.__call__, *args, **kwargs)
      
          def __repr__(self):
              return 'ComWrapper<{}>'.format(repr(self._wrapped_object))
      
      
      _xl = win32com.client.dynamic.Dispatch('Excel.Application')
      xl = ComWrapper(_xl)
      
      # Do stuff with xl instead of _xl, and calls will be attempted until the timeout is
      # reached if "Call was rejected by callee."-exceptions are thrown.
      

      我在这里对一个较新的问题给出了相同的答案: https://stackoverflow.com/a/55892457/2828033

      【讨论】:

        【解决方案4】:

        我运行密集的 Excel 工作表,在计算周期运行时始终显示此(阻塞)错误。

        解决方案是使用 for 循环。

        我提供了我的代码解决方案中有效的部分:

        # it failed, keep trying
        attempt_number = 0
        reading_complete = False
        while reading_complete==False:
            try:
                workbook = xw.Book(file_to_read)
                reading_complete = True
                print('file read...')
            except:
                reading_complete = False
                attempt_number += 1
                print('attempt:', attempt_number)
                if attempt_number > 5:
                    print('no good: exiting the process')
                    exit()
        

        地点:

        • file_to_read 是 Excel 工作簿的完整路径和名称。
        • attempt_number 变量设置为限制尝试次数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-12-04
          • 2011-06-24
          • 1970-01-01
          • 2018-10-03
          • 2019-12-05
          • 2019-08-21
          • 1970-01-01
          • 2021-09-26
          相关资源
          最近更新 更多