Python Web Scraping part 5 - วิธีเลียนแบบการรับส่งข้อมูลของเว็บเป้าหมาย
Tags: python, tutorial, datascience, html
Published: 2020-08-12 15:05:27

บทความนี้คิดว่าจะเป็น part สุดท้าย สำหรับการสอน scraping เบื้องต้นแล้วนะครับ หลังจากนี้ ถ้ายังสนใจกันอยู่ คิดว่าจะพา "ทำโจทย์จริง" กันดู อาจจะเป็นบทความแบบนี้อีก หรืออาจจะมาเป็นคลิป เดี๋ยวจะประกาศให้ทราบนะครับ

**ก่อนอื่น ขอทบทวนเนื้อหา parts ก่อน ๆ สักเล็กน้อยนะครับ**

_**part 1:**_ เราพูดถึงภาพหยาบ ๆ ของการ scrape ไว้ ว่าเราอาจจะมองได้เป็น **2 ขั้นตอน** ก็คือ _การ scrape_ คือดึงข้อมูลดิบ ๆ ลงมาให้ได้ และ _การ extract_ คือการสกัดส่วนที่เราสนใจ ออกมาจากข้อมูลดิบนั้น

_**part 2:**_ ก็จะพูดถึงเรื่องของการ extract ข้อมูลก่อน โดยจะแนะนำเครื่องมือที่ช่วยให้สามารถ **ตรวจดูโครงสร้างของหน้า web** ได้แบบไว ๆ เพื่อหาวิธีสกัดข้อมูลที่เราต้องการออกมา

_**part 3:**_ จะสอนการสกัดข้อมูลด้วย **XPath** เบื้องต้น โดยใช้ประโยชน์จากโครงสร้างของหน้า web ที่เราได้เรียนรู้มา

_**part 4:**_ จะเล่าเกี่ยวกับส่วน scrape บ้าง และได้แนะนำ 7 เทคนิค ที่ช่วยให้เราสามารถ **ดึงข้อมูลจากหลาย ๆ เว็บ** _ที่มีระบบซับซ้อน หรือมีกลไกการป้องกันการ scrape เอาไว้_

เช่นเดียวกับ part ที่แล้ว ในบทความนี้ เนื้อหาก็จะยังอยู่กับส่วนของ _การดึงข้อมูล_ ครับ โดยจะแนะนำเครื่องมืออีกชิ้นที่ช่วยให้เราสามารถเข้าใจการรับส่งข้อมูลระหว่างหน้าเว็บของเป้าหมาย กับตัว server ได้ โดยไม่ต้องไปตามอ่านโค้ดของเว็บทุกบรรทัดให้เสียเวลา

อธิบายก่อนว่า อย่างเช่นตัวอย่างใน part ที่ 1 เว็บเป้าหมายของเราเป็นเว็บ Wikipedia ใช่ไหมครับ ซึ่งเว็บ _Wikipedia_ เนี่ยไม่ได้ซับซ้อนมากนัก **ข้อมูลทุกอย่างแสดงอยู่บนหน้าเว็บตั้งแต่ต้นแล้ว เราก็สามารถที่จะดึงทั้งหน้านั้นมาตรง ๆ ได้เลย**

**แต่ในเว็บไซต์ส่วนใหญ่ไม่ได้เป็นแบบนั้นครับ** หลาย ๆ เว็บก็จะมีกลไกในการโหลดข้อมูลมาแสดงเพิ่มเติมทีหลังได้อีก เช่น ต้องกดปุ่มก่อนแล้วข้อมูลถึงจะถูกโหลดขึ้นมาแสดง หรือต้องเลื่อนหน้าจอ (scroll) ลงไปล่างสุด แล้วจะโหลดเนื้อหามาแสดงเพิ่มทีหลัง ดังนั้นถ้าเราโหลดเฉพาะหน้าเว็บของมันมาตรง ๆ แบบตอน part ที่ 1 ก็อาจจะได้มาแค่หน้าว่าง ๆ ที่ยังไม่มีข้อมูล ...แบบนี้จะทำอย่างไร?

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jxsmnm1o6qfpiuedguy3.png)
_ตัวอย่าง: เว็บ Pantip จะค่อย ๆ โหลดข้อมูลมาเพิ่มเมื่อ scroll ลง_

