Проблемы с переходом по разным ссылкам с помощью puppeteer

Я написал крошечные сценарии в узле, используя puppeteer для циклического выполнения кликов по ссылке разных сообщений с целевой страницы Веб-сайт.

Ссылка на сайт, используемая в моих сценариях, является заполнителем. К тому же они не динамичны. Так что кукловод может быть излишним. Тем не менее, Мое намерение состоит в том, чтобы изучить логику нажатия.

Когда я выполняю свой первый скрипт, он щелкает один раз и выдает следующую ошибку, когда выходит из источника.

const puppeteer = require("puppeteer");

(async () => {
    const browser = await puppeteer.launch({headless:false});
    const [page] = await browser.pages();
    await page.goto("https://stackoverflow.com/questions/tagged/web-scraping",{waitUntil:'networkidle2'});
    await page.waitFor(".summary");
    const sections = await page.$$(".summary");

    for (const section of sections) {
        await section.$eval(".question-hyperlink", el => el.click())
    }

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

Ошибка, с которой сталкивается приведенный выше скрипт:

(node:9944) UnhandledPromiseRejectionWarning: Error: Execution context was destroyed, most likely because of a navigation.

Когда я выполняю следующее, скрипт делает вид, что щелкает один раз (на самом деле это не так) и сталкивается с той же ошибкой, что и раньше.

const puppeteer = require("puppeteer");

(async () => {
    const browser = await puppeteer.launch({headless:false});
    const [page] = await browser.pages();
    await page.goto("https://stackoverflow.com/questions/tagged/web-scraping");

    await page.waitFor(".summary .question-hyperlink");
    const sections = await page.$$(".summary .question-hyperlink");

    for (let i=0, lngth = sections.length; i < lngth; i++) {
        await sections[i].click();
    }

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

Ошибка выше выдает:

(node:10128) UnhandledPromiseRejectionWarning: Error: Execution context was destroyed, most likely because of a navigation.

Как я могу позволить моему сценарию выполнять клики циклически?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
3 521
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема:

Execution context was destroyed, most likely because of a navigation.

В ошибке говорится, что вы хотели щелкнуть какую-то ссылку или сделать что-то на какой-то странице, которой больше не существует, скорее всего, из-за того, что вы ушли.

Логика:

Думайте о сценарии кукловода как о реальном человеке, просматривающем настоящую страницу.

Сначала мы загружаем URL-адрес (https://stackoverflow.com/questions/tagged/веб-скрейпинг).

Далее мы хотим просмотреть все вопросы, заданные на этой странице. Чтобы сделать это, что мы обычно делаем? Мы бы сделали одно из следующего,

  • Откройте одну ссылку в новая вкладка. Сосредоточьтесь на этой новой вкладке, завершите нашу работу и вернитесь к исходной вкладке. Продолжить следующую ссылку.
  • Щелкаем по ссылке, делаем свое дело, вернуться на предыдущую страницу, продолжаем дальше.

Таким образом, оба они включают в себя уход от текущей страницы и возврат к ней.

Если вы не будете следовать этому потоку, вы получите сообщение об ошибке, как указано выше.

Решение

Есть по крайней мере 4 или более способов решить эту проблему. Я пойду с самыми простыми и сложными.

Путь: Извлечение ссылки

Сначала мы извлекаем все ссылки на текущей странице.

const links = await page.$$eval(".hyperlink", element => element.href);

Это дает нам список URL. Мы можем создать новую вкладку для каждой ссылки.

for(let link of links){
  const newTab = await browser.newPage();
  await newTab.goto(link);
  // do the stuff
  await newTab.close();
}

Это будет проходить по каждой ссылке по одной. Мы могли бы улучшить это, используя promise.map и различные библиотеки очередей, но вы поняли идею.

Путь: Возвращение на главную страницу

Нам нужно будет каким-то образом сохранить состояние, чтобы мы могли знать, какую ссылку мы посетили в прошлый раз. Если мы посетили третий вопрос и вернулись на страницу тегов, нам нужно посетить 4-й вопрос в следующий раз, и наоборот.

Проверьте следующий код.

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto(
    `https://stackoverflow.com/questions/tagged/web-scraping?sort=newest&pagesize=15`
  );

  const visitLink = async (index = 0) => {
    await page.waitFor("div.summary > h3 > a");

    // extract the links to click, we need this every time
    // because the context will be destryoed once we navigate
    const links = await page.$$("div.summary > h3 > a");
    // assuming there are 15 questions on one page,
    // we will stop on 16th question, since that does not exist
    if (links[index]) {
      console.info("Clicking ", index);

      await Promise.all([

        // so, start with the first link
        await page.evaluate(element => {
          element.click();
        }, links[index]),

        // either make sure we are on the correct page due to navigation
        await page.waitForNavigation(),
        // or wait for the post data as well
        await page.waitFor(".post-text")
      ]);

      const currentPage = await page.title();
      console.info(index, currentPage);

      // go back and visit next link
      await page.goBack({ waitUntil: "networkidle0" });
      return visitLink(index + 1);
    }
    console.info("No links left to click");
  };

  await visitLink();

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

Результат:

Обновлено: Есть несколько вопросов, похожих на этот. Я буду ссылаться на них, если вы захотите узнать больше.

Вместо того, чтобы циклически щелкать все ссылки, я считаю, что лучше проанализировать все ссылки, а затем перейти к каждой из них, используя один и тот же браузер. Дать ему шанс:

const puppeteer = require("puppeteer");

(async () => {
    const browser = await puppeteer.launch({headless:false});
    const [page] = await browser.pages();
    const base = "https://stackoverflow.com"
    await page.goto("https://stackoverflow.com/questions/tagged/web-scraping");
    let links = [];
    await page.waitFor(".summary .question-hyperlink");
    const sections = await page.$$(".summary .question-hyperlink");

    for (const section of sections) {
        const clink = await page.evaluate(el=>el.getAttribute("href"), section);
        links.push(`${base}${clink}`);
    }

    for (const link of links) {
        await page.goto(link);
        await page.waitFor('h1 > a');
    }
    await browser.close();
})();

Функция WaitFor() устарела, вы можете заменить ее функцией WaitForSelector().

j.c 21.10.2020 09:21

Другие вопросы по теме