YStudio Y++

谷歌填表类 — 完整使用说明

Y++ / YStudio 开发文档 · 一点滴

谷歌填表类 通过 Chrome DevTools Protocol(CDP) 控制本机 Chrome(或兼容的 Chromium / Edge),对当前附着的页面做:

  • 导航、等待加载
  • CSS 选择器填表 / 点击 / 取文本 / 取值
  • 执行(JS):在页面里跑任意 JavaScript,结果转成文本返回(采集、复杂操作的主入口)

类型名必须写 谷歌填表类(扩展类库)。


目录

  1. 它能做什么 / 不能做什么
  2. 最小闭环
  3. 启动、连接、打开、断开
  4. 启动前配置
  5. 状态与错误
  6. CSS 选择器基础
  7. DOM 封装方法详解
  8. 执行(JS) 详解
  9. DOM 常见写法大全
  10. JS 常见写法大全
  11. 动态列表 / 直播间类页面
  12. 表单与登录场景
  13. iframe、Shadow DOM、多标签
  14. 与定时器配合轮询
  15. 方法 / 属性总表
  16. 常见错误与排查
  17. 限制与注意

1. 它能做什么 / 不能做什么

能做

场景 怎么做
打开网址、跳转 打开(url)
填输入框、点按钮 填入 / 点击
读标签、标签文字 取值 / 取文本
等元素出现 等待出现 / 是否存在
任意页面逻辑 执行("…JS…")
抓列表、弹幕区、用户信息(只要在页面 DOM/JS 里) 执行 + 自己的选择器/脚本
window 上的全局状态 执行("JSON.stringify(window.xxx)")
本机自动起带调试口的 Chrome 连接()(或先 启动()

不能 / 不负责

说明
不是 HTTP 客户端 不会替你发「纯接口 POST」;页面表单点提交可以
没有内置「抖音弹幕类」 能力在 CDP + 你的 JS/选择器;平台 DOM 变了要改选择器
不管风控/封号 技术上能写;合规与账号风险自负
默认只附着一个页面目标 多标签用 打开新标签 / 连接调试地址(高级)
填入/点击/取* 只用 querySelector(第一个匹配) 要第 N 个、要批量,用 执行

2. 最小闭环

谷歌填表类 g
g.置超时(15000)
g.置端口(9333)

如果 (g.连接() == 假) {
    信息框(g.错误信息)
    返回()
}
如果 (g.打开("https://www.baidu.com") == 假) {
    信息框(g.错误信息)
    返回()
}
g.等待出现("#kw", 10000)
g.填入("#kw", "Y++")
g.点击("#su")
g.等待出现("#content_left", 12000)
输出(g.取标题())
g.断开()

推荐顺序:

  1. (可选)置端口 / 置超时 / 置用户目录
  2. 连接() — 附着调试页;本机没有调试口会自动启动 Chrome
  3. 打开(网址) — 导航
  4. DOM 操作或 执行
  5. 断开() — 只断 CDP,不强制关浏览器窗口

3. 启动、连接、打开、断开

3.1 启动() / 启动浏览器()

  • 确保本机调试口起来(必要时拉起 Chrome)
  • 附着页面、导航
  • 二者完全等价
如果 (g.启动() == 假) {
    输出(g.错误信息)
}
' 之后仍须 连接() 才能填表 / 执行

3.2 连接([端口], [主机])

  • 附着某个调试页的 WebSocket(CDP)
  • 端口/主机省略时用 置端口 / 置主机
  • 本机且调试口未开时:自动启动浏览器再连
  • 远程主机:不会自动启动,对方必须已开 --remote-debugging-port
g.连接()                 ' 用默认/已置端口
g.连接(9333)             ' 指定端口
g.连接(9333, "127.0.0.1")

成功后再 打开;连上时页面可能是 about:blank

3.3 连接调试地址(调试URL)

高级:直接连 /json/list 里的 webSocketDebuggerUrl

' url 形如 ws://127.0.0.1:9333/devtools/page/XXXX
如果 (g.连接调试地址(url) == 假) { … }

3.4 打开新标签([url], [端口], [主机])

新建标签并附着到该页;本机可自动起浏览器。

g.打开新标签("https://example.com")
g.打开新标签()   ' about:blank

3.5 打开(url)

已连接后导航,并等待页面达到可交互(interactive / complete,受超时限制)。

g.打开("https://example.com/login")
g.打开("file:///D:/page.html")      ' 本地文件也可(注意权限)

3.6 等待加载([毫秒])

再次等待 document.readyState;毫秒 ≤0 用会话超时。

g.打开(url)
g.等待加载(8000)

3.7 断开() / 重置()

方法 行为
断开() 断开 CDP;浏览器窗口可继续留着
重置() 断开 + 清错误;保留超时/端口等配置

关窗前建议先 断开(),避免残留连接。


4. 启动前配置

均须在 启动 / 连接 触发启动之前 设置才对自动拉起的进程生效。

方法 说明
置超时(毫秒) CDP/等待超时;≤0 回退 30000
置端口(端口) 默认调试口,常用 9222 / 9333;占用时库会尝试换空闲口
置主机(主机) 默认 127.0.0.1;仅本机自动启动
置框架(选择器) 后续 DOM 操作限定该 iframe;空=清除(见 13.1)
置用户目录(路径) --user-data-dir;空则用临时目录(按端口复用,二次启动更快)
置缓存目录(路径) --disk-cache-dir
置浏览器路径(路径) chrome.exe / msedge.exe 全路径;空则自动找
g.置超时(20000)
g.置端口(9333)
g.置用户目录("D:\\ypl_chrome_profile")
g.置缓存目录("D:\\ypl_chrome_cache")
g.置浏览器路径("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe")
g.连接()

独立用户目录的好处:不跟日常 Chrome 抢配置,调试参数不会被已开着的普通 Chrome「吞掉」。


5. 状态与错误

方法

方法 返回 说明
是否已连接() 逻辑 是否仍附着
取错误() 文本 最近一次失败原因
取页面URL() 文本 location.href(取标题前会刷新元数据)
取标题() 文本 document.title
取超时() 整数 当前超时毫秒

属性(无括号)

属性 等价
g.是否已连接 是否已连接()
g.错误信息 取错误()
g.超时 取超时()
g.页面URL 取页面URL()
g.页面标题 取标题()
如果 (g.连接() == 假) {
    输出(g.错误信息)
}
如果 (g.是否已连接) {
    输出(g.页面标题)
}

约定:返回逻辑假 / 空文本时,先看 错误信息


6. CSS 选择器基础

填入 / 点击 / 取文本 / 取值 / 是否存在 / 等待出现 的选择器都是 CSS,内部等价于:

document.querySelector(选择器)   // 只取第一个匹配

6.1 常用写法

选择器 含义
#id id
.class class
input 标签名
input[name=user] 属性
input[type=password] 类型
button[type=submit] 提交按钮
form#login input.user 后代
div > span 子代
a[href*="login"] 属性包含
[data-id="1"] data-*
#list li:first-child 伪类(视浏览器支持)
#list li:nth-child(2) 第 2 个子节点

6.2 在浏览器里怎么找选择器

  1. F12 → Elements
  2. 右键节点 → Copy → Copy selector
  3. 或自己写短选择器,在 Console 试:document.querySelector('你的选择器')

6.3 选择器里的引号(Y++ 字符串)

g.填入("#user", "admin")
g.填入("input[name=password]", "123456")
g.点击("button[type=submit]")
' 选择器本身若含双引号,用单引号包属性或换写法:
g.点击("a[title='下一页']")

6.4 只要第 N 个、要全部 — 用 执行

封装方法不能#list li 的第 3 个;请用 JS:

文本 t = g.执行(@"(function(){
  var el=document.querySelectorAll('#list li')[2];
  return el?el.textContent:'';
})()")

