Nitipat Lowichakornthikun
nitip.at

nitip.at

วิธีการดูดข้อมูลเว็บ (Web scraping) ด้วย Node.js

วิธีการดูดข้อมูลเว็บ (Web scraping) ด้วย Node.js

Nitipat Lowichakornthikun's photo
Nitipat Lowichakornthikun
·Jul 11, 2019·

2 min read

หนึ่งในงานที่เราชาว Developer แทบทุกคนต้องเคยเจอในชีวิตการทำงานก็คือการหาวิธีดึงข้อมูลจากเว็บคนอื่นเค้า ฮ่า ๆ เหตุผลหลัก ๆ เลยก็เพราะบางครั้ง ข้อมูลที่พวกเราอยากได้ในเว็บนั้นมันไม่มี Public API ให้เราดึงข้อมูลมาได้ด้วยท่าที่ง่าย ๆ

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

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

วันนี้เราจะมาทดลองดึงข้อมูลกันครับ และ ก่อนที่เราจะเริ่มใช้งานเครื่องมือนี้ ผมจะพาพวกเราทุกคนไปทำความรู้จักหลักการของการทำ Web scraping กันครับ

Concept ของการทำ Web scraping

เครื่องมือในการทำ Web scraping นั้นมีหลายตัว ขึ้นอยู่กับภาษาเลยครับ แต่วันนี้เราจะมาลองเล่นเครื่องมือในภาษา Node.js กันครับ โดยเครื่องมือนี้มีชื่อว่า Osmosis ครับ โดยทั่วไปการทำ Web scraping นั้นเราต้องมาทำความเข้าใจโครงสร้างของ HTML เบื้องต้นก่อนครับ

ปกติการเข้าเว็บไซต์ซักหนึ่งเว็บสิ่งที่เกิดขึ้นเป็นดังภาพนี้คือ

https://people.rit.edu/~agy5732/140/proj2/http

  1. เมื่อผู้ใช้เปิดเว็บไซต์ด้วย Browser เช่น Chrome สิ่งแรกตัว Browser จะไป Request ข้อมูลหาฝั่ง Server (ความจริงมันมีผ่านหลายชั้นทั้ง DNS server มากมาย แต่เอาคร่าว ๆ แบบนี้แล้วกัน)
  2. ฝั่ง Server ก็ทำการประมวลผลออกมาให้อยู่ในรูปแบบของ HTML, JavaScript และ CSS ทั่วไป (แน่นอนว่าเราอยากได้ข้อมูลจากฐานข้อมูลเค้าแต่ก็ทำไม่ได้น่ะ นอกจากจะสามารถมีสิทธิเข้าไปถึงเครื่อง Server ได้)
  3. Browser ได้รับไฟล์กลับมา และ แสดงผลให้กับผู้ใช้งาน

ในการทำ Web scrapping เราจะทำในขั้นตอนสุดท้ายนั่นก็คือขั้นตอนที่ 3 โดยเสมือนกับว่าเรานี่หล่ะจะเป็นคนเข้าใช้งานเว็บไซต์ทั่ว ๆ ไป แต่เราจะเขียนโปรแกรมให้มันสามารถดึงออกมาได้โดยไม่ต้องนั่งไล่หาเองด้วยมือ

เราจะใช้อะไร อ้างอิงว่าข้อมูลอยู่ตรงไหนในหน้าเว็บ?

ถ้าด้วยสายตาของเราก็ทำได้แน่นอน แต่การที่จะทำแบบนั้นในการเขียนโปรแกรมมันไม่ใช่เรื่องง่าย ๆ เลย ฮ่า ๆ ซึ่งอย่างที่บอกใบ้ไปครับว่าจริง ๆ แล้วหน้าตาเว็บไซต์ที่เราเห็นมันมี HTML เป็นเบื้องหลัง ดังนั้นเราจะทำหน้าที่ไต่ตาม Tag ของ HTML ครับผม

ใน Osmosis รองรับการอ้างอิง Tag ของ HTML อยู่ 2 แบบด้วยกันครับ

  1. CSS Selector — เราสามารถอ้างอิง Tag ต่าง ๆ ได้ด้วย CSS เช่น
ข้อความที่อยากได้

เราก็เรียกใช้งานด้วย div#content

2. XPath — เราสามารถอ้างอิง Tag ต่าง ๆ ด้วยรูปแบบของ XPath ได้ด้วย เช่น

ข้อความที่อยากได้

เราก็เรียกใช้งานด้วย A/B/C

เริ่มกันดีกว่า!

  1. ทำการ Clone code จาก repo ที่ผมสร้างไว้ดังนี้

git clone https://github.com/nitipatl/osmosis-playground

2. เมื่อเข้าไปให้ทำการติดตั้งให้เรียบร้อยด้วยคำสั่ง npm i หรือใครจะใช้ yarn ก็จัดเลยครับ

3. ทดลองสั่งการใช้งาน node-osmosis ที่ผมเขียนไว้เริ่มต้นด้วยคำสั่ง

node index.js

เราจะได้ข้อมูลออกมาหน้าตาแบบนี้ เป็นอันว่าสามารถทำงานได้ถูกต้อง

