Python Web Scraping #2: ใช้ Requests มาช่วย Pandas ดึงข้อมูล

Nior Atthakorn
3 min readApr 12, 2020

--

จากตอนที่แล้ว เราได้ใช้ Pandas ในการทำ Web Scraping มาแล้ว ซึ่งก็มีข้อจำกัดอยู่หลายอย่าง ไม่ว่าจะเป็นการที่ข้อมูลจะต้องเป็นตารางเท่านั้น หรือตัว url ที่จะมีตัวอักษรที่แปลกประหลาดหน่อย (อย่างเช่นตัวอักษรภาษาไทย) ไม่ได้เลย

ในบทความนี้เราจึงจะใช้ Requests ซึ่งเป็น library สำหรับการส่ง http request อย่างง่าย มาช่วยแก้ปัญหาที่ตัว Pandas ไม่สามารถอ่าน url แปลกๆ ได้ จะมีวิธีการทำยังไง ก็อ่านต่อด้านล่างนี้เลย Let’s Go!

ดูใน GitHub หรือ ลองเล่นใน Google Colab

ก่อนหน้านี้เราใช้แค่ function read_html ของ Pandas โดยใส่ url เข้าไป ก็สามารถทำการ Scrape ข้อมูลที่เราต้องการได้ทันที แต่ถ้า url ใช้ไม่ได้หล่ะ ?

ถ้ายังจำกันได้จากขั้นตอนที่ 3 ในบทความที่แล้ว ที่บอกไว้ว่าตัว read_html สามารถรับ parameter ได้หลายรูปแบบ โดยในบทความนี้เราจะส่ง HTML text ให้ read_html ดึงข้อมูลที่เป็นตารางที่เราต้องการจากหน้า website

แล้วเราจะได้ HTML text มาได้ยังไง ? ก็แหงหล่ะครับ ดูจากชื่อบทความก็น่าจะพอเดาได้ไม่ยากนะครับ เราจะใช้ library ที่ชื่อ Requests มาดึงข้อมูล HTML จาก website นั่นเองงงง

ในเมื่อ Pandas ดึงข้อมูลจาก website ที่มี url แปลกๆ ไม่ได้ ก็ให้ Requests ดึงมาให้ แล้วให้ Pandas จัดการต่อ

ขั้นตอนที่ 1 ก็เหมือนๆ เดิมครับ import library ที่จะใช้ ในที่นี้จะใช้ 2 ตัว

  • Requests -> ดึงข้อมูลหน้า website
  • Pandas -> แปลงข้อมูลที่ได้มาเป็นตาราง
import requests
import pandas as pd

ขั้นตอนที่ 2 ใส่ url ของ website ที่เราต้องการ scrape เหมือนเดิม ซึ่งจากบทความแรก url ต้องเป็นภาษาอังกฤษเท่านั้น (ถ้า url มีภาษาไทย ใช้วิธีเดิมไม่ได้) แต่ในบทความนี้สามารถใช้ได้ ในที่นี้จะใช้ website สถิติหวยย้อนหลัง 29 ปี ของ myhora.com ต้องขออนุญาตและขอขอบคุณมา ณ ที่นี้ด้วยนะครับ -/\-

url = 'https://www.myhora.com/หวย/' \
+ 'สถิติหวย-ย้อนหลัง-29-ปี.aspx?mode=year-range&value=29'

url ยาว เลยเขียนแยกเป็น 2 ท่อนเหมือนเดิมจ้า

ขั้นตอนที่ 3 ให้เราใช้ function get ของ Requests ในการดึงข้อมูลหน้า website โดยใส่ parameter เป็น url ที่เราได้กำหนดไว้ในขั้นตอนที่แล้ว

response ที่ได้จะเป็น object ที่เก็บผลการทำ http request โดยมี attribute หลายอย่างที่ถูกเก็บไว้ คงไม่ได้ไล่ให้ทั้งหมด แต่เดี๋ยวจะคอยอธิบายแต่ละตัวตอนที่ใช้ละกันครับ

response = requests.get(url)

ขั้นตอนที่ 4 ให้เราใช้ function read_html ของ pandas โดยตัวอย่างที่แล้วเราใส่ url เป็น argument ซึ่งจะทำให้ตัว function ไปดึงข้อมูลทั้งหมดจาก website นั้นๆ

แต่ในที่นี้เราใส่ข้อมูล HTML text (response.text คือ HTML text ที่มาพร้อมกับ response) เข้าไปเลย ตัว function read_html ก็จะ scan ดูทั้ง HTML ที่ใส่เข้าไป ว่ามีตารางอยู่ตรงไหนบ้าง แปลงแต่ละตารางเป็น DataFrame แล้วใส่ไว้ใน list ให้เราได้เหมือนเดิม สะดวกเกิ๊น

dfs = pd.read_html(response.text)

ตอนนี้ก็จบขั้นตอนการ scrape แล้ว มาลองดูผลลัพธ์กันสักหน่อย