7. DOM 封装方法详解

7.1 填入(选择器, 值) → 逻辑

对第一个匹配元素:

  1. focus()
  2. value
  3. 派发 inputchange(bubbles)

适合:<input><textarea>,以及带 value 的控件。
不一定适用于高度定制的富文本 / contenteditable(改用 执行)。

如果 (g.填入("#user", "admin") == 假) {
    输出(g.错误信息)   ' 常见:未找到元素
}
g.填入("input[name=wd]", "关键词")
g.填入("#kw", "")      ' 清空

7.2 点击(选择器) → 逻辑

对第一个匹配调用 element.click()

g.点击("#su")
g.点击("button[type=submit]")
g.点击("a.login")

页面用 React 等且普通 click 无效时,用 执行 派发更完整的鼠标事件(见第 10 节)。

7.3 取文本(选择器) → 文本

textContent(含后代文字,不含输入框的 value)。

文本 tip = g.取文本("#msg")
文本 nick = g.取文本(".user-name")

7.4 取值(选择器) → 文本

value(输入框当前内容)。

g.填入("#kw", "QT")
文本 v = g.取值("#kw")   ' "QT"

7.5 是否存在(选择器) → 逻辑

如果 (g.是否存在("#kw")) {
    g.填入("#kw", "x")
}

