Amazon Web サービス

Livedoor の次は Amazon かよ...って所ですが。既に気づいた方もいらっしゃるかもしれませんが、このサイトの右側の下の方に Amazon の商品を表示しています。すっかり、アフィリエイトづいています。

これ、今読んでいたりする本や聴いている CD などの関連商品です。Amazon Web サービスでは関連商品を調べることができるのですね。ここに表示される商品を見ると、なにに興味を持っているかが分かるっていうスンポー。

Livedoor の天気も Amazon を使った関連商品の表示も PHP で作っているのですが、一番手こずった部分は、実は JavaScript の部分ということは秘密。

REST で XML が返ってくるという部分は Livedoor お天気と同じなので AppleScript でも処理を行うことができます。ということで、今聴いている iTunes の曲のアーティストを使って Amazon で検索してみるスクリプトなんかを。

Script Editor で開く

property tmpFolder : path to temporary items folder from user domain
property tmpFile : POSIX path of ((tmpFolder as Unicode text) & "request.xml")

property LF : ASCII character 10

property baseurl : "http://webservices.amazon.co.jp/onca/xml"
property service : "AWSECommerceService"
property accesskey : "your access key is here." -- ここにアクセスキー
property associateid : "your associate id is here." -- ここにアソシエイト ID
property operation : "ItemSearch"
property SearchIndex : "Music"
property responsegroup : "Request,Small,Images"
property AWSVersion : "2006-05-17"
property pagenumber : "1"
property sort : "salesrank"

tell application "iTunes"
    set theArtist to artist of current track
end tell

set encodedText to my encodeURL(theArtist)

set keywords to encodedText

set request to baseurl
set request to request & "?Service=" & service
set request to request & "&AWSAccessKeyId=" & accesskey
set request to request & "&AssociateTag=" & associateid
set request to request & "&Operation=" & operation
set request to request & "&Keywords=" & keywords
set request to request & "&SearchIndex=" & SearchIndex
set request to request & "&ResponseGroup=" & responsegroup
set request to request & "&Page=" & pagenumber
set request to request & "&Version=" & AWSVersion
set request to request & "&Sort=" & sort

do shell script "curl -o " & quoted form of tmpFile & " " & quoted form of request

tell application "System Events"
    set xmlFile to XML file tmpFile

    set root to XML element 1 of xmlFile
    set itemsElement to XML element "Items" of root
    set html to ""
    repeat with thisElement in (XML elements of itemsElement whose name is "Item")
        set theURL to value of XML element "DetailPageURL" of thisElement
        set itemTitle to value of XML element "Title" of XML element "ItemAttributes" of thisElement
        set html to html & "<a href=\"" & theURL & "\">" & itemTitle & "</a>" & LF
    end repeat
end tell

html

on encodeURL(theText)
    if not ((theText starts with "'") and (theText ends with "'")) then
        set theText to quoted form of theText
    end if

    try
        do shell script "echo " & theText & " | " & "php -r 'echo rawurlencode(`cat -`);'"
    on error eMessage number eNumber
        tell application (path to frontmost application as Unicode text)
            activate
            display dialog (eNumber & return & eMessage & return & return) as Unicode text buttons {"OK"} default button 1 with icon 0
        end tell
        return ""
    end try
end encodeURL

検索結果を使って商品へのリンクタグを作っています。実際に利用するには Amazon でデベロッパー登録をしてアクセスキーをもらう必要があります。アフィリエイト ID は特に必要ないですが、あると検索結果の商品の URL にアフィリエイト ID が埋め込まれます。

検索結果がなかったときと Amazon でエラーが起きた時の処理は行っていませんので、その辺りご注意を。

天気予報

気がつけば、6 月も半ばを過ぎている...。ちょっとした驚きですね。

6 月に入った頃から、ADSL モデムの調子が悪くてネットに接続できないようになっていました。これは、モデムの故障で交換したらなおったのですが。こういったこともあったのですが、少し前から Web アプリケーションのことを調べていてそれに時間を取られていました。って、今も取られているのだけど。

Web アプリケーション、Web API、Web サービス...といった関連ですね。マッシュアップなんてものが流行っている昨今、私も流行に感化されました。

しかし、ネットに接続したいというときにモデムが故障して...。そうなれば、Ajax や Web サービスといったってネットにつなげられなきゃ、役立たずというごく当然の結論。これは、なかなか興味深い発見でした。

...いま、カーネルパニックが起きました。おい。なんか、調子悪いな...。

