掘金 阅读 ( ) • 2024-03-28 23:49

theme: geek-black

咱们书接上文,这篇文章继续列出常用的十个 puppeteer 中常用的方法,希望各位大佬提出宝贵的意见。

11. 输入 input 框

这段代码定义了一个异步函数 editInput,它接受三个参数:page(表示当前操作的页面对象),selector(用于选择 input 元素的 CSS 选择器),和 inputVal(要输入到 input 框中的值)。函数首先通过 page.$(selector) 查找页面上的 input 元素,如果找不到则输出错误信息并结束进程。如果找到了,就通过三次点击选中 input 框中的内容(这里点击三次是为了确保选中所有内容,但具体次数可能需要根据实际情况调整),等待 300 毫秒后,使用 page.type 方法将 inputVal 输入到 input 框中,再等待 300 毫秒以确保输入完成。

const editInput = async (page,selector,inputVal) => {
  // 查找地址输入框
  const addressEl = await page.$(selector);
  if(!addressEl){
    console.error(`Error:找不到 ${selector} 元素!`);
    process.exit(1); // 结束进程,检测失败
  }
  // 填写地址
  await page.click(selector, { clickCount: 3 });
  await page.waitForTimeout(300);
  await page.type(selector, inputVal);
  await page.waitForTimeout(300);
}

12. 清空 input 框

这里提供了四种清空 input 框的方法:

  • 方式一方式二:直接通过 page.evaluateframe.$eval 修改 input 元素的值。这两种方法简单直接,但可能不会触发页面上的监听事件。
  • 方式三:通过获取 input 框的当前值,然后使用循环和 page.keyboard.press('Backspace') 模拟按键删除操作来清空 input 框。这种方式可以触发监听键盘事件的处理程序。
  • 方式四:类似于方式一和方式二,但是通过多次点击 input 框来选中其内容,然后再输入空字符串来“清空”它。同样,这种方式可能不会触发所有类型的监听事件。
// 方式一:请注意 这种方案只是给input的值清空了,页面有可能还有侦听事件,是不能触发的。
await page.evaluate( () => document.getElementById("inputID").value = "")

// 方式二:请注意 这种方案只是给input的值清空了,页面有可能还有侦听事件,是不能触发的。
await frame.$eval('inputID', el => el.value = '')

// 方式三:模拟键盘删除事件,有多少字符删除多少次
const inputValue = await page.$eval('#inputID', el => el.value);
for (let i = 0; i < inputValue.length; i++) {  
    await page.keyboard.press('Backspace');
}

// 方式四:模拟 input多次点击 操作,多次点击后会选中input的值,再次输入你想输入的字符
const input = await page.$('#inputID');
await input.click({ clickCount: 3 });
await input.type("", { delay: 100 });

13. 模拟 input 框失焦(position:fixed)

对于悬浮(position: fixed)和非悬浮两种情况,代码分别进行了处理。对于悬浮的情况,它首先使用 page.focus 将焦点设置到指定的 input 元素上,然后在页面上下文中执行 JavaScript 代码来设置 input 的值并使其失焦(通过调用 blur 方法)。对于非悬浮的情况,它直接点击 input 框并输入文本(这里假设 playname 是一个已经获取的 input 元素对象)。

// 如果待输入的 input 弹窗有悬浮【position:fixed】和在页面文档流中两种方式,则下面两个情况的代码均要写入检测脚本,避免任何一种情况检测不通过

// 【弹窗为悬浮情况】检测输入标题
await page.focus('input[name=input-playlist-title]');
await page.evaluate((selector) => {
    if(document.querySelector(selector)){
        document.querySelector(selector).value = "曲目2050";
        document.querySelector(selector).blur();
    }
}, 'input[name=input-playlist-title]');

// 【弹窗为不是悬浮情况】检测输入标题
await playname.click({ clickCount: 3 }); // playname 是输入框对象 page.$()
await playname.type("曲目2050");

14. 单选表单

这段代码首先通过 CSS 选择器定位到一个单选按钮并点击它。然后,它使用 page.$$eval 和一个过滤器函数来获取所有选中的单选按钮的值。但是,注意这里的 CSS 选择器语法似乎有误(input[(type = "radio")][(name = "gender")] 应该是 input[type="radio"][name="gender"]),并且 checkVal 的获取方式可能不会按预期工作,因为过滤器函数中的条件可能不正确(它应该检查 el.checked 是否为 true 而不是仅仅检查 el.checked 的存在性)。

   //点击单选中的一个单选按钮
   await page.click("input[type=radio][name=gender]#female"); 
    // 获取单选选中的值
   const checkVal = await page.$$eval(
     input[(type = "radio")][(name = "gender")],
     (els) => Array.from(els).filter((el) => el.checked)[0].value
   );

15. 多选表单

