Проблемы при нажатии на разные ссылки с помощью кукловода - PullRequest
1 голос
/ 28 мая 2019

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

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

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

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.

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

Ответы [ 2 ]

2 голосов
/ 28 мая 2019

Проблема:

Контекст выполнения был разрушен, скорее всего, из-за навигации.

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

Логика:

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

Сначала мы загружаем URL (https://stackoverflow.com/questions/tagged/web-scraping).

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

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

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

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

Решение

Существует как минимум 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();
}

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

Способ: возвращение на главную страницу

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

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

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.log("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.log(index, currentPage);

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

  await visitLink();

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

Результат: enter image description here

РЕДАКТИРОВАТЬ: Есть несколько вопросов, похожих на этот.Я буду ссылаться на них, если вы хотите узнать больше.

1 голос
/ 29 мая 2019

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

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();
})();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...