7.6 等待出现(选择器, [毫秒]) → 逻辑

轮询直到存在或超时;毫秒 ≤0 用会话超时。等待期间会泵 UI 消息,降低假死感。

如果 (g.等待出现("#content_left", 15000) == 假) {
    输出(g.错误信息)
}

典型组合:

g.打开(url)
如果 (g.等待出现("#user", 10000) == 假) { 返回() }
g.填入("#user", "a")
g.填入("#password", "b")
g.点击("#login")
g.等待出现("#welcome", 10000)
输出(g.取文本("#welcome"))

8. 执行(JS) 详解

8.1 语义

文本 结果 = g.执行(表达式)
  • 当前页面执行 JavaScript
  • 返回值会转成文本(对象/数组建议自己 JSON.stringify
  • 表达式为空或未连接 → 空文本,并设错误信息

底层大致是 CDP Runtime.evaluatereturnByValue),并把结果字符串化。

8.2 短表达式 vs 长脚本

短:

文本 title = g.执行("document.title")
文本 href = g.执行("location.href")
文本 n = g.执行("document.querySelectorAll('.item').length")

长脚本:用原始字符串 @"",避免转义地狱:

文本 json = g.执行(@"(function(){
  var nodes = document.querySelectorAll('.chat-item');
  var arr = [];
  for (var i = 0; i < nodes.length; i++) {
    arr.push({
      text: nodes[i].innerText,
      html: nodes[i].innerHTML
    });
  }
  return JSON.stringify(arr);
})()")

8.3 必须「有返回值」

执行 要的是表达式结果。纯语句建议包成 IIFE:

' 推荐
g.执行(@"(function(){ document.body.style.zoom='1.2'; return 'ok'; })()")

' 不推荐:无返回值的语句,结果常为空或不稳定
g.执行("document.body.style.zoom='1.2'")

8.4 返回类型约定

JS 侧 建议
字符串/数字/布尔 直接 return x,Y++ 收到文本
对象/数组 return JSON.stringify(x),再用 JSON类解析
DOM 元素 不要直接 return 节点;return 其 textContent / 属性
undefined 往往变成空文本

8.5 与封装方法的分工

需求 优先
简单填一个 input 填入
简单点一下 点击
第 N 个、批量、复杂条件 执行
checkbox / select / contenteditable 执行
读全局状态、算数据 执行
等元素 等待出现(或 JS 里轮询,一般更慢)

9. DOM 常见写法大全

下列均可直接改选择器后使用。

9.1 读属性 / dataset / HTML

文本 href = g.执行("document.querySelector('a.more').getAttribute('href')")
文本 src = g.执行("document.querySelector('img.avatar').src")
文本 html = g.执行("document.querySelector('#panel').innerHTML")
文本 cls = g.执行("document.querySelector('#btn').className")

9.2 设置属性 / 样式

g.执行(@"(function(){
  var e=document.querySelector('#kw');
  if(!e) return 'no';
  e.setAttribute('placeholder','请输入');
  e.style.border='2px solid red';
  return 'ok';
})()")

9.3 checkbox / radio

