Table of contents
Intro
สวัสดีครับ บทความนี้จะพาทำ Web scraping คือการดูดข้อมูลหรือดึงข้อมูลจาก URL หรือเว็บไชต์อื่น ๆ เพื่อเอาข้อมูลเหล่านั้นมาใช้ประโยชน์ โดยจะมี 2 ขั้นตอนคือ
- การดึงข้อมูลหน้าเว็บทั้งหมด
- การสกัดเอาเฉพาะข้อมูลที่ต้องการ
ก่อนเริ่ม
การดึงหรือดูดข้อมูลจากเว็บไชต์อื่นหรือทำซ้ำข้อมูล การเก็บข้อมูล โดยไม่ได้รับอนุญาตให้ระวังเรื่องลิขสิทธิ์ของข้อมูล ควรขออนุญาตจากเจ้าของข้อมูลก่อนเพื่อไม่ให้มีปัญหาภายหลัง
ในตัวอย่างการทำ Web scraping นี้จะใช้เครื่องมือหลัก ๆ 2 ตัวคือ
- Cheerio สำหรับจัดการ Cheerio
- Axios สำหรับการยิง Request
ภาษาที่จะใช้คือ Javascript รันบน NodeJS เนื่องจากมีความเร็วพวกสมควรและ Cheerio สามารถจัดการกับ Element ของ HTML ได้ง่ายมากเริ่มกันเลยครับ
โจทย์
โจทย์ที่เราต้องการคือดึงข้อมูลที่อยู่ในตารางนี้
Weekend of July 9 - 11, 2021
Title | Weekend | Gross | Weeks |
---|---|---|---|
Black Widow | $80.4M | $80.4M | 1 |
F9 | $11.4M | $141.9M | 3 |
The Boss Baby: Family Business | $8.9M | $34.9M | 2 |
The Forever Purge | $7.1M | $27.9M | 2 |
A Quiet Place Part II | $3.2M | $150.8M | 7 |
Cruella | $2.4M | $80.9M | 7 |
Hitman's Wife's Bodyguard | $1.6M | $35.0M | 4 |
Peter Rabbit 2: The Runaway | $1.3M | $37.7M | 5 |
The Conjuring: The Devil Made Me Do It | $655K | $63.8M | 6 |
Zola | $620K | $3.5M | 2 |
และต้องการข้อมูลที่อยู่ในรูปแบบ Objects ดังนี้
{
weekendOf: "Weekend of July 9 - 11, 2021",
movies: [
{
title: "Black Widow",
weekend: "$80.4M",
gross: "$80.4M",
weeks: "1",
}
...
]
}
วิเคราะห์ HTML Elements
การสกัดข้อมูลจาก HTML นั้นให้กด F12 หรือ คลิกขวาที่หน้า Webpage ใน Web Browser จากนั้นเลือก Inspect Element
weekendOf
สำหรับลำดับ Selector ของ weekendOf นั้นเข้าถึงง่ายมากไม่มีปัญหาอะไร แถมมี div และ id = boxoffice มาครอบไว้ดังนั้นเราจะได้ Selector ดังนี้
#boxoffice > h4
Title
สำหรับลำดับ Selector ของ Title ในอยู่ใน table โชคดีที่แยกส่วนเป็น tbody แต่มีปัญหาอยู่ว่ามี Element span ครอบไว้ดังนั้นก่อนจะดึงข้อมูลใน td ได้ต้องเข้าถึง span ก่อน
สำหรับเข้าถึงเฉพาะข้อมูลตัวแรก
#boxoffice > table > tbody > tr:nth-child(1) > td.titleColumn > span
เมื่อเราจะเข้าถึงข้อมูลทุกตัวของ Selector ของ Title จะได้ดังนี้
#boxoffice > table > tbody > tr
จะได้
Title = td[0] > span
Weekend
สำหรับ Weekend นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 2 ดังนั้น Selector คือ
Weekend = td[1]
Gross
สำหรับ Gross นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 3 ดังนั้น Selector คือ
Gross = td[2] > span
Weeks
สำหรับ Weeks นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 4 ดังนั้น Selector คือ
Weekend = td[3]
ขั้นตอนการสกัดข้อมูล
mkdir web-scraping-by-cheerio && cd ./web-scraping-by-cheerio
touch index.js
npm init -y
npm install cheerio axios
const axios = require("axios")
const cheerio = require("cheerio")
const app = async () => {
// ใช้ axios ส่ง request เพื่อร้องขอข้อมูลแล้วให้ response ข้อมูลกลับ
const html = await fetchData('https://blog.2my.xyz/2021/07/07/web-scraping-by-cheerio')
// เมื่อได้ HTML จาก axios แล้วก็ส่งไปสกัดเพื่อเอาเฉพาะข้อมูลที่ต้องการ
const result = extractData(html)
console.log(result)
}
const fetchData = (url) => {
// ส่ง request เพื่อร้องขอข้อมูลหน้าเว็บเพจทั้งหน้า
return axios.get(url).then(response => response.data)
}
const extractData = (html) => {
// ส่งข้อมูล HTML เข้า Method load ของ cheerio
// จากนั้นเราสามารถเข้าถึงข้อมูลทุก Element ผ่านตัวแปร $
const $ = cheerio.load(html);
// ดึงข้อมูล weekendOf จาก Selector ที่เราได้วิเคราะห์ไว้ แล้วสกัดข้อมูล Text ออกมา
const weekendOf = $('#boxoffice > h4').text()
// สำหรับข้อมูลในตารางมีหลายแถวดังนั้นเราจึงใช้ Loop แบบ Map มาช่วย
const movies = $('#boxoffice > table > tbody > tr').map((i, element) => {
// วน Loop ทีละ Element tr (แต่ละแถวของข้อมูล)
// เข้าถึง td และเก็บไว้ที่ตัวแปร
const td = $(element).find("td");
return {
// เข้าถึง Selector คอลั่มน์ที่ 1 แล้วเข้าไปที่ span จากนั้นสกัดข้อมูล Text ออกมา
title: $(td[0]).children('span').text(),
// เข้าถึง Selector คอลั่มน์ที่ 2 แล้วสกัดข้อมูล Text ออกมา
weekend: $(td[1]).text(),
// เข้าถึง Selector คอลั่มน์ที่ 3 แล้วเข้าไปที่ span จากนั้นสกัดข้อมูล Text ออกมา
gross: $(td[2]).children('span').text(),
// เข้าถึง Selector คอลั่มน์ที่ 2 แล้วสกัดข้อมูล Text ออกมา
weeks: $(td[3]).text(),
}
}).toArray()
return { weekendOf, movies }
}
app()
node index.js
ผลลัพธ์
{
weekendOf: 'Weekend of July 9 - 11, 2021',
movies: [
{
title: 'Black Widow',
weekend: '$80.4M',
gross: '$80.4M',
weeks: '1'
},
{ title: 'F9', weekend: '$11.4M', gross: '$141.9M', weeks: '3' },
{
title: 'The Boss Baby: Family Business',
weekend: '$8.9M',
gross: '$34.9M',
weeks: '2'
},
{
title: 'The Forever Purge',
weekend: '$7.1M',
gross: '$27.9M',
weeks: '2'
},
{
title: 'A Quiet Place Part II',
weekend: '$3.2M',
gross: '$150.8M',
weeks: '7'
},
{ title: 'Cruella', weekend: '$2.4M', gross: '$80.9M', weeks: '7' },
{
title: "Hitman's Wife's Bodyguard",
weekend: '$1.6M',
gross: '$35.0M',
weeks: '4'
},
{
title: 'Peter Rabbit 2: The Runaway',
weekend: '$1.3M',
gross: '$37.7M',
weeks: '5'
},
{
title: 'The Conjuring: The Devil Made Me Do It',
weekend: '$655K',
gross: '$63.8M',
weeks: '6'
},
{ title: 'Zola', weekend: '$620K', gross: '$3.5M', weeks: '2' }
]
}
สรุป
สำหรับการทำ Web Scraping ด้วย Cheerio นั้นสรุปหลักการดังนี้
- ใช้ axios หรือ fetch request สักตัวในการดึง HTML Content ทั้งหน้าที่ต้องการมา
- ใช้ Cheerio ในการสกัดข้อมูลใน HTML
ถ้าคุณมีพื้นฐานของ jQuery อยู่แล้วก็จะเข้าใจและใช้งาน Cheerio ได้เร็ว แต่ถ้าคุณไม่ได้มีความรู้ในการเข้าถึงข้อมูลใ น Elements มากนักแนะนำให้ศึกษาเพิ่มเติมจากที่นี่ https://cheerio.js.org