首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用 AI 把一张金山文档表格改成「实时行情看板」:7 只股票 30 秒自动刷新的完整实战

用 AI 把一张金山文档表格改成「实时行情看板」:7 只股票 30 秒自动刷新的完整实战

原创
作者头像
用户12881434
发布于 2026-11-07 18:33:12
发布于 2026-11-07 18:33:12
580
举报

一、起因:我不想再手抄价格了

我有一张金山文档表格,用来跟踪 7 只票的挂单价位:招金黄金、东百集团、博纳影业、天顺风能、西部黄金、顺灏股份、钧达股份。每只票一个区块,结构是「基准价 → 上涨触发价 / 下跌触发价 → 现价」。

盯盘软件当然能看价格,但我要的不是价格,是我自己设的那几个价位离现价还有多远。这个判断只有把行情填进我这张表里才成立。于是每天早上开盘,我就开始重复一个动作:切到行情软件,看一个价格,切回表格,手打进去,再切走……七只票,一天几十次。

我决定把它自动化。但我不会写前端,也不想搭服务器、买域名,更没有精力维护一套定时任务。我的想法很简单:能不能让 AI 把这件事从头到尾做完,最后我只负责打开文档看一眼?

下面是我和 AI 一起把这件事做成的全过程,包括中间踩的四个坑——我认为这几个坑比最后的成品更有价值。


二、第一次尝试:脚本直连行情接口,撞上白名单

金山文档自带 AirScript(基于 JavaScript 的表格自动化脚本),理论上最优雅的方案是:脚本定时执行 → 请求行情接口 → 写入单元格。

我一开始就是这么干的,核心代码大概长这样:

代码语言:javascript
复制
function fetchQuotes() {
  var url = "https://qt.gtimg.cn/q=sh601808,sz000506";
  var resp = HTTP.get(url);          // 期望拿到行情字符串
  var text = resp.text;
  // 解析 "v_sh601808=~中海油服~601808~12.75~..." 这种格式
  return parse(text);
}

跑起来直接报错:

代码语言:plaintext
复制
hostName not in whitelists

这是第一个认知纠正:AirScript 的 HTTP 请求走的是平台侧代理,只允许访问白名单内的域名。所有行情站点(腾讯、新浪、东方财富)都不在名单里,而且这个名单用户改不了。

也就是说,"文档内脚本直接抓行情"这条路在架构上就是死的,不是我代码写错了。这时候正确的选择是换架构,而不是继续调代码。


三、换成「外部抓 + Webhook 推」:绕开限制

既然文档脚本出不去,那就让外面的人进来。金山文档给每个脚本提供一个 Script Token Webhook:外部程序 POST 一个 JSON 过去,就能触发文档里的脚本函数,并且能把参数传进来。

于是架构变成两条腿:

代码语言:plaintext
复制
[本机 Python 推送器]  --每30秒-->  腾讯行情接口(qt.gtimg.cn,GBK 编码)
        |
        | POST + AirScript-Token
        v
[金山文档 AirScript]  -->  解析参数  -->  写入对应单元格

推送器核心就二十行:

代码语言:python
复制
CODES = ["sz000506", "sh600693", "sz001330", "sz002531",
         "sh601069", "sz002565", "sz002865"]

raw = requests.get("https://qt.gtimg.cn/q=" + ",".join(CODES),
                   timeout=15).content.decode("gbk", "ignore")

quotes = {}
for seg in raw.split(";"):
    m = re.search(r'v_(s[hz]\d+)="([^"]*)"', seg)
    if not m:
        continue
    f = m.group(2).split("~")
    quotes[m.group(1)] = {
        "price": float(f[3]),    # 现价
        "pct":   float(f[32]),   # 涨跌幅
        "high":  float(f[33]),   # 当日最高
        "low":   float(f[34]),   # 当日最低
    }

requests.post(WEBHOOK, headers={"AirScript-Token": TOKEN},
              json={"Context": {"argv": {"quotes": quotes}}})

这里有个值得记的细节:腾讯这个接口返回的是 GBK 编码,直接 resp.text 会得到一串乱码股票名。必须 content.decode("gbk")。我第一次就是因为这个,明明请求成功却解析出 0 条数据,排查了半小时。

另外,f[33] / f[34] 这两个字段直接给出当日最高 / 最低——这意味着"记录当天最高价"这件事根本不需要我自己维护状态、不需要存历史,接口白送。省掉一整个模块。


