เล่นกับ Big Data ภาษาไทย ต้องเข้าใจ Unicode, Part 2

เล่นกับ Big Data ภาษาไทย ต้องเข้าใจ Unicode, Part 2

15 June 2020

บทความนี้เป็นส่วนหนึ่งของมินิซีรีส์ “เล่นกับ Big Data ภาษาไทย ต้องเข้าใจ Unicode” โดยผู้อ่านสามารถเข้าถึงภาคแรกของบทความนี้ได้ที่นี่ครับ : เล่นกับ Big Data ภาษาไทย ต้องเข้าใจ Unicode : Part 1

ในบทความที่แล้วนั้น เราได้ทำความรู้จักกับ Unicode ซึ่งเป็นมาตรฐานสากลเพื่อช่วยให้การสื่อสารด้วยข้อความภาษาต่าง ๆ ทั่วโลกเกิดขึ้นได้อย่างเสมอภาคและเป็นระบบ และยังได้เรียนรู้กับ ASCII representation, Unicode representation, encoding and decoding สำหรับบทความนี้ เราจะดูกันว่า ความรู้เบื้องต้นเหล่านี้สามารถช่วยเราแก้ปัญหาต่าง ๆ ในสถานการณ์จริงได้อย่างไร ผมขอเสนอสองตัวอย่าง คือ เวลาเราจัดการกับ JSON เพื่อเขียนข้อมูลลงเป็นไฟล์ และ เวลาเราสร้าง BeautifulSoup object เพื่อทำ web scraping ครับ

จัดการกับ JSON เพื่อเขียนข้อมูลลงเป็นไฟล์

สมมติว่าเรามี JSON data ที่เรียบง่ายมากอันหนึ่งที่เราต้องการเขียนเก็บลงเป็นไฟล์ (serialization) ตาม Code Block 1

c = {'ไม่ระบุเพศ': 205, 'หญิง': 1040, 'ชาย': 510}

# dict
type(c)
import json
with open('test.json', 'w') as json_file:
    json.dump(c, json_file)

ถ้าเราลองแปลง c ไปเป็น stream ให้ file pointer เขียนข้อมูลลงไฟล์ด้วยฟังก์ชัน json.dump ดูตาม Code Block 2 เราจะได้ผลลัพธ์ในไฟล์ test.json ดังนี้

{"u0e44u0e21u0e48u0e23u0e30u0e1au0e38u0e40u0e1eu0e28": 205,
"u0e2bu0e0du0e34u0e07": 1040,
"u0e0au0e32u0e22": 510}

ทำใมเราได้ข้อความที่อ่านไม่ออก? เนื่องจาก json.dump มีพฤติกรรมตั้งต้น (default) ของฟังก์ชันนี้ ว่าจะแปลง JSON data เป็นข้อความ (ให้ file pointer) ที่มีแต่ตัวอักขระ ASCII เท่านั้น นั่นหมายความว่า ตัวอักขระประเภทอื่น ๆ จะถูก “escaped” หรือ แปลงให้อยู่ในรูปที่เขียนได้ด้วยอักขระ ASCII (เราแปลง “ช” เป็น “\u0e0a” ทุกตัวเป็นอักขระ ASCII หมด ทั้ง “”, “u”, “0”, “e”, “a”)

แต่เดี๋ยวก่อน ผู้อ่านสังเกตเห็นไหมครับ ว่าจริง ๆ แล้วเราอ่านผลของ json.dump ข้างต้นออก? ตัวอักษรภาษาไทยต่าง ๆ ไม่ได้อยู่ในกลุ่มอักขระ ASCII จึงถูกแปลงให้อยู่ในรูปของ Unicode code points นั่นเอง! ในบทความภาคแรก เราได้เรียนรู้ถึงเครื่องมือที่ช่วยให้เราดู Unicode code points ของตัวอักษรภาษาไทยต่าง ๆ ได้ ดังเช่นที่เราแปลง “ช” เป็น U+0E0A หรือ “\u0e0a” หากเขียนตามแบบที่ json.dump ใช้

รู้อย่างนี้แล้ว วิธีแก้ไขก็คือ เราปรับพารามิเตอร์ให้ json.dump ให้สนับสนุนการ serialize ที่มีตัวอักขระนอกเหนือจาก ASCII ด้วย แล้วเราก็เลือกเปิดไฟล์สำหรับการเขียนไฟล์ (writing operation) ที่สนับสนุน Unicode (ซึ่งเราจะใช้ UTF-8) ตาม Code Block 3 เราก็จะได้ผลลัพธ์ที่ต้องการแล้วครับ

import json
with open('test.json', 'w', encoding='utf8') as json_file:
    json.dump(c, json_file, ensure_ascii=False)

แล้วเราก็จะได้ผลลัพธ์ในไฟล์ test.json ใหม่ดังนี้ :

{"ไม่ระบุเพศ": 205, "หญิง": 1040, "ชาย": 510}