g.执行(@"(function(){
  var e=document.querySelector('#agree');
  if(!e) return 'no';
  e.checked = true;
  e.dispatchEvent(new Event('change',{bubbles:true}));
  return String(e.checked);
})()")

9.4 <select> 下拉框

' 按 value
g.执行(@"(function(){
  var s=document.querySelector('#city');
  if(!s) return 'no';
  s.value='330100';
  s.dispatchEvent(new Event('change',{bubbles:true}));
  return s.value;
})()")

' 按可见文本
g.执行(@"(function(){
  var s=document.querySelector('#city');
  if(!s) return 'no';
  for (var i=0;i<s.options.length;i++){
    if(s.options[i].text.indexOf('杭州')>=0){
      s.selectedIndex=i;
      s.dispatchEvent(new Event('change',{bubbles:true}));
      return s.value;
    }
  }
  return 'miss';
})()")

9.5 contenteditable / 富文本近似

g.执行(@"(function(){
  var e=document.querySelector('[contenteditable=true]');
  if(!e) return 'no';
  e.focus();
  e.innerText='要填的内容';
  e.dispatchEvent(new InputEvent('input',{bubbles:true}));
  return e.innerText;
})()")

9.6 滚动

g.执行("window.scrollTo(0, document.body.scrollHeight)")
g.执行("document.querySelector('#list').scrollTop = 99999")
g.执行(@"(function(){
  var e=document.querySelector('#target');
  if(e) e.scrollIntoView({behavior:'instant',block:'center'});
  return e?'ok':'no';
})()")

9.7 键盘事件(回车提交等)

g.执行(@"(function(){
  var e=document.querySelector('#kw');
  if(!e) return 'no';
  e.focus();
  var ev=new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,which:13,bubbles:true});
  e.dispatchEvent(ev);
  return 'ok';
})()")

9.8 更「真」的点击(React 等)

g.执行(@"(function(){
  var e=document.querySelector('#su');
  if(!e) return 'no';
  var opts={bubbles:true,cancelable:true,view:window};
  e.dispatchEvent(new MouseEvent('mousedown',opts));
  e.dispatchEvent(new MouseEvent('mouseup',opts));
  e.dispatchEvent(new MouseEvent('click',opts));
  return 'ok';
})()")

9.9 文件选择 <input type=file>

CDP 页面脚本通常无法可靠地给 file 输入框赋本地路径(浏览器安全限制)。
若业务必须上传:优先看站点是否提供「粘贴/拖拽」或其它入口;或改用本机自动化其它方案。本类不保证 file 输入。

9.10 隐藏元素

querySelector 能选到 display:none 的节点,但 click() 可能无效。可先改样式再点,或直接调站点暴露的函数。

g.执行(@"(function(){
  var e=document.querySelector('#hidden-btn');
  if(!e) return 'no';
  e.style.display='block';
  e.click();
  return 'ok';
})()")

10. JS 常见写法大全

10.1 页面信息

g.执行("document.title")
g.执行("location.href")
g.执行("location.pathname")
g.执行("document.readyState")
g.执行("document.cookie")
g.执行("navigator.userAgent")

10.2 统计数量

g.执行("document.querySelectorAll('.chat-item').length")
g.执行("document.images.length")

10.3 批量导出文本列表(JSON)

文本 批 = g.执行(@"(function(){
  var list=document.querySelectorAll('.item .title');
  var out=[];
  for(var i=0;i<list.length;i++) out.push(list[i].innerText.trim());
  return JSON.stringify(out);
})()")

10.4 带结构的对象数组

文本 批 = g.执行(@"(function(){
  var rows=document.querySelectorAll('tr.data-row');
  var out=[];
  for(var i=0;i<rows.length;i++){
    var tds=rows[i].querySelectorAll('td');
    out.push({
      name: tds[0]?tds[0].innerText:'',
      score: tds[1]?tds[1].innerText:''
    });
  }
  return JSON.stringify(out);
})()")
' 随后可用 JSON类 解析 批

10.5 读 / 写全局变量