วิธีการหนึ่งที่ทำได้คือเราก็ต้องเข้าไปดูว่า ข้อมูลที่มันโหลดมาเพิ่มเนี่ย มันโหลดมาจากไหน โหลดมาอย่างไร จากนั้นเราก็แค่เลียนแบบมันก็จบ! ในบทความนี้จึงจะมาแนะนำ **Network Inspector** เครื่องมือชิ้นหนึ่งใน **Google Chrome DevTools** สำหรับการตรวจสอบการโหลดข้อมูลให้ได้รู้จักกันครับ

# **Get Started!**
ถ้ายังจำกันได้ใน part ที่ 2 เราก็ได้พูดถึงเครื่องมือชิ้นหนึ่งของ **Google Chrome DevTools** กันไปแล้ว คือตัว **Code Inspector** ที่เอาไว้ใช้สำหรับตรวจสอบโครงสร้างของหน้าเว็บได้ เอาไว้ให้ developer ได้ตรวจสอบ ว่าโค้ดที่เขียนสามารถ render ออกมาเป็น HTML ได้ถูกต้องหรือเปล่า

ตัว **Network Inspector** ที่จะแนะนำให้รู้จักนี้ก็มีเอาไว้ด้วยเหตุผลคล้าย ๆ กันครับ ก็คือเพื่อให้ developer สามารถตรวจสอบได้ ว่าหน้าเว็บที่สร้างเนี่ย ได้ทำการรับส่งข้อมูลได้ถูกต้องตามที่ออกแบบเอาไว้หรือไม่ครับ เช่น เมื่อกดปุ่ม _submit_ แล้ว ข้อมูลใน form ที่กรอกเอาไว้ ถูกส่งไปที่ server ได้ถูก URL ไหม และข้อมูลอยู่ใน format ที่ถูกต้องหรือเปล่านั่นเองครับ

และเช่นเดียวกับ Code Inspector คือเราสามารถใช้เครื่องมือตัวนี้ ให้เป็นประโยชน์ในการทำ scraping ได้ครับ!

### **เว็บข่าวไทยรัฐ**
เว็บที่จะใช้เป็นตัวอย่างครั้งนี้คือเว็บข่าวของไทยรัฐ หน้านี้นะครับ [thairath.co.th/news/politic](https://www.thairath.co.th/news/politic) ครับ โดยให้เราเลื่อนหน้าจอลงมาที่ส่วนที่เขียนว่า _ข่าวอื่นๆ_ ตามในภาพด้านล่าง จะเห็นว่าหน้าเว็บนี้เลือกที่จะแสดงแค่ 12 ข่าวล่าสุดเท่านั้น โดยจะมีปุ่ม _ดูเพิ่ม_ ที่ด้านล่างให้กด เพื่อให้เว็บทำการโหลดข่าวมาแสดงเพิ่มครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w6fqjdyikk8xauudsuop.png)

ในกรณีแบบนี้ ถ้าเรายิง requests ไปที่ URL `https://www.thairath.co.th/news/politic` โดยตรง เราจะไม่ได้ข้อมูลตรงนี้ีมาครับ เนื่องจากข้อมูลมันจะยังไม่ถูกโหลดเข้ามาในตอนแรก เพราะฉะนั้นเราจะต้อง **ทำการเลียนแบบการ request ของปุ่ม "ดูเพิ่ม" (ขั้นตอนที่ 4 ใน diagram ตรงลูกศรสีแดง)** ดูว่ามันโหลดข้อมูลใหม่มาเพิ่มได้อย่างไร แล้วเราก็ทำตามนั่นเองครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/p3jqmm7nfy4tvgsjcvbk.png)
_diagram แสดงการรับส่งข้อมูล ของเว็บ Thairath กับฝั่ง server_