类似于单选表单的处理,这段代码首先点击两个多选框来选中它们(注意这里的 CSS 选择器语法也有误),然后使用 page.$$eval 和一个过滤器函数来获取所有选中的多选框的值。过滤器函数正确地检查了 el.checked 是否为 true

   // 点击多选按钮
   await page.click( input[type=checkbox][name=photography]) 
   await page.click( input[type=checkbox][name=reading])
   // 获取多选选中的值 
   const checkVals = await page.$$eval("input[type=checkbox]", (els) => {
      els = Array.from(els);
      return els.filter((el) => el.checked).map((el) => el.value);
   });

16. 检测激活样式是否使用

这个函数接受多个参数,用于检查一系列元素中是否有正确应用激活样式(activeClass)。它遍历指定范围内的元素,检查每个元素的类名是否包含激活样式。如果当前应该激活的元素没有应用激活样式,或者不应该激活的元素应用了激活样式,函数将输出错误信息并结束进程。但是,注意这里的代码有一些问题:CSS 选择器的拼接似乎有误(${selector + j}) 应该是 ${selector}${j}),并且可能需要更复杂的逻辑来正确处理类名的检查(比如使用正则表达式而不是简单的 indexOf 检查)。

const checkActiveClass = async (page,minNum,maxNum,activeNum,selector,activeClass) => {
  for (let j = minNum; j < maxNum; j++) {
    const b = await page.evaluate((selector) => {
      return document.querySelector(selector)&&document.querySelector(selector).className;
    }, `${selector + j})`);
    if (j == activeNum && b.indexOf(activeClass) == -1) {
      console.error("Error:当前操作的标签未使用激活样式 ${activeClass}");
      process.exit(1); // 结束进程,检测失败
    } else if (j != activeNum && b.indexOf(activeClass) > -1) {
      console.error(
        "Error:其他标签未恢复默认样式(即,移除激活样式 ${activeClass}"
      );
      process.exit(1); // 结束进程,检测失败
    }
  }
}

17. 获取元素样式

这段代码展示了两种获取元素样式的方法:一种是获取元素的 className(类名),另一种是获取元素的完整计算样式(通过 window.getComputedStyle)。page.evaluate 用于在页面上下文中执行 JavaScript 代码并返回结果,而 page.$eval 则接受一个选择器和一个函数作为参数,并在选定的元素上执行该函数。

// 获取 className
const b = await page.evaluate((s) => {
  return document.querySelector(s)&&document.querySelector(s).className;
}, selector);

// 获取 style
const style = await page.$eval(selector, (el) =>
  el&&JSON.parse(JSON.stringify(window.getComputedStyle(el)))
);

18. waitUntil 合理使用

这段代码展示了如何使用 page.goto 方法的 waitUntil 选项来指定页面加载成功的条件。waitUntil 可以接受多个字符串值,每个值代表一种页面加载状态的检查方式。这里列出了四种常见的选项:loaddomcontentloadednetworkidle0networkidle2,并解释了它们的含义。但是,注意这里的注释格式有误(使用了中文冒号而不是英文冒号),并且应该去掉 load:window.onload 这一行(因为它不是 waitUntil 的有效选项)。

await page.goto(pageURL, {
      waitUntil: ["load", "domcontentloaded", "networkidle0", "networkidl2"],
});
// waitUntil 代表什么时候才认为页面加载成功。
load:window.onload //onload 事件被触发时候页面加载成功,某些情况下它根本不会发生。
domcontentloaded //Domcontentloaded事件触发时候认为导航成功
networkidle0 //在 500ms 内没有网络连接时就算成功(全部的request结束),才认为页面加载成功
networkidle2 //500ms 内有不超过 2 个网络连接时就算成功(还有两个以下的request),就认为页面加载成功

19. 等待网络请求结束(在 500 ms 内没有网络连接就算成功)

这段代码似乎有误:它提到了 newPage.waitForNetworkIdle(),但 newPage 并没有在之前的代码中定义(可能是 page 的误写)。正确的做法应该是使用已经存在的 page 对象调用 waitForNetworkIdle() 方法来等待网络请求结束。此外,注意 waitForNetworkIdle() 方法默认就是在 500 毫秒内没有网络连接时认为页面加载成功;如果需要更改这个时间限制,可以通过传递一个选项对象作为参数来设置。

 await newPage.waitForNetworkIdle();

20. 点击列表元素其中一个

这段代码展示了如何使用 CSS 选择器来定位并点击列表中的一个元素(这里是第一个 .btn 类的元素)。page.click 方法接受一个选择器作为参数,并模拟在该选择器匹配的元素上进行点击操作。这里的 .btn:nth-child(1) 选择器表示选择作为其父元素的第一个子元素的 .btn 类的元素。但是,请注意,如果 .btn 元素不是其父元素的直接子元素,或者页面上有多个具有相同选择器的元素,这种方法可能不会按预期工作。在这种情况下,可能需要使用更具体的选择器或额外的逻辑来确保正确定位目标元素。

 await page.click(`.btn:nth-child(1)`); // 点击第一个 btn 元素