Почему моя функция бесконечной прокрутки в Apify не работает?

avatar
Manuel Jiménez
8 августа 2021 в 20:39
302
1
1

Я пытаюсь получить данные о товарах с веб-сайта, который загружает список товаров, когда пользователь прокручивает страницу вниз. Я использую Apify для этого. Моей первой мыслью было посмотреть, не решил ли кто-нибудь уже эту проблему, и я нашел 2 полезные ссылки: Как заставить краулер Apify прокручивать всю страницу, когда веб-страница имеет бесконечную прокрутку? и Как выполнить динамическую очистку -загрузка списка и отдельных страниц с помощью Apify?. Однако, когда я попытался применить упомянутые функции, мой сканер Apify не смог загрузить контент.

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

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

Я использую следующую функцию PageFunction:

async function pageFunction(context) {
    // Establishing uility constants to use throughout the code
    const { request, log, skipLinks } = context;
    const $ = context.jQuery;
    const pageTitle = $('title').first().text();
    context.log.info('Wait for website to render')
    await context.waitFor(2000)

    //Creating function to scroll the page til the bottom
    const infiniteScroll = async (maxTime) => {
        const startedAt = Date.now();
        let itemCount = $('.upcName').length;
        
        for (;;) {
            log.info(`INFINITE SCROLL --- ${itemCount} initial items loaded ---`);
            // timeout to prevent infinite loop
            if (Date.now() - startedAt > maxTime) {
                return;
            }
            
            scrollBy(0, 99999);
            await context.waitFor(1000); 
            
            const currentItemCount = $('.upcName').length;
            log.info(`INFINITE SCROLL --- ${currentItemCount} items loaded after scroll ---`);

            if (itemCount === currentItemCount) {
                return;
            }
            itemCount = currentItemCount;

        }

    };

    context.log.info('Initiating scrolling function');
    await infiniteScroll(60000);
    context.log.info(`Scraping URL: ${context.request.url}`);

    var results = []
    $(".itemGrid").each(function() {
        results.push({
            name: $(this).find('.upcName').text(),
            product_url: $(this).find('.nombreProductoDisplay').attr('href'),
            image_url: $(this).find('.lazyload').attr('data-original'),
            description: $(this).find('.block-with-text').text(),
            price: $(this).find('.upcPrice').text()
        });

    });

    return results
}

Я заменил цикл while(true){...} на for(;;){...}, потому что получал ошибку Unexpected constant condition. (no-constant-condition)ESLint.

Кроме того, я пробовал изменять величину прокрутки и периоды ожидания.

Несмотря на все это, я не могу заставить поисковый робот получить более 32 результатов.

Может ли кто-нибудь объяснить мне, что я делаю неправильно?

################ ОБНОВЛЕНИЕ ################### Я продолжал работать над этим и не смог заставить его работать с платформой Apify, поэтому мой первоначальный вопрос остается в силе. Однако мне удалось заставить функцию прокрутки работать, запустив скрипт с моего компьютера.

Источник

Ответы (1)

avatar
pocesar
8 августа 2021 в 23:12
0

в этом конкретном случае вы можете проверить видимость счетчика загрузки после прокрутки вместо того, чтобы пытаться подсчитать количество элементов.

немного изменив код, можно сделать так:

async function pageFunction(context) {
    // Establishing uility constants to use throughout the code
    const { request, log, skipLinks } = context;
    const $ = context.jQuery;
    const pageTitle = $('title').first().text();
    context.log.info('Wait for website to render')
    // wait for initial listing
    await context.waitFor('.itemGrid'); 

    context.log.info(`Scraping URL: ${context.request.url}`);

    let tries = 5; // keep track of the load spinner being invisible on the page
    const results = new Map(); // this ensures you only get unique items
   
    while (true) { // eslint-disable-line
        log.info(`INFINITE SCROLL --- ${results.size} initial items loaded ---`);
        // when the style is set to "display: none", it's hidden aka not loading any new items
        const hasLoadingSpinner = $('.itemLoader[style*="none"]').length === 0; 

        if (!hasLoadingSpinner && tries-- < 0) {
            break;
        }
        
        // scroll to page end, you can adjust the offset if it's not triggering the infinite scroll mechanism, like `document.body.scrollHeight * 0.8`
        scrollTo({ top: document.body.scrollHeight });

        $(".itemGrid").each(function() {
            const $this = $(this);

            results.set($this.find('#upcProducto').attr('value'), {
                name: $this.find('.upcName').text(),
                product_url: $this.find('.nombreProductoDisplay').attr('href'),
                image_url: $this.find('.lazyload').data('original'),
                description: $this.find('.block-with-text').text(),
                price: $this.find('.upcPrice').text()
            });
        });
      
        // because of the `tries` variable, this will effectively wait at least 5 seconds to consider it not loading anymore
        await context.waitFor(1000);       
        // scroll to top, sometimes scrolling past the end of the page does not trigger the "load more" mechanism of the page
        scrollTo({ top: 0 }); 
    }

    return [...results.values()]
}

этот метод также работает для виртуального разбиения на страницы, например результатов React Virtual или Twitter, которые удаляют узлы DOM, когда они не находятся в области просмотра.

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

вы также можете отслеживать document.body.scrollHeight, так как он будет меняться при появлении новых элементов.

Manuel Jiménez
10 августа 2021 в 19:26
0

Привет, большое спасибо за ответ. У меня все еще проблемы со скроллером. Хотя код работает и получает первые 20 результатов веб-страницы, он повторяет цикл несколько раз, фактически не загружая больше результатов (должно появиться 93). Похоже, либо прокрутка не работает, либо результаты загружаются неправильно. Есть ли способ отладить это?