您是否遇到過需要「點擊按鈕」等操作才能顯示更多內容的網頁?此類頁面稱為“動態網頁”,因為它們根據使用者互動加載更多內容。相較之下,靜態網頁會立即顯示所有內容,無需使用者操作。
從動態頁面中抓取內容可能會令人畏懼,因為它需要模擬用戶交互,例如單擊按鈕來訪問其他隱藏內容。在本教程中,您將學習如何透過「載入更多」按鈕從無限滾動的網頁中抓取資料。
要學習本教程,您需要:
此外,您需要對 HTML、CSS 和 JavaScript 有基本的了解。您還需要一個網頁瀏覽器,例如 Chrome。
建立一個新資料夾,然後在程式碼編輯器中開啟它。在程式碼編輯器中找到“終端”標籤並打開一個新終端。以下是使用 Visual Studio Code 發現它的方法。
接下來,在終端機中執行以下命令來安裝此建置所需的軟體包。
$ 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); } })();
該腳本將開啟一個無頭瀏覽器,如下所示:
瀏覽器加載,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
利用這個概念,讓我們使用它們的類別屬性來取得價格、圖片 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
下一步是將解析後的產品資訊(目前採用 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:
本教學深入研究了從需要模擬才能存取其隱藏內容的頁面中抓取資料的複雜性。您學習如何使用 Node.js 和一些附加程式庫在動態頁面上執行網頁抓取,將抓取的資料解析為更有條理的格式,並將其解壓縮到 CSV 檔案中。
以上是如何從無限滾動的頁面中抓取數據的詳細內容。更多資訊請關注PHP中文網其他相關文章!