### **1. เปิด Network Inspector**
ขั้นแรกให้เราไปที่ [หน้าเว็บ](https://www.thairath.co.th/news/politic) ก่อนนะครับ แล้วกดปุ่ม `F12` เพื่อเปิด **Chrome DevTools** ขึ้นมา จะมีหน้าต่างด้านขวาคล้าย ๆ ในภาพขึ้นมาครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/beuqvlx4esw7g8e2ow3b.png)

จากนั้นให้เราเลือกเครื่อง **Network Inspector** โดยคลิกที่คำว่า **Network** จากแทบด้านบน (ของบางท่านอาจจะถูกย่อเอาไว้อยู่ ให้ลองเปิดหาดูจากตรงลูกศร `>>`) เมื่อเปิดมาแล้วจะเห็นเป็นหน้าว่าง ๆ แบบในรูปครับ นี่คือ Network Inspector พร้อมใช้งานแล้วครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rvn60ie7yisvl6c4p3qg.png)

### **2. ลองโหลดข้อมูล**
ตอนนี้เมื่อเปิดหน้า Inspector ขึ้นมาแล้ว ถ้าหากมีการโหลดข้อมูลอะไรก็ตามเกิดขึ้น รายละเอียดของการโหลดข้อมูลจะแสดงขึ้นมาทันทีครับ

ให้ลองกดปุ่ม "ดูเพิ่ม" เพื่อโหลดข่าวมาแสดงเพิ่มเติม ก็จะเห็นข้อมูลหลายอย่างถูกโหลดขึ้นมาพร้อมกันดังภาพ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x8fz34m3vm9uyarkabp9.png)

แถบสีด้านบนจะบอกว่าข้อมูลแต่ละชิ้น ถูกโหลดมาตอนไหน และใช้เวลาในการโหลดนานเท่าไหร่ ส่วนตารางรายการด้านล่างจะบอกว่ามันโหลดอะไรมาบ้างครับ

### **3. ดูข้อมูลของการ request**
จากในรายการจะเห็นว่าตั้งแต่ข้อมูลบรรทัดที่ 2 เป็นต้นไป จะเป็นชื่อมั่ว ๆ ทั้งนั้น ซึ่งเป็นรูปภาพของแต่ละข่าวครับ สังเกตุได้จากที่คอลัมน์ `Type` จะเขียนว่าเป็นไฟล์ `jpeg` หรือลอง double คลิกเข้าไปก็จะสามารถดูภาพแต่ละภาพได้ครับ

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

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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x6rfm75hp4ryid9rb0l0.png)

ซึ่งข้อมูลที่แสดงออกมานี้ เพียงพอให้เราสามารถนำไป request เลียนแบบเองใน python ได้ครับ ตั้งแต่ **URL คือปลายทางที่เราส่งข้อมูลไป, method คือวิธีการส่ง ซึ่งสำหรับกรณีนี้คือ GET request, headers ที่ใช้ request** หรือถ้าบางที method ที่ใช้เป็น POST request ในส่วนท้ายสุดของหน้าก็จะแสดง Form Data พ่วงมาให้ด้วยครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w7ly7zejygyqs0zu695h.png)
_ตัวอย่าง Form Data ที่จะถูกแสดงออกมาสำหรับ POST request (ไม่เกี่ยวกับเว็บนี้นะครับ เอามาให้ดูตัวอย่างเฉย ๆ)_

ก่อนอื่นเลยนะครับ ให้เราเช็คให้แน่ใจก่อนว่าอันนี้เป็น URL ที่เราต้องการจริง ๆ คือมันจะให้ข้อมูลที่เราต้องการจริง ๆ ก่อน โดยการกดไปที่แทบ _Preview_ ครับ เพื่อดูว่ามันให้ข้อมูลอะไรกลับมา

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a6ijj9mou69zgg78ahtr.png)

ก็จะเห็นว่าข้อมูลที่ถูกส่งกลับมา เป็น **JSON** ครับ โดยมี items เป็น array ของรายละเอียดแต่ละข่าวที่ถูกโหลดขึ้นมาจริง ๆ เพราะฉะนั้น อันนี้ก็คือ URL ของ API ที่เราต้องการจริง ๆ นั่นเองครับ

### **4. เลียนแบบ request**
ต่อไปเราก็เอารายละเอียดของ request ตัวนั้นมาลอกเลียนแบบใน Python นะครับ ก็จะได้เป็นตามโค้ดด้านล่าง
```python
import requests
url = 'https://www.thairath.co.th/loadmore&section=/news/politic&ts=1597188900&limit=8'

resp = requests.get(url)
data = resp.json()  # เนื่องจากผลลัพธ์ที่ออกมาเป็น JSON เราสามารถใช้ .json() เพื่อแปลงให้เป็น dict ใน Python ได้เลย
```
จะได้ข้อมูล data ออกมาเป็น dict หน้าตาประมาณนี้นะครับ
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cxrfxgpdh0ikbp1aj5du.png)
...
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aa5wiwx2dwykbleijcz7.png)

### **5. Pagination?**
เพิ่มเติมอีกเล็กน้อยครับ จากข้อก่อนเราได้ข้อมูลมาเสร็จแล้วก็จริงครับ แต่มันได้มาแค่หน้าเดียว ทีนี้ถ้าเราอยากจะเก็บให้หมดทุกหน้าเลย ก็ต้องใช้ Inspector ไปก๊อปมาทุกหน้าเรื่อย ๆ หรือ??? คำตอบคือไม่ใช่ครับ

เนื่องจากว่าเราได้รู้ตัว API ของเว็บที่ใช้เป็นตัวโหลดข้อมูลแต่ละหน้ามาเรียบร้อย **วิธีการโหลดหน้าอื่น ๆ เราก็แค่เปลี่ยนข้อมูลที่ส่งให้ถูกต้องเท่านั้นครับ**

ซึ่งวิธีการเปลี่ยนเนี่ย ก็ขึ้นอยู่กับเว็บเป้าหมายกำหนดเลยครับ บางเว็บใช้เลขหน้าแบบง่าย ๆ 1, 2, 3, 4, ... บางเว็บอาจใช้เลข ID ของ content ที่โหลดไปล่าสุดส่งต่อไปให้ เป็นต้น ลักษณะพวกนี้เรียกว่า **Pagination** ครับ

อย่างในกรณีนี้ ถ้าเราดู URL ที่เราใช้ดี ๆ ก็จะเห็นว่ามันส่งข้อมูลไป 2 อย่าง ได้แก่ `ts=1597188900` และ `limit=8` ตัว limit=8 เนี่ยเราเดาได้ใช่ไหมครับ ว่ามันหมายถึงจำนวนของข่าวที่ขอไป คือให้โหลดมาแค่ 8 ข่าวต่อครั้งก็พอ แล้ว ts ล่ะ??

คำใบ้อยู่ที่ข้อมูลที่ส่งกลับมาครับ จะเห็นว่าในแต่ละครั้งที่ request ข้อมูลที่ส่งกลับมาจะมีค่า minTs อยู่ด้วย ซึ่งจำนวนหลักและตัวเลขมันก็ดูคล้าย ๆ กับ ts ที่เราส่งไปนะครับ

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aa5wiwx2dwykbleijcz7.png)

