首页 >web前端 >js教程 >如何从无限滚动的页面中抓取数据

如何从无限滚动的页面中抓取数据

Mary-Kate Olsen
Mary-Kate Olsen原创
2025-01-03 12:41:08800浏览

您是否遇到过需要“单击按钮”等操作才能显示更多内容的网页?此类页面称为“动态网页”,因为它们根据用户交互加载更多内容。相比之下,静态网页会立即显示所有内容,无需用户操作。

从动态页面中抓取内容可能会令人畏惧,因为它需要模拟用户交互,例如单击按钮来访问其他隐藏内容。在本教程中,您将学习如何通过“加载更多”按钮从无限滚动的网页中抓取数据。

先决条件

要学习本教程,您需要:

  • Node.js:安装标记为“LTS”(长期支持)的版本,该版本比最新版本更稳定。
  • Npm:这是一个用于安装包的包管理器。好消息是“npm”会自动随 Node.js 安装,这使得速度更快。
  • Cheerio:用于解析 HTML
  • Puppeteer:您将使用它来控制无头浏览器。
  • 用于构建 Scraper 的 IDE:您可以获得任何代码编辑器,例如 Visual Studio Code。

此外,您需要对 HTML、CSS 和 JavaScript 有基本的了解。您还需要一个网络浏览器,例如 Chrome。

初始化项目

创建一个新文件夹,然后在代码编辑器中打开它。在代码编辑器中找到“终端”选项卡并打开一个新终端。以下是使用 Visual Studio Code 发现它的方法。

How to Scrape Data from a Page with Infinite Scroll

接下来,在终端中运行以下命令来安装此构建所需的软件包。

$ npm install cheerio puppeteer

在代码编辑器中的项目文件夹中创建一个新文件,并将其命名为dynamicScraper.js。

干得好,伙计!

访问页面内容

Puppeteer 是一个功能强大的 Node.js 库,可让您控制无头 Chrome 浏览器,使其成为与网页交互的理想选择。借助 Puppeteer,您可以使用 URL 定位网页、访问内容并轻松从该页面提取数据。

在本节中,您将学习如何使用无头浏览器打开页面、访问内容以及检索该页面的 HTML 内容。您可以在这里找到本教程的目标网站。

注意:您要将所有代码编写在dynamicScraper.js 中。

首先使用 Node.js 内置函数 require() 导入 Puppeteer,它可以帮助您加载模块:核心模块、第三方库(如 Puppeteer)或自定义模块(如本地 JS 文件)。

$ npm install cheerio puppeteer

接下来,定义一个变量来存储您的目标 URL。这样做不是强制性的,但它使您的代码更清晰,因为您只需从代码中的任何位置引用此全局变量即可。

const puppeteer = require('puppeteer');

下一步是创建启动无头浏览器并检索目标页面的 HTML 内容的函数。您应该选择立即调用函数表达式 (IIFE) 方法以使事情变得更快。

使用 try-and-catch 块定义异步 IIFE:

const url = 'https://www.scrapingcourse.com/button-click';

注意:您应该在 try 块内编写本教程片段的所有其他代码。

在 IIFE 内部,创建一个新的 Puppeteer 实例并打开一个新页面进行交互。

使用 launch 方法启动 puppeteer 库的新实例,并将无头模式传递给它。无头模式可以设置为 true 或 false。将无头模式设置为 true 会使无头浏览器在 puppeteer 启动时不可见,但将其设置为 false 会使浏览器可见。

启动 Puppeteer 后,您还需要调用 newPage 方法,该方法会触发在无头浏览器中打开新选项卡。

(async () => {
    try {
        // Code goes here
    } catch (error) {
        console.error('Error:', error.message);
    }
})();

现在,查询 newPage 方法以定位预期的 URL,并使用 page.goto 方法在此新选项卡中打开该网站。除此之外,您要确保 Puppeteer 仅当且仅当页面加载了所有必需资源(如图像和 JS)时才认为页面已准备好进行交互和数据提取。

