Puppeteer的page.click可以正常工作,但是page.evaluate + document click不能正常工作。

6

我看到了很多帖子(例如请参见这里这里),说我可以通过以下代码点击某些东西,

await page.click('.route-redirect-box');   // via Puppeteer page.click

await page.evaluate((css_selector) => {
  document.querySelector(css_selector).click();  // or via page.evaluate
}, css);

然而,在我测试了一些网站后,似乎 page.evaluate 在使用 headless: false 模式时无法正常工作,但是 page.click 始终有效。例如,在这个网站的页面上,我尝试按以下方式点击某些内容:
var css = '#searchPaginationTop > nav > a:nth-child(5)';
await page.evaluate((css_selector) => { document.querySelector(css_selector).click();}, css);

什么也没有发生,但如果我使用page.click,它会按预期工作。

我在想,我想要点击的元素不是一个普通的可点击元素,因为该元素的html代码如下:

<a class="svg" data-goto-page="3" data-total-pages="3" data-ga="event" data-ga-category="Brands at allbeauty-Burberry-Pagination" data-ga-action="Brands at allbeauty-Burberry-Pagination-Next-Touch" data-ga-label="Brands at allbeauty-Burberry-Pagination-Next-Link">
    <svg viewBox="0 0 21.9 38.7" alt="Next Page" title="Next Page ">
        <use xlink:href="#icon-ab-arrow-right">
        </use>
    </svg>
</a>

可能是因为这个元素是一些 data-ga 的东西,所以 page.evaluate 无法单击它吗?


对我来说,https://www.allbeauty.com/gb/en/brands/burberry?page=1 没有 #searchPaginationTop 的子元素。我可以想象,它可能是动态注入的,也就是说,您需要等待相应的 JavaScript 运行。 - Ryuno-Ki
@pguardiario,你说得对,没有href,我猜应该存在一些事件监听器,但我就是找不到。那么page.evaluate + document.click不能触发该事件监听器吗?你能否给我更多的指引? - avocado
@ChristosLytras,我使用 headless=false 模式,这样当代码执行时,我可以看到元素是否真正被点击。 - avocado
1
@avocado 这与 puppeteer 的工作方式无关。如果您在浏览器中打开页面并打开开发工具控制台,然后执行 document.querySelector('#searchPaginationTop > nav > a:nth-child(5)').click(),也不会发生任何事情。这是因为这不是直接链接,而是JS应用程序处理鼠标事件。它甚至可以注册全局窗口事件以捕获所有 mousedown/click 事件。 - Christos Lytras
1
它使用Newrelic平台。他们有一个很大的github账户。@avocado为什么你想使用JavaScript进行点击?Puppeteer的click方法有什么问题吗?要在浏览器JS中实现这一点,您将不得不反转JS代码并查看如何注册/处理鼠标事件(甚至将其定位到github源代码),或随机尝试使用dispatchEventMouseEvent进行实验。 - Christos Lytras
显示剩余5条评论
2个回答

8

简短回答

  • page.evaluate(() => document.querySelector('SELECTOR').click()); 只是触发 click 事件
  • page.click('SELECTOR') 尝试模拟人类点击行为

解释

让我们查看两种方法的文档,以真正了解发生了什么。

page.evaluate(() => document.querySelector('SELECTOR').click());

让我们看看MDN文档说了什么:

[...] 它触发元素的 click 事件。

它仅仅是触发了 click 事件,使监听该元素点击事件的所有处理程序被调用。这意味着,它不关心元素是否在当前视口之外。该元素甚至可能被隐藏(通过CSS),而 click 事件仍会触发。

让我们将其与 "puppeteer方式" 进行比较:

page.click

Puppeteer文档中关于 page.click 的部分:

该方法通过 selector 获取元素,如有必要,将其滚动到视图中,并使用 page.mouse 在元素的中心位置单击。[...]

这意味着 puppeteer 在此处模拟了人类行为。首先,元素被滚动到视图中,然后鼠标移动到元素的顶部(在路上触发任何其他事件,如 mouseovermouseenter 等)。最后,通过模拟鼠标点击按钮(参见 puppeteer 代码中相应的Mouse 类),同时也会触发任何相关事件(如 mousedown)。

复杂的UI库可能不喜欢您自己触发JavaScript事件。请记住,它们通常是针对人类交互进行优化的,而不是针对与机器人的交互进行优化的。这意味着,UI库可能会监听 mousedownmouseenter 事件(例如)而不是直接监听 click 事件。

类人行为

在与未知网站交互时,最好尽可能像人类一样行为。即使没有任何特定的“反机器人”措施的页面,也可能使用期望特定事件流的框架。

顺便说一下,你不是唯一遇到这个问题的人。查看以下类似问题:


非常感谢您的详细解释,:-) - avocado

4

据我所知,该内容似乎是动态注入的。也就是说,通过使用 Puppeteer 的 waitForSelector ,您可以在继续之前等待它发生。以下内容应该可以实现此功能(由于我不知道如何触发导航更新,因此无法自行复制):

await page.click('.route-redirect-box');
const css = '#searchPaginationTop > nav > a:nth-child(5)';
await page.waitForSelector(css);
await page.evaluate((css_selector) => { document.querySelector(css_selector).click();}, css);

我添加了 waitForSelector,但是 evaluate 仍然无法工作。 - avocado
你能否添加一些说明,如何进入一个状态,以便 #searchPaginationTop 能够被填充?否则我无法帮助你。 - Ryuno-Ki
抱歉,我还不知道如何做到这一点,但我猜问题可能就在@pguardiario在评论中提到的。 - avocado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接