如何在Google Scripts中处理动态表单URL实现高尔夫预订自动提交
可落地的自动化实现方案
核心逻辑:每日变动的Google表单本质上只是随机替换了表单ID、题目对应的entry数字ID,只要题目的文本、顺序完全不变,就可以通过解析公开表单页面的源码自动匹配提交地址和字段映射,不需要手动硬编码修改。
方案1:自动匹配新表单地址与对应entry字段(对应需求1)
原来需要手动修改的2部分内容(硬编码的form_url、13个entry ID映射)全部通过脚本自动解析,日常仅需把每日新表单的公开访问链接粘贴到表格固定单元格即可,不需要改代码。
- 实现逻辑:
- 拉取表单公开页面的源码,自动提取真实提交用的
formResponse地址 - 匹配每个题目的纯文本标题和对应的entry ID建立映射表
- 按照表格中存储的预订信息,自动绑定对应字段值,拼接提交的payload
- 拉取表单公开页面的源码,自动提取真实提交用的
- 完整可运行代码:
// 辅助函数:输入表单公开访问链接,自动返回提交地址和题目标题-entry ID的映射 function parseGolfForm(formPublicUrl) { const formHtml = UrlFetchApp.fetch(formPublicUrl, {muteHttpExceptions: true}).getContentText(); // 自动提取真实提交用的formResponse地址 const formSubmitMatch = formHtml.match(/action="(https:\/\/docs\.google\.com\/forms\/d\/e\/[^/]+\/formResponse)"/); if (!formSubmitMatch) throw new Error("无法解析表单提交地址,请检查公开链接是否正确"); const submitUrl = formSubmitMatch[1]; // 自动匹配题目标题和对应的entry ID const questionMap = {}; const questionRegex = /<div class="M2f2gb"[^>]*>(.*?)<\/div>.*?name="entry\.(\d+)"/gs; let match; while ((match = questionRegex.exec(formHtml)) !== null) { const rawTitle = match[1].replace(/<[^>]+>/g, '').trim(); // 去掉HTML标签获取纯题目文本 const entryId = `entry.${match[2]}`; questionMap[rawTitle] = entryId; } // 单独识别表单内置的邮箱字段 const hasEmailField = formHtml.includes('name="emailAddress"'); return {submitUrl, questionMap, hasEmailField}; } function golf_booking() { const workbook = SpreadsheetApp.getActiveSpreadsheet(); const worksheet = workbook.getSheetByName("Details"); // 每日仅需在这里粘贴新表单的公开访问链接(存在B15单元格),无需修改代码 const formPublicUrl = worksheet.getRange("B15").getDisplayValue(); const formInfo = parseGolfForm(formPublicUrl); // 把key替换成你表单上完全一模一样的题目文字,和表格读取的值做绑定 const titleValueMap = { "联系邮箱": worksheet.getRange("B1").getDisplayValue(), "1号选手姓名": worksheet.getRange("B2").getDisplayValue(), "1号选手会员号": worksheet.getRange("B3").getDisplayValue(), "联系手机号": worksheet.getRange("B4").getDisplayValue(), "预订日期": worksheet.getRange("B5").getDisplayValue(), "预订类型": worksheet.getRange("B6").getDisplayValue(), "偏好开球时间": worksheet.getRange("B7").getDisplayValue(), "2号选手姓名": worksheet.getRange("B8").getDisplayValue(), "2号选手会员号": worksheet.getRange("B9").getDisplayValue(), "3号选手姓名": worksheet.getRange("B10").getDisplayValue(), "3号选手会员号": worksheet.getRange("B11").getDisplayValue(), "4号选手姓名": worksheet.getRange("B12").getDisplayValue(), "4号选手会员号": worksheet.getRange("B13").getDisplayValue() }; // 自动拼接提交用的datamap,不需要手写entry ID const datamap = {}; if (formInfo.hasEmailField) datamap.emailAddress = titleValueMap["联系邮箱"]; Object.entries(titleValueMap).forEach(([title, value]) => { if (title === "联系邮箱") return; const matchedEntry = formInfo.questionMap[title]; if (!matchedEntry) throw new Error(`表单中找不到题目:${title},请检查题目文本是否完全匹配`); datamap[matchedEntry] = value; }); // 提交逻辑和原脚本完全一致 const options = {method: "post", payload: datamap}; UrlFetchApp.fetch(formInfo.submitUrl, options); }
- 使用说明:
- 在
Details工作表新增B15单元格,用来存放每日的新表单公开打开链接 - 代码中
titleValueMap的key值必须和表单题目文字完全一致,不能有多余空格、标点差异,否则会匹配失败
- 在
方案2:按题目顺序循环提交(对应需求2)
如果表单的题目顺序长期固定,不需要逐字匹配题目文本,直接按题目出现的顺序循环匹配entry ID即可,代码逻辑更轻量。
- 实现逻辑:
- 解析页面时按顺序提取所有entry ID存入数组
- 把需要提交的预订值按照和表单题目一致的顺序排成数组
- 循环遍历数组,自动拼接提交payload,不需要手写entry映射
- 完整可运行代码:
// 按页面出现顺序提取所有entry ID function parseFormByOrder(formPublicUrl) { const formHtml = UrlFetchApp.fetch(formPublicUrl, {muteHttpExceptions: true}).getContentText(); const submitUrl = formHtml.match(/action="(https:\/\/docs\.google\.com\/forms\/d\/e\/[^/]+\/formResponse)"/)[1]; // 按出现顺序抓所有entry ID,自动去重 const entryList = []; const entryRegex = /name="entry\.(\d+)"/g; let match; while ((match = entryRegex.exec(formHtml)) !== null) { const entryId = `entry.${match[2]}`; if (!entryList.includes(entryId)) entryList.push(entryId); } return {submitUrl, entryList, hasEmail: formHtml.includes('name="emailAddress"')}; } function golf_booking() { const workbook = SpreadsheetApp.getActiveSpreadsheet(); const worksheet = workbook.getSheetByName("Details"); const formPublicUrl = worksheet.getRange("B15").getDisplayValue(); const formInfo = parseFormByOrder(formPublicUrl); // 按照表单题目顺序,把要提交的值排成一致的数组 const valueOrderList = [ worksheet.getRange("B2").getDisplayValue(), // 第1题:1号选手姓名 worksheet.getRange("B3").getDisplayValue(), // 第2题:1号选手会员号 worksheet.getRange("B4").getDisplayValue(), // 第3题:联系手机号 worksheet.getRange("B7").getDisplayValue(), // 第4题:偏好开球时间 worksheet.getRange("B8").getDisplayValue(), // 第5题:2号选手姓名 worksheet.getRange("B9").getDisplayValue(), // 第6题:2号选手会员号 worksheet.getRange("B10").getDisplayValue(), // 第7题:3号选手姓名 worksheet.getRange("B11").getDisplayValue(), // 第8题:3号选手会员号 worksheet.getRange("B12").getDisplayValue(), // 第9题:4号选手姓名 worksheet.getRange("B13").getDisplayValue(), // 第10题:4号选手会员号 worksheet.getRange("B5").getDisplayValue(), // 第11题:预订日期 worksheet.getRange("B6").getDisplayValue() // 第12题:预订类型 ]; // 循环自动拼接待提交的datamap const datamap = {}; if (formInfo.hasEmail) datamap.emailAddress = worksheet.getRange("B1").getDisplayValue(); for (let i = 0; i < formInfo.entryList.length; i++) { datamap[formInfo.entryList[i]] = valueOrderList[i]; } UrlFetchApp.fetch(formInfo.submitUrl, {method: "post", payload: datamap}); }
注意事项
- 解析用的正则如果遇到Google前端结构调整,仅需微调匹配规则,核心逻辑(提取提交地址、匹配input字段名)不会变化
- 表单的题目文本、顺序如果发生变动,需要同步调整脚本内的映射关系或值的排列顺序
- 脚本仅拉取公开表单页面的源码,不需要额外的Google账号授权,只要表单可公开填写即可正常运行
内容的提问来源于stack exchange,提问作者miwotsukushi
相关产品推荐
相关产品推荐

