
最近折腾定时把爬虫数据写进 Google Sheets,踩了几个坑,这篇把问题说清楚。
一次性往表格里写数据很简单,但要实现每小时自动同步、无人值守,问题就来了:
这篇文章介绍服务账号这条路,以及我写的一个封装好的 Actor,直接调用就行。
服务账号是 Google Cloud 项目里的一个机器人身份。它自己签名令牌,不需要授权页面,也不用手动刷新。
client_email -> 编辑者权限。这个服务账号只能访问你明确共享给它的表格。在控制台删除密钥,访问立刻终止。
用的 Actor 是 feedsmith/google-sheets-sync。Python 调用示例:
import json, os, requests rows = [ {"sku": "A-100", "name": "Desk lamp", "price": {"usd": 24.9}, "tags": ["home", "light"]}, {"sku": "A-101", "name": "Monitor arm", "price": {"usd": 59.0}, "tags": ["office"]},
] run = requests.post( "https://api.apify.com/v2/acts/feedsmith~google-sheets-sync/run-sync-get-dataset-items", params={"token": os.environ["APIFY_TOKEN"]}, json={ "serviceAccountKey": open("service-account.json").read(), "spreadsheet": "https://docs.google.com/spreadsheets/d/<your-sheet-id>/edit", "sheetName": "Prices", "mode": "upsert", "keyColumns": ["sku"], "rawData": rows, }, timeout=300,
)
print(json.dumps(run.json(), indent=2))
返回一条汇总记录(真实运行,耗时 8.6 秒):
[{"mode": "upsert", "spreadsheetId": "12EqRkXF...", "sheetName": "Prices", "rowsAppended": 2, "rowsUpdated": 0, "rowsSkippedDuplicate": 0, "newColumns": ["sku", "name", "price.usd", "tags"], "totalCellsAfter": 52104, "warnings": []}]
第二天再跑,发送 {"sku": "A-100", "price": {"usd": 19.9}} 加一个新的 SKU A-102,同样的调用。返回显示 "rowsAppended": 1, "rowsUpdated": 1,表格内容如下:
| sku | name | price.usd | tags |
|---|---|---|---|
| A-100 | Desk lamp | 19.9 | home, light |
| A-101 | Monitor arm | 59 | office |
| A-102 | Cable tray | 12.5 |
A-100 保留了 name 和 tags 字段,只更新了价格。
内部处理逻辑:
price.usd 被展平为独立列(嵌套对象变成 a.b.c 格式),tags 数组被转成 home, light 字符串。upsert 模式按 sku 匹配:已存在的行原地更新,新 SKU 追加到末尾。明天再跑一遍,新价格覆盖旧价格,每个 SKU 始终只有一行。Prices 不存在,会自动创建。其他模式:append(从不清空数据,新字段追加到右侧,已有的列顺序保持不变)、replace(清空整个工作表重写,旧数据会以 JSON 格式备份)和 read(把表格内容以 JSON 行数组返回,key 是表头名称)。
在 Apify 控制台打开爬虫 -> 集成 -> 连接 Actor -> 选择 Google Sheets Import & Export,把 datasetId 设为 {{resource.defaultDatasetId}}。爬虫每次跑完,结果自动进表格,不需要写代码。
通过 Apify MCP(模型上下文协议)服务器,代理可以直接调用这个 Actor,传入 rawData。因为不需要浏览器登录,从 Claude、Cursor 或任何 MCP 客户端调用,跟从 cron 定时任务跑,效果完全一样。
=IMPORTXML(...) 或 +1-... 这种,用 USER_ENTERED 模式写入会变成活的公式。默认是 RAW 模式;如果要用 USER_ENTERED,除非设置 allowFormulas,否则这类值会被转义处理。实测用 USER_ENTERED 时,=1+1 会作为文本存入(不是 2),+84 912 保持为字符串,2026-09-27 则被识别为真正的日期。每次成功运行 $0.002,每处理 1000 行额外 $0.005。失败的 dry run 不收费。
算笔账:每天同步 500 行大约 $0.21/月,每小时同步一次大约 $5/月。
本文与 Google 无关联。利益披露:这是我写的 Actor。这篇文章由 AI(人工智能,Claude)辅助起草,所有示例输出均来自 2026-09-27 的真实运行。