如何在Google Apps Script中通过URL获取图片EXIF信息?无需存Drive方案
无需保存到Google Drive提取JPEG图片EXIF的解决方案
首先,你之前遇到的ReferenceError: Drive is not defined错误,是因为你使用了Google Drive的高级API服务,但没在脚本编辑器里启用它。不过既然你不想把图片存到Drive,咱们直接换个思路——直接解析图片的二进制数据提取EXIF,或者用本地工具批量处理,下面给你两个可行方案:
方案一:在Google Apps Script中直接解析(无需存Drive)
这个方案不用把图片存到Drive,直接通过UrlFetch获取图片的二进制数据,手动解析JPEG里的EXIF信息,最后生成Google表格:
function extractExifFromUrl(imageUrl) { // 获取图片的二进制数据 const response = UrlFetchApp.fetch(imageUrl); const bytes = response.getBlob().getBytes(); // 核心解析函数:从二进制数据中提取EXIF function getExifData(rawBytes) { const exifData = {}; // 先验证是不是JPEG格式 if (rawBytes[0] !== 0xFF || rawBytes[1] !== 0xD8) return null; let offset = 2; const totalLength = rawBytes.length; // 遍历JPEG的APP段,找到存储EXIF的APP1段 while (offset < totalLength) { if (rawBytes[offset] === 0xFF && rawBytes[offset + 1] === 0xE1) { const appSegmentLength = (rawBytes[offset + 2] << 8) | rawBytes[offset + 3]; const exifHeader = Utilities.newBlob(rawBytes.slice(offset + 4, offset + 10)).getDataAsString(); if (exifHeader === "Exif\0\0") { // 解析TIFF头和IFD标签 let tiffStart = offset + 10; const endianType = rawBytes[tiffStart] === 0x49 ? "LE" : "BE"; const ifdOffset = tiffStart + 8; const tagCount = endianType === "BE" ? (rawBytes[ifdOffset] << 8) | rawBytes[ifdOffset + 1] : (rawBytes[ifdOffset + 1] << 8) | rawBytes[ifdOffset]; // 遍历每个EXIF标签,提取常用字段 for (let i = 0; i < tagCount; i++) { const tagPos = ifdOffset + 2 + i * 12; const tagId = endianType === "BE" ? (rawBytes[tagPos] << 8) | rawBytes[tagPos + 1] : (rawBytes[tagPos + 1] << 8) | rawBytes[tagPos]; const tagType = endianType === "BE" ? (rawBytes[tagPos + 2] << 8) | rawBytes[tagPos + 3] : (rawBytes[tagPos + 3] << 8) | rawBytes[tagPos + 2]; const valueCount = endianType === "BE" ? ((rawBytes[tagPos + 4] << 24) | (rawBytes[tagPos + 5] << 16) | (rawBytes[tagPos + 6] << 8) | rawBytes[tagPos + 7]) : ((rawBytes[tagPos + 7] << 24) | (rawBytes[tagPos + 6] << 16) | (rawBytes[tagPos + 5] << 8) | rawBytes[tagPos + 4]); let tagValue; // 处理ASCII字符串类型的标签(比如相机品牌、型号) if (tagType === 2) { const valueStart = endianType === "BE" ? ((rawBytes[tagPos + 8] << 24) | (rawBytes[tagPos + 9] << 16) | (rawBytes[tagPos + 10] << 8) | rawBytes[tagPos + 11]) : ((rawBytes[tagPos + 11] << 24) | (rawBytes[tagPos + 10] << 16) | (rawBytes[tagPos + 9] << 8) | rawBytes[tagPos + 8]); tagValue = Utilities.newBlob(rawBytes.slice(valueStart, valueStart + valueCount - 1)).getDataAsString(); } // 处理SHORT类型的标签(比如光圈值) else if (tagType === 3 && valueCount === 1) { tagValue = endianType === "BE" ? (rawBytes[tagPos + 8] << 8) | rawBytes[tagPos + 9] : (rawBytes[tagPos + 9] << 8) | rawBytes[tagPos + 8]; } // 映射标签ID到易懂的名称 switch(tagId) { case 0x010F: exifData["CameraMake"] = tagValue; break; case 0x0110: exifData["CameraModel"] = tagValue; break; case 0x0132: exifData["DateTimeOriginal"] = tagValue; break; case 0x829A: exifData["ExposureTime"] = tagValue; break; case 0x829D: exifData["FNumber"] = tagValue; break; } } return exifData; } } // 移动到下一个APP段 const segmentLength = (rawBytes[offset + 2] << 8) | rawBytes[offset + 3]; offset += segmentLength + 2; } return null; } return getExifData(bytes); } // 生成EXIF表格的主函数 function generateExifSpreadsheet() { // 替换成你的亚马逊S3图片URL列表 const imageUrls = [ "https://your-s3-image-url-1.jpg", "https://your-s3-image-url-2.jpg" // 可以添加更多URL ]; // 表格表头 const tableHeaders = ["图片URL", "相机品牌", "相机型号", "拍摄日期", "曝光时间", "光圈值"]; const tableRows = [tableHeaders]; // 批量处理每个图片URL imageUrls.forEach(url => { const exifInfo = extractExifFromUrl(url); tableRows.push([ url, exifInfo?.CameraMake || "无", exifInfo?.CameraModel || "无", exifInfo?.DateTimeOriginal || "无", exifInfo?.ExposureTime ? `${exifInfo.ExposureTime}s` : "无", exifInfo?.FNumber ? `f/${exifInfo.FNumber}` : "无" ]); }); // 创建新的Google表格并写入数据 const newSheet = SpreadsheetApp.create("图片EXIF信息汇总表").getActiveSheet(); newSheet.getRange(1, 1, tableRows.length, tableRows[0].length).setValues(tableRows); // 打印表格链接,方便你查看 Logger.log("EXIF表格已生成,链接:" + SpreadsheetApp.getActiveSpreadsheet().getUrl()); }
使用步骤:
- 打开Google Apps Script编辑器(https://script.google.com)
- 新建一个脚本项目,把上面的代码粘贴进去
- 把
imageUrls数组替换成你的亚马逊S3图片URL列表 - 点击运行
generateExifSpreadsheet,第一次运行需要授权脚本访问网络和表格服务
方案二:用Python本地批量处理(更灵活)
如果你有Python环境,这个方案会更简单——用exifread库解析图片EXIF,pandas生成Excel表格,完全不需要依赖Google服务:
import requests import exifread import pandas as pd # 替换成你的图片URL列表 image_urls = [ "https://your-s3-image-url-1.jpg", "https://your-s3-image-url-2.jpg" ] exif_records = [] # 批量处理每个URL for url in image_urls: try: # 流式请求图片,避免下载整个文件 response = requests.get(url, stream=True, timeout=10) response.raise_for_status() # 解析EXIF信息 exif_tags = exifread.process_file(response.raw, details=False) # 整理需要的字段 record = { "图片URL": url, "相机品牌": str(exif_tags.get("Image Make", "无")), "相机型号": str(exif_tags.get("Image Model", "无")), "拍摄日期": str(exif_tags.get("EXIF DateTimeOriginal", "无")), "曝光时间": str(exif_tags.get("EXIF ExposureTime", "无")), "光圈值": str(exif_tags.get("EXIF FNumber", "无")) } exif_records.append(record) except Exception as e: print(f"处理URL {url} 出错:{str(e)}") exif_records.append({"图片URL": url, "相机品牌": "解析失败", "相机型号": "解析失败", "拍摄日期": "解析失败", "曝光时间": "解析失败", "光圈值": "解析失败"}) # 生成Excel表格 df = pd.DataFrame(exif_records) df.to_excel("图片EXIF信息表.xlsx", index=False) print("EXIF表格已生成,保存为:图片EXIF信息表.xlsx")
使用步骤:
- 安装依赖库:在命令行执行
pip install requests exifread pandas openpyxl - 把代码里的
image_urls替换成你的URL列表 - 运行脚本,会在当前目录生成Excel表格
内容的提问来源于stack exchange,提问作者Андрей Малахов




