featured

มาทำ Web Scraping อย่างง่ายด้วย cheerio กันเถอะ!

Written by Phumrapee Limpianchop on 07 February, 2019

สวัสดีครับ ในสัปดาห์นี้เราจะมาสอนทำ Web Scraping กันโดยใช้ library ที่ชื่อว่า cheerio กันนะครับ >w<

Web Scraping เป็นการดึงข้อมูลดิบๆ ทางทางเว็บไซต์เพื่อเอาตัวแปร หรือค่าต่างๆ เอาไปประมวลผลต่อไป

งั้นเราก็มาเริ่มต้นการทำ Web Scraping กันเลยดีกว่า

เราจะใช้ HTML อันนี้เป็นตัวอย่างการเขียน Web Scraping สมมุติว่าหน้านี้อยู่ใน https://example.com/

<html>
<head>
<title>Awesome Title</title>
</head>
<body>
<div id="contentPrint">
<div class="row">
<div class="title">First Row</div>
<div class="data">Data 1-1</div>
<div class="data">Data 1-2</div>
</div>
<div class="row">
<div class="title">Second Row</div>
<div class="data">Data 2-1</div>
<div class="data">Data 2-2</div>
</div>
</div>
<a id="blog" href="https://blog.rayriffy.com/">Awesome Blog Link</a>
</body>
</html>
view raw index.html hosted with ❤ by GitHub

เราก็จะมาเริ่มเขียนกันเลย

ขั้นตอนที่ 1: ติดตั้ง Dependencies ให้พร้อม

การติดตั้งก็ง่ายมาก แค่ใช้ npm หรือ yarn ในการติดตั้ง dependencies

ก็เริ่มจากลง cheerio ก่อน

$ yarn add cheerio

แล้วเราจะรับ raw HTML มาจาก request-promise

$ yarn add request-promise

ขั้นตอนที่ 2: โค๊ดแม่ง

คราวนี้ใน src/index.js ของเราก็จะ import และ intitialize dependency

คำเตือน! อันนี้เราจะเขียนใน ES6 ต้องเอาโค๊ดไป compile เป็น JS ก่อนถึงจะใช้งานได้ (แนะนำให้ใช้ backpack-core)

import cheerio from 'cheerio'
import rp from 'request-promise'
view raw index.js hosted with ❤ by GitHub

เราก็จะเริ่มโดยการใช้ request-promise ฝนการไปดึง data มากจากเว็บก่อน

import cheerio from 'cheerio'
import rp from 'request-promise'
rp({
uri: `https://example.com/`,
transform: body => {
return cheerio.load(body)
},
})
.then($ => {
console.log($)
})
.catch(e => {
console.error(e)
})
view raw index.js hosted with ❤ by GitHub

Pause แค่นี้ก่อนเรามาอธิบายโค๊ดส่วนนี้สักนิสนึง

rp({
  uri: `https://example.com/`,
  transform: body => {
    return cheerio.load(body)
  },
})

โค๊ดนี้เราบอกว่าให้ไปดึงข้อมูลมากจาก https://example.com/ แล้วหลังจากดึงมากแล้วให้ transform โดยใช้ cheerio load ข้อมูลมา

  .then($ => {
    console.log($)
  })
  .catch(e => {
    console.error(e)
  })

ถ้าหากรับข้อมูลผ่านก็จะเข้าไปที่ .then() แต่ถ้าไม่ผ่านก็จะวิ่งไป .catch()

แล้วเราก็จะมา Scraping ข้อมูลกันเลยดีกว่า!

เอาของง่ายๆ มากันก่อน เราจะดึงคำว่า First Row ยังไง? คำตอบคือเราจะต้อง Specify ด้วย Selector วิธีที่ง่ายที่สุดคือให้เราเปิด DevTools ขึ้นมาบนเว็บเลื่อนหา First Row แล้วคลิดขวาไปที่ Copy > Copy selector แล้วเราก็จะได้ Selector ออกมาแล้ว

DevTools