为了确保页面准备就绪,Puppeteer 提供了一个名为 waitUntil 的选项,它可以接受定义加载页面的不同条件的各种值:

  • load:等待加载事件触发,该事件在 HTML 文档及其资源(例如图像、CSS、JS)加载后发生。但是,这可能无法解释加载事件后加载的其他 JavaScript 渲染内容。

  • domcontentloaded:等待 DOMContentLoaded 事件,该事件在解析初始 HTML 后触发。但这会在外部资源(如图像或其他 JS)加载之前加载。

  • networkidle2:等待 500 毫秒,直到没有超过两个活动网络请求(正在进行的 HTTP 请求(例如,加载图像、脚本或其他资源))。当处理发出小型连续请求但不影响主要内容的页面时,首选此值。

// Launch Puppeteer
const browser = await puppeteer.launch({ headless: false }); // Headless mode
const page = await browser.newPage(); // Open a new page

最后,您只需要使用 page.content() 检索当前页面的所有 HTML 内容即可。最重要的是,您应该关闭浏览器实例,以避免不必要的内存使用,这会降低系统速度。在脚本末尾使用 browser.close() 来关闭浏览器。

$ npm install cheerio puppeteer

使用您现在的代码,浏览器加载和关闭的速度会非常快,您甚至可能无法很好地查看页面。在这种情况下,您可以使用 page.waitForTimeout 方法将浏览器延迟几秒钟。该方法应该出现在 browser.close 方法之前。

const puppeteer = require('puppeteer');

这是本节的完整代码:

const url = 'https://www.scrapingcourse.com/button-click';

保存文件并使用以下命令在终端内运行脚本:

(async () => {
    try {
        // Code goes here
    } catch (error) {
        console.error('Error:', error.message);
    }
})();

该脚本将打开一个无头浏览器,如下所示:

How to Scrape Data from a Page with Infinite Scroll

浏览器加载,Puppeteer 获取其整个 HTML 内容,Console 将内容记录到终端。

这是您应该在终端中获得的输出:

// Launch Puppeteer
const browser = await puppeteer.launch({ headless: false }); // Headless mode
const page = await browser.newPage(); // Open a new page

接下来,您想要循环模拟点击。模拟将使用运行 i 次的 for 循环,其中 i 将是 clicks 变量。

// Navigate to the target URL
await page.goto(url, {
    waitUntil: 'networkidle2', // Ensure the page is fully loaded
});

注意:本节的其余代码应编写在 for 循环的 try 块内。

为了帮助调试和跟踪输出,请注销当前的点击尝试。

// Get the full HTML content of the page
const html = await page.content();

// Log the entire HTML content
console.log(html);

// Close the browser
await browser.close();

接下来,您希望能够找到“加载更多”按钮并单击至少三次。但在模拟点击之前,您应该确保“加载更多”按钮可用。

Puppeteer 提供了 waitForSelector() 方法来在使用元素之前检查它的可见性。

对于“加载更多”按钮,您必须首先使用其 id 选择器的值找到它,然后检查可见性状态,如下所示:

// Delay for 10 seconds to allow you to see the browser
await page.waitForTimeout(10000);

现在您知道“加载更多”按钮可用,您可以使用 Puppeteer click() 方法单击它。

const puppeteer = require('puppeteer');

const url = 'https://www.scrapingcourse.com/button-click';

(async () => {
    try {
        // Launch Puppeteer
        const browser = await puppeteer.launch({ headless: false }); // Headless mode
        const page = await browser.newPage(); // Open a new page

        // Navigate to the target URL
        await page.goto(url, {
            waitUntil: 'networkidle2', // Ensure the page is fully loaded
        });

        // Get the entire HTML content of the page
        const html = await page.content();

        // Log the entire HTML content
        console.log(html);

        // Delay for 10 seconds to allow you to see the browser
        await page.waitForTimeout(10000);

        // Close the browser
        await browser.close();
    } catch (error) {
        console.error('Error fetching the page:', error.message);
    }
})();

一旦模拟单击“加载更多”按钮,您应该等待内容加载后再模拟另一次单击,因为数据可能取决于服务器请求。您必须使用 setTimeout() 在请求之间引入延迟。

下面的代码通知脚本等待至少两秒钟,然后再模拟再次单击“加载更多”按钮。

$ node dynamicScraper.js