文本 st = g.执行("JSON.stringify(window.__INITIAL_STATE__ || {})")
g.执行(@"(function(){ window.__MY_FLAG__=1; return String(window.__MY_FLAG__); })()")

10.6 localStorage / sessionStorage

g.执行("localStorage.getItem('token')")
g.执行(@"(function(){ localStorage.setItem('k','v'); return localStorage.getItem('k'); })()")
g.执行("JSON.stringify(sessionStorage)")

10.7 调用页面已有函数

g.执行(@"(function(){
  if(typeof window.sendChat==='function'){
    window.sendChat('hello');
    return 'ok';
  }
  return 'no-fn';
})()")

在页面里发请求(同源 / CORS 规则跟浏览器一致):

文本 body = g.执行(@"(async function(){
  var r = await fetch('/api/list',{credentials:'include'});
  var t = await r.text();
  return t;
})()")

注意:若运行时把 执行 当同步表达式,顶层 async 可能拿不到 Promise 结果。更稳妥:

  • 站点若把数据放在 DOM/全局,优先读 DOM/全局;或
  • 用同步 XHR(不推荐但有时可用):
文本 body = g.执行(@"(function(){
  var xhr=new XMLHttpRequest();
  xhr.open('GET','/api/list',false);
  xhr.withCredentials=true;
  xhr.send(null);
  return xhr.responseText;
})()")

10.9 等待条件(在 JS 里短等)

长时间等待优先用 Y++ 的 等待出现 或定时器轮询 执行。JS 内短等示例:

文本 ok = g.执行(@"(function(){
  var t0=Date.now();
  while(Date.now()-t0<3000){
    if(document.querySelector('#ready')) return '1';
  }
  return '0';
})()")

(会占满页面主线程几秒,慎用。)

10.10 去掉脚本 / 规范化文本

g.执行(@"(function(){
  var e=document.querySelector('.msg');
  return e?(e.innerText||'').replace(/\s+/g,' ').trim():'';
})()")

11. 动态列表 / 直播间类页面

没有单独的「弹幕/礼物/用户」API,但只要数据在页面上,就可以写。

11.1 流程

  1. 连接()
  2. 打开(直播间或列表页 URL)(或手动打开后再 连接
  3. 等待出现(列表容器选择器)
  4. 循环或定时器里 执行 抓取

11.2 抓当前全部弹幕/消息文本

函数 抓消息列表() 文本 {
    返回(g.执行(@"(function(){
      var nodes=document.querySelectorAll('.chat-item'); /* 改成实页选择器 */
      var out=[];
      for(var i=0;i<nodes.length;i++){
        out.push(nodes[i].innerText);
      }
      return JSON.stringify(out);
    })()"))
}

11.3 只取最新一条

函数 最新一条() 文本 {
    返回(g.执行(@"(function(){
      var nodes=document.querySelectorAll('.chat-item');
      if(!nodes.length) return '';
      return nodes[nodes.length-1].innerText;
    })()"))
}

11.4 解析昵称 + 内容(结构示例)

文本 json = g.执行(@"(function(){
  var nodes=document.querySelectorAll('.chat-item');
  var out=[];
  for(var i=0;i<nodes.length;i++){
    var n=nodes[i];
    var nameEl=n.querySelector('.nick');
    var textEl=n.querySelector('.text');
    out.push({
      name: nameEl?nameEl.innerText:'',
      text: textEl?textEl.innerText:(n.innerText||'')
    });
  }
  return JSON.stringify(out);
})()")

11.5 礼物 / 用户列表

同理:F12 找到礼物条、在线用户节点,换成对应 class:

g.执行("document.querySelectorAll('.gift-item').length")
g.执行(@"(function(){
  var u=document.querySelectorAll('.user-list .user');
  var out=[];
  for(var i=0;i<u.length;i++) out.push(u[i].innerText);
  return JSON.stringify(out);
})()")

11.6 从全局状态读(若站点有)

文本 room = g.执行("JSON.stringify(window.__ROOM__ || window.__STORE__ || {})")

11.7 去重轮询思路

文本 上次 = ""

