Puppeteer ,带回空白数组

Puppeteer , bringing back blank array

我正在尝试从 ebay 获取产品并在亚马逊上打开它们。

到目前为止,我已经在亚马逊上搜索了它们,但我正在努力从搜索结果中选择产品。

目前它正在输出一个空白数组,我不确定为什么。在没有 grabTitles 和 for 循环的单独脚本中进行了测试。所以我猜这里面有什么东西导致了问题。

这里有什么我遗漏的东西阻止了 prodResults 的数据返回吗?

const puppeteer = require('puppeteer');

const URL = "https://www.amazon.co.uk/";
const selectors = {
  searchBox: '#twotabsearchtextbox',
  productLinks: 'span.a-size-base-plus.a-color-base.a-text-normal',
  productTitle: '#productTitle'
};

(async() => {
  const browser = await puppeteer.launch({
    headless: false
  });
  const page = await browser.newPage();
  await page.goto('https://www.ebay.co.uk/sch/jmp_supplies/m.html?_trkparms=folent%3Ajmp_supplies%7Cfolenttp%3A1&rt=nc&_trksid=p2046732.m1684');

  //Get product titles from ebay
  const grabTitles = await page.evaluate(() => {
    const itemTitles = document.querySelectorAll('#e1-11 > #ResultSetItems > #ListViewInner > li > .lvtitle > .vip');
    var items = []
    itemTitles.forEach((tag) => {
      items.push(tag.innerText)
    })
    return items
  })

  //Search for the products on amazon in a new tab for each product 
  for (i = 0; i < grabTitles.length; i++) {

    const page = await browser.newPage();

    await page.goto(URL)
    await page.type(selectors.searchBox, grabTitles[i++])
    await page.keyboard.press('Enter');

    //get product titles from amazon search results
    const prodResults = await page.evaluate(() => {
      const prodTitles = document.querySelectorAll('span.a-size-medium.a-color-base.a-text-normal');
      let results = []
      prodTitles.forEach((tag) => {
        results.push(tag.innerText)
      })
      return results
    })
    console.log(prodResults)
  }
})()

您遇到了 Puppeteer 的一个古老问题,知道页面何时完全完成呈现或加载。

您可以尝试添加以下内容:

await page.waitForNavigation({ waitUntil: 'networkidle2' })
await page.waitForTimeout(10000)

通常我发现 networkidle2 并不总是足够可靠,所以我添加了一个任意的额外 waitForTimeout。您需要调整超时值(10000 = 10 秒)才能得到您想要的东西,我知道这不是很理想,但我还没有找到更好的方法。

脚本存在一些潜在问题:

  1. await page.keyboard.press('Enter'); 触发导航,但您的代码从不等待导航完成后再尝试 select 结果元素。使用 waitForNavigationwaitForSelectorwaitForFunction(不是 waitForTimeout)。

    如果您确实要等待导航,则需要使用 Promise.all 的特殊模式来避免竞争条件,如 docs 中所示。

    此外,您可以通过自己构建字符串直接转到搜索 URL 来跳过页面加载。这应该会提供显着的加速。

  2. 您的代码为每个需要处理的项目生成一个新页面,但这些页面永远不会关闭。我看到 grabTitles.length 为 60。因此您将打开 60 个选项卡。这浪费了很多资源。在我的机器上,它可能会挂起所有东西。我建议制作一个页面并反复浏览它,或者在完成后关闭每一页。如果您想要并行性,请考虑同时执行几页任务 queue 或 运行。

  3. grabTitles[i++] -- 为什么在这里递增 i?它已经通过循环递增,因此这似乎跳过了元素,除非你的 select 或你有重复的元素或者你有其他原因要这样做。

  4. span.a-size-medium 对我不起作用,可能是 locality-specific。我看到 a span.a-size-base-plus.a-color-base.a-text-normal,但您可能需要调整一下才能品尝。

这是一个最小的例子。我将只处理 eBay 数组中的前 2 项,因为它通过得很好。

const puppeteer = require("puppeteer"); // ^13.5.1

let browser;
(async () => {
  browser = await puppeteer.launch({headless: true});
  const [page] = await browser.pages();
  const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
  await page.setExtraHTTPHeaders({"Accept-Language": "en-US,en;q=0.9"});
  await page.setUserAgent(ua);
  const titles = [
    "Chloraethyl | Dr. Henning | Spray 175 ml",
    "Elmex Decays Prevention Toothpaste 2 x 75ml",
  ];

  for (const title of titles) {
    await page.goto("https://www.amazon.co.uk/");
    await page.type("#twotabsearchtextbox", title);
    await Promise.all([
      page.keyboard.press("Enter"),
      page.waitForNavigation(),
    ]);
    const titleSel = "a span.a-size-base-plus.a-color-base.a-text-normal";
    await page.waitForSelector(titleSel);
    const results = await page.$$eval(titleSel, els =>
      els.map(el => el.textContent)
    );
    console.log(title, results.slice(0, 5));
  }
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close())
;

输出:

Chloraethyl | Dr. Henning | Spray 175 ml [
  'Chloraethyl | Dr. Henning | Spray 175 ml',
  'Wild Fire (Shetland)',
  'A Dark Sin: A chilling British detective crime thriller (The Hidden Norfolk Murder Mystery Series Book 8)',
  'A POLICE DOCTOR INVESTIGATES: the Sussex murder mysteries (books 1-3)',
  'Rites of Spring: Sunday Times Crime Book of the Month (Seasons Quartet)'
]
Elmex Decays Prevention Toothpaste 2 x 75ml [
  'Janina Ultra White Whitening Toothpaste (75ml) – Diamond Formula. Extra Strength. Clinically Proven. Low Abrasion. For Everyday Use. Excellent for Stain Removal',
  'Elmex Decays Prevention Toothpaste 2 x 75ml',
  'Elmex Decays Prevention Toothpaste 2 x 75ml by Elmex',
  'Elmex Junior Toothpaste 2 x 75ml',
  'Elmex Sensitive Professional 2 x 75ml'
]

请注意,我添加了用户代理和 headers 以便能够使用 headless: true,但这是上面主要解决方案的附带结果。您可以 return 到 headless: false 或查看 How to avoid being detected as bot on Puppeteer and Phantomjs? and Why does headless need to be false for Puppeteer to work? 等规范线程,如果您有进一步的检测问题。