四、文档端:写入的几个硬规矩

AirScript 2.0 环境和浏览器里的 JS 不完全一样,我踩了三条:

1)写值必须用 Value2,不能用 Value

代码语言:javascript
复制
// 静默失败,单元格毫无变化,也不报错
sheet.Range("B6").Value = 13.97;
// 正确
sheet.Range("B6").Value2 = 13.97;

2)读文本用 Text,不要用 Value2

判断某个格子是不是"现价"这个标签时,Value2 有时拿到的是数字类型,字符串比较会失败。Range("B6").Text 才是稳定拿到显示文本的方式。

3)不要写死行号

我最初把 7 只票的位置写死成 B6 / B12 / B18……结果用户在表格里插了两行,全部错位。后来改成按股票名称做锚点,再向下找标签:

代码语言:javascript
复制
function findLabelRow(sheet, label, anchor) {
  // 只在锚点下方找,不能向上搜
  for (var r = anchor + 1; r <= anchor + 12; r++) {
    if (sheet.Range("B" + r).Text === label) return r;
  }
  return null;
}

这段代码里"只在下方找"是关键——我第一版写成上下各搜 12 行,结果每只票都找到了上一只票的"现价"标签,7 个价格整体往上串了一行。表面上"有数字",实际上全错位,这种 bug 最阴险。


五、四个真实的坑(建议收藏)

坑 1:ClearContents() 静默失效