函数 拉增量() {
    文本 现在 = 最新一条()
    如果 (现在 != "" 且 现在 != 上次) {
        上次 = 现在
        输出(现在)
    }
}

配合「第 14 节」定时器即可。


12. 表单与登录场景

12.1 经典登录

g.打开("https://example.com/login")
g.等待出现("#user", 10000)
g.填入("#user", "admin")
g.填入("#password", "secret")
g.点击("button[type=submit]")
g.等待出现("#dashboard", 15000)
输出(g.取标题())

12.2 验证码 / 二次验证

自动化填验证码依赖业务;常见做法是:等到验证码输入框出现后暂停,人工输入,再继续脚本(或接打码接口——需你自己接,本类不内置)。

12.3 多步向导

g.填入("#step1-name", "张三")
g.点击("#next")
g.等待出现("#step2", 8000)
g.填入("#step2-phone", "13800000000")
g.点击("#submit")

12.4 搜索页

g.打开("https://www.baidu.com")
g.等待出现("#kw", 8000)
g.填入("#kw", "关键词")
g.点击("#su")
g.等待出现("#content_left", 12000)
输出(g.取标题())

13. iframe、Shadow DOM、多标签

13.1 iframe(穿透框架)

填入 / 点击 / 取文本 / 取值 / 是否存在 / 等待出现 已支持同源框架:

  1. 未置框架:先查顶层 document;找不到再递归穿透所有同源 iframe/frame(含嵌套)。
  2. 置框架(选择器):之后上述 API 都只在该框架内操作,直到 清除框架() 或再次 打开(导航会自动清框架)。
  3. 框架填入 / 框架点击 / …:一次性指定框架,不改会话里的「置框架」。
' 方式 A:自动穿透(常见登录框在同源 iframe 里)
g.填入("#user", "admin")
g.点击("button[type=submit]")

' 方式 B:先锁定框架,再连续操作
g.置框架("iframe#login")
g.填入("#user", "admin")
g.填入("#password", "123")
g.点击("button.submit")
g.清除框架()

' 方式 C:一次性指定
g.框架填入("iframe#main", "#user", "admin")
g.框架点击("iframe#main", "button.submit")
文本 t = g.框架取文本("iframe#main", "#msg")

限制:

  • 跨域 iframe 读不了 contentDocument(浏览器同源策略),会报「跨域框架无法穿透」。
  • 框架尚未加载完可能「框架无文档」——先 等待出现 或稍后再试。
  • Shadow DOM 仍需 执行(见 13.2)。

仍可用手动 执行(复杂场景):

g.执行(@"(function(){
  var f=document.querySelector('iframe#main');
  if(!f||!f.contentDocument) return 'no-frame';
  var e=f.contentDocument.querySelector('#user');
  if(!e) return 'no-el';
  e.value='admin';
  e.dispatchEvent(new Event('input',{bubbles:true}));
  return 'ok';
})()")

13.2 Shadow DOM

g.执行(@"(function(){
  var host=document.querySelector('my-widget');
  if(!host||!host.shadowRoot) return 'no-shadow';
  var e=host.shadowRoot.querySelector('input');
  if(!e) return 'no-el';
  e.value='x';
  return e.value;
})()")

13.3 多标签

  • 打开新标签(url):新标签并附着
  • 或浏览器 /json/list 取其它页的 webSocketDebuggerUrl,再 连接调试地址
  • 一次会话通常只附着一个 page target;切换标签需重新连接对应调试 URL

14. 与定时器配合轮询

谷歌填表类 g
定时器类 tm
文本 上次弹幕 = ""

函数 拉弹幕() {
    如果 (g.是否已连接 == 假) { 返回() }
    文本 现 = g.执行(@"(function(){
      var n=document.querySelectorAll('.chat-item');
      if(!n.length) return '';
      return n[n.length-1].innerText;
    })()")
    如果 (现 != "" 且 现 != 上次弹幕) {
        上次弹幕 = 现
        输出(现)
    }
}

函数 主窗口.创建完毕() {
    g.置端口(9333)
    如果 (g.连接() == 假) { 信息框(g.错误信息) 返回() }
    g.打开("你的页面地址")
    g.等待出现(".chat-item", 60000)
    tm.启动(1000, &拉弹幕)   ' 每秒拉一次;方法名以你工程定时器类为准
}

