Jan 26, 2011
Wednesday, January 26, 2011

Workshop AutoIt :1 ดึงข้อมูลจากเว็บในส่วนที่เราต้องการ

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

     เช่นเดียวกับการฝึกเขียนโปรแกรม เมื่อคุณเข้าใจวิธีการทำงานของคำสั่งพื้นฐาน รู้จัก If Then Else, While, For, Do, Select, Switch  แต่ไม่สามารถเพาะสร้างแนวคิดในเชิงตรรกะที่ซับซ้อน ความก้าวหน้าในการเรียนรู้ก็จะลดต่ำลงเรื่อยๆ (เป็นเรื่องแปลกแต่ก็เป็นเรื่องจริง) วิธีแก้ปัญหาดังกล่าว ทำได้ง่ายคือ การฝึกสร้างโปรแกรมอะไรขึ้นมาก็ได้สักโปรแกรมหนึ่ง โดยอาศัยพื้ันฐานความรู้ที่ศึกษามา การเขียนโปรแกรมก็ถือว่าเป็นทักษะอย่างหนึ่ง ยิ่งทำยิ่งคล่อง เกิดความชำนาญจนเชี่ยวชาญ รู้แล้วไม่ทำก็รู้อยู่แค่นั้น แถมปล่อยทิ้งไว้นานๆ ก็จะลืมไปหมดสิ้น

     ผมจะยกตัวอย่างการสร้างโปรแกรมธรรมดาคือ โปรแกรมดึงข้อมูลจากเว็บในส่วนที่เราต้องการ มาเป็นตัวอย่าง โดยในตัวอย่างนี้ผมจะใช้เว็บ http://th.wikipedia.org โดยดึงเอาข้อความในตาราง “รายชื่อคำในภาษาไทยที่มักเขียนผิด” ออกมาบันทึกเป็นไฟล์ข้อความธรรมดา

     เพื่อให้เข้าใจก่อนเริ่มทำ ในบทความนี้ผมจะไม่พูดถึงเรื่องพื้นฐานทั่วไป เช่น if คืออะไร while คืออะไร เพราะเคยอธิบายมาแล้ว คำสั่งอื่นก็จะไม่อธิบายจนหมด หากคุณสงสัยคำสั่งไหนก็คัดลอกคำสั่งไปวางไว้ในโปรแกรม SciTE Script Editor แล้วกดปุ่ม F1 เพื่อดูคำอธิบายและตัวอย่างการใช้คำสั่ง ที่ผ่านมาในบทความ AutoIt ผมมักจะอธิบายแทบทุกบรรทัดคำสั่ง การทำแบบนี้ด้วยเจตนาดีต้องการให้ผู้อ่านเข้าใจง่ายๆ แต่มันกลับส่งผลร้ายออกมาอย่างชัดเจน เพราะการอธิบายหมดทุกอย่างก็ไม่เหลืออะไรให้คิดต่อ ไม่เหลืออะไรให้สงสัย บางครั้งผู้อ่านอาจหลงผิดคิดว่าเข้าใจหมด ทั้งที่ไม่ได้เข้าใจอะไรเลย รอแค่การเอาป้อนให้อย่างเดียว หรือพอจะคิดทำอะไรที่แตกต่างไปจากตัวอย่างก็เกิดอาการใบ้กิน ทำไม่ได้  บางคนอาจเกิดความรู้สึกว่าอ่านบทความทำไมมันง่าย แต่พอลองทำจริงๆ มันยากไปไม่เป็นไม่รู้จะเริ่มตรงไหน