แล้วเราก็เอาไปใส่ cheerio แล้วบอกให้แสดง text ออกมา

import cheerio from 'cheerio'
import rp from 'request-promise'
rp({
uri: `https://news.sanook.com/lotto/`,
transform: body => {
return cheerio.load(body)
},
})
.then($ => {
console.log($('#contentPrint > div:nth-child(1) > div.title').text())
})
.catch(e => {
console.error(e)
})
view raw index.js hosted with ❤ by GitHub

เราจะสามารถ Extract text ออกมาจาก Selector นั้นได้ด้วยการใช้ .text()

$('#contentPrint > div:nth-child(1) > div.title').text()

:nth-child(1) มีไว้เพื่อบอกว่าให้วิ่งเข้าไปที่ div อันแรก ถ้าสังเกตดีๆ จะเห็นว่าในหน้าเว็ยเราก่อนที่จะเข้าไปที่ div.title นั้นมี div.row ที่เป็นชื่อซ้ำกันอยู่ เราถึงใช้ :nth-child(1) เพื่อบอกว่าให้วิ่งไปอันแรก

ง่ายๆคือ ถ้าเป็นเป็น :nth-child(2) มันก็จะวิ่งไปที่ Second Row แทน แต่ถ้าเอาออกไปเลยล่ะ!????? มันก็จะเข้าทั้งคู่เลย แล้วเก็บข้อมูลออกมาเป็น Array นั่นเอง!!!

Giphy

เราก็เรียกออกมาได้ด้วยการ Loop

import cheerio from 'cheerio'
import rp from 'request-promise'
rp({
uri: `https://news.sanook.com/lotto/`,
transform: body => {
return cheerio.load(body)
},
})
.then($ => {
$('#contentPrint > div > div.title').each((i, elem) => {
console.log($(elem).text())
})
})
.catch(e => {
console.error(e)
})
view raw index.js hosted with ❤ by GitHub

จากที่เห็น cheerio มี Function ที่ช่วยในการทำ Loop ให้แล้วโดยใช้ .each() แล้วจะมี parameter อยู่ 2 ตัว

  • i จะเป็นเลข Indexes เผื่อต้องการใช้
  • elem จะเป็น Element ใน Loop นั้น

แล้วเราก็เอา elem ออกมา Extract text ออกมา

$(elem).text()

ความรู้เพียงแค่นี้ก็สามารถเอาไปเขียน Web Scraping ได้จริงแล้ว แต่เดี๋ยวนะริฟฟี่! ใน a#blog มี href ด้วยอ่ะจะเอาออกมายังไง?

เราสามารถดึง HTML Attribute ต่างๆ ออกมาได้ด้วยการใช้ .attr()

import cheerio from 'cheerio'
import rp from 'request-promise'
rp({
uri: `https://news.sanook.com/lotto/`,
transform: body => {
return cheerio.load(body)
},
})
.then($ => {
console.log($('#blog').attr('href'))
})
.catch(e => {
console.error(e)
})
view raw index.js hosted with ❤ by GitHub

ว่าจากตัวอย่างโค๊ดน่าจะเดาได้นะว่าเกิดอะไรขึ้น

สรุป

จากตัวอย่างแค่นี้ เราก็สามารถทำ Web Scraping ได้แล้ว ตัวอย่างโปรเจคของเราก็จะเป็น API ที่เอาไว้ตรวจหวย คือใช้ cheerio ไปดึงข้อมูลมาจาก sanook.com แล้วเอา data ประมวลผลออกมาเป็น JSON

ตัวอย่างที่ GitHub

แล้วคำแนะนำเพิ่มเติมคือ ถ้าอยากจะหรูหรามากกว่านี้แนะนำให้ลองไปดู Puppeteer แต่หลักๆ แล้วแค่นี้ก็ทำได้เพียงพอแล้ว ก็หวังว่าจะได้ความรู้อะไรกลับไปบ้างนะครับ 555 ขอบคุณที่อ่านมาถึงจุดนี้ครับ แล้วเจอกันอีกทีสัปดาห์หน้า!

Resources Link