Delphi 10.4使用TEdgeBrowser提取网页游戏数据方法咨询
Delphi 10.4 基于TEdgeBrowser提取游戏轮次数据实现方案
目标站点为动态渲染的在线游戏运行页,静态爬取方式无法拿到实时更新的轮次数据,基于TEdgeBrowser(WebView2内核)的JS注入+消息通信能力可以实现稳定采集,具体实现流程如下:
前置配置
- 确保开发环境已正确安装WebView2运行时,TEdgeBrowser组件可正常加载目标页面。
- 所有脚本注入逻辑必须在页面完全加载完成后执行,避免因DOM未渲染导致的节点查找失败。
- 不推荐使用固定间隔轮询DOM的方式采集数据,性能损耗高且延迟大,优先使用DOM变动监听+主动消息推送的方案。
核心实现步骤
1. 页面加载完成后注入数据监听脚本
绑定TEdgeBrowser的OnNavigationCompleted事件,确认页面加载成功后,注入自定义JS脚本,实现两个核心能力:
- 自动匹配页面中显示总投注额、赔付系数的文本节点
- 监听DOM变动,数据更新时主动将结构化数据推送到Delphi层
注入的JS参考代码如下:
// 提取当前页面展示的游戏数据 function fetchCurrentGameData() { let totalBet = null; let payout = null; // 遍历节点匹配目标字段,适配动态class/ID的场景 document.querySelectorAll('*').forEach(el => { const content = el.innerText?.trim() || ''; if (content.startsWith('Total Bets:')) { totalBet = parseFloat(content.replace('Total Bets:', '').trim()); } if (content.startsWith('Payout coefficient:')) { payout = parseFloat(content.replace('Payout coefficient:', '').trim()); } }); return { totalBet: totalBet, payoutCoefficient: payout, updateTime: Date.now() }; } // 注册DOM变动监听器 const domObserver = new MutationObserver(() => { const currentData = fetchCurrentGameData(); // 仅当两个字段都为有效数值时推送消息 if (!isNaN(currentData.totalBet) && !isNaN(currentData.payoutCoefficient)) { window.chrome.webview.postMessage(JSON.stringify(currentData)); } }); // 启动全局监听 domObserver.observe(document.body, { childList: true, subtree: true, characterData: true }); // 首次加载完成主动推送一次初始数据 window.chrome.webview.postMessage(JSON.stringify(fetchCurrentGameData()));
如果游戏运行在iframe嵌套容器内,需要先定位到对应iframe的
contentWindow,在iframe的上下文内执行上述脚本,顶层页面直接注入无法读取iframe内部数据。
2. Delphi端接收消息并写入数据表
绑定TEdgeBrowser的OnWebMessageReceived事件,解析前端推送的JSON数据,校验合法性后写入自有数据表即可,参考代码片段:
uses System.JSON, System.SysUtils, Data.DB; procedure TMainForm.EdgeBrowser1NavigationCompleted(Sender: TObject; const Args: TNavigationCompletedEventArgs); var LMonitorScript: string; begin // 页面加载成功后注入监听脚本,脚本可存为RC资源避免硬编码 if Args.IsSuccess then begin LMonitorScript := LoadResString(RES_GAME_DATA_MONITOR); EdgeBrowser1.ExecuteScript(LMonitorScript); end; end; procedure TMainForm.EdgeBrowser1WebMessageReceived(Sender: TObject; const Args: TWebMessageReceivedEventArgs); var LJsonVal: TJSONValue; LTotalBet: Double; LPayout: Double; begin LJsonVal := TJSONObject.ParseJSONValue(Args.ArgsAsString); if not Assigned(LJsonVal) then Exit; try // 校验字段有效性 if LJsonVal.TryGetValue<Double>('totalBet', LTotalBet) and LJsonVal.TryGetValue<Double>('payoutCoefficient', LPayout) then begin // 此处执行数据入库逻辑,建议增加去重判断 // 可通过赔付系数重置特征(新一轮启动时系数回到初始值)区分轮次,避免写入同轮次的中间跳动值 qryGameStats.Append; qryGameStats.FieldByName('total_bet').AsFloat := LTotalBet; qryGameStats.FieldByName('payout_coef').AsFloat := LPayout; qryGameStats.FieldByName('record_time').AsDateTime := Now; qryGameStats.Post; end; finally LJsonVal.Free; end; end;
优化建议
- 如果页面DOM结构随版本更新频繁变动,可以在注入脚本中重写
window.fetch和XMLHttpRequest原型方法,直接拦截游戏前后端通信的接口响应,从接口报文中提取目标字段,不需要依赖DOM结构,长期稳定性更高。 - 若页面开启了JS上下文隔离导致DOM读取失败,可切换为WebView2的网络请求拦截API,在DevTools协议层监听游戏接口的响应数据,无需注入页面上下文即可拿到数据。
内容的提问来源于stack exchange,提问作者Maurilio Lima Junior
相关产品推荐
相关产品推荐

