【问题标题】:New tab doesnt open in puppeteer Node js新选项卡未在 puppeteer Node js 中打开
【发布时间】:2021-09-09 14:27:30
【问题描述】:

我正在使用 Puppeteer 构建一个自动购物机器人。我已经使用addToCart 将产品添加到购物车中。添加产品后,我想在同一个浏览器会话中打开一个新 url,但它不起作用。

const puppeteer = require("puppeteer");

const product_url =
  "https://www.amazon.com/gp/product/B08ZL7LZW3?pf_rd_r=J6QNRZDRJ7Z8FSF2HVXR&pf_rd_p=6fc81c8c-2a38-41c6-a68a-f78c79e7253f&pd_rd_r=d3adff00-5e6f-4456-8fd0-bd187f2ff86d&pd_rd_w=oO6kF&pd_rd_wg=aoGGq&ref_=pd_gw_unk";

const checkout_url = "https://www.amazon.com/gp/buy/shipoptionselect/handlers/display.html?hasWorkingJavascript=1";

async function givePage() {
  const browser = await puppeteer.launch({
    headless: false,
  });
  const page = await browser.newPage();
  return page;
}

async function addToCart(page) {
  await page.goto(product_url);
  await page.waitForSelector(
    "button[class='single_add_to_cart_button button alt']"
  );

  await page.click(
    "button[class='single_add_to_cart_button button alt']",
    (elem) => elem.click()
  );
}

async function checkout() {
  var page = await givePage();
  await addToCart(page);
  await page.waitForNavigation();
  const page2 = await browser.newPage(); // open new tab
  await page2.goto("https://www.amazon.com/gp/buy/shipoptionselect/handlers/display.html?hasWorkingJavascript=1"); // go to github.com
  await page2.bringToFront(); // make the tab active
}

checkout();

【问题讨论】:

  • 你的browser变量被声明为inside givePage(),所以你不能在checkout()函数中使用它。

标签: javascript node.js async-await puppeteer


【解决方案1】:

对givePage 的每次调用都会从头开始创建一个新浏览器,但在函数返回时放弃对浏览器对象的引用,从而无法获取新页面或关闭它。

您可能想要制作一个浏览器,从中拉出一个页面,然后通过不同的功能重复访问同一页面。

或者,您可能希望使用一个浏览器,然后按需生成新页面(“标签”),就像这里的情况一样。

在变量中缓存 promise 是实现这些目标的一种方法。在外部范围内,你可以写

const browserPromise = puppeteer.launch({headless: false});

请注意,没有await。这个想法是缓存解析到浏览器的承诺。当稍后在代码中调用 await browserPromise 时,promise 始终解析为相同的底层浏览器实例。同样的方法也适用于页面——const pagePromise = browser.newPage(); 多次调用将始终为您提供相同的页面对象。

这是一个例子。错误处理和getting past Amazon's robot blocker 留作练习。

const puppeteer = require("puppeteer");

const product_url = "https://www.amazon.com/gp/product/B08ZL7LZW3?pf_rd_r=J6QNRZDRJ7Z8FSF2HVXR&pf_rd_p=6fc81c8c-2a38-41c6-a68a-f78c79e7253f&pd_rd_r=d3adff00-5e6f-4456-8fd0-bd187f2ff86d&pd_rd_w=oO6kF&pd_rd_wg=aoGGq&ref_=pd_gw_unk";
const checkout_url = "https://www.amazon.com/gp/buy/shipoptionselect/handlers/display.html?hasWorkingJavascript=1";

const browserPromise = puppeteer.launch({headless: false});

const newPage = () => browserPromise.then(browser => browser.newPage());

async function addToCart(page) {
  await page.goto(product_url);
  await page.waitForSelector(
    "button[class='single_add_to_cart_button button alt']"
  );

  await page.click(
    "button[class='single_add_to_cart_button button alt']",
    (elem) => elem.click()
  );
}

async function checkout() {
  const page = await newPage();
  await addToCart(page);
  await page.waitForNavigation();
  const page2 = await newPage(); // open new tab
  await page2.goto("https://www.amazon.com/gp/buy/shipoptionselect/handlers/display.html?hasWorkingJavascript=1"); // go to github.com
  await page2.bringToFront(); // make the tab active

  const browser = await browserPromise;
  await browser.close();
}

checkout();

也就是说,对于像这样的小型脚本,您始终可以将所有代码内联到一个函数中。如果您的脚本执行一个简单的任务并且可以用 10 或 20 行代码编写,那么创建抽象可能还为时过早。

对于较大的脚本,您可能希望增加抽象并将浏览器和相关页面功能收集在一个类或对象中,但无论您有对象属性还是松散的承诺,awaiting 一个浏览器或页面的底层方法多次承诺都是一样的。

【讨论】:

  • 嗨,@ggorlen 感谢您的详细解释。我尝试了这个解决方案,但收到了未处理的承诺拒绝警告,并且新标签页没有打开所需的 URL。
  • 具体的错误信息是什么?正如我在帖子中提到的,亚马逊可能会检测到您是机器人并阻止请求,从而导致超时(TimeoutError: waiting for selector `button[class='single_add_to_cart_button button alt']` failed: timeout 30000ms exceeded)。这与通过缓存承诺使浏览器对象可访问是不同的问题。如果您注释掉 await addToCart(page); 和 await page.waitForNavigation();,您会看到基本模式是正确的,然后按照链接帖子中的提示绕过机器人检测器。
猜你喜欢
  • 2022-12-18
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多