为了总结本节的内容,您希望在每次单击后使用 content() 方法获取当前的 HTML 内容,然后将输出注销到终端。



    <title>Load More Button Challenge - ScrapingCourse.com</title>


    <header>
        <!-- Navigation Bar -->
        <nav>
            <a href="/">
                <img src="logo.svg" alt="如何从无限滚动的页面中抓取数据">
                <span>Scraping Course</span>
            </a>
        </nav>
    </header>

    <main>
        <!-- Product Grid -->
        <div>



<p>Note that the code structure above is what your output should look like.</p>

<p>Wow! You should be proud of yourself for getting this far. You’ve just completed your first attempt at scraping the contents of a webpage. </p>

<h2>
  
  
  Simulate the LOad More Products Process
</h2>

<p>Here, you want to access more products, and to do that, you need to click on the “Load more” button multiple times until you’ve either exhausted the list of all products or gotten the desired number of products you want to access. </p>

<p>To access this button and click on it, you must first locate the element using any CSS selectors (the class, id, attribute of the element, or tag name). </p>

<p>This tutorial aims to get at least 48 products from the target website, and to do that, you’ll have to click on the “Load more” button at least three times.</p>

<p>Start by locating the “Load more” button using any of the CSS selectors on it. Go to the target website, find the “Load more” button, right-click, and select the inspect option. </p>

<p><img src="https://img.php.cn/upload/article/000/000/000/173587927350910.jpg" alt="How to Scrape Data from a Page with Infinite Scroll"></p>

<p>Selecting the inspect option will open up developer tools just like the page below:</p>

<p><img src="https://img.php.cn/upload/article/000/000/000/173587927639663.jpg" alt="How to Scrape Data from a Page with Infinite Scroll"></p>

<p>The screenshot above shows that the “Load more” button element has an id attribute with the value "load-more-btn". You can use this id selector to locate the button during the simulation and click on it multiple times.</p>

<p>Back to the code, still inside the try block, after the line of code that logs out the previous HTML content for the default 12 products on the page.</p>

<p>Define the number of times you want to click the button. Recall that each click loads an additional 12 products. For 48 products, three clicks are required to load the remaining 36.<br>
</p>

<pre class="brush:php;toolbar:false">// Number of times to click "Load More"
const clicks = 3;

到目前为止您的完整代码:

