如何使用 Node.js 構建亞馬遜產品爬蟲

已發表: 2022-03-10
快速總結↬在您設計產品或尋找便宜貨時,亞馬遜擁有的大量數據可以產生巨大的影響。 但是,開發人員如何獲取這些數據? 很簡單,通過使用網絡刮刀。 以下是使用 Node.js 構建數據提取機器人的方法。

您是否曾經處於需要深入了解特定產品市場的位置? 也許您正在推出一些軟件並且需要知道如何定價。 或者,您可能已經在市場上擁有自己的產品,並希望了解添加哪些功能以獲得競爭優勢。 或者,也許您只是想為自己買點東西,並希望確保您物有所值。

所有這些情況都有一個共同點:您需要準確的數據才能做出正確的決定。 實際上,他們還分享了另一件事。 所有場景都可以從使用網絡爬蟲中受益。

網絡抓取是通過使用軟件提取大量網絡數據的做法。 因此,從本質上講,這是一種將點擊“複製”然後“粘貼”200 次的繁瑣過程自動化的方法。 當然,機器人可以在你讀完這句話的時候做到這一點,所以它不僅不那麼無聊,而且速度也快了很多。

但迫切的問題是:為什麼有人要抓取亞馬遜頁面?

你即將發現! 但首先,我現在想澄清一點——雖然抓取公開數據的行為是合法的,但亞馬遜有一些措施來防止它出現在他們的頁面上。 因此,我敦促您在抓取時始終注意網站,注意不要損壞它,並遵守道德準則。

推薦閱讀Andreas Altheimer 的“使用 Node.js 和 Puppeteer 對動態網站進行道德抓取的指南”

為什麼要提取亞馬遜產品數據

作為地球上最大的在線零售商,可以肯定地說,如果你想買東西,你可以在亞馬遜上買到。 因此,不言而喻,該網站有​​多大的數據寶庫。

抓取網絡時,您的主要問題應該是如何處理所有這些數據。 雖然有許多個人原因,但它歸結為兩個突出的用例:優化您的產品和尋找最優惠的價格。

讓我們從第一個場景開始。 除非您設計了一款真正創新的新產品,否則您很有可能已經在亞馬遜上找到了至少類似的產品。 抓取這些產品頁面可以為您提供寶貴的數據,例如:

  • 競爭對手的定價策略
    因此,您可以調整價格以具有競爭力並了解其他人如何處理促銷交易;
  • 客戶意見
    了解您未來的客戶群最關心什麼以及如何改善他們的體驗;
  • 最常見的功能
    查看您的競爭對手提供什麼,以了解哪些功能至關重要,哪些可以留待以後使用。

從本質上講,亞馬遜擁有深入市場和產品分析所需的一切。 您將更好地準備好利用這些數據設計、發布和擴展您的產品系列。

第二種情況適用於企業和普通人。 這個想法與我之前提到的非常相似。 您可以抓取所有可以選擇的產品的價格、功能和評論,因此,您將能夠選擇以最低價格提供最大利益的產品。 畢竟,誰不喜歡好交易呢?

並非所有產品都值得如此關注細節,但它可以與昂貴的購買產生巨大的差異。 不幸的是,雖然好處是顯而易見的,但在爬取亞馬遜的過程中也會遇到許多困難。

跳躍後更多! 繼續往下看↓

抓取亞馬遜產品數據的挑戰

並非所有網站都相同。 根據經驗,一個網站越複雜和廣泛,就越難抓取它。 還記得我說過亞馬遜是最著名的電子商務網站嗎? 好吧,這使得它既非常流行又相當複雜。

首先,亞馬遜知道抓取機器人的行為方式,因此該網站制定了相應的對策。 也就是說,如果爬蟲遵循可預測的模式,以固定的間隔發送請求,比人類更快或使用幾乎相同的參數,亞馬遜將注意到並阻止 IP。 代理可以解決這個問題,但我不需要它們,因為我們不會在示例中抓取太多頁面。

接下來,亞馬遜故意為其產品使用不同的頁面結構。 也就是說,如果您檢查不同產品的頁面,您很有可能會發現它們的結構和屬性存在顯著差異。 這背後的原因很簡單。 您需要針對特定係統調整您的爬蟲代碼,如果您在新類型的頁面上使用相同的腳本,則必須重寫其中的一部分。 因此,它們本質上是讓您為數據工作更多。