เวลาเรา load JSON file กลับเข้ามาใน Python environment ก็สามารถทำได้ง่าย ๆ ดัง Code Block 4 ครับ

with open('test.json', 'r', encoding='utf8') as f:
    read_json = json.load(f)

ในการกำหนด UTF-8 ในฟังก์ชัน open นี้ บางครั้งก็เป็น default มาให้เลย แต่ขึ้นอยู่กับบาง OS / platform ค่าตั้งต้นอาจจะไม่ได้ใช้ UTF-8 ก็ได้ ถ้าจะให้แน่ใจ เราควรกำหนดลงไปชัด ๆ เลยตาม Code Block 3 และ 4 ดีกว่าครับ แต่หากใครสงสัยว่าระบบของตนเองใช้ default encoding อะไรอยู่ สามารถตรวจสอบได้ด้วยคำสั่ง import locale; locale.getpreferredencoding() ใน Python ครับ

สร้าง BeautifulSoup object เพื่อทำ Web Scraping จากข้อมูลภาษาไทย

สำหรับผู้อ่านที่ยังไม่คุ้นเคยกับพื้นฐาน web scraping ผู้อ่านสามารถศึกษาบทความที่สอนวิธีการทำ web scraping เบื้องต้นได้ที่บทความ ดึงข้อมูลจากเว็บไซต์ (Data Scraping) ด้วย Python Part 1 ครับ

เวลาที่เราทำ web scraping เราอาจจะต้องดึงข้อมูลจากเว็บที่มีข้อความที่ถูกเข้ารหัสในรูปแบบต่าง ๆ ที่ไม่ใช่ ASCII แล้วเวลาที่เราได้ข้อมูลที่ดึงจากหน้าเว็บมา เราอาจจะได้มาเป็น bytes sequence ที่มีรูปร่างหน้าตาดังตัวอย่างใน Code Block 5

html_content = b'<html><head>
<title>xe0xb8xa3xe0xb8xabxe0xb8
xb1xe0xb8xaaxe0xb8xabxe0xb8x99
xe0xb9x88xe0xb8xa7xe0xb8xa2xe0
xb8x87xe0xb8xb2xe0xb8x99xe0xb8
x9axe0xb8xa3xe0xb8xb4xe0xb8x81
xe0xb8xb2xe0xb8xa3xe0xb8xaaxe0
xb8xb8xe0xb8x82xe0xb8xa0xe0xb8
xb2xe0xb8x9e xe0xb8x81xe0xb8xa2
xe0xb8x9c.</title></head><body>
<p class="title">
<b>Very Important Data!
xe0xb8x84xe0xb9x89xe0xb8x99xe0
xb8xabxe0xb8xb2xe0xb8x82xe0xb9
x89xe0xb8xadxe0xb8xa1xe0xb8xb9
xe0xb8xa5xe0xb8xa3xe0xb8xabxe0
xb8xb1xe0xb8xaaxe0xb8xabxe0xb8
x99xe0xb9x88xe0xb8xa7xe0xb8xa2
xe0xb8x87xe0xb8xb2xe0xb8x99</b>
</p></body></html>'

เราต้องการนำข้อมูล html_content นี้ไปสร้าง BeautifulSoup object ซึ่งในขณะนี้ อ่านไม่ออกเลย ? แต่จากบทความ Part 1 สังเกตว่าข้อมูลนี้อยู่ในรูปแบบ bytes (สังเกตตัว b หน้าสุด ในค่าของ html_content) แสดงว่าต้องถูกเข้ารหัส (encoded) อยู่แน่ ๆ ? ถ้าเราหาตัวถอดรหัส (decoder) ที่ถูกต้องได้ เราก็จะมีข้อความที่อ่านออก ไปทำการวิเคราะห์ต่อได้

ตอนนี้เรารู้จัก decoder หลายตัวแล้ว เช่น UTF-8, UTF-32, ASCII, แต่นอกจากนี้ยังมีเช่น ISO-8859-1 ที่บทความนี้จะไม่ขอกล่าวถึงในรายละเอียด แต่ ณ ตอนนี้ เราสามารถลองสิ่งต่อไปนี้

# UnicodeDecodeError: can't decode bytes in position 0-3 ...
html_content.decode("utf-32")

# Success!
html_content.decode()

เราจะพบว่า UTF-8 สามารถ decode bytes sequence นี้ออกมาได้สำเร็จ! นั่นหมายความว่า เราสามารถสร้าง BeautifulSoup เพื่อวิเคราะห์ข้อมูลที่ได้มาดัง Code Block 7 และจะได้ผลลัพธ์ออกมาดัง Code Block 8

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_content, 'lxml', from_encoding='UTF-8')
print(soup.prettify())
<html>
 <head>
  <title>
   รหัสหน่วยงานบริการสุขภาพ กยผ.
  </title>
 </head>
 <body>
  <p class="title">
   <b>
    Very Important Data! ค้นหาข้อมูลรหัสหน่วยงาน
   </b>
  </p>
 </body>