ทีนี้เราจะมาดูกันครับว่า Code ที่ผมเขียนเป็นตัวอย่างทำงานอย่างไรบ้าง ให้ลองดูที่ไฟล์ index.js ได้เลยครับ

  • เรามาเริ่มต้นกันที่บรรทัดที่ 4 กันครับ มันคือคำสั่ง get ในการดึงข้อมูลเว็บไซต์ที่เราต้องการนำมาวิเคราะห์ โดยในครั้งนี้เราจะทดลองดึงข้อมูลจากเว็บไซต์ http://quotes.toscrape.com/ ที่มีคนใจดีสร้างขึ้นมาเพื่อให้เราได้ศึกษาการทำ Web scraping ครับ
  • จากโค๊ดที่ผมเขียนไว้นี้ เราจะดึง tag title ออกมาจากหน้าเว็บไซต์ครับ ซึ่งก็จะเป็นคำสั่งในบรรทัดที่ 5
  • ส่วนบรรทัดที่ 6 คือการระบุว่าก้อนข้อมูลที่ได้จาก title นี้ จะใช้ชื่อ attribute ว่า title กันครับ
  • ส่วนบรรทัดที่ 7 คือการที่หลังจากเสร็จแล้ว ให้เข้ามาแสดงข้อมูลที่เราได้ออกมาครับ
  • ส่วนบรรทัดที่ 10–12 เป็นการแสดงช้อมูลต่าง ๆ อาทิ log, error log, debug log ครับ ตอนใช้งานจริงไม่ต้องใส่ก็ได้ครับ อันนี้ใส่ไว้ในขั้นตอนการพัฒนาจะช่วยให้ง่ายว่ามันติดปัญหาตรงไหนไหม?

ทีนี้ลองแก้บรรทัดที่ 6 เป็นแบบนี้ครับ

.set('test')

ผลที่ออกมาจะเป็นดังนี้ครับ

จะเห็นว่า Object ของข้อมูลที่ออกมาจะถูกเปลี่ยนเป็น attribute ว่า test แทนแล้วครับ

มาทำโจทย์กันครับ

โจทย์เพื่อทดลองใช้งาน node-osmosis ของเราคือเราจะดูดข้อมูลของเว็บนี้

Quotes to Scrape
"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking."
quotes.toscrape.com

โดยเราจะดึงเอา quoute, tag, คนเขียน quote ที่มีอยู่หลายอันในส่วนซ้ายของเว็บออกมาจากเว็บนี้ครับ

ก่อนจะเริ่มกันเรามาแกะโครงสร้างของ HTML เว็บนี้เพื่อทำความเข้าใจกันก่อนน่ะครับ เพื่อจะได้นำเอาไปเขียนโปรแกรมดึงข้อมูลได้ในขั้นตอนต่อไป

จากรูปด้านบนเราจะเห็นว่าแต่ละ quote จะแบ่งด้วย div class=”quote” ไว้ครับ และ ข้อความ quote จะอยู่ใน span class=”text” ครับ

  1. เราจะทำการทดลองดึงข้อมูลแต่ละ quote ออกมาก่อน

เมื่อดูสิ่งที่เราแก้ไขคือบรรทัดที่ 5 เราใช้การ find เพื่อค้นหา tag ของ div.quote (CSS Selector) ครับ จากนั้นบรรทัดที่ 6 คือการที่เราจะ set ค่าของการค้นหาลงใน attribute ครับ ซึ่งเราก็ใช้การเลือก span.text (CSS Selector เช่นกัน)

ผลลัพธ์ที่ออกมาเราจะได้ข้อมูลแบบนี้ครับ

ซึ่งเป็น List Array ของ Object ที่มี attribute ว่า quote ครับ

2. ขั้นตอนถัดมาเราจะมาดึงข้อมูลชื่อของผู้เขียนกันครับ ย้อนกลับไปดูโครงสร้าง HTML ในหน้านั้น จะพบว่าข้อมูลของชื่อผู้เขียนอยู่ใน span > small > class ”author” ดังนั้นเราจะทดลองเขียนดังนี้ครับ

ลองดูบรรทัดที่ 8 น่ะครับ จะพบว่าเราเพิ่ม author ขึ้นมา โดยใช้ span > small.author ถ้าใครเคยเขียน CSS มาก็คงคุ้นน่ะครับ ถ้า งง ๆ ต้องลองศึกษาเรื่อง CSS เพิ่มเติมครับผม

ผลลัพธ์ที่เราได้ออกมาก็คือ…

เราจะได้ author เพิ่มขึ้นมาแล้วในแต่ละ Object ครับ

โจทย์ที่อยากให้ทดลองทำกันเอง

ตอนนี้เราจะเห็นว่ามันมี Tag แสดงอยู่ในแต่ละ quote ครับ โจทย์คือเราจะดึงข้อมูลพวกนี้มาด้วยอย่างไร ทดลองทำเลยครับผมจะได้ทำความเข้าใจมากยิ่งขึ้น :)

ใบ้ให้ว่า… รอบนี้ข้อมูลที่เราอยากได้ต้องออกมาเป็น Array น่ะครับ ข้อมูลจะหน้าตาประมาณนี้ครับ

ลองดูตัวอย่างจากที่นี่ครับ

rchipka/node-osmosis
Web scraper for NodeJS. Contribute to rchipka/node-osmosis development by creating an account on GitHub.
github.com

.

.

.

หากใครยัง งง ๆ สามารถดูเฉลยในโจทย์นี้กันได้ที่นี่เลยครับ

สำหรับบทความนี้ก็จบกันแต่เพียงเท่านี้ครับ ยังไงก็นำเอาความรู้นี้ไปใช้ในเชิงสร้างสรรค์กันน่ะครับผม :)

 
Share this