You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyInstaller打包Google Sheets API脚本为exe后报UnknownApiNameOrVersion错误

问题:PyInstaller打包Google Sheets脚本为.exe后执行报错

我编写了Python脚本用于读写Google Sheet数据,使用PyInstaller打包为.exe文件后无法执行,报错信息如下:

File "GoogleSheet.py", line 9, in __init__
File "Google_Process.py", line 31, in __init__
File "googleapiclient\_helpers.py", line 130, in positional_wrapper
File "googleapiclient\discovery.py", line 287, in build
File "googleapiclient\discovery.py", line 404, in _retrieve_discovery_doc
googleapiclient.errors.UnknownApiNameOrVersion: name: sheets  version: v4

相关代码文件:

GoogleSheet.py

from Google_Process import GoogleAPIClient
import pandas as pd

class GoogleSheets(GoogleAPIClient):
    def __init__(self, status = 'DONE', order_number = '') -> None:
        self.status = status
        self.order_number = order_number
        # 调用GoogleAPIClient.__init__,传入服务名、版本、权限范围
        super().__init__(
            'sheets',
            'v4',
            ['https://www.googleapis.com/auth/spreadsheets'],
        )

    def appendWorksheet(self):
        if self.status == 'DONE':
            df=pd.DataFrame(
            {'Order': [self.order_number],
            'Status': ['DONE'],
            'Mark': ['-']}
            )
            self.googleAPIService.spreadsheets().values().append(
                spreadsheetId='<ID>',
                range='sheet1',
                valueInputOption='USER_ENTERED',
                body={
                    'majorDimension': 'ROWS',
                    'values': df.values.tolist()
                },
            ).execute()
            return

Google_Process.py

import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

class GoogleAPIClient:
    SECRET_PATH = '.\PackingElf\.credentials\client_secrets_file.json'
    CREDS_PATH = '.\PackingElf\.credentials\cred.json'
    
    def __init__(self, serviceName: str, version: str, scopes: list) -> None:
        self.creds = None
        # client_secret.json用于存储用户的访问和刷新令牌,首次授权完成后自动创建

        if os.path.exists(self.CREDS_PATH):
            self.creds = Credentials.from_authorized_user_file(self.CREDS_PATH, scopes)

        # 若无有效凭证,引导用户登录授权
        if not self.creds or not self.creds.valid:
            if self.creds and self.creds.expired and self.creds.refresh_token:
                self.creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    self.SECRET_PATH, scopes)
                self.creds = flow.run_local_server(port=0)
            # 保存凭证供下次使用
            with open(self.CREDS_PATH, 'w') as token:
                token.write(self.creds.to_json())
            
            self.googleAPIService = build(serviceName, version, credentials=self.creds)

if __name__ == '__main__':
    googleSheetAPI = GoogleAPIClient(
        'sheets',
        'v4',
        ['https://www.googleapis.com/auth/spreadsheets'],
        )

    print(googleSheetAPI.googleAPIService)

解决方案

报错原因是PyInstaller打包时未包含Google API客户端的发现文档,导致build()方法无法获取到Sheets API的服务定义。以下是两种可行解决方法:

方法一:修改PyInstaller打包命令,包含发现文档

在打包时,通过--add-data参数将Google API客户端的发现文档目录包含到exe中:

# 替换<Python路径>为你的Python安装路径,比如C:\Python39
pyinstaller --onefile --add-data "<Python路径>\Lib\site-packages\googleapiclient\discovery_docs;googleapiclient\discovery_docs" GoogleSheet.py

该命令会将本地Python环境中googleapiclient的discovery_docs目录复制到打包后的程序目录对应位置,让build()方法能找到离线的API定义。

方法二:修改代码,使用本地离线发现文档

  1. 下载Sheets v4的发现文档:访问API官方定义地址,将内容保存为sheets_v4.json,放到项目根目录。
  2. 修改Google_Process.py代码,添加路径处理逻辑并指定离线发现文档:
import os.path
import sys
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

class GoogleAPIClient:
    def __init__(self, serviceName: str, version: str, scopes: list) -> None:
        self.creds = None
        # 获取程序运行路径(打包后用sys._MEIPASS,未打包用当前脚本路径)
        base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
        # 修正凭证文件路径,避免打包后找不到
        self.SECRET_PATH = os.path.join(base_path, 'PackingElf', '.credentials', 'client_secrets_file.json')
        self.CREDS_PATH = os.path.join(base_path, 'PackingElf', '.credentials', 'cred.json')
        
        if os.path.exists(self.CREDS_PATH):
            self.creds = Credentials.from_authorized_user_file(self.CREDS_PATH, scopes)

        if not self.creds or not self.creds.valid:
            if self.creds and self.creds.expired and self.creds.refresh_token:
                self.creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    self.SECRET_PATH, scopes)
                self.creds = flow.run_local_server(port=0)
            with open(self.CREDS_PATH, 'w') as token:
                token.write(self.creds.to_json())
            
            # 使用本地离线发现文档
            discovery_doc_path = os.path.join(base_path, 'sheets_v4.json')
            self.googleAPIService = build(
                serviceName, 
                version, 
                credentials=self.creds,
                discoveryServiceUrl=f'file://{discovery_doc_path}'
            )

if __name__ == '__main__':
    googleSheetAPI = GoogleAPIClient(
        'sheets',
        'v4',
        ['https://www.googleapis.com/auth/spreadsheets'],
        )

    print(googleSheetAPI.googleAPIService)
  1. 打包时将sheets_v4.json和凭证文件也包含进去:
pyinstaller --onefile --add-data "sheets_v4.json;." --add-data "PackingElf\.credentials\client_secrets_file.json;PackingElf\.credentials" GoogleSheet.py

内容的提问来源于stack exchange,提问作者Alexllll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 06:05:22