Last active
April 30, 2026 09:37
-
-
Save cnDelbert/53ccf95c7996a2e6a5dd5b9788bc5f02 to your computer and use it in GitHub Desktop.
DownloadViewOnlyGoogleSpreadSheet.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import re | |
| import os | |
| # --- Multi-language Configuration / 多语言配置中心 --- | |
| I18N_CONFIG = { | |
| 'zh': { | |
| 'parsing': "正在解析 URL: {id}", | |
| 'downloading': "正在通过导出引擎下载完整工作簿...", | |
| 'error_url': "错误:无法解析 URL。请确保是标准的 Google Sheets 链接。", | |
| 'error_restricted': "错误:无法导出。该文档可能设置了“禁止下载、打印和复制”权限。", | |
| 'error_403': "错误 403:权限受限。所有者可能彻底关闭了导出功能。", | |
| 'error_generic': "下载失败,HTTP 状态码: {code}", | |
| 'success': "✅ 成功!合并单元格、多页签和格式均已保留。", | |
| 'save_path': "文件保存路径: {path}" | |
| }, | |
| 'en': { | |
| 'parsing': "Parsing URL: {id}", | |
| 'downloading': "Downloading full workbook via export engine...", | |
| 'error_url': "Error: Could not parse URL. Please ensure it is a standard Google Sheets link.", | |
| 'error_restricted': "Error: Cannot export. This document may have 'Download, print, and copy' disabled.", | |
| 'error_403': "Error 403: Access denied. The owner may have disabled exports entirely.", | |
| 'error_generic': "Download failed, HTTP Status Code: {code}", | |
| 'success': "✅ Success! Merged cells, tabs, and formatting are preserved.", | |
| 'save_path': "Saved to: {path}" | |
| }, | |
| 'de': { | |
| 'parsing': "Analysiere URL: {id}", | |
| 'downloading': "Lade das vollständige Arbeitsblatt über die Export-Engine herunter...", | |
| 'error_url': "Fehler: URL konnte nicht analysiert werden.", | |
| 'error_restricted': "Fehler: Export nicht möglich. Download/Drucken/Kopieren ist deaktiviert.", | |
| 'error_403': "Fehler 403: Zugriff verweigert.", | |
| 'error_generic': "Download fehlgeschlagen, HTTP-Statuscode: {code}", | |
| 'success': "✅ Erfolg! Verbundene Zellen und Reiter wurden beibehalten.", | |
| 'save_path': "Gespeichert unter: {path}" | |
| }, | |
| 'es': { | |
| 'parsing': "Analizando URL: {id}", | |
| 'downloading': "Descargando el libro completo a través del motor de exportación...", | |
| 'error_url': "Error: No se pudo analizar la URL.", | |
| 'error_restricted': "Error: No se puede exportar. El documento puede tener restringida la descarga.", | |
| 'error_403': "Error 403: Acceso denegado.", | |
| 'error_generic': "Error en la descarga, código HTTP: {code}", | |
| 'success': "✅ ¡Éxito! Se han conservado las celdas combinadas y las pestañas.", | |
| 'save_path': "Guardado en: {path}" | |
| }, | |
| 'fr': { | |
| 'parsing': "Analyse de l'URL : {id}", | |
| 'downloading': "Téléchargement du classeur complet via le moteur d'exportation...", | |
| 'error_url': "Erreur : Impossible d'analyser l'URL.", | |
| 'error_restricted': "Erreur : Impossible d'exporter. Le téléchargement peut être désactivé.", | |
| 'error_403': "Erreur 403 : Accès refusé.", | |
| 'error_generic': "Échec du téléchargement, code HTTP : {code}", | |
| 'success': "✅ Succès ! Les cellules fusionnées et les onglets sont conservés.", | |
| 'save_path': "Enregistré sous : {path}" | |
| } | |
| } | |
| # Set current language here / 在此处设置当前语言: 'zh', 'en', 'de', 'es', 'fr' | |
| CURRENT_LANG = 'zh' | |
| def get_msg(key, **kwargs): | |
| """ | |
| Get text in the corresponding language. | |
| 根据选定语言获取对应文本。 | |
| """ | |
| msg = I18N_CONFIG.get(CURRENT_LANG, I18N_CONFIG['zh']).get(key, "") | |
| return msg.format(**kwargs) | |
| def download_spreadsheet_perfect(url, output_filename="Result.xlsx"): | |
| """ | |
| Download view-only Google Sheets as a full XLSX workbook. | |
| 以完整 XLSX 工作簿格式下载仅查看权限的 Google 表格。 | |
| """ | |
| # 1. Extract Spreadsheet ID / 提取表格 ID | |
| # Use regex to find the ID between /d/ and /edit | |
| # 使用正则表达式查找 /d/ 和 /edit 之间的 ID | |
| ss_id_match = re.search(r"/d/([a-zA-Z0-9-_]+)", url) | |
| if not ss_id_match: | |
| print(get_msg('error_url')) | |
| return | |
| spreadsheet_id = ss_id_match.group(1) | |
| print(get_msg('parsing', id=spreadsheet_id)) | |
| # 2. Construct Export URL / 构造导出 URL | |
| # By specifying format=xlsx and removing GID, Google exports the entire workbook. | |
| # 通过指定 format=xlsx 且不带 GID,Google 会导出包含所有页签的完整工作簿。 | |
| export_url = f"https://docs.google.com/spreadsheets/d/{spreadsheet_id}/export?format=xlsx" | |
| print(get_msg('downloading')) | |
| # Standard headers to mimic a browser / 模拟浏览器的标准请求头 | |
| headers = { | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" | |
| } | |
| try: | |
| # Request the file as a stream to handle large files / 以流式方式请求文件以处理大文件 | |
| response = requests.get(export_url, headers=headers, stream=True) | |
| if response.status_code == 200: | |
| # Check if we got an HTML page instead of a file (common if download is restricted) | |
| # 检查是否由于权限限制而返回了 HTML 页面而非文件 | |
| if 'html' in response.headers.get('Content-Type', ''): | |
| print(get_msg('error_restricted')) | |
| return | |
| # Write the binary content to a file / 将二进制内容写入文件 | |
| with open(output_filename, 'wb') as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| print(get_msg('success')) | |
| print(get_msg('save_path', path=os.path.abspath(output_filename))) | |
| elif response.status_code == 403: | |
| print(get_msg('error_403')) | |
| else: | |
| print(get_msg('error_generic', code=response.status_code)) | |
| except Exception as e: | |
| # Error handling for network or file system issues / 处理网络或文件系统错误 | |
| print(f"Runtime Error / 运行出错: {e}") | |
| # --- Execution / 执行 --- | |
| if __name__ == "__main__": | |
| # Your target Google Sheets URL / 你的目标 Google 表格链接 | |
| target_url = "https://docs.google.com/spreadsheets/d/1matpJ4yM7xN57q1KzMHU2nqO0H_ZsaQxdb-qWVAXiAY/edit?pli=1&gid=1057002407#gid=1057002407" | |
| # Run the download / 执行下载 | |
| download_spreadsheet_perfect(target_url, "Integrated_MultiSheet_Export.xlsx") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment