---
title: "หัด Python สำหรับคนเป็น Excel : ตอนที่ 7 – Web Scraping ด้วย Beautiful Soup"
url: https://www.thepexcel.com/python-excel-07-web-scraping-beautiful-soup/
type: post
date: 2020-11-28
updated: 2022-04-22
author: Sira Ekabut
tags: [python, web scraping]
---

# หัด Python สำหรับคนเป็น Excel : ตอนที่ 7 – Web Scraping ด้วย Beautiful Soup

[ตอนที่แล้วเราพูดถึง Library เจ๋งๆ ไปหลายตัว](https://www.thepexcel.com/python-excel-06-module-packages/) ในตอนนี้ผมจะขอพูดถึงการใช้ Python ดึงข้อมูลจาก website กันครับ ซึ่งเครื่องมือที่จะใช้ชื่อว่า [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) นั่นเอง ซึ่งบทความนี้อาจไม่ค่อยเกี่ยวกับการทำงานใน Excel เท่าไหร่ แต่เรียกว่ามาอุดจุดอ่อนของการใช้ Excel จะดีกว่า เพราะใน Excel จะดึงข้อมูลจากเว็บได้ไม่ค่อยดี แม้จะมีฟังก์ชัน WEBSERVICE, FILTERXML หรือแม้จะใช้ Power Query ก็ตาม ก็ยังไม่ค่อย Work เท่าการใช้ Python

 

## การเรียกใช้งาน Beautiful Soup

 

เวลาใช้งาน Beautiful Soup เรามักจะใช้กับ module อีกตัวที่เรียกว่า requests เพื่อจะขอข้อมูลจาก website กลับมา โดยจะถูกใช้ร่วมกันประมาณนี้

 

```
import requests
from bs4 import BeautifulSoup

myURL="https://www.thepexcel.com/category/highlights/"  #ใส่เว็บที่เราต้องการ

r = requests.get(myURL)  #ขอข้อมูลจาก url ที่กำหนดเก็บไว้ใน object r (request)
s = BeautifulSoup(r.content, "html.parser")  #แปลงโครงสร้าง html ใน content เข้า object s (soup)
```

 

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

 ![Beautiful Soup](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-005-1024x560.png) 

## วิธีเลือกเอา element ที่ต้องการ

 

ทีนี้เจ้า Beautiful Soup เนี่ย สามารถดึงข้อมูลเฉพาะส่วนที่เราต้องการจาก html ได้ โดยเราจะต้องเข้าใจก่อนว่าเว็บไซต์ต่างๆ ที่เราเห็นนั้นจะถูกเก็บข้อมูลในลักษณะของภาษา HTML ซึ่งมีลักษณะที่เป็น Tag ลงไปเป็นชั้นๆ โดยจะมีการเปิด Tag และ Tag ทุกครั้ง เช่น

 

```
<Tagแม่>
   <Tagลูก> Content1 </Tagลูก>
   <Tagลูก> Content2 </Tagลูก>
   <Tagลูก> Content3 </Tagลูก>
</Tagแม่>
<Tagแม่>
   <Tagลูก> Content4 </Tagลูก>
   <Tagลูก> Content5 </Tagลูก>
   <Tagลูก> Content6 </Tagลูก>
</Tagแม่>
```

 

เราสามารถใช้ Beautiful Soup เลือกข้อมูลได้ด้วยวิธีเขียนแบบนี้

 

```
s.element1   #เข้าไปใน element1 อันแรก
```

 

หรือ

 

```
s.find('element1')  #เข้าไปใน element1 อันแรก
```

 

เช่น

 

```
s.Tagแม่   #เข้าไปใน Tagแม่ อันแรก

#จะได้แบบนี้
#<Tagแม่>
#   <Tagลูก> Content1 </Tagลูก>
#   <Tagลูก> Content2 </Tagลูก>
#   <Tagลูก> Content3 </Tagลูก>
#</Tagแม่>
```

  

### ถ้าจะเข้าไปชั้นที่ลึกขึ้น

 

```
s.element1.element2   #เข้าไปใน element1 อันแรก -> element2 อันแรก
```

 

หรือ

 

```
s.find('element1').find('element2')  #เข้าไปใน element1 อันแรก -> element2 อันแรก
```

 

เช่น

 

```
s.Tagแม่.Tagลูก   #เข้าไปใน Tagแม่ อันแรก -> เช้าไป Tag ลูกอันแรกในนั้น

#จะได้แบบนี้
#   <Tagลูก> Content1 </Tagลูก>
```

 

เช่น ถ้าลองกับเว็บของผม จะเป็นดังนี้

 

```
s.body.h2  #ได้ element h2 อันแรกสุด ใน body ของเพจ
s.body.p  #ได้ element p อันแรกสุด ใน body ของเพจ
```

 ![1](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-006x-1024x163.png) 

### ดึงข้อมูลใน element ออกมา

 

ถ้าใน element นั้นมีข้อมูลหลายส่วน เช่น ใน h2 ของผมมีข้อมูลเยอะแยะ เราสามารถดึงบางส่วนมากได้ เช่น

 

```
myText=s.body.h2.get_text()  #ได้ข้อความข้างใน
myLink=s.body.h2.a.get('href')  #ได้ link ข้างใน ของ a ตัวแรกสุด ใน h2 แรกสุด
myImageURL=s.body.img.get('src')  #ได้ url ของรูปอันแรก ข้างใน body

print(myText)
print(myLink)
print(myImageURL)
```

 ![2](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-007-1024x337.png) 

## วิธีเลือกเอา element ที่ต้องการทั้งหมดทุกตัว

 

```
s('element1')   #เอา element1 กลับมาทุกตัวที่เจอ
```

 

หรือ

 

```
s.find_all('element1')  #เอา element1 กลับมาทุกตัวที่เจอ
```

 

เช่น

 

```
s.body.find_all('h2')  # ได้ h2 ทุกตัว
```

 ![3](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-008-1024x336.png) 

เราสามารถเข้าถึง item ย่อยลำดับที่ต้องการได้ ด้วยการใส่เลข index ในวงเล็บเหลี่ยม คล้ายกับ list ปกติเลย (แปลว่าเราวน loop ได้) เช่น

 

```
s.body.find_all('h2')[1]  # ได้ h2 index ที่ๅ ซึ่งคือตัวที่2
```

 ![4](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-009-1024x99.png) 

แต่เราไม่สามารถ get_text() ตรงๆ จากการ find_all ได้ มันจะขึ้น error

 ![5](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-010-1024x259.png) 

เราต้องแก้ไขด้วยการ เข้าไป get_text() ในแต่ละ item ของ h2 เลย **ด้วยการวน loop เพื่อใช้คำสั่ง get_text()** **กับ item ข้างในทีละตัว**เป็นต้น

 

```
for i in s.body.find_all('h2'):
  print(i.get_text())
```

 ![6](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-011-1024x573.png) 

## วิธีเลือกเอา element ที่มีลักษณะที่กำหนด

 

ก่อนหน้านี้ ผมได้ h2 กลับมาเยอะเกินที่ต้องการ เพราะดันได้ h2 ของ widget บทความล่าสุดด้านขวากลับมาด้วย แต่**ที่ผมต้องการคือ h2 เฉพาะของหน้าหลักจริงๆ** ดังนั้นผมจะต้องใช้วิธีค้นหาเฉพาะตัวที่มีลักษณะที่ผมต้องการเท่านั้น

 

ด้วยคำสั่ง **attrs= dictionary ลักษณะที่ต้องการ **

 

เช่น ผมต้องการ h2 ที่มี class เป็น post-title entry-title is-size-3 เท่านั้น (ซึ่งรู้จากการใช้ google chrome inspect เอา)

 ![7](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-012-1024x517.png) 

ผมก็สามารถเขียนได้ดังนี้

 

```
for i in s.body.find_all('h2', attrs={'class':'post-title entry-title is-size-3'}): 
#เอาเฉพาะ h2 class ที่ต้องการ
  print(i.get_text())
```

 ![8](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-014-1024x298.png) 

## ลองค้นหาข้อมูลใน Pantip Hitz

 

เดี๋ยวลองดึงข้อมูล topic ในหน้าเว็บของ [Pantip Hitz](https://pantip.com/home/hitz) ครับ

 

```
import requests
from bs4 import BeautifulSoup
myURL="https://pantip.com/home/hitz"  #ใส่เว็บที่เราต้องการ

r = requests.get(myURL)  #ขอข้อมูลจาก url ที่กำหนดเก็บไว้ใน object r (request)
s = BeautifulSoup(r.content, "html.parser")  #แปลงโครงสร้าง html ใน content เข้า object s (soup)
```

 ![9](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-015-1024x440.png) 

จะเห็นว่าข้อมูลอยู่ใน tag a ซึ่งอยู่ใน h2 อีกที ดังนั้นเราจะดึงข้อมูลได้โดยการใส่คำสั่งว่า

 

```
for i in s.find_all('h2'): #เอาเฉพาะ h2 class ที่ต้องการ
  #เอา text ใน a ที่มี class ตามที่ต้องการ
  print(i.find('a',attrs={'class':'gtm-hitz-page1'}).get_text()) 
```

 ![10](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-016-1024x578.png) 

สมมติว่าผมอยากได้ hashtag ของแต่ละ Topic เราจะดึงข้อมูลยังไงดี? มาดูกัน

 

ซึ่งก่อนอื่นผมก็ต้องไปหาก่อนว่ามันอยู่ใน Tag อะไรยังไง?

 ![11](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-017-1024x463.png) 

พอไปสำรวจดูพบว่า อยู่ใน a ที่อยู่ใน div class ชื่อว่า pt-list-item__tag อีกที

 

ดังนั้นถ้าลองดึงข้อมูลมาจะเป็นดังนี้

 

```
for i in s.find_all('div',attrs={'class':'pt-list-item__tag'}): #เอาเฉพาะ h2 class ที่ต้องการ
  for tag in i.find_all('a'):
    print(tag.get_text()) 
```

 ![12](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-018-1024x635.png) 

จะพบว่าแม้จะดึงข้อมูลมาได้ แต่มันสะเปะสะปะมากเลย และเกิดปัญหาตามมาคือ ผมไม่รู้ว่ารู้ว่า Tag ไหนเป็นของ Topic ไหน

 

ทีนี้จะทำไงดี?

 

## การจัดกลุ่มข้อมูลที่ดึงมาได้

 

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

 

ก่อนจะทำการดึงข้อมูลหลายๆ เรื่องพร้อมกัน **เราจะต้องทำให้มั่นใจว่า topic และ tag ที่ดึงมามันสอดคล้องกัน **โดยไม่ใช่ดึงรอบแรกได้ข้อมูล 10 อันมาชุดนึง แต่พอดึงอีกที ได้ข้อมูล 10 อันที่ไม่ตรงกัน (เช่น มีการอัปเดทข้อมูลชุดใหม่มา ลำดับอาจจะเลื่อนได้)** ดังนั้นต้องไปเช็คดูก่อนว่าจริงๆ แล้วทั้งคู่อยู่ใน item แม่อะไรกันแน่** แล้วเข้าไปไล่ดึงสิ่งที่ต้องการใน item นั้นๆ มาให้ครบทีเดียว

 ![13](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-020-1024x398.png) 

ปรากฏว่าจริงๆ แล้วทั้ง Topic และ Tag อยู่ใน li ที่มีชื่อ class ว่า pt-list-item ทั้งคู่ ดังนั้น ผมจะต้องวน loop ตามแต่ละ item ของ li แต่ละตัว แล้วภายในนั้นผมค่อยเอา Topic(H2) ในนั้น (มีอันเดียว) และ Tag (a) ที่ต้องการมาให้หมด(มีหลายอัน)

 

โดยผมจะดึงข้อมูล มาใส่ใน Dictionary เปล่าที่สร้างไว้ จนครบทุก item

 

```
pantipDict={} #สร้าง Dict เปล่าๆ

Myli=s.find_all('li',attrs={'class':'pt-list-item'})
for num in range(len(Myli)):
  tempDict={} #สร้าง dict ชั่วคราว
  topicText=Myli[num].find('h2').get_text()
  tempDict['Topic']=topicText
  tagList=Myli[num].find('div',attrs={'class':'pt-list-item__tag'})
  tempList=[]
  for tag in tagList.find_all('a'):  #หา tag แต่ละตัว
    tempList.append(tag.get_text())
  tempDict['Tag']=tempList #ใส้ tempList เข้าไปใน dict ตัวจริง
  pantipDict[num]=tempDict #ใส้ tempdict เข้าไปใน dict ตัวจริง

pantipDict
```

 ![14](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-022-1024x651.png) 

## แปลงเป็น Data Frame และ Export เป็น CSV

 

ถ้าหากเรารู้สึกว่า Dictionary มันดูยาก เราก็สามารถ convert ให้กลายเป็น Data Frame ได้แบบง่ายๆ เลย ดังนี้

 

```
import pandas as pd
pantipDF=pd.DataFrame.from_dict(pantipDict, orient='index')
pantipDF
```

 ![15](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-023-1024x605.png) 

และถ้าจะ Export Data Frame เป็น csv ก็สามารถทำได้ง่ายๆ เช่นกัน ดังนี้

 

```
from google.colab import files
pantipDF.to_csv('pantipHitz.csv') 
files.download('pantipHitz.csv')
```

 ![16](https://www.thepexcel.com/wp-content/uploads/2020/11/Python07-025-1024x512.png) 

## จบตอน

 

หวังว่าเพื่อนๆ จะพอเห็นภาพการทำ Web Scraping แบบเบื้องต้นด้วย Beautiful Soup นะครับ การเขียน code บางอันในบทความนี้ผมอาจไม่ได้ใช้วิธีที่เหมาะสมที่สุด ถ้าอย่างไรใครใช้ Python เป็นช่วยแนะนำผมได้เลยนะครับ

  

## สารบัญ Series Python

---

_Source: [https://www.thepexcel.com/python-excel-07-web-scraping-beautiful-soup/](https://www.thepexcel.com/python-excel-07-web-scraping-beautiful-soup/)_