なんとなく理解(こういう中途半端が一番危ないと思うのですが)したので手頃な Web API がないかなと探していたところ、思い出した。livedoorWeather Hacks というのを公開していました。公開されてすぐに oomori.com さんがこのサービスを使った LiveOtenki という Cocoa アプリケーションを配布していましたが、これが手頃です。

仕様を読んでみる...。REST で XML が返ってくるんだな。よし、まずは、AppleScript で検証してみよう。

Script Editor で開く

property baseurl : "http://weather.livedoor.com/forecast/webservice/rest/v1"
property areaList : {"北海道地方", "東北地方", "関東地方", "信越・北陸地方", "東海地方", "近畿地方", "中国地方", "四国地方", "九州地方", "南西諸島地方"}
property prefList : {{"道北", "道央", "道東", "道南"}, {"青森県", "秋田県", "岩手県", "宮城県", "山形県", "福島県"}, {"茨城県", "栃木県", "群馬県", "埼玉県", "東京都", "千葉県", "神奈川県", "山梨県"}, {"富山県", "石川県", "福井県", "新潟県", "長野県"}, {"静岡県", "愛知県", "岐阜県", "三重県"}, {"滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県"}, {"岡山県", "広島県", "島根県", "鳥取県", "山口県"}, {"徳島県", "香川県", "愛媛県", "高知県"}, {"福岡県", "大分県", "長崎県", "佐賀県", "熊本県", "宮崎県", "鹿児島県"}, {"沖縄県"}}
property cityList : {{{"稚内", "旭川", "留萌"}, {"札幌", "岩見沢", "倶知安"}, {"網走", "北見", "紋別", "根室", "釧路", "帯広"}, {"室蘭", "浦河", "函館", "江差"}}, {{"青森", "むつ", "八戸"}, {"秋田", "横手"}, {"盛岡", "宮古", "大船渡"}, {"仙台", "白石"}, {"山形", "米沢", "酒田", "新庄"}, {"福島", "小名浜", "若松"}}, {{"水戸", "土浦"}, {"宇都宮", "大田原"}, {"前橋", "みなかみ"}, {"さいたま", "熊谷", "秩父"}, {"東京", "大島", "八丈島", "父島"}, {"千葉", "銚子", "館山"}, {"横浜", "小田原"}, {"甲府", "河口湖"}}, {{"富山", "伏木"}, {"金沢", "輪島"}, {"福井", "敦賀"}, {"新潟", "長岡", "高田", "相川"}, {"長野", "松本", "飯田"}}, {{"静岡", "網代", "三島", "浜松"}, {"名古屋", "豊橋"}, {"岐阜", "高山"}, {"津", "尾鷲"}}, {{"大津", "彦根"}, {"京都", "舞鶴"}, {"大阪"}, {"神戸", "豊岡"}, {"奈良", "風屋"}, {"和歌山", "潮岬"}}, {{"岡山", "津山"}, {"広島", "庄原"}, {"松江", "浜田", "西郷"}, {"鳥取", "米子"}, {"下関", "山口", "柳井", "萩"}}, {{"徳島", "日和佐"}, "高松", {"松山", "新居浜", "宇和島"}, {"高知", "室戸", "清水"}}, {{"福岡", "八幡", "飯塚", "久留米"}, {"大分", "中津", "日田", "佐伯"}, {"長崎", "佐世保", "厳原", "福江"}, {"佐賀", "伊万里"}, {"熊本", "阿蘇乙姫", "牛深", "人吉"}, {"宮崎", "延岡", "都城", "高千穂"}, {"鹿児島", "鹿屋", "種子島", "名瀬"}}, {{"那覇", "名護", "久米島", "南大東島", "宮古島", "石垣島", "与那国島"}}}
property cityIDList : {{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9", "10", "11", "12"}, {"13", "14", "15", "16"}}, {{"17", "18", "19"}, {"20", "21"}, {"22", "23", "24"}, {"25", "26"}, {"27", "28", "29", "30"}, {"31", "32", "33"}}, {{"54", "55"}, {"56", "57"}, {"58", "59"}, {"60", "61", "62"}, {"63", "64", "65", "66"}, {"67", "68", "69"}, {"70", "71"}, {"75", "76"}}, {{"44", "45"}, {"46", "47"}, {"48", "49"}, {"50", "51", "52", "53"}, {"72", "73", "74"}}, {{"34", "35", "36", "37"}, {"38", "39"}, {"40", "41"}, {"42", "43"}}, {{"77", "78"}, {"79", "80"}, {"81"}, {"82", "83"}, {"84", "85"}, {"86", "87"}}, {{"88", "89"}, {"90", "91"}, {"92", "93", "94"}, {"95", "96"}, {"97", "98", "99", "100"}}, {{"101", "102"}, {"103"}, {"104", "105", "106"}, {"107", "108", "109"}}, {{"110", "111", "112", "113"}, {"114", "115", "116", "117"}, {"118", "119", "120", "121"}, {"122", "123"}, {"124", "125", "126", "127"}, {"128", "129", "130", "131"}, {"132", "133", "134", "135"}}, {{"136", "137", "138", "139", "140", "141", "142"}}}

on run
    set theResult to chooseIt(areaList, item 1 of areaList)
    if theResult is false then return
    repeat with i from 1 to count areaList
        if item i of areaList is theResult then exit repeat
    end repeat

    set areaNum to i
    set thisPref to a reference to item areaNum of prefList
    set theResult to chooseIt(thisPref, item 1 of thisPref)
    if theResult is false then return
    repeat with i from 1 to count thisPref
        if item i of thisPref is theResult then exit repeat
    end repeat

    set prefNum to i
    set thisCity to a reference to item prefNum of item areaNum of cityList
    set theResult to chooseIt(thisCity, item 1 of thisCity)
    if theResult is false then return
    repeat with i from 1 to count thisCity
        if item i of thisCity is theResult then exit repeat
    end repeat

    set cityID to item i of item prefNum of item areaNum of cityIDList
    set tmp to path to desktop folder as Unicode text
    set tmp to POSIX path of (tmp & "tmp")
    weatherQuery("today", cityID, tmp)
    lwws((tmp as POSIX file) as Unicode text)
end run

on weatherQuery(when, cityID, theFile)
    set query to baseurl & "?city=" & cityID
    set query to query & "&day=" & when

    do shell script "curl " & quoted form of query & " > " & theFile
end weatherQuery

on chooseIt(theList, defaultItems)
    tell application (path to frontmost application as Unicode text)
        activate
        set thisItem to choose from list theList default items defaultItems
        if thisItem is false then return false
        set thisItem to thisItem as Unicode text
    end tell
end chooseIt

on lwws(theFile)
    tell application "System Events"
        set xmlFile to XML file theFile

        set root to XML element 1 of xmlFile

        set area to value of XML attribute "area" of XML element "location" of root
        set pref to value of XML attribute "pref" of XML element "location" of root
        set city to value of XML attribute "city" of XML element "location" of root

        set forecastday to value of XML element "forecastday" of root
        if forecastday is "today" then
            set forecastday to "今日"
        else if forecastday is "tomorrow" then
            set forecastday to "明日"
        else
            set forecastday to "明後日"
        end if

        set forecastdate to value of XML element "forecastdate" of root
        set publictime to value of XML element "publictime" of root
        set telop to value of XML element "telop" of root
        set desc to value of XML element "description" of root
        set temperature to XML element "temperature" of root
        set max to value of XML element "celsius" of XML element "max" of temperature as Unicode text
        if max is "" then set max to "--"
        set min to value of XML element "celsius" of XML element "min" of temperature as Unicode text
        if min is "" then set min to "--"
        set imageURL to value of XML element "url" of XML element "image" of root

        return {area, pref, city, forecastday, forecastdate, publictime, telop, desc, max, min, imageURL}
    end tell
end lwws

なるほど。このまま日記の今日の天気欄に挿入したりして使えますね。感じがつかめたところで PHP で Web 上で動くものを作ってみる。それが、これ(...なくなりました)。

うむ、天気予報が表示されたところでどうってことはないな。これだけでは面白くも何ともないということが確認できました。やっぱり、なにかと組み合わせないと...。

しかし、Web アプリケーションを作るときの問題はコーディングではなくて、インターフェイスに時間がかかるということ。これは、私の向き不向きなのでしょうが、デザインって苦手だ。HTML と CSS でああでもない、こうでもないと。簡単に済ますなら、フォームでポップアップメニューを使って市町村の名前をずらずらと並べればいいのですが、これは避けたかった。だって、150 近くの市の中から目的のものなんて探したくないですから。でも、よく使われていますね。都道府県をメニューから選んだりするやつとか。あれでも 50 近くありますね。誕生日の日の選択とか。

どうすれば、簡単に目的のものを選択できるかを考えた結果、3 段階に分けたメニューにしたのですが...こうすると、動くブラウザと動かないブラウザがでてくるんですよね。

そんなわけで、AppleScript から少し離れている今日この頃でした。