[專案介紹] BookFinder爬蟲比價網(上)

De_
6 min readAug 17, 2021

--

專案網址,對 code 有興趣可以到 Github Repo 看看!

動機

身為一個省錢的學生,每次開學要買原文書都要先去各大網站搜索一番,希望可以找到便宜的書籍,或者是一般課外書如果圖書館借不到也會貨比三家再抉擇。心想如果能夠有個網站把所有的書籍整理起來給我,那會讓我找書的過程更有效率。

專案規劃

爬蟲來源:以我個人買書會搜尋的三個網站進行爬蟲,分別是博客來、蝦皮書城以及城邦。

專案主要邏輯:考量到書籍的價格變動頻率不高,且為了避免頻繁發送請求 IP 會被封鎖,僅有第一次搜尋關鍵字時會進行爬蟲並將商品存到資料庫,隨後若輸入重複的關鍵字,會直接從資料庫撈取書籍資料。

更新資料庫:搜尋過的關鍵字會存放到資料庫,每天凌晨根據這些關鍵字進行爬蟲並自動更新資料庫。

爬蟲資料來源

技術面

環境:Node.js / Express.js

資料庫:MySQL

其他相關套件:cheerio、request、nodemailer、node-schedule

STEP1 爬蟲

由於上述的三個網站都沒有提供api串接,所以使用最陽春的方式—直接去該網站剖析html,抓取需要的資訊。

首先下載需要的套件

npm i cheerio request

使用request發送請求,使用方式如下,第一個參數放想存取的網站,第二個參數則是放callback,細節請參考官方文件

將取得的body放入cheerio.load中即可載入html文件並開始剖析

request('https://www.books.com.tw/', function (error, response, body) {
if (error || !body) {
return;
}
const $ = cheerio.load(body); // 載入 body

});

剖析的方式和 css selector 的方式非常像,從頁面上選取需要的元素

tbody.find('td').eq(2).find('.list-date li a')// 從 tbody 中找到第3個 td, 並找到所有在 list-date class 中 list 的 a
  • 在抓不到想要的元素時可以使用.html()把選取結果印出來
tbody.find(‘td’).eq(3).find(‘a’).html() //<a> ... </a>
  • 和陣列的index 一樣,標籤的順序是從 0 開始算起
tbody.find('td').eq(2) //這邊選取的是第三個 <td>

以博客來來說,它的每個商品都是包在 <tbody> 的標籤當中,因此我先將所有<tbody>取出來再跑迴圈,把所有元素整理好放到陣列

為了增加比對的精確性,加了 Regular expression進一步判斷書名或作者是否包含關鍵字

最後再從 index.js 引用這個函式就可以成功爬蟲了,城邦書店也是用類似的方法進行。

蝦皮書城因為它是採取AJAX技術動態載入資料,跟其它兩個網站的爬蟲方式有點不同,如果直接去看網頁原始碼,會發現找不到頁碼上的資訊。

為了取得所需資訊,要去找網頁向伺服器發送的請求網址。

首先按F12 打開開發者模式,接著打開Netword頁籤並重新整理,就可以看到網頁發送的請求,我們再從裡面找出哪個網址是用來請求商品資訊

如上圖所示,在 https://shopee.tw/api/v4/search/search_items?by=relevancy… 點擊 preview 會看到回傳的正是我們要的商品資訊,也就代表我們要用這個網址來獲得 api 資訊。

回傳的資料為JSON格式,商品資訊都是包在名為item_basic的物件,運用迴圈將資料接住,和另外兩個網站比起來,蝦皮不需要一個一個分析html tag,寫爬蟲的速度比較快,但有些資料需要自己拼湊,像是商品連結、照片等。

另外為了避免蝦皮反爬蟲的機制,可以在發送request時一併在headers加入user-agent, cookie等偽裝。

STEP2 更新資料庫

資料爬完後,會存到資料庫,每天固定時間更新,計時的工具使用 node-schedule 搭配node-mailer,等資料庫更新完畢後,會寄信通知,若出錯也會寄信,讓整個流程更為可控。

使用方式很簡單,schedule.sheduleJob函式中放指定時間和預期要做的事情

const schedule = require('node-schedule');

const job = schedule.scheduleJob('42 * * * *', function(){
console.log('The answer to life, the universe, and everything!');
});

時間格式如下(取自官方文件)

*    *    *    *    *    *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ │
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── month (1 - 12)
│ │ │ └────────── day of month (1 - 31)
│ │ └─────────────── hour (0 - 23)
│ └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)

以上就是爬蟲大致的程式邏輯,下一篇會開始介紹基本功能和過程中的收穫和心得!

--

--

De_

Who dares, wins. | Backend Engineer | Voracious Reader | Dog Person | Open to challenge