在網際網路(尤其是 WWW)發達的現在,我們會從很多 Web 上搜尋想要的資訊或資料,但有時候該 Web 的設計並不友善,沒有很直接的方法取出我們想要得到的資料,或是也沒有提供 RSS 之類的東西,這時就會想要自己寫個程式來抓網頁的資料了。

抓網頁的資料並不困難,尤其是各個語言都有 HTTP 相關的 framework 或是 toolkit,這裡我以抓取中央氣象局發佈的「中央氣象局台灣各地1週天氣預報」作為例子,因為這個網頁沒有提供 RSS(當然可能其它天氣網站會有 XD),所以我想當我的 script 一執行之後,就能從這個網頁擷取出我想要的一週天氣預報。

Ruby 裡有個很好用的 module 叫作 open-uri,這個 module 提供了一個 open 的函式,可以幫你打開 URI 的內容,並且傳回一個 File 物件,如下:

require 'open-uri'
require 'iconv'

ic = Iconv.new('UTF-8', 'BIG5')  # 因為這個網頁是用 big5 編碼,我要轉成 UTF-8 編碼
page = open('http://www.cwb.gov.tw/V5/forecast/taiwan/week.htm')
data = ic.iconv(page.read)
puts data

執行這段 script ,你會看到像這樣的輸出:

<HTML lang="zh-TW">
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="Tue, 20 Aug 1996 14:25:27 GMT">
<TITLE> 台灣各地1週天氣預報 </TITLE>
<link rel="stylesheet" href="/V5/css/global.css">
</HEAD>
......略

所以我們可以用 data = ic.iconv(page.read) 把這份網頁的 HTML 全部放進 data 這個變數中,再來就是要分析一下這份網頁的 HTML 來作一些字串處理了。

這個網頁每天都會更新(那還不提供一下 RSS),資料都是當天起未來一週的資料,分析一下這個網頁的 HTML ,我們可以看得出來它是用表格(<table>)來排版的,而且每一個地區是一列(<tr>)來排,這樣事情就單純多了,比方說我只想抓出台北市未來一週的資料,我只要看這一段就可以了:

<tr bgcolor=white><th bgcolor=#f5f5f5>台北市</th>
<td align=center valign=middle><img src="/V5/symbol/symbol09.gif" alt="多雲午後短暫雷陣雨" title="多雲午後短暫雷陣雨"><BR>26~33</td>
<td align=center valign=middle><img src="/V5/symbol/symbol09.gif" alt="多雲午後短暫雷陣雨" title="多雲午後短暫雷陣雨"><BR>26~34</td>
<td align=center valign=middle><img src="/V5/symbol/symbol06.gif" alt="多雲轉陰陣雨" title="多雲轉陰陣雨"><BR>25~32</td>
<td align=center valign=middle><img src="/V5/symbol/symbol06.gif" alt="陰陣雨" title="陰陣雨"><BR>24~28</td>
<td align=center valign=middle><img src="/V5/symbol/symbol06.gif" alt="陰陣雨" title="陰陣雨"><BR>24~28</td>
<td align=center valign=middle><img src="/V5/symbol/symbol09.gif" alt="多雲午後短暫雷陣雨" title="多雲午後短暫雷陣雨"><BR>25~31</td>
<td align=center valign=middle><img src="/V5/symbol/symbol09.gif" alt="多雲午後短暫雷陣雨" title="多雲午後短暫雷陣雨"><BR>25~32</td></tr>

所以我們可以寫成這樣:

require 'open-uri'
require 'iconv'

ic = Iconv.new('UTF-8', 'BIG5')

page = open('http://www.cwb.gov.tw/V5/forecast/taiwan/week.htm')
data = ic.iconv(page.read)
start = false
data.each do |line|
  if start && line =~ /<tr.*/
    break;
  end

  if start
    content = line.scan(/title="(.*)"><BR>(.*?)</)[0]
    puts content[0] + ", " + content[1]
  end

  if line =~ /台北市/
    start = true
  end
end

data.each 的用意在於讀進來的 HTML 是一行一行以 array 的型態存好的,所以這裡用一個迴圈一行一行去掃這個網頁的資料,然後當發現這一行有包含「台北市」字串時,把 start 變數設成 true,表示接下來的每一行都是我們要擷取的內容。

因為我想要取得 imgtitle 的內容,表示天氣的狀況,以及在後面的氣溫分佈,所以我用了 regular expression 取得這兩筆資料的內容,它就會分別放在 content[0]content[1] 裡面了,剩下就是整理成好看的輸出格式而已了,這裡就不多作介紹,在此收工。

如果哪天氣象局改了網頁的格式,那「字串處理」的工作就要再修改囉 :P

 

歷史上的今天