เปิดเว็บสำรวจก่อนทำ
     เมื่อจะเริ่มเขียนโปรแกรมที่เกี่ยวข้องกับเว็บหรืออะไรอื่น ก็ต้องทำความเข้าใจในสิ่งที่เราจะเขียนเสียก่อน ลองเปิดเว็บ http://th.wikipedia.org ขึ้นมาดู ข้อความที่เราจะดึงออกมาคือคอลัมน์ คำที่เขียนถูก และ มักเขียนผิดเป็น


     พอเลื่อนลงมาอีกหน่อยก็พบว่าคำเรียงตามตัวอักษร และมีการสร้างตารางใหม่ตามอักษรแต่ละตัวโดยใช้แท็ก Table (ภาษา HTML ในการเขียนเว็บ)จุดประสงค์ในการเขียนสคริปต์ของผมคือ ต้องการแค่ดึงเอาข้อความเขียนถูกและมักเขียนผิดมาเรียงต่อกันเท่านั้น โดยไม่เอาตารางมาด้วย การที่ตารางในเว็บแยกตามตัวอักษรจะทำให้มีข้อความ คำที่เขียนถูก มักเขียนผิดเป็น ปรากฏขึ้นมาเวลาใช้คำสั่งดึงข้อมูลออกมาด้วย  ปัญหาต่างๆ เหล่านี้สามารถแก้ไขได้ด้วยการเขียนสคริปต์ตามตัวอย่างในหัวข้อถัดไป


เริ่มเขียนสคริปต์ AutoIt ดึงข้อมูลจากเว็บ
    เริ่มแรกก็เขียนสคริปต์เปิดหน้าเว็บ ด้วยความที่ URL ค่อนข้างยาวผมจะใช้ตัว  & _ (Line Continuation) ในการเชื่อม URL ที่แยกไปแต่ละบรรทัด