ลองดูก่อนเหมือนเดิมว่าจำนวนตารางที่ได้ตรงกับในหน้า website ที่เราเห็นมั้ย

In [5]: len(dfs)
Out[5]: 29

เจอตารางมากถึง 29 ตาราง แต่เอ๊ะ! ลองเลื่อนดูผ่านๆ ในหน้า website มันก็ดูไม่น่ามีถึง 29 ตารางนี่นา ทำไมมันออกมาตั้ง 29 ตาราง ?

ถ้าคุณคิดแบบนี้ไม่ต้องตกใจไปครับ (เพราะตอนแรกผมก็คิดแบบนั้น) ในหน้า website เขามีบางตารางที่ (เหมือนกับ) ซ่อนอยู่ (แต่จริงๆ เขาไม่ได้ซ่อนน้า) ลองเลื่อนไปดูแถวๆ ด้านล่างของแต่ละกล่อง จะเจอปุ่มประมาณนี้

ภาพจากหน้า website myhora.com

ลองกดดูซิ จะเป็นยังไงน้าาาา

ภาพจากหน้า website myhora.com

นี่ไง มีตารางซ่อนอยู่ในนี้อีก ซึ่งถ้าเราลองกดดูทั้งหมดทั้งมวล แล้วลองนับตารางดู เราก็จะได้ 29 ตารางพอดิบพอดีเลย

เนื่องจากใน website นี้มีตารางเยอะมาก ในที่นี้เลยขอดึงมาดูแค่ 2 ตาราง คือตารางแรกกับตารางสุดท้าย ว่าได้หน้าตาอย่างที่เราอยากได้มั้ย

เริ่มจากตารางแรกก่อนเลย

dfs[0].head()
ตารางที่ 1 ที่ได้

พอได้อยู่ อาจจะต้องจัดการอีกนิดนึง แต่ก็ถือว่าใช้ได้แล้ว

มาลองดูตารางสุดท้ายกันบ้าง

dfs[-1].head()
ตารางสุดท้ายที่ได้

อ้าวเฮ้ย ไม่เหมือนที่คุยกันไว้นี่หน่า

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

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

df = dfs[-1][0].str.split(expand=True)

โอ้โห อะไรไม่รู้ยั้วเยี๊ยเต็มไปหมดเลย ลองค่อยๆ เจาะดูกันทีละส่วนครับ

dfs[-1]

ส่วนแรกนี้คือการเลือกตารางสุดท้ายออกมาจากผลลัพธ์การ scrape ซึ่งก็คือตารางสุดยุ่งเหยิงที่เราจะเอามาทำการแปลงโฉมนั่นเอง (โดยตารางเป็นประเภท Pandas DataFrame)

dfs[-1][0]

หลังจากเลือกตารางแล้ว ก็มาเลือก column อีกที เนื่องจากคำสั่งที่เราจะใช้ต่อไปนี้เป็นคำสั่งที่ใช้กับ Pandas Series เท่านั้น

dfs[-1][0].str

ตัว .str นี้ทำให้เราสามารถใช้ method ที่คล้ายๆ ว่าจะเป็น string method กับ Pandas Series ได้

dfs[-1][0].str.split(expand=True)

ส่วนสุดท้ายนี้ก็คือ method split ที่จะตัดแบ่ง string ตามตัวอักษรที่เรากำหนดไว้ (ในที่นี้ไม่ได้กำหนดไว้ method ก็จะไปดึงค่า default นั่นคือ space " " มาใช้ในการแบ่ง) พร้อมทั้งใส่ parameter expand=True เป็นการบอกให้กระจายผลลัพธ์ที่ได้จากการแบ่งเป็น column หลายๆ อัน

ดูสภาพหลังแปลงแล้ว

ตารางสุดท้ายที่ได้ หลังจากแปลงโฉมแล้ว

สวยงามตามท้องเรื่องงงง

เป็นไงกันบ้างครับกับการใช้ Requests มาช่วย Pandas ทำ Web Scraping ง่ายเหมือนเดิมใช่ไหมหล่ะครับ แต่ก็ยังมีเงื่อนไขในการใช้งานอยู่อีกพอสมควร

ด้วยข้อจำกัดใหญ่ที่ข้อมูลต้องเป็นตารางเท่านั้น แล้วถ้าข้อมูลไม่ใช่ตารางหล่ะ ? เราจะทำยังไงต่อ ใช้เครื่องมืออะไร ติดตามตอนต่อไปที่นี่เลย

ส่วนใครยังไม่ได้อ่านตอนแรก หรืออ่านแล้วลืมแล้ว สามารถย้อนอ่านตอนแรกได้ที่นี่เลย:

สำหรับวันนี้ก็ แค่นี้แหละครับ บายยยย

--

--

Nior Atthakorn

โปรแกรมบ้าง ดนตรีบ้าง ปนๆ กันไป