for (let i = 0; i 



<p>以下是模拟按钮点击三次获得 48 个产品的输出:<br>
</p>

<pre class="brush:php;toolbar:false">console.log(`Clicking the 'Load More' button - Attempt ${i + 1}`);

现在,您应该只关心与所有 48 个产品的输出进行交互。为此,您需要清理上一节中的先前代码。

您还需要在 for 循环块之后降低 html 变量,这样您就只能获得所有 48 种产品的一个输出。

您的清理代码应与此代码片段相同:

$ npm install cheerio puppeteer

现在,让我们使用 Cheerio 进行 HTML 解析。

首先,Cheerio 需要访问它想要解析的 HTML 内容,为此,它提供了一个 load() 方法来接收该 HTML 内容,使其可以使用类似 jQuery 的语法进行访问。

使用 HTML 内容创建 Cheerio 库的实例:

const puppeteer = require('puppeteer');

您现在可以使用 $ 来查询和操作加载的 HTML 中的元素。

接下来,初始化一个数组来存储产品信息。该数组将保存提取的数据,每个产品将存储为一个对象,包含其名称、价格、图像和链接。

const url = 'https://www.scrapingcourse.com/button-click';

回想一下,每个产品都有一个类 .product-item。您将使用它与 Cheerio ($) 的变量实例来获取每个产品,然后执行一些操作。

.each() 方法用于使用 .product-item 类选择器迭代每个匹配的元素。

(async () => {
    try {
        // Code goes here
    } catch (error) {
        console.error('Error:', error.message);
    }
})();

让我们使用特定详细信息的类选择器从每个产品中检索产品详细信息。例如,要获取产品名称,您需要使用类选择器 .product-item 查找每个产品中的子元素。检索该子元素的文本内容并修剪它以防出现任何空格。

// Launch Puppeteer
const browser = await puppeteer.launch({ headless: false }); // Headless mode
const page = await browser.newPage(); // Open a new page
  • $(element).find('.product-name'):在当前 .product-item 中搜索类为 .product-name 的子元素。
  • .text():检索元素内的文本内容。
  • .trim():删除文本中不必要的空格。

利用这个概念,让我们使用它们的类属性来获取价格、图像 URL 和链接。

// Navigate to the target URL
await page.goto(url, {
    waitUntil: 'networkidle2', // Ensure the page is fully loaded
});

现在您已经获得了所有预期的信息,下一步是将每个解析的产品信息作为单独的对象推送到产品数组中。

// Get the full HTML content of the page
const html = await page.content();

// Log the entire HTML content
console.log(html);

// Close the browser
await browser.close();

最后,注销产品数组以在终端中获得预期的输出。

// Delay for 10 seconds to allow you to see the browser
await page.waitForTimeout(10000);

您的整个代码应类似于以下代码片段:

const puppeteer = require('puppeteer');

const url = 'https://www.scrapingcourse.com/button-click';

(async () => {
    try {
        // Launch Puppeteer
        const browser = await puppeteer.launch({ headless: false }); // Headless mode
        const page = await browser.newPage(); // Open a new page

        // Navigate to the target URL
        await page.goto(url, {
            waitUntil: 'networkidle2', // Ensure the page is fully loaded
        });

        // Get the entire HTML content of the page
        const html = await page.content();

        // Log the entire HTML content
        console.log(html);

        // Delay for 10 seconds to allow you to see the browser
        await page.waitForTimeout(10000);

        // Close the browser
        await browser.close();
    } catch (error) {
        console.error('Error fetching the page:', error.message);
    }
})();

以下是保存并运行脚本时的输出:

$ node dynamicScraper.js

将产品信息导出至 CSV

下一步是将解析后的产品信息(目前采用 JavaScript 对象表示法 (Json) 格式)导出为逗号分隔值 (CSV) 格式。我们将使用 json2csv 库将解析的数据转换为其相应的 CSV 格式。

首先导入所需的模块。

Node.js 提供了用于文件处理的文件系统(fs)模块,例如将数据写入文件。导入 fs 模块后,您应该解构 json2csv 库中的 parse() 方法。

$ npm install cheerio puppeteer

CSV 文件通常需要列标题;按照与解析信息相同的顺序仔细写下此内容。这里,解析的数据是产品数组,其中每个元素都是一个具有四个键(名称、价格、图像和链接)的对象。您应该使用这些对象键来命名列标题以进行正确的映射。

定义 CSV 文件的字段(列标题):

const puppeteer = require('puppeteer');

现在您已经定义了字段,下面的操作是将当前解析的信息转换为 CSV 格式。 parse() 方法的工作格式如下: parse(WHAT_YOU_WANT_TO_CONVERT, { YOUR_COLUMN_HEADERS }).

const url = 'https://www.scrapingcourse.com/button-click';

您现在必须将此 CSV 信息保存到文件扩展名为 .csv 的新文件中。使用 Node.js 时,您可以使用 fs 模块上的 writeFileSync() 方法来处理文件创建。该方法有两个参数:文件名和数据。

(async () => {
    try {
        // Code goes here
    } catch (error) {
        console.error('Error:', error.message);
    }
})();

本节的完整代码应如下所示:

// Launch Puppeteer
const browser = await puppeteer.launch({ headless: false }); // Headless mode
const page = await browser.newPage(); // Open a new page

保存并运行脚本后,您应该会看到名为 products.csv 的文件自动添加到您的文件结构中。

输出 - products.csv:
How to Scrape Data from a Page with Infinite Scroll

结论

本教程深入研究了从需要模拟才能访问其隐藏内容的页面中抓取数据的复杂性。您学习了如何使用 Node.js 和一些附加库在动态页面上执行网页抓取,将抓取的数据解析为更有条理的格式,并将其解压到 CSV 文件中。

以上是如何从无限滚动的页面中抓取数据的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
上一篇:How Can I Efficiently Remove Duplicate Values from JavaScript Arrays?下一篇:Why Doesn't Top-Level `async/await` Behave as Expected?

相关文章

查看更多