#include <IE.au3>
$oIE = _IECreate ("http://th.wikipedia.org/wiki/"
& _
"%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
"8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
"%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
"99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
"%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
"E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
"%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
"E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

ตอนนี้ก็ได้สคริปต์เปิดหน้าเว็บมาแล้ว จากนั้นก็เขียนสคริปต์ต่อเพื่อตรวจดูว่าในเว็บมีตารางจำนวนกี่ตารางกันแน่ สรุปแล้วในหน้าเว็บมีตาราง 41 อัน (เริ่มต้นที่ 0)

#include <IE.au3>
$oIE = _IECreate ("http://th.wikipedia.org/wiki/" & _
"%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
"8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
"%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
"99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
"%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
"E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
"%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
"E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

  $oTable = _IETableGetCollection ($oIE)
  $iNumTables = @extended
MsgBox(0, "Table Info", "There are " & $iNumTables & " tables on the page")


     ลองใช้คำสั่งดึงเอาตารางแต่ละอันมาดู และเนื่องจากคำสั่ง _IETableGetCollection  จะดึงเอาข้อมูลไปเก็บไว้ในอาเรย์อัตโนมัติ จึงต้องเพิ่มคำสั่ง #include <Array.au3> เข้ามาด้วย เพื่อใช้คำสั่ง  _ArrayDisplay ในการแสดงข้อมูลในอาเรย์ จากนั้นก็ไล่ดูทีละตารางว่ามีข้อมูลอะไรอยู่


#include <IE.au3>
#include <Array.au3>
$oIE = _IECreate ("http://th.wikipedia.org/wiki/" & _
"%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
"8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
"%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
"99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
"%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
"E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
"%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
"E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

  $oTable = _IETableGetCollection ($oIE,1)
    $aTable = _IETableWriteToArray ($oTable, True)
    _ArrayDisplay(  $aTable)


   ไล่ดูแล้วปรากฏว่าตารางแรกไม่ต้องดึงออกมา และตารางสุดท้ายก็ไม่จำเป็น สรุปแล้วเราจะดึงเอาข้อมูลจากตาราง 1 ถึง 39 เท่านั้น ทดลองเขียนคำสั่งดึงเอาตาราง 1 ถึง 39 ออกมาได้ดังนี้

#include <IE.au3>
#include <Array.au3>
$oIE = _IECreate("http://th.wikipedia.org/wiki/" & _
        "%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
        "8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
        "%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
        "99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
        "%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
        "E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
        "%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
        "E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

For $i = 1 To 39
    $oTable = _IETableGetCollection($oIE, $i)
    $aTable = _IETableWriteToArray($oTable, True)
    _ArrayDisplay($aTable)
Next


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



#include <IE.au3>
#include <Array.au3>
$oIE = _IECreate("http://th.wikipedia.org/wiki/" & _
        "%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
        "8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
        "%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
        "99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
        "%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
        "E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
        "%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
        "E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

For $i = 1 To 39
    $oTable = _IETableGetCollection($oIE, $i)
    $aTable = _IETableWriteToArray($oTable, True)
    _ArrayDelete($aTable, 0)
    _ArrayDisplay($aTable)
Next


     ปัญหาถัดมาคือคอลัมน์ที่ 3 ซึ่งเป็นคอลัมน์ที่แสดง หมายเหตุ วิธีการแก้ไขปัญหาตรงนี้ก็ง่ายๆ เลยคือ สร้างตัวแปรแบบอาเรย์ที่เก็บค่าได้แค่ 2 คอลัมน์ โดยใช้คำสั่ง Dim $tb[1][2] เพื่อนำเอาข้อมูลคอลัมน์แรกและคอลัมน์ที่สองใส่ไปเท่านั้น ปัญหาสุดท้ายก็คือเราจะเอาตารางทั้งหมดมารวมกันเป็นตารางเดียวได้อย่างไร วิธีแก้ไขปัญหาก็อาศัยตัวแปรแบบอาเรย์ที่เราสร้างขึ้นมานั่นเอง โดยเอาค่าตารางที่ได้แต่ละอันจับมาใส่ในตัวแปรแบบอาเรย์นั้นครับ เพื่อให้เข้าใจยิ่งขึ้นต้องเขียนโค้ดแยกออกมาต่างหาก จะได้ดังนี้

For $i = 0 To UBound($aTable) - 1
        ReDim $tb[UBound($tb) + 1][2]
        $tb[UBound($tb, 1) - 1][0] = $aTable[$i][0]
        $tb[UBound($tb, 1) - 1][1] = $aTable[$i][1]
    Next


     ผมจะใช้คำสั่ง for ในการวนลูป แต่จำนวนการวนแต่ละลูปจะไม่เท่ากันเพราะจะอ้างอิงขนาดข้อมูลในตารางจึงต้องใช้คำสั่ง UBound มานับข้อมูลในตาราง แต่จะติดปัญหานิดหนึ่งคือมันจะข้อมูลเป็นจำนวนไม่ใช่แถวที่อ้างอิงในอาเรย์ดังนั้นต้องใส่ –1 ต่อลงไป เพราะแถวข้อมูลในอาเรย์จะเริ่มต้นที่แถว 0 นั่นเอง

เสร็จแล้วก็เปลี่ยนขนาดของอาเรย์เดิมโดยใช้คำสั่ง ReDim คือ
    ReDim $tb[UBound($tb) + 1][2]

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

        $tb[UBound($tb, 1) - 1][0] = $aTable[$i][0]
        $tb[UBound($tb, 1) - 1][1] = $aTable[$i][1] 


     จะเป็นการดึงเอาข้อมูลในตัวอาเรย์เดิมมาจับใส่ในในอาเรย์ที่เพิ่งปรับขนาด และจะเกิดปัญหาอีกอย่างหนึ่งขึ้นมาอีกคือ ในการใส่คำสั่ง For ซ้อนเข้าไปใน For เดิม การวนลูปตารางในตัวอักษรถัดไป จะทำให้เกิดการล้างข้อมูลเดิมทิ้ง ดังนั้นจึงจำเป็นต้องเขียนแยกออกเป็นฟังก์ชัน โดยขนเอาข้อมูลอาเรย์ในลูป For ที่อยู่ในคำสั่งแรกออกไปใส่ วิธีประกาศฟังก์ชันตามปกติข้อมูลจะถูกล้างเหมือนเดิม เราจะใช้เทคนิคอยู่อย่างหนึ่งก็คือ ใช้คำสั่ง ByRef ควบคุมตัวแปร $tb เพื่อให้มันเก็บข้อมูลเดิมเอาไว้เสมอเมื่อมีการเพิ่มข้อมูลใหม่เข้ามา วิธีเขียนส่งตัวแปรเข้าไปในฟังก์ชันก็เขียนดังนี้

tbsum($tb, $aTable) ส่วนหัวฟังก์ชันก็จะได้ดังนี้ (ชื่อตัวแปรไม่จำเป็นต้องเขียนเหมือนตัวแปรที่ส่งเข้าไป แต่เพื่อให้เข้าใจง่ายผมจะเขียนชื่อเหมือนเดิม)

Func tbsum(ByRef $tb,$aTable)

เมื่อนำมาแก้ไขก็จะได้ดังนี้ (ลองเอาไปรันดู)

#include <IE.au3>
#include <Array.au3>
$oIE = _IECreate("http://th.wikipedia.org/wiki/" & _
        "%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
        "8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
        "%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
        "99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
        "%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
        "E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
        "%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
        "E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

Dim $tb[1][2]
For $i = 1 To 39
    $oTable = _IETableGetCollection($oIE, $i)
    $aTable = _IETableWriteToArray($oTable, True)
    _ArrayDelete($aTable, 0)
    tbsum($tb, $aTable)
Next

    _ArrayDelete($aTable, 0)
_ArrayDisplay($tb)
Func tbsum(ByRef $tb,$aTable)
    For $i = 0 To UBound($aTable) - 1
        ReDim $tb[UBound($tb) + 1][2]
        $tb[UBound($tb, 1) - 1][0] = $aTable[$i][0]
        $tb[UBound($tb, 1) - 1][1] = $aTable[$i][1]
    Next
EndFunc


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

 $file = FileOpen("test.txt", 2)


For $i = 0 To UBound($tb) - 1
FileWriteLine($file, "ลำดับที่:"&$i+1&$tb[$i][1]&" คำที่เขียนถูกคือ "&$tb[$i][0] & @CRLF)
Next


FileClose($file)
Run("notepad.exe test.txt")

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



#include <IE.au3>
#include <Array.au3>


$oIE = _IECreate("http://th.wikipedia.org/wiki/" & _
        "%E0%B8%A3%E0%B8%B2%E0%B8%A2%E0%B8%" & _
        "8A%E0%B8%B7%E0%B9%88%E0%B8%AD" & _
        "%E0%B8%84%E0%B8%B3%E0%B9%83%E0%B8%" & _
        "99%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0" & _
        "%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%" & _
        "E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%A1%E0%B8" & _
        "%B1%E0%B8%81%E0%B9%80%E0%B8%82%E0%B8%B5%" & _
        "E0%B8%A2%E0%B8%99%E0%B8%9C%E0%B8%B4%E0%B8%94")

Dim $tb[1][2]
For $i = 1 To 39
    $oTable = _IETableGetCollection($oIE, $i)
    $aTable = _IETableWriteToArray($oTable, True)
    _ArrayDelete($aTable, 0)
    tbsum($tb, $aTable)
Next
_ArrayDelete($tb, 0)

$file = FileOpen("test.txt", 2)
For $i = 0 To UBound($tb) - 1
FileWriteLine($file, "ลำดับที่:"&$i+1&$tb[$i][1]&" คำที่เขียนถูกคือ  "&$tb[$i][0] & @CRLF)
Next
FileClose($file)
Run("notepad.exe test.txt")

Func tbsum(ByRef $tb, $aTable)
    For $i = 0 To UBound($aTable) - 1
        ReDim $tb[UBound($tb) + 1][2]
        $tb[UBound($tb, 1) - 1][0] = $aTable[$i][0]
        $tb[UBound($tb, 1) - 1][1] = $aTable[$i][1]
    Next
EndFunc


##จบแล้วครับ##

1 comments:

  1. http://itp.ne.jp/result/?kw=%83%47%83%4E%83%58%83%65%83%8A%83%41%8D%48%8E%96%81%40%8D%E9%8B%CA%8C%A7&dcad=27&sr=1&evdc=1&num=20&pg=50
    ถ้าเป็น web นี้ถึงยังไงครับ ตรงผลการค้นหา

    ReplyDelete

    ส่วนนี้สำหรับแสดงความคิดเห็นทั่วไป สอบถามปัญหาตั้งถามได้ที่ฟอรั่ม


>>> [โปรดอ่าน] เนื่องจาก บทความการใช้งานบางโปรแกรมได้โฟสไปนานแล้ว
โปรแกรมอาจมีการอัปเดท วิธีการใช้งาน อาจใช้ไม่ได้ หรือมีวิธีที่ง่ายกว่าในเวอร์ชั่นใหม่
หากคุณพบว่าวิธีการใช้งานไม่ตรงกับบทความในบล็อกนี้ สามารถแนะนำเพิ่มเติมได้ครับ