(定时器类具体 API 见扩展类库文档;此处表达的是「周期调用 执行」模式。)


15. 方法 / 属性总表

配置

签名 返回
置超时([整数 毫秒=30000])
取超时() 整数
置用户目录(文本 路径)
置缓存目录(文本 路径)
置浏览器路径(文本 路径)
置端口([整数 端口=9222])
置主机([文本 主机="127.0.0.1"])
置框架([文本 框架选择器=""])
清除框架()
取框架() 文本

生命周期

签名 返回
启动() / 启动浏览器() 逻辑
连接([整数 端口=0], [文本 主机=""]) 逻辑
连接调试地址(文本 调试URL) 逻辑
打开新标签([文本 url], [整数 端口], [文本 主机]) 逻辑
断开()
重置()
是否已连接() 逻辑

导航与等待

签名 返回
打开(文本 url) 逻辑
等待加载([整数 毫秒=0]) 逻辑
等待出现(文本 选择器, [整数 毫秒=0]) 逻辑
是否存在(文本 选择器) 逻辑

DOM

签名 返回
填入(文本 选择器, 文本 值) 逻辑
点击(文本 选择器) 逻辑
取文本(文本 选择器) 文本
取值(文本 选择器) 文本

框架(iframe)

签名 返回
置框架([文本 框架选择器=""])
清除框架()
取框架() 文本
框架填入(文本 框架选择器, 文本 选择器, 文本 值) 逻辑
框架点击(文本 框架选择器, 文本 选择器) 逻辑
框架取文本(文本 框架选择器, 文本 选择器) 文本
框架取值(文本 框架选择器, 文本 选择器) 文本
框架是否存在(文本 框架选择器, 文本 选择器) 逻辑
框架等待出现(文本 框架选择器, 文本 选择器, [整数 毫秒=0]) 逻辑

详见第 13.1 节。

JS / 状态

签名 返回
执行(文本 表达式) 文本
取错误() 文本
取页面URL() 文本
取标题() 文本

属性

超时错误信息是否已连接页面URL页面标题(见第 5 节)。


16. 常见错误与排查

现象 可能原因 处理
连接失败 / 调试口未就绪 端口被占、Chrome 未开调试 置端口;结束残留 chrome;看错误信息
窗口「未响应」 旧版本阻塞 connect 更新运行时库
填入/点击 选择器错、未加载完、跨域框架 F12 验证;先 等待出现;同源用 置框架/框架填入
执行 表达式无返回值、JS 抛错 IIFE + return;先在 Console 试
点了没反应 框架拦截了 click 第 9.8 节完整鼠标事件
iframe 里填不了 跨域,或框架未加载 第 13.1 节;同源用穿透/置框架
日常 Chrome 已开,调试参数无效 单例合并 置用户目录 独立配置
组件 [g] 不存在 写成了控件方法如 打开开发者工具 用本类的 打开 / 执行

手动验证调试口:

浏览器打开:http://127.0.0.1:9333/json/version(端口按你的设置),应返回 JSON。


17. 限制与注意

  1. 下标 / 多元素:封装 API 只打第一个 querySelector;批量用 执行
  2. 加载策略打开interactive/complete 即可继续,不等无穷长资源。
  3. 断开 ≠ 关浏览器:只断 CDP。
  4. 本机 CDPws://127.0.0.1:端口/...,不走系统代理。
  5. 安全执行 能跑任意页面 JS,等同你在 Console 操作。
  6. 合规:抓取直播/用户数据须遵守平台协议与法律;文档只讲技术写法。

附录 A — 一页速查

谷歌填表类 g
g.置超时(15000)
g.置端口(9333)
g.连接()
g.打开(url)
g.等待出现(sel, ms)
g.填入(sel, 值)
g.点击(sel)
g.取文本(sel)
g.取值(sel)
g.执行("JS 表达式")
g.取标题()
g.错误信息
g.断开()

附录 B — 相关文档