我要清掉表格底部一堆残留的旧公式(全是 #DIV/0!)。写的是:

代码语言:javascript
复制
sheet.Range("A45:J60").ClearContents();   // 没有任何效果,也不报错

在 AirScript 2.0 里这个方法对合并单元格区域是静默失败的。最后改成逐格写空字符串:

代码语言:javascript
复制
function clearCells(sheet, r1, r2, c1, c2) {
  var n = 0;
  for (var r = r1; r <= r2; r++) {
    for (var c = c1; c <= c2; c++) {
      var addr = String.fromCharCode(64 + c) + r;
      try { sheet.Range(addr).Value2 = ""; n++; } catch (e) {}
    }
  }
  return n;
}

教训:在一个沙箱化的脚本环境里,"没报错"不等于"成功了"。凡是写操作,做完必须读回来验证。

坑 2:合并单元格会让写入"消失"

用户手动调格式时把 B8:B10 合并了。之后往 B9 写值,值确实写进去了,但显示在合并区域的左上角 B8——肉眼看就是"没写进去"。更麻烦的是,同一行其他列(C、D)没有被合并,于是整行看起来错位。

检测方法:

代码语言:javascript
复制
var mc = sheet.Range("B9").MergeCells;   // 判断是否处于合并区域

结论:任何"按坐标写入"的自动化,遇到用户手改过格式的表,都要先做一次结构体检。

坑 3:脚本错建工作表,用户以为"数据全没了"

我做"每日收盘存档"功能时,脚本自动 Sheets.Add 建了一张「历史记录」表。建完之后,文档当前显示的工作表变成了这张空白新表。

用户打开文档,看到一片空白,第一反应是"我的数据被你搞没了"。

实际上数据都在「工作表1」里,只是视图被切走了。修复只需要一行:

代码语言:javascript
复制
Application.Sheets.Item("工作表1").Activate();

这是纯粹的体验教训:任何会自动改动文档结构的功能,都必须自己负责把用户送回原来的位置,并在日志里明确说明"新建了什么、现在在哪张表"。技术上 100% 正确,体验上依然是事故。

坑 4:撤销键会把修复也撤销掉

我在文档里批量改了配色和结构,用户看到某处不对,习惯性按 Ctrl+Z——把我刚修好的东西一起撤回了,于是同样的乱象反复出现两次。

这事没法用代码解决,只能靠沟通:自动化接管了表格的"样式层"之后,人工撤销会破坏一致性。要么接受由脚本统一管理样式,要么明确哪些区域脚本不碰。


六、从"能用"到"愿意天天看":排版是被低估的一半

功能跑通那天,7 只票的价格每 30 秒精准落位。但我打开文档看了三秒就关了——太丑,不想看。

于是又花了一轮时间重做视觉,规则很简单:

项目

做法

原因

区块底色

7 只票统一淡蓝灰

交替底色会让人以为是两张表拼的

区块分隔

块之间加粗分隔线

密集数字里靠颜色分块不够,要有物理边界

现价区

米黄色底 + 加大加粗

一眼区分"我设的价位"和"市场当前价"

涨 / 跌

鲜红 / 青绿(同花顺配色)

用户长期看同花顺,颜色语义必须一致

当日最高 / 最低

红 / 绿加粗,紧跟现价

高低价差就是当天的波动幅度,放一起才有意义

这里有个通用原则:表格类工具的可用性,很大程度取决于「扫一眼能不能抓到关键信息」。如果用户需要逐行读,那自动化就只完成了一半——把判断成本从"抄写"降到了"阅读",但没降到"扫视"。


七、最终效果与成本

图 1:7 只票统一区块结构,现价 / 当日最高 / 当日最低实时刷新

图 2:统一为同花顺配色语义,涨红跌绿

现在的状态:

  • 刷新频率:30 秒(可调),只在交易时段 9:15–11:35 / 12:55–15:05 工作,收盘自动休眠
  • 写入内容:现价、涨跌幅、当日最高、当日最低,7/7 全部命中
  • 额外功能:现价距触发价 1% 以内自动变黄预警;每交易日 15:05 自动把收盘数据追加到「历史记录」表
  • 成本:0 元。没有服务器,推送器就是本机一个 Python 脚本,几十行

用户后来还问:"我把这个脚本拷走,是不是就不依赖你了?"——是的。pusher.py 加一个 启动.bat,双击就跑;离线包里还带了 一键开机自启.bat,用 schtasks 注册登录时自动启动:

代码语言:bat
复制
schtasks /create /tn "股价自动推送" /tr "%~dp0启动推送.bat" /sc onlogon /f

八、几点反思

1. 平台限制要先摸清,再动手写代码。

"hostName not in whitelists" 这个错误,本质是产品设计层面的决策(安全隔离),不是 bug。在这种约束下继续优化原来的代码路径,纯属浪费时间。识别"这是路封死了"比"把路走通"更重要。

2. 自动化最脆弱的地方不是算法,是人的手工操作。

四个坑里有三个(合并单元格、插行、撤销)都是"用户手动改过表格"引起的。任何面向真实用户的表格自动化,都必须假设结构随时会被人改,所以定位要用语义锚点(按名称找),不要用坐标。

3. 写操作必须回读验证。

ClearContents() 不报错但没生效这件事,如果不是我顺手读回来检查,会一直以为清干净了。沙箱环境的 API 完整度参差不齐,"无异常"是最不可靠的成功信号。

4. AI 的价值在于扛住"脏活",但判断权得留给人。

这个项目里 AI 干了什么:查接口字段、调编码、写脚本、部署到文档编辑器、反复跑验证、改配色。我干了什么:说清我要什么、在表格里指出"这块乱了"、决定价位。分工其实很清晰——AI 处理确定性劳动,人处理意图和验收。


附:这套方案的适用边界

同构的需求可以直接套:任何"把外部实时数据填进一张自己设计的在线表格",比如库存监控、竞品价格跟踪、服务器指标看板、项目进度日报。核心就三步:

  1. 外部程序取数 → 2. Webhook 推进文档脚本 → 3. 脚本按语义锚点写入并回读校验

不适合的场景也说清楚:需要毫秒级响应的(推送有延迟)、需要多人协同实时编辑同一区域的(会互相覆盖)、数据源需要付费授权的。

本文提到的接口字段与平台行为基于 2026 年 11 月的实测,平台迭代后可能变化;涉及股票数据仅为技术演示,不构成任何投资建议。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、起因:我不想再手抄价格了
  • 二、第一次尝试:脚本直连行情接口,撞上白名单
  • 三、换成「外部抓 + Webhook 推」:绕开限制
  • 四、文档端:写入的几个硬规矩
  • 五、四个真实的坑(建议收藏)
    • 坑 1:ClearContents() 静默失效
    • 坑 2:合并单元格会让写入"消失"
    • 坑 3:脚本错建工作表,用户以为"数据全没了"
    • 坑 4:撤销键会把修复也撤销掉
  • 六、从"能用"到"愿意天天看":排版是被低估的一半
  • 七、最终效果与成本
  • 八、几点反思
  • 附:这套方案的适用边界
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档