Skip to main content

วิธีทำ Web scraping ดูดข้อมูลจากเว็บอื่นมาใช้ประโยชน์ง่าย ๆ ด้วย Cheerio

Kongvut Sangkla
Tags:

Intro

สวัสดีครับ บทความนี้จะพาทำ Web scraping คือการดูดข้อมูลหรือดึงข้อมูลจาก URL หรือเว็บไชต์อื่น ๆ เพื่อเอาข้อมูลเหล่านั้นมาใช้ประโยชน์ โดยจะมี 2 ขั้นตอนคือ

  1. การดึงข้อมูลหน้าเว็บทั้งหมด
  2. การสกัดเอาเฉพาะข้อมูลที่ต้องการ

ก่อนเริ่ม

คำเตือน

การดึงหรือดูดข้อมูลจากเว็บไชต์อื่นหรือทำซ้ำข้อมูล การเก็บข้อมูล โดยไม่ได้รับอนุญาตให้ระวังเรื่องลิขสิทธิ์ของข้อมูล ควรขออนุญาตจากเจ้าของข้อมูลก่อนเพื่อไม่ให้มีปัญหาภายหลัง

ในตัวอย่างการทำ Web scraping นี้จะใช้เครื่องมือหลัก ๆ 2 ตัวคือ

  • Cheerio สำหรับจัดการ Cheerio
  • Axios สำหรับการยิง Request

ภาษาที่จะใช้คือ Javascript รันบน NodeJS เนื่องจากมีความเร็วพวกสมควรและ Cheerio สามารถจัดการกับ Element ของ HTML ได้ง่ายมากเริ่มกันเลยครับ

โจทย์

โจทย์ที่เราต้องการคือดึงข้อมูลที่อยู่ในตารางนี้

Weekend of July 9 - 11, 2021


TitleWeekendGrossWeeks
Black Widow

$80.4M

$80.4M1
F9

$11.4M

$141.9M3
The Boss Baby: Family Business

$8.9M

$34.9M2
The Forever Purge

$7.1M

$27.9M2
A Quiet Place Part II

$3.2M

$150.8M7
Cruella

$2.4M

$80.9M7
Hitman's Wife's Bodyguard

$1.6M

$35.0M4
Peter Rabbit 2: The Runaway

$1.3M

$37.7M5
The Conjuring: The Devil Made Me Do It

$655K

$63.8M6
Zola

$620K

$3.5M2

และต้องการข้อมูลที่อยู่ในรูปแบบ 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

img img

สำหรับลำดับ Selector ของ weekendOf นั้นเข้าถึงง่ายมากไม่มีปัญหาอะไร แถมมี div และ id = boxoffice มาครอบไว้ดังนั้นเราจะได้ Selector ดังนี้

#boxoffice > h4

Title

img img

สำหรับลำดับ 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

img img

สำหรับ Weekend นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 2 ดังนั้น Selector คือ

Weekend = td[1]

Gross

img img

สำหรับ Gross นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 3 ดังนั้น Selector คือ

Gross = td[2] > span

Weeks

img img

สำหรับ Weeks นั้นอยู่ภายใต้ tr แต่อยู่ td ลำดับที่ 4 ดังนั้น Selector คือ

Weekend = td[3]

ขั้นตอนการสกัดข้อมูล

สร้างโปรเจคใหม่ และติดตั้ง Packages ที่ต้องใช้งาน
mkdir web-scraping-by-cheerio && cd ./web-scraping-by-cheerio
touch index.js
npm init -y
npm install cheerio axios
index.js
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 นั้นสรุปหลักการดังนี้

  1. ใช้ axios หรือ fetch request สักตัวในการดึง HTML Content ทั้งหน้าที่ต้องการมา
  2. ใช้ Cheerio ในการสกัดข้อมูลใน HTML

ถ้าคุณมีพื้นฐานของ jQuery อยู่แล้วก็จะเข้าใจและใช้งาน Cheerio ได้เร็ว แต่ถ้าคุณไม่ได้มีความรู้ในการเข้าถึงข้อมูลใน Elements มากนักแนะนำให้ศึกษาเพิ่มเติมจากที่นี่ https://cheerio.js.org

อ้างอิง

Loading...