Контекст выполнения Puppeteer был уничтожен, скорее всего, из-за навигации - PullRequest
1 голос
/ 27 апреля 2019

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

Error "We have an error Error: the execution context was destroyed, probably because of a navigation."

Это страница каталога, котораясодержит 15 компаний на страницу, и затем я хочу посетить каждую компанию, чтобы получить информацию.

try {
    const browser = await pupputer.launch({
        headless: false,
        devtools: true,
        defaultViewport: {
            width: 1100,
            height: 1000
        }
    });

    const page = await browser.newPage();
    await page.goto('MyLink');

    await page.waitForSelector('.list-firms');

    for (var i = 1; i < 10; i++) {

        const listeCompanies = await page.$$('.list-firms > div.firm');

        for (const companie of listeCompanies) {

            const name = await companie.$eval('.listing-body > h3 > a', name => name.innerText);
            const link = await companie.$eval('.listing-body > h3 > a', link => link.href);

            await Promise.all([
                page.waitForNavigation(),
                page.goto(link),
                page.waitForSelector('.firm-panel'),
            ]);

            const info = await page.$eval('#info', e => e.innerText);

            const data = [{
                name: name,
                information: info,
            }];

            await page.goBack();

        }
        await Promise.all([
            page.waitForNavigation(),
            page.click('span.page > a[rel="next"]')
        ]);
    }
} catch (e) {
    console.log('We have error', e);
}

Мне удалось получить данные только первой компании.

1 Ответ

2 голосов
/ 27 апреля 2019

Задача

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

const listeCompanies = await page.$$('.list-firms > div.firm');

Сначала вы используете эту переменную в цикле, затем перемещаетесь по page.goto, и после этого ваш цикл пытается извлечь следующий элемент из переменной listeCompanies. Но после того, как произошла навигация, дескрипторы элемента в этой переменной больше не присутствуют, и поэтому выдается ошибка. Вот почему первая итерация работает.

Решение

Есть несколько способов исправить это.

  1. Извлечение данных со страницы сразу (перед использованием цикла)
  2. Используйте вторую страницу, чтобы выполнить "циклическую навигацию", чтобы вашей главной странице не нужно было перемещаться
  3. «Обновить» вашу переменную путем повторного выполнения селектора после вызова page.goBack

Вариант 1. Извлечение данных перед входом в цикл

Это самый чистый способ сделать это. Вы извлекаете информацию на первой странице сразу, а затем перебираете извлеченные данные. nameLinkList будет массивом со значениями name и link (например, [{name: '..', link: '..'}, {name: '..', link: '..'}]). Также нет необходимости вызывать page.goBack в конце цикла, поскольку данные уже извлечены.

const nameLinkList = await page.$$eval(
    '.list-firms > div.firm',
    (firms => firms.map(firm => {
        const a = firm.querySelector('.listing-body > h3 > a');
        return {
            name: a.innerText,
            link: a.href
        };
    }))
);

for (const {name, link} of arr) {
    await Promise.all([
        page.waitForNavigation(),
        page.goto(link),
        page.waitForSelector('.firm-panel'),
    ]);

    const info = await page.$eval('#info', e => e.innerText);

    const data = [{
        name: name,
        information: info,
    }];
}

Вариант 2: использовать вторую страницу

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

const page2 = await browser.newPage();
for (const companie of listeCompanies ){
    const name = await companie.$eval('.listing-body > h3 > a', name => name.innerText);
    const link = await companie.$eval('.listing-body > h3 > a', link => link.href);

    await Promise.all([
        page2.goto(link),
        page2.waitForSelector('.firm-panel'),
    ]);

    const info = await page2.$eval('#info', e => e.innerText);
    // ...
}

Вариант 3: «Обновить» селекторы

Здесь вы просто повторно запускаете свой селектор после возврата на свою "главную страницу". Обратите внимание, что for..of должен быть заменен на цикл итератора при замене массива.

let listeCompanies  = await page.$$('.list-firms > div.firm');
for (let i = 0; i < listeCompanies.length; i++){
    // ...

    await page.goBack();
    listeCompanies = await page.$$('.list-firms > div.firm');
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...