【问题标题】:How to scrape dynamic-loading listing and individual pages using Apify?如何使用 Apify 抓取动态加载列表和单个页面?
【发布时间】:2021-01-08 23:28:43
【问题描述】:

如何使用 Apify 的功能生成完整的 URL 列表,以便在用户滚动到底部时从索引页面中按顺序批量添加项目?换句话说,它是动态加载/无限滚动,而不是点击按钮。

具体来说,这个页面 - https://www.provokemedia.com/agency-playbook 除了最初显示的 13 个条目之外,我无法让它识别任何其他条目。

这些元素似乎位于每个段的底部,display: none 在每次添加段时更改为 display: block。此处没有“style”标签在原始源代码中可见,只能通过 DevTools Inspector。

<div class="text-center" id="loader" style="display: none;">
    <h5>Loading more ...</h5>
</div>

这是我对 web-scraper 的基本设置...

起始网址:

https://www.provokemedia.com/agency-playbook
{
  "label": "START"
}

链接选择器:

div.agencies div.column a

伪网址:

https://www.provokemedia.com/agency-playbook/agency-profile/[.*]
{
  "label": "DETAIL"
}

页面功能:

async function pageFunction(context) {
    const { request, log, skipLinks } = context;
    // request: holds info about current page
    // log: logs messages to console
    // skipLinks: don't enqueue matching Pseudo Links on current page
    // >> cf. https://docs.apify.com/tutorials/apify-scrapers/getting-started#new-page-function-boilerplate



    // *********************************************************** //
    //                          START page                         //
    // *********************************************************** //
    if (request.userData.label === 'START') {
        log.info('Store opened!');
        // Do some stuff later.
    }


    // *********************************************************** //
    //                          DETAIL page                        //
    // *********************************************************** //
    if (request.userData.label === 'DETAIL') {
        log.info(`Scraping ${request.url}`);
        await skipLinks();
        // Do some scraping.
        return {
            // Scraped data.
        }
    }
}

大概,在 START 的内容中,我需要确保显示要排队的整个列表,而不仅仅是 13 个。

我已阅读 Apify 的文档,包括“Waiting for dynamic content”。 await waitFor('#loader'); 似乎是个不错的选择。

我在 START 部分添加了以下内容...

    let timeoutMillis; // undefined
    const loadingThing = '#loader';
    while (true) {
        log.info('Waiting for the "Loading more" thing.');
        try {
            // Default timeout first time.
            await waitFor(loadingThing, { timeoutMillis });
            // 2 sec timeout after the first.
            timeoutMillis = 2000;
        } catch (err) {
            // Ignore the timeout error.
            log.info('Could not find the "Loading more thing", '
                + 'we\'ve reached the end.');
            break;
        }
        log.info('Going to load more.');
        // Scroll to bottom, to expose more
        // $(loadingThing).click();
        window.scrollTo(0, document.body.scrollHeight);
    }

但它没有工作......

2021-01-08T23:24:11.186Z INFO  Store opened!
2021-01-08T23:24:11.189Z INFO  Waiting for the "Loading more" thing.
2021-01-08T23:24:11.190Z INFO  Could not find the "Loading more thing", we've reached the end.
2021-01-08T23:24:13.393Z INFO  Scraping https://www.provokemedia.com/agency-playbook/agency-profile/gci-health

与其他网页不同,当我在 DevTools 控制台中手动输入 window.scrollTo(0, document.body.scrollHeight); 时,此页面不会滚动到底部。

但是,当在 Console 中手动执行时,此代码添加了一个小延迟 - setTimeout(function(){window.scrollBy(0,document.body.scrollHeight)}, 1); - 发现 in this question - 确实 每次都跳到底部...

如果我添加 that 行来替换上面 while 循环的最后一行,但是,循环仍然会记录它找不到元素。

我在使用这些方法吗?不知道该转向哪个方向。

【问题讨论】:

    标签: web-scraping apify


    【解决方案1】:

    @LukášKřivka 在How to make the Apify Crawler to scroll full page when web page have infinite scrolling? 的回答为我的回答提供了框架...

    总结:

    • 创建一个函数来强制滚动到页面底部
    • 获取所有元素

    详情:

    • while 循环中,滚动到页面底部。
    • 等待例如。新内容呈现 5 秒。
    • 保持对目标链接选择器数量的运行计数,以获取信息。
    • 直到没有更多的项目加载。

    仅当 pageFunction 正在检查索引页面时调用此函数(例如,任意页面名称,如用户数据中的 START/LISTING)。

    async function pageFunction(context) {
    
    
    
        // *********************************************************** //
        //                      Few utilities                          //
        // *********************************************************** //
        const { request, log, skipLinks } = context;
        // request: holds info about current page
        // log: logs messages to console
        // skipLinks: don't enqueue matching Pseudo Links on current page
        // >> cf. https://docs.apify.com/tutorials/apify-scrapers/getting-started#new-page-function-boilerplate
        const $ = jQuery;
    
    
    
    
    
        // *********************************************************** //
        //                Infinite scroll handling                     //
        // *********************************************************** //
        // Here we define the infinite scroll function, it has to be defined inside pageFunction
        const infiniteScroll = async (maxTime) => { //maxTime to wait
    
            const startedAt = Date.now();
            // count items on page
            let itemCount = $('div.agencies div.column a').length; // Update the selector
    
            while (true) {
    
                log.info(`INFINITE SCROLL --- ${itemCount} items loaded --- ${request.url}`)
                // timeout to prevent infinite loop
                if (Date.now() - startedAt > maxTime) {
                    return;
                }
                // scroll page x, y
                scrollBy(0, 9999);
                // wait for elements to render
                await context.waitFor(5000); // This can be any number that works for your website
                // count items on page again
                const currentItemCount = $('div.agencies div.column a').length; // Update the selector
    
                // check for no more
                // We check if the number of items changed after the scroll, if not we finish
                if (itemCount === currentItemCount) {
                    return;
                }
                // update item count
                itemCount = currentItemCount;
    
            }
    
        }
    
    
    
    
    
    
        // *********************************************************** //
        //                          START page                         //
        // *********************************************************** //
        if (request.userData.label === 'START') {
            log.info('Store opened!');
            // Do some stuff later.
    
            // scroll to bottom to force load of all elements
            await infiniteScroll(60000); // Let's try 60 seconds max
    
        }
    
    
        // *********************************************************** //
        //                          DETAIL page                        //
        // *********************************************************** //
        if (request.userData.label === 'DETAIL') {
            log.info(`Scraping ${request.url}`);
            await skipLinks();
            
            // Do some scraping (get elements with jQuery selectors)
    
            return {
                // Scraped data.
            }
        }
    }
    

    【讨论】:

    • 嗨罗伯特,你能成功吗?
    • 是的,@LukášKřivka - 我上面给出的答案是受我从你那里找到的帖子的启发。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-18
    • 2019-12-18
    • 2020-10-24
    • 1970-01-01
    • 2017-07-24
    • 1970-01-01
    相关资源
    最近更新 更多