</html>

จริง ๆ แล้ว ใน BeautifulSoup จะมีความฉลาดอยู่คือ ถ้าเราลองสร้าง

soup = BeautifulSoup(html_content, 'lxml')

โดยไม่ใบ้ encoding ให้เลย BeautifulSoup ก็สามารถที่จะเดา encoding ที่ใช้ และอาจจะถอดรหัสได้ถูกต้องอยู่ดี แต่การที่เราเข้าใจข้อมูล bytes sequence ที่เราได้มา และสามารถลองผิดลองถูกเพื่อหาการถอดรหัสที่ถูกต้องได้เอง ก็จะทำให้เราสามารถ debug ข้ามผ่านขั้นตอนการเตรียมข้อมูลภาษาไทยนี้ก่อนทำการวิเคราะห์ต่อไปได้อย่างรวดเร็ว

บทสรุป

Unicode website
รูปที่ 1 เว็บไซต์ทางการของ Unicode (home.unicode.org)

Unicode เป็นมาตรฐานสากลที่รองรับการสื่อสารกันทางข้อความด้วยภาษาต่าง ๆ ทั่วโลก ในมินิซีรีส์นี้ เราได้เรียนรู้ถึงหลักการทำงานเบื้องต้น, วิธีที่ Unicode จัดระเบียบอักขระให้เก็บเลขอัตลักษณ์ได้โดยไม่ซ้ำกัน, การแสดงผล การเข้ารหัสและถอดรหัสของ Unicode, มาตรฐานที่เกี่ยวข้องอย่าง ASCII, และสถานการณ์ที่ความรู้เหล่านี้มีประโยชน์ในการใช้งานจริงบน Python

การสร้างความคุ้นเคยกับ concepts เหล่านี้ จะทำให้เราเล่นกับข้อมูลประเภทข้อความผ่าน APIs และ libraries ที่เกี่ยวข้องได้อย่างชำนาญขึ้น สามารถ debug ผลลัพธ์ที่ไม่เป็นไปตามคาดได้รวดเร็วขึ้น เพราะเมื่อเราเจอกับข้อมูลที่อ่านไม่ออก เราจะพอเดาได้ว่ามันคืออะไร (เช่น encoding ข้อความที่อยู่ในชนิดตัวแปร bytes, หรือข้อมูลกำลังอยู่ในรูปแบบ code points) ผมหวังว่าบทความนี้จะช่วยสร้างพื้นฐานที่สำคัญส่วนหนึ่งสำหรับการวิเคราะห์ข้อมูล Big Data ภาษาไทย และข้อมูลข้อความอื่น ๆ ต่อไปครับ ?

Former-Editor-in-Chief at BigData.go.th and Senior Data Scientist at Government Big Data Institute (GBDi )

แบ่งปันบทความ

กลุ่มเนื้อหา

แท็กยอดนิยม

แจ้งเรื่องที่อยากอ่าน

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

PDPA Icon

We use cookies to optimize your browsing experience and improve our website’s performance. Learn more at our Privacy Policy and adjust your cookie settings at Settings

Privacy Preferences

You can choose your cookie settings by turning on/off each type of cookie as needed, except for necessary cookies.

Accept all
Manage Consent Preferences
  • Strictly Necessary Cookies
    Always Active

    This type of cookie is essential for providing services on the website of the Personal Data Protection Committee Office, allowing you to access various parts of the site. It also helps remember information you have previously provided through the website. Disabling this type of cookie will result in your inability to use key services of the Personal Data Protection Committee Office that require cookies to function.
    Cookies Details

  • Performance Cookies

    This type of cookie helps the Big Data Institute (Public Organization) understand user interactions with its website services, including which pages or areas of the site are most popular, as well as analyze other related data. The Big Data Institute (Public Organization) also uses this information to improve website performance and gain a better understanding of user behavior. Although the data collected by these cookies is non-identifiable and used solely for statistical analysis, disabling them will prevent the Big Data Institute (Public Organization) from knowing the number of website visitors and from evaluating the quality of its services.

  • Functional Cookies

    This type of cookie enables the Big Data Institute (Public Organization)’s website to remember the choices you have made and deliver enhanced features and content tailored to your usage. For example, it can remember your username or changes you have made to font sizes or other customizable settings on the page. Disabling these cookies may result in the website not functioning properly.

  • Targeting Cookies

    "This type of cookie helps the Big Data Institute (Public Organization) understand user interactions with its website services, including which pages or areas of the site are most popular, as well as analyze other related data. The Big Data Institute (Public Organization) also uses this information to improve website performance and gain a better understanding of user behavior. Although the data collected by these cookies is non-identifiable and used solely for statistical analysis, disabling them will prevent the Big Data Institute (Public Organization) from knowing the number of website visitors and from evaluating the quality of its services.

Save settings
This site is registered on wpml.org as a development site. Switch to a production site key to remove this banner.