【问题标题】:How to select a DOM Element to scroll on it in Puppeteer如何在 Puppeteer 中选择要在其上滚动的 DOM 元素
【发布时间】:2019-04-17 09:36:51
【问题描述】:

我对 Puppeteer 和等待/异步语​​法很陌生。我正在尝试构建一个机器人来尝试从 Instagram 获取数据。具体来说,我想获得给定个人资料的关注者。一切正常,直到弹出追随者窗口。我想选择要在其上滚动的 DOM 元素,并在每次迭代时将跟随者推送到数组中。我搜索了论坛并尝试了不同的方法,但它总是返回未定义。我能够获得一个 ElementHandle (scrollBox3) 并获得像 scrollHeight 这样的属性,但不能获得实际的 DOM 元素。 代码如下,对文件的不同部分进行了描述。

任何帮助将不胜感激:)

下一部分选择 DOM 元素。 CRED 文件是我的用户名和密码所在的位置。

const puppeteer = require('puppeteer');
const CREDS = require('./creds');

// Dom Elements
const loginPage = 'https://www.instagram.com/accounts/login/';
const usernameInput = 'input[name="username"]';
const passwordInput = 'input[name="password"]';
const submitButton = 'button[type="submit"]';
const userToSearch = 'nicolekidman';
const searchUser = `https://www.instagram.com/${userToSearch}`;
const followers = `a[href='/${userToSearch}/followers/']`;

这部分以数组的形式记录scrollBox中可见的followers。

// Extract followers from a user profile
const extractFollowers = () => {
  let followers = [];
  let elements = document.getElementsByClassName('FPmhX notranslate _0imsa ');
  for (let element of elements)
      followers.push(element.textContent);
  return followers;
}

这是代码中断的滚动功能。基本上我想在这个滚动框上循环和滚动,但我无法抓住 DOM 元素。

// Scrolling Function
async function scrapeInfiniteScrollItems(
  page,
  extractFollowers,
  followersTargetCount,
  scrollDelay = 1000,
) {
  let items = [];
  // Next 2 lines return undefined
  // .isgrP and .PZuss are classes inside this div, PZuss is the one we want to scroll on
  let scrollBox1 = await page.$eval('.isgrP', el => el.querySelector('body > div:nth-child(15) > div > div > div.isgrP > ul > div'));
  let scrollBox2 = await page.$eval('body > div:nth-child(15) > div > div > div.isgrP > ul > div', el => el);

  // Next line returns an ElementHandle
  let scrollBox3 = await page.$('.PZuss');

  console.log(scrollBox3);
  let scrollBoxHeight = await page.$eval('.PZuss', el => el.scrollHeight);
  console.log(scrollBoxHeight);
  try {
    while (items.length < followersTargetCount) {
      items = await page.evaluate(extractFollowers);
      console.log(extractFollowers());
      // await page.evaluate('scrollBox.scrollTo(0, scrollable_popup.scrollHeight)');
      // await page.waitForFunction(`scrollBox.scrollHeight > ${previousHeight}`);
      // await page.waitFor(scrollDelay);
    }
  } catch(e) { }
  return items;
}

这是实际的异步功能,我正在做所有工作以访问 Instagram 并调用滚动功能来记录给定个人资料的关注者。

(async() => {
  // headless false for visual debugging in browser
  const browser = await puppeteer.launch({
    headless: false
  });
  const page = await browser.newPage();
  await page.goto(loginPage, {waitUntil: 'networkidle2'});
  // Type username
  await page.click(usernameInput);
  await page.keyboard.type(CREDS.username);

  // Type password and submit
  await page.click(passwordInput);
  await page.keyboard.type(CREDS.password);
  await page.click(submitButton);
  await page.waitFor(2000);

  // Search User with URL
  await page.goto(searchUser);
  await page.click(followers);
  await page.waitFor(2000);

  const findFollowers = await scrapeInfiniteScrollItems(page, extractFollowers, 100);
  console.log(findFollowers);
  await page.screenshot({ path: '../screenshots/insta.png' });

  // await browser.close();
})();

【问题讨论】:

    标签: javascript scroll async-await instagram puppeteer


    【解决方案1】:

    是的,我对 Instagram 不熟悉,但我将尝试与您逐步合作。您的代码一目了然(不幸的是,我没有办法测试此代码,因为我没有注册 Instagram),但有一些突出的地方。

    scrapeInfiniteScrollItems函数:

    let scrollBox1 = await page.$eval('.isgrP', el => el.querySelector('body > div:nth-child(15) > div > div > div.isgrP > ul > div'));
    let scrollBox2 = await page.$eval('body > div:nth-child(15) > div > div > div.isgrP > ul > div', el => el);
    

    您指出这两行都返回未定义。这是因为您没有完全正确地使用 $eval 方法。 $eval 方法允许您执行 querySelector 指令来定位特定的 DOM 元素(与您声明的 CSS 选择器匹配),然后内部函数在该 DOM 元素上实时执行 JavaScript 指令.

    让我们看看你的第一行:你要求它为一个类为isgrP 的元素执行querySelector,但随后你又在该元素上运行querySelector,它使用了一个CSS 选择器以body 开头?这没有意义。

    我还从那个奇怪的选择器中看到它以div.isgrP &gt; ul &gt; div 结尾,巧合的是,它有一个div,其类名与您最初使用$eval 方法查询的类名相同。那么,您是否一直打算在div.isgrP &gt; ul &gt; div 找到元素?

    您可以使用 puppeteer 直接访问 DOM 元素,方法是按如下方式修改您的代码:

    const scrollBox = await page.$eval('div.isgrP > ul > div.PZuss', (uiElement) => {
      return uiElement;
    });
    

    这将为您一直在搜索的可滚动框返回您的 DOM 元素(不是 ElementHandle 实例)。

    请让我知道这是否有帮助以及导致您下一个问题的原因。

    【讨论】:

    • OK 我已经更新了我的代码以包含正确的选择器。对我来说很好用!
    • 您介意分享整个代码吗?我复制粘贴了你的,但一直不确定,我不明白......
    【解决方案2】:

    我使用 .hover() 方法解决了这个问题。我在每次迭代时选择 div 中的最后一个元素,这会触发滚动到视图中。通过这种方式,我可以获得定义为参数的关注者数量。这种方式很方便,功能也更短。虽然仍然无法选择 DOM 元素本身。

    async function scrapeInfiniteScrollItems(
      page,
      extractFollowers,
      followersTargetCount
    ) {
      let items = [];
      // Next line returns undefined
      let x;
      try {
        while (items.length < followersTargetCount) {
          items = await page.evaluate(extractFollowers);
          childToSelect = items.length;
          await page.hover(`div.isgrP > ul > div > li:nth-child(${childToSelect})`);
        }
      } catch(e) { }
      items.length = followersTargetCount;
      return items;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 2023-02-25
      • 2019-02-27
      相关资源
      最近更新 更多