ถ้าเราลองใช้ minTs ที่ส่งมา เอาไปยิงต่อดูตามโค้ดด้านล่างก็จะเห็นว่าจะได้ข้อมูลของหน้าถัดไปมาพอดีครับ เพราะฉะนั้นต่อให้เราไม่รู้ว่า ts คืออะไร เราก็จะพอเดาได้ว่า minTs ที่ส่งมาเนี่ย เอาไว้สำหรับให้เราสามารถโหลดข้อมูลของหน้าต่อ ๆ ไปได้นั่นเองครับ
```python
url = 'https://www.thairath.co.th/loadmore&section=/news/politic&ts=1597149600&limit=8'
resp = requests.get(url)
data2 = resp.json()
```
_note: จริง ๆ แล้ว ts ก็คือ timestamp นั่นเองครับ เห็นตัวย่อ และเห็นตัวแรกแล้ว หลาย ๆ ท่านน่าจะเดาได้ตั้งแต่แรกใช่ไหมครับ 55_

เพราะฉะนั้นเราสามารถที่จะ automate การดึงข่าวมาหลาย ๆ หน้าดังโค้ดด้านล่าง
```python
import requests

def get_url(ts=None):
    if ts is None:
        return 'https://www.thairath.co.th/loadmore&section=/news/politic&limit=8'

    return f'https://www.thairath.co.th/loadmore&section=/news/politic&ts={ts}&limit=8'

url = get_url()                   # ใช้ url เริ่มต้น คือไม่ต้องส่งค่า ts ไป
for i in range(10):               # วนไป 10 หน้า
    resp = requests.get(url)      # โหลดข้อมูล
    data = resp.json()            # แปลงเป็น dict

    for x in data['items']:       # ไล่ print title ของแต่ละข่าว
        print(x['title'])

    url = get_url(data['minTs'])  # สร้าง url ใหม่ โดยส่ง ts ให้มีค่าเท่ากับ minTs
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rt0ubummrv73d5y2ja54.png)

# **Summary**
จบไปแล้วนะครับ 5 parts สำหรับพื้นฐานทั้งหมดในการทำ Web Scraping ด้วย Python ถ้าเข้าใจทั้งหมดก็น่าจะสามารถดึงข้อมูลจากเว็บส่วนใหญ่ได้เยอะแล้วครับ แล้วก็อย่าลืมติดตามบทความต่อ ๆ ไปเกี่ยวกับการ scraping ด้วยนะครับ เดี๋ยวเรามาลองวิชากับเว็บจริง ๆ กัน

และก็ขอย้ำอีกครั้ง <เหมือนที่ย้ำอยู่ตลอดตั้งแต่ part แรก> ก็คือบทความนี้ทำขึ้นเพื่อการศึกษานะครับ ในการนำไปใช้จริง ควรคำนึงถึงความเหมาะสม และไม่ทำให้ผู้อื่นเดือดร้อนนะครับ

ถ้ามีเรื่องไหนที่สนใจเพิ่มเติมสามารถ comment เอาไว้ได้นะครับ

FB Page: [Copy Paste Engineer](https://www.facebook.com/CopyPasteEng)

<center>**- ขอบคุณที่อ่านครับ -**</center>

**part อื่น ๆ ใน series** 
\- [part 1: การดูดข้อมูลเบื้องต้น ด้วย Python](https://dev.to/copypasteengineer/python-web-scraping-part-1-python-49ce) 
\- [part 2: Chrome's Code Inspector](https://dev.to/copypasteengineer/python-web-scraping-part-2-chrome-s-code-inspector-3ok6) 
\- [part 3: เทคนิคการ extract ข้อมูลด้วย XPath](https://dev.to/copypasteengineer/python-web-scraping-part-3-extract-xpath-18h)
\- [part 4: ทำไมถึง scrape บางเว็บไม่ได้??? 7 เทคนิคง่าย ๆ ให้ scrape เว็บส่วนใหญ่ได้ลื่นปรื๊ด](https://dev.to/copypasteengineer/python-web-scraping-part-4-scrape-7-scrape-4ko4)
\- [part 5: วิธีเลียนแบบการรับส่งข้อมูลของเว็บเป้าหมาย](https://dev.to/copypasteengineer/python-web-scraping-part-5-47nf)