最後,亞馬遜是一個龐大的網站。 如果您想收集大量數據,在您的計算機上運行抓取軟件可能會花費太多時間來滿足您的需求。 過快會使您的刮板被阻塞,這一事實進一步鞏固了這個問題。 因此,如果您想要快速加載數據,您將需要一個真正強大的抓取工具。

好了,問題說夠了,讓我們專注於解決方案!

如何為亞馬遜構建 Web Scraper

為簡單起見,我們將逐步編寫代碼。 隨意與指南並行工作。

尋找我們需要的數據

所以,這裡有一個場景:幾個月後我要搬到一個新地方,我需要幾個新書架來存放書籍和雜誌。 我想知道我所有的選擇,並儘可能地達成交易。 所以,讓我們去亞馬遜市場,搜索“貨架”,看看我們得到了什麼。

此搜索的 URL 和我們將要抓取的頁面在這裡。

可以在亞馬遜市場上購買的貨架
這些壞男孩可以裝這麼多書。 (來源:(大預覽)

好的,讓我們盤點一下我們這裡有什麼。 只需瀏覽一下頁面,我們就可以很好地了解以下內容:

  • 貨架的外觀;
  • 包裹包括什麼;
  • 客戶如何評價他們;
  • 他們的價格;
  • 產品鏈接;
  • 為某些物品提供更便宜的替代品的建議。

這超出了我們的要求!

獲取所需工具

在繼續下一步之前,讓我們確保我們已經安裝和配置了以下所有工具。

  • 鉻合金
    我們可以從這裡下載。
  • VSCode
    按照此頁面上的說明將其安裝在您的特定設備上。
  • 節點.js
    在開始使用 Axios 或 Cheerio 之前,我們需要安裝 Node.js 和 Node Package Manager。 安裝 Node.js 和 NPM 的最簡單方法是從 Node.Js 官方源獲取安裝程序之一併運行它。

現在,讓我們創建一個新的 NPM 項目。 為項目創建一個新文件夾並運行以下命令:

 npm init -y

要創建網絡爬蟲,我們需要在項目中安裝幾個依賴項:

  • 切里奧
    一個開源庫,通過解析標記和提供用於操作結果數據的 API 來幫助我們提取有用的信息。 Cheerio 允許我們使用選擇器來選擇 HTML 文檔的標籤: $("div") 。 這個特定的選擇器幫助我們選擇頁面上的所有<div>元素。 要安裝 Cheerio,請在項目文件夾中運行以下命令:
 npm install cheerio
  • 愛訊
    用於從 Node.js 發出 HTTP 請求的 JavaScript 庫。
 npm install axios

檢查頁面源

在以下步驟中,我們將詳細了解信息在頁面上的組織方式。 這個想法是為了更好地了解我們可以從源頭中抓取什麼。

開發人員工具幫助我們以交互方式探索網站的文檔對像模型 (DOM)。 我們將使用 Chrome 中的開發人員工具,但您可以使用任何您喜歡的網絡瀏覽器。

讓我們通過右鍵單擊頁面上的任意位置並選擇“檢查”選項來打開它:

右鍵單擊網頁上的任意位置時出現的選項菜單
macOS 和 Windows 的過程相同。 (大預覽)

這將打開一個包含頁面源代碼的新窗口。 正如我們之前所說,我們正在尋找每個貨架的信息。

檢查亞馬遜市場頁面上的 HTML 代碼
這看起來很嚇人,但實際上比看起來要容易。 (大預覽)

從上面的截圖中我們可以看到,保存所有數據的容器具有以下類:

 sg-col-4-of-12 s-result-item s-asin sg-col-4-of-16 sg-col sg-col-4-of-20

在下一步中,我們將使用 Cheerio 選擇包含我們需要的數據的所有元素。

獲取數據

在我們安裝了上面提供的所有依賴項之後,讓我們創建一個新的index.js文件並鍵入以下代碼行:

 const axios = require("axios"); const cheerio = require("cheerio"); const fetchShelves = async () => { try { const response = await axios.get('https://www.amazon.com/s?crid=36QNR0DBY6M7J&k=shelves&ref=glow_cls&refresh=1&sprefix=s%2Caps%2C309'); const html = response.data; const $ = cheerio.load(html); const shelves = []; $('div.sg-col-4-of-12.s-result-item.s-asin.sg-col-4-of-16.sg-col.sg-col-4-of-20').each((_idx, el) => { const shelf = $(el) const title = shelf.find('span.a-size-base-plus.a-color-base.a-text-normal').text() shelves.push(title) }); return shelves; } catch (error) { throw error; } }; fetchShelves().then((shelves) => console.log(shelves));

正如我們所看到的,我們在前兩行導入了我們需要的依賴項,然後我們創建了一個fetchShelves()函數,使用 Cheerio 從頁面中獲取包含我們產品信息的所有元素。

它遍歷它們中的每一個並將其推送到一個空數組以獲得格式更好的結果。

fetchShelves()函數此時只會返回產品的標題,所以讓我們獲取我們需要的其餘信息。 請在我們定義變量title的行之後添加以下代碼行。

 const image = shelf.find('img.s-image').attr('src') const link = shelf.find('aa-link-normal.a-text-normal').attr('href') const reviews = shelf.find('div.a-section.a-spacing-none.a-spacing-top-micro > div.a-row.a-size-small').children('span').last().attr('aria-label') const stars = shelf.find('div.a-section.a-spacing-none.a-spacing-top-micro > div > span').attr('aria-label') const price = shelf.find('span.a-price > span.a-offscreen').text() let element = { title, image, link: `https://amazon.com${link}`, price, } if (reviews) { element.reviews = reviews } if (stars) { element.stars = stars }

並將shelves.push(title)替換為shelves.push(element)

我們現在選擇我們需要的所有信息並將其添加到一個名為element的新對像中。 然後將每個元素推送到shelves數組以獲取僅包含我們正在查找的數據的對象列表。

這是一個shelf對像在添加到我們的列表之前的樣子:

 { title: 'SUPERJARE Wall Mounted Shelves, Set of 2, Display Ledge, Storage Rack for Room/Kitchen/Office - White', image: 'https://m.media-amazon.com/images/I/61fTtaQNPnL._AC_UL320_.jpg', link: 'https://amazon.com/gp/slredirect/picassoRedirect.html/ref=pa_sp_btf_aps_sr_pg1_1?ie=UTF8&adId=A03078372WABZ8V6NFP9L&url=%2FSUPERJARE-Mounted-Floating-Shelves-Display%2Fdp%2FB07H4NRT36%2Fref%3Dsr_1_59_sspa%3Fcrid%3D36QNR0DBY6M7J%26dchild%3D1%26keywords%3Dshelves%26qid%3D1627970918%26refresh%3D1%26sprefix%3Ds%252Caps%252C309%26sr%3D8-59-spons%26psc%3D1&qualifier=1627970918&id=3373422987100422&widgetName=sp_btf', price: '$32.99', reviews: '6,171', stars: '4.7 out of 5 stars' }

格式化數據

現在我們已經設法獲取了我們需要的數據,最好將其保存為.CSV文件以提高可讀性。 獲取所有數據後,我們將使用 Node.js 提供的fs模塊,並將一個名為saved-shelves.csv的新文件保存到項目的文件夾中。 導入文件頂部的fs模塊並複製或寫入以下代碼行:

 let csvContent = shelves.map(element => { return Object.values(element).map(item => `"${item}"`).join(',') }).join("\n") fs.writeFile('saved-shelves.csv', "Title, Image, Link, Price, Reviews, Stars" + '\n' + csvContent, 'utf8', function (err) { if (err) { console.log('Some error occurred - file either not saved or corrupted.') } else{ console.log('File has been saved!') } })

正如我們所看到的,在前三行中,我們通過使用逗號連接擱置對象的所有值來格式化我們之前收集的數據。 然後,使用fs模塊,我們創建一個名為saved-shelves.csv的文件,添加一個包含列標題的新行,添加我們剛剛格式化的數據並創建一個處理錯誤的回調函數。

結果應如下所示:

包含從 Amazon 抓取的數據的 CVS 文件。
甜蜜,有組織的數據。 (大預覽)

獎金提示!

抓取單頁應用程序

如今,動態內容正在成為標準,因為網站比以往任何時候都更加複雜。 為了盡可能提供最佳的用戶體驗,開發人員必須對動態內容採用不同的加載機制,這讓我們的工作變得更加複雜。 如果您不知道這意味著什麼,想像一下缺少圖形用戶界面的瀏覽器。 幸運的是,有 Puppeteer——一個神奇的 Node 庫,它提供了一個高級 API 來通過 DevTools 協議控制 Chrome 實例。 儘管如此,它仍提供與瀏覽器相同的功能,但必須通過鍵入幾行代碼以編程方式對其進行控制。 讓我們看看它是如何工作的。

在之前創建的項目中,通過運行npm install puppeteer Puppeteer 庫,創建一個新的puppeteer.js文件,然後復製或寫入以下代碼行:

 const puppeteer = require('puppeteer') (async () => { try { const chrome = await puppeteer.launch() const page = await chrome.newPage() await page.goto('https://www.reddit.com/r/Kanye/hot/') await page.waitForSelector('.rpBJOHq2PR60pnwJlUyP0', { timeout: 2000 }) const body = await page.evaluate(() => { return document.querySelector('body').innerHTML }) console.log(body) await chrome.close() } catch (error) { console.log(error) } })()

在上面的示例中,我們創建了一個 Chrome 實例並打開了一個新的瀏覽器頁面,該頁面需要轉到此鏈接。 在下一行中,我們告訴無頭瀏覽器等到具有類rpBJOHq2PR60pnwJlUyP0的元素出現在頁面上。 我們還指定了瀏覽器應該等待頁面加載的時間(2000 毫秒)。

使用page變量的evaluate方法,我們指示 Puppeteer 在元素最終加載後立即在頁面上下文中執行 Javascript 片段。 這將允許我們訪問頁面的 HTML 內容並將頁面的正文作為輸出返回。 然後我們通過調用chrome變量的close方法來關閉 Chrome 實例。 生成的工作應該包含所有動態生成的 HTML 代碼。 這就是 Puppeteer 可以幫助我們加載動態 HTML 內容的方式。

如果您對使用 Puppeteer 感到不自在,請注意有幾個替代方案,例如 NightwatchJS、NightmareJS 或 CasperJS。 它們略有不同,但最終,過程非常相似。

設置user-agent

user-agent是一個請求標頭,它告訴您正在訪問的網站有關您自己的信息,即您的瀏覽器和操作系統。 這用於優化設置的內容,但網站也使用它來識別發送大量請求的機器人——即使它改變了 IPS。

這是user-agent標頭的樣子:

 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36

為了不被檢測和阻止,您應該定期更改此標頭。 請特別注意不要發送空的或過時的標頭,因為對於普通用戶來說永遠不會發生這種情況,您會脫穎而出。

速率限制

網絡爬蟲可以非常快速地收集內容,但您應該避免以最快的速度前進。 有兩個原因:

  1. 短期內過多的請求會降低網站服務器的速度,甚至導致其癱瘓,從而給網站所有者和其他訪問者帶來麻煩。 它本質上可以成為 DoS 攻擊。
  2. 如果沒有輪換代理,這類似於大聲宣布您正在使用機器人,因為沒有人會每秒發送數百或數千個請求。

解決方案是在您的請求之間引入延遲,這種做法稱為“速率限制”。 (實現起來也很簡單!

在上面提供的 Puppeteer 示例中,在創建body變量之前,我們可以使用 Puppeteer 提供的waitForTimeout方法等待幾秒鐘,然後再發出另一個請求:

 await page.waitForTimeout(3000);

其中ms是您希望等待的秒數。

另外,如果我們想對 axios 示例做同樣的事情,我們可以創建一個調用setTimeout()方法的 Promise,以幫助我們等待所需的毫秒數:

 fetchShelves.then(result => new Promise(resolve => setTimeout(() => resolve(result), 3000)))

通過這種方式,您可以避免對目標服務器施加太大壓力,並且可以為網頁抓取帶來更人性化的方法。

結束的想法

這就是你為亞馬遜產品數據創建自己的網絡爬蟲的分步指南! 但請記住,這只是一種情況。 如果您想抓取不同的網站,則必須進行一些調整才能獲得任何有意義的結果。

相關閱讀

如果您仍然希望看到更多的網絡抓取,這裡有一些對您有用的閱讀材料:

  • “使用 JavaScript 和 Node.Js 進行 Web 抓取的終極指南”,Robert Sfichi
  • “使用 Puppeteer 進行高級 Node.JS Web 抓取”,Gabriel Cioci
  • “Python Web Scraping:構建 Scraper 的終極指南”,Raluca Penciuc