Archive for the 'インターネット(internet)' Category

2017/07/09 指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする

指定URLのHTMLにリンクされているPDFをすべて指定のフォルダにダウンロードするAppleScriptです。

従来、この手の処理はSafariに対してdo javascript命令を実行していましたが、呼び出しにそこそこ時間がかかります。

そこで、SafariまかせにせずにAppleScript側でオープンソースのフレームワーク「HTMLReader」を呼び出してHTMLを解析したところ、圧倒的に高速になりました。

同じページ内のリンク箇所の抽出処理だと、

  Safari+do javascript:89 seconds
  HTMLReader.framework:0.064 seconds (First Run Time:0.831 seconds)

と、Safariに対してdo javascriptコマンドを実行しないAppleScriptのほうが100〜1,400倍高速に処理できています。本Script全体の処理時間については、PDFのダウンロード処理をともなうためネットワークの速さに依存しますが、1分かからない程度で終わることでしょう。Safari+do javascriptだとリンク先の抽出がまだ終わっていないぐらいの時間です。

実行にあたっては、HTMLReaderのプロジェクトをダウンロードしてXcode上でビルドし、出来上がったフレームワークのバイナリを~/Library/Frameworksにインストールしておく必要があります。

ただ、PDFのURLが厳密にわかっている連番のファイルならshellのcurlコマンドを呼び出してダウンロードさせれば1行で終わってしまう内容ではあります。

AppleScript名:指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする
– Created 2017-07-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4720

set aTargFol to POSIX path of (choose folder with prompt “PDFダウンロード先のフォルダを選択”)
set aTargPath to current application’s NSString’s stringWithString:aTargFol

set aStr to “http://yakumo-tajimi.com/dl.html” –Safariの最前面のウィンドウからとってきてもよい
set aList to getWebLinkURLs(aStr, “pdf”) of me

repeat with i in aList
  set j to contents of i
  
set jURL to (current application’s |NSURL|’s URLWithString:j)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(jURL, 10) of me
  
  
if exRes = true then
    set cURL to (current application’s |NSURL|’s URLWithString:j)
    
set cFileName to (cURL’s |lastPathComponent|()) as string
    
set savePath to (aTargPath’s stringByAppendingPathComponent:cFileName)
    
set wRes to (aData’s writeToFile:savePath atomically:true)
  end if
end repeat

–指定のURLのページのHTMLソースからリンクを抽出して、指定拡張子に合うものだけをフルパスのURL化して返す
on getWebLinkURLs(anURLstr, linkFileExt)
  –URLの妥当性チェック(存在チェック)
  
set aURL to (current application’s |NSURL|’s URLWithString:anURLstr)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
  
if exRes = false then error “Illegal URL Error” –エラー発生時に処理打ち切り
  
  
–HTMLのソースを取得する
  
set conType to headerRes’s valueForKeyPath:“Content-Type”
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
–リンク箇所を抽出
  
set aTextArray to ((aHTML’s nodesMatchingSelector:“a”)’s textContent) as list –リンク文字
  
set aLinkArray to ((aHTML’s nodesMatchingSelector:“a”)’s attributes’s valueForKeyPath:“href”) as list –URL
  
  
  
–取得したリンクを拡張子で絞り込みつつ、それぞれフルパスのURLを組み立てる
  
set urlList to {}
  
set aaURL to aURL’s URLByDeletingLastPathComponent()
  
  
repeat with i in aLinkArray
    set bURL to (current application’s |NSURL|’s URLWithString:i)
    
set aRes to (bURL’s |scheme|()) as string
    
set aExt to (bURL’s |pathExtension|()) as string
    
    
if aRes = “missing value” and aExt = linkFileExt then
      –想定URL(指定サイト内)のファイルへのリンクの処理
      
set aaaURL to (aaURL’s URLByAppendingPathComponent:i)
      
set aaaURLstr to (aaaURL’s absoluteString()) as string
      
set the end of urlList to aaaURLstr
    else if aRes is not “missing value” and aExt = linkFileExt then
      –指定外のURL(想定サイト外のファイルへのリンクなど)の処理
      
set the end of urlList to (aaURL’s absoluteString()) as string
    end if
  end repeat
  
  
–重複部分を除去してユニークなリストにして返す
  
return uniquify1DList(urlList, true) of me
end getWebLinkURLs

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

–1D/2D Listをユニーク化
on uniquify1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
return bArray as list
end uniquify1DList

★Click Here to Open This Script 

2017/06/14 Googleで検索して結果を返す v2

指定キーワードをGoogle検索して該当するWebサイトのURLをリストで返すAppleScriptです。

もともとはShane StanleyがAppleScript Users MLで発表したオリジナル版(v1)があり、これに対して、私が検索件数の拡張(100件まで)を行なったものです(3月の時点でShaneにフィードバックずみ)。

本Scriptは、AppleScriptからCocoaの機能を呼び出す処理を洗練させまくった内容であり、よくこんなもんを考えついて実装したものだと心底感心させられます。いままで見たScriptの中でもダントツに(いろんな意味で)突き抜けています。突き抜けすぎていて、掲載するまで(Googleに刺されないかどうか)かなり悩まされたほどです。

# ほかの言語でも同じような処理をしている例を見つけたので、問題は少ないものと判断

Webブラウザ経由でGoogle検索を行い、結果をAppleScriptで取得するようなやりかたは、短期的には使うことができていましたが、Google側の広告や表示スタイルの変更などによってScriptの定期的な書き換えや修正が必要になっていました。その場で試す分には使えるものの、長期的に使い続けることには疑問が生じていました。

本Scriptでは検索結果のHTMLをXMLとして評価し、Webブラウザ側に表示される各種要素には関係なく検索結果のURLのみを抽出。Webブラウザ経由のGoogle検索の手口を悪魔的に洗練させたものといえます。最大検索件数は100件なので用途は制限されますが、お手軽な検索程度であれば使えます。

本当に業務で長期的にGoogle検索をシステムやツールの一部の機能として利用するのであれば、Google Web API(REST API)経由でGoogle検索エンジンを呼び出すようにすることが望ましいでしょう。AppleScriptからMicrosoftのBing検索エンジンにREST API経由で検索実行することも可能です

AppleScript名:Googleで検索して結果を返す v2
– Created 2017-03-31 by Shane Stanley
– Modified 2017-03-31 by Takaaki Naganoya
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4687

set theQuery to “AppleScript”
set aResList to searchByGoogle(theQuery, 100) of me

on searchByGoogle(theQuery, maxNum)
  – build and escape query string
  
set theQuery to current application’s NSString’s stringWithString:theQuery
  
set theQuery to theQuery’s stringByReplacingOccurrencesOfString:space withString:“+”
  
set escQuery to theQuery’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())
  
  
– do search
  
set searchURLString to current application’s NSString’s stringWithFormat_(“https://www.google.com/search?client=safari&rls=en&ie=UTF-8&oe=UTF-8&q=%@&complete=0&num=%@&as_qdr=all”, escQuery, (maxNum as string))
  
set theURL to current application’s |NSURL|’s URLWithString:searchURLString
  
set {theData, theError} to current application’s NSData’s dataWithContentsOfURL:theURL options:0 |error|:(reference)
  
if theData = missing value then error (theError’s localizedDescription() as text)
  
  
– convert to XML
  
set {theXMLDoc, theError} to current application’s NSXMLDocument’s alloc()’s initWithData:theData options:(current application’s NSXMLDocumentTidyHTML) |error|:(reference)
  
if theXMLDoc = missing value then error (theError’s localizedDescription() as text)
  
  
– filter via XPath and predicate
  
set xpathStr to “//*[@class=\”r\”]/a/@href”
  
set {theNodes, theError} to theXMLDoc’s nodesForXPath:xpathStr |error|:(reference)
  
if theNodes = missing value then error (theError’s localizedDescription() as text)
  
set nodeStrings to (theNodes’s valueForKey:“stringValue”)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“self BEGINSWITH ’/url?q=’”
  
set nodeStrings to nodeStrings’s filteredArrayUsingPredicate:thePred
  
  
– trim URL strings
  
set theURLStrings to {}
  
repeat with aString in nodeStrings
    set theOffset to (aString’s rangeOfString:“&”)
    
set end of theURLStrings to (aString’s substringWithRange:{7, (theOffset’s location) - 7}) as text
  end repeat
  
  
return theURLStrings
end searchByGoogle

★Click Here to Open This Script 

2017/06/05 Webサービスを用いてIPアドレスから位置情報を検索

Web APIを用いて与えられたIPアドレスから対応する位置情報を検索するAppleScriptです。

IPアドレスから位置情報を提供するWeb APIは割とたくさんあって、無料で使えるものが多いものの、サービスの提供が長続きしなかったり、サービス状態が安定しなかったり(時間帯によってアクセスできなかったり)、無料がゆえに呼び出し頻度の制約が割ときつかったりします(仕事で継続的に利用する場合には各サービスの有償コースの利用をおすすめします)。

サービス名 アクセス制限 サービス運営者の所在国
ipinfo.io 1日あたり1000リクエスト 記載なし(イギリス?)
ip-api.com 1分あたり240リクエスト ルーマニア
ipinfodb.com 1秒間に2リクエスト マレーシア

それでも、サービスを利用できると助かるケースが多いため、実際に呼び出し方法について確認しておくとよいでしょう。

AppleScript名:ipinfo.ioでIPアドレスから場所を検索
– Created 2017-06-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4675

set myIP to getGeoLocationByIPinfo(“45.59.69.202″) of me
–>  {region:”Delaware”, city:”Wilmington”, country:”US”, org:”AS3800 Talent House, Inc.”, hostname:”No Hostname”, postal:”19801″, ip:”45.59.69.202″, loc:”39.7157,-75.5281″}

–http://ipinfo.io/developers
on getGeoLocationByIPinfo(myIP)
  set aURL to “http://ipinfo.io/” & myIP
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
set aRESTres to (json of aRes)
  
set aRESTcode to responseCode of aRes
  
if aRESTcode is not equal to 200 then return false
  
return aRESTres as record
end getGeoLocationByIPinfo

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2017/05/30 メールアドレスからMessage-IDらしきものを作成

与えられたメールアドレスから、メールヘッダに入るMessage-IDらしきものを生成するAppleScriptです。実行結果は、毎回変わります(そういう目的のために作ったので)。

高速メール送信が行えるWeb API「SendGrid」でメールの高速送信をAppleScriptから行ってみました。1通あたり0.2秒程度。さらに複数一括指定送信を行うと、AppleScriptからでも1通あたり0.007秒ぐらいで送信できたので(100通一括指定時)、たいへんに有用性が高いサービスだと感じられました。

そんなテストを繰り返す中、テスト送信先のメールアドレスがGmailのアドレスで、しかも1つのGMailアドレスに対して「+」でメールアドレス拡張を行ったときに、複数のメールが1つのメールとして見なされるという問題が起こりました。

その対処方法を探してみたところ、Twitter上で「Mail-HeaderにMessage-IDを入れるといいよ」と教えてもらいました。

Message-IDの指定について調べてみると、SendGrid呼び出し時にMessage-IDフィールドにメールアドレスをそのまま突っ込むといった乱暴なサンプルが見つかりました。とりあえず1回送るだけならこれでも問題がなさそうですが、ちゃんとユニークな値のMessage IDを入れないと、同じメールアドレスに複数回メールを送ったときに問題が発生する可能性がありそうでした。

厳密にRFC5322に準拠していなくても、それっぽくて一意に区別できれば問題ないものと考え、それっぽいものを作るAppleScriptを用意してみました。一応、MailCoreのプロジェクトの中にMessage-IDを作るメソッドがないかさがしてみたものの、単体で呼び出せるようなメソッドが見つからなかったので、まーこんなもんでしょう。

AppleScript名:メールアドレスからMessage-IDらしきものを作成
– Created 2017-05-30 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4672

set anAddress to “piyomarusoft+0001@gmail.com”
set anID to getMessageIDFromMailAddress(anAddress) of me
–>  ”<D6A128D3-2D53-4163-B2F7-53A25FB9608C@gmail.com>”

–とりあえずMessage IDらしきものを組み立ててみた
on getMessageIDFromMailAddress(anAddress as string)
  set anOffset to (offset of “@” in anAddress)
  
if anOffset = 0 then return false –Error
  
set aDomain to text anOffset thru -1 of anAddress
  
set aStr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
return (“< " & aStr & aDomain & “>”)
end getMessageIDFromMailAddress

–offset命令の実行を横取りする
on offset of searchStr in str
  set aRes to getOffset(str, searchStr) of me
  
return aRes
end offset

on getOffset(str, searchStr)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

★Click Here to Open This Script 

2017/05/27 Wikipediaで検索を行ってキーワードのカテゴリを取得する v1

与えられたキーワードをWikipediaで検索して、キーワードのカテゴリ名を取得するAppleScriptです。

何かのキーワードのカテゴリを取得するのに、いろいろプログラムやシソーラス辞書を検索することも試してみましたが、もっと乱暴かつ粗暴で即物的で低レベルな解決方法を見つけたので試してみました。

wiki0_resized.png

WikipediaをREST API経由で検索して、本文を取得し、文末にあるカテゴリ情報を取得すればいい感じのカテゴリ情報になるんでは? と思いつき、試してみたところ、、、うまく行ってしまいました(汗)

wiki1_resized.png

まだ、キーワードのフォワーディング(Mac OS X→macOS)、同音異義語や類義語への分岐(カール→複数の同音異義語への分岐)には対応していませんが、対応するのも(パターンがわかれば)それほど難しくなさそうです。

→ 実際にやってみたら割と簡単に処理できてしまいました(汗) 

AppleScript名:Wikipediaで検索を行ってキーワードのカテゴリを取得する v1
– Created 2017-05-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4666

set aRes1 to getCategoryFromKeyword(“カール (スナック菓子)”) of me
–>  {”スナック菓子の商品名”, “明治 (企業)”, “登録商標”}

on getCategoryFromKeyword(aText)
  set wikiStr to getBodyFromWikipedia(aText) of me
  
set aList to categoryTagListFromWikipedia(wikiStr, “[[Category:”) of me
  
set bList to cleanupArray(aList, “[[Category:”, “]]”) of me
  
return bList
end getCategoryFromKeyword

on categoryTagListFromWikipedia(sText, pickUpKey)
  set aString to current application’s NSString’s stringWithString:sText
  
set anArray to aString’s componentsSeparatedByString:(string id 10)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“SELF BEGINSWITH %@”, pickUpKey)
  
return (anArray’s filteredArrayUsingPredicate:aPredicate) as list
end categoryTagListFromWikipedia

–Wikipediaで指定キーワードの検索を行う(同義語、類義語などへの分岐に未対応)
on getBodyFromWikipedia(aText)
  set reqURLStr to “https://jp.wikipedia.org/w/api.php” –Japanese Version
  
  
set aRec to {action:“query”, titles:aText, |prop|:“revisions”, rvprop:“content”, |format|:“json”}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESTres to (pages of query of json of aRes)
  
  
set allKeys to (aRESTres’s allKeys()) as list
  
repeat with i in allKeys
    set j to contents of i
    
set aKeyPath to j & “.” & “revisions.*”
    
set aContent to (aRESTres’s valueForKeyPath:aKeyPath)
    
return aContent as string
  end repeat
end getBodyFromWikipedia

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

on cleanupArray(aList, fromStr, toStr)
  set newList to {}
  
  
repeat with i in aList
    set b1 to repChar(i, fromStr, “”) of me
    
set b2 to repChar(b1, toStr, “”) of me
    
    
set b3 to offset of “|” in b2
    
if b3 is not equal to 0 then
      set b2 to text 1 thru (b3 - 1) of b2
    end if
    
set the end of newList to b2
  end repeat
  
  
return newList
end cleanupArray

–Pure AppleScript Version
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

2017/05/24 restcountriesですべての国の情報を取得する

REST API経由で世界の国情報を配信しているrestcountries.euのサービスを呼び出して、すべての国情報を取得するAppleScriptです。

restcountriesのREST APIサービスについては、ひととおりすべての機能をAppleScriptから呼び出せることを確認しています。

国情報についてはそれほど変更があるわけでもないので(人口などの統計は年単位でしかアップデートされないでしょうし、首都などもコロコロ変わるわけではなく)、いっそOS側にビルトインで最初から用意されていてもいい気がします。

アプリケーションの各国語ローカライズを行う場合などに国情報は参考にするので、意外と国情報にREST API経由でアクセスできるのは助かります。

→ 調査してみたところ、オープンソースの「countries」というプロジェクトでデータが公開されているのを見つけました。JSON形式のデータも入っているので、このあたりをローカルで小突き回して加工するのがよさそうです。

AppleScript名:ALL(すべての国の情報を取得する)
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4660

set reqURLStr to “https://restcountries.eu/rest/v2/all”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then return false

set aRESTres to (json of aRes)

return aRESTres
–> {{alpha2Code:”AF”, alpha3Code:”AFG”, nativeName:”افغانستان“, region:”Asia”, borders:{”IRN”, “PAK”, “TKM”, “UZB”, “TJK”, “CHN”}, numericCode:”004″, currencies:{{name:”Afghan afghani”, symbol:”؋“, code:”AFN”}}, translations:{fr:”Afghanistan”, pt:”Afeganistão”, de:”Afghanistan”, it:”Afghanistan”, es:”Afganistán”, br:”Afeganistão”, ja:”アフガニスタン”}, flag:”https://restcountries.eu/data/afg.svg”, name:”Afghanistan”, callingCodes:{”93″}, timezones:{”UTC+04:30″}, subregion:”Southern Asia”, altSpellings:{”AF”, “Afġānistān”}, topLevelDomain:{”.af”}, population:27657145, area:652230.0, regionalBlocs:{{otherNames:{}, otherAcronyms:{}, acronym:”SAARC”, name:”South Asian Association for Regional Cooperation”}}, latlng:{33.0, 65.0}, capital:”Kabul”, gini:27.8, demonym:”Afghan”, languages:{{nativeName:”پښتو“, iso639_2:”pus”, name:”Pashto”, iso639_1:”ps”}…

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2017/05/15 感情推定(極性判定) v2

apitoreのREST API「感情推定(極性判定) v2」を呼び出すAppleScriptです。

400文字以下のテキストを解析して、極性(感情)を判定します。ただし、文章が短すぎると判定しづらくなる傾向があるようです。これまでの感情推定APIではpositive/negativeとして判定する傾向があったため、このv2 APIではneutralとして判定する幅を広めにとってもらいました。

本Scriptをテストするためには、apitoreにサインアップしてAccess Tokenを取得し、Script末尾の伏字部分にコピー&ペーストしてください(掲載リストをそのまま実行してもエラーになります)。

AppleScript名:感情推定(極性判定) v2
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4639

set aString to "このePubの考え方にそぐわない文章要素がいろいろある。代表的なものは「表」だ。データを掲載しておくのに、「表」は便利な存在だ。書く方からしても、いちいち長々とした文章で説明しなくていいし、読む方からしても整理された情報を読めるわけで、とくにお年寄りに好まれる。ところが、この「表」がePubの出版物にそぐわない。表の幅は変わってほしくないし、文字サイズが大きくなって数ページにわたって表示されるといったことは好ましくない。"
set a1Res to getSentimentFromString(aString) of me
–>  "positive"(なのか?)

on getSentimentFromString(aString)
  set reqURLStr to "https://api.apitore.com/api/39/sentiment-v2/predict"
  
set accessToken to retAccessToken() of me
  
set aRec to {access_token:accessToken, |text|:aString}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESCode to (responseCode of aRes) as integer
  
if aRESCode is not equal to 200 then return false
  
  
set aRESTres to ((json of aRes)’s valueForKeyPath:"predict.sentiment") as string
  
return aRESTres
end getSentimentFromString

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return "xxXXXxxX-XxXx-XXXX-xXXX-XXxXXxxXxxXx" –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2017/05/14 Twitter ScripterでAppleScriptからTwitter投稿、検索

AppleScriptから利用する専用のGUIなしTwitterクライアント「Twitter Scripter」(By Mousedown)を呼び出してTwitter投稿や検索を行うAppleScriptです。

Twitter Scripterはフリーでダウンロードして利用できます。Twitter Scripterは本当にAppleScriptから呼び出すためだけに作られているので、ユーザーインタフェースは一切ありません。

tw1.png

システム環境設定から確認できる(macOS 10.12, Sierra)、Twitterアカウントの一覧を取得します。Twitterアカウントは複数登録されている可能性があるため、リスト(配列)で返ってきます。

AppleScript名:Twitterアカウントを取得
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
end tell

★Click Here to Open This Script 

さらに、得られたアカウント一覧から適当なものを選んでTwitter投稿してみましょう。

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
  
  
if length of availableAccounts = 0 then return
  
  
set twitterAccount to first item of availableAccounts
  
–> “piyomaru”
  
  
tweet “てすと” using account twitterAccount
end tell

★Click Here to Open This Script 

twitter2.png

システムからTwitterアカウントを取得しなくても、直接文字列で指定することも(当然)できます。ただし、自分で所有してOSに登録していないTwitterアカウントを指定するとエラー応答が返ってきます。

–> {response:{{}}, action:false}

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ2)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  tweet “てすと” using account “piyomaru”
end tell

★Click Here to Open This Script 

Twitter Scripterからはコマンド実行時に大量のデータが返ってくるため、Cocoaの機能を利用してデータの絞り込みを行うのにぴったりです。むしろ、Cocoaの機能を使わないと大量のデータからのしぼりこみを行うのは大変です。

画像を指定してテキストと一緒に画像を投稿することもできるのですが、画像添付についてはうまく動くときと画像が投稿されないときがあるようです(なんで? どうして???)。

検索も可能で、出力対象にrecent(最近の投稿から)、popular(話題の投稿から)、mixed(デフォルト)の3タイプから選択可能です。出力件数には数値で自由に指定できるようになっていますが、実際にためしてみたところ最大件数は100件のようです。

AppleScript名:Twitterを検索する
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “mixed”
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(最近の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “recent” –最近の投稿
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(話題の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “popular” –話題の投稿
end tell

★Click Here to Open This Script 

その他のサンプルScriptもMousedownのWebサイトに掲載されています。

2017/05/06 Bing Web Search APIでWebキーワード検索

MicrosoftのBing Web Search APIを呼び出してWebのキーワード検索を行うAppleScriptです。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に1,000回まで無料で使用できます(1秒間に7回までという制限もあります)。

アカウント登録を行い、Bing Web SearchをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

個人的にBingは検索エンジンとして常用していません。Google検索にくらべて見つかる情報量が少ないと実感していることがその理由です。そのため、Bingの検索エンジンを呼び出せてもあまりメリットを見出せないところ。あくまで実験の一環です。

AppleScript名:Bing Web Search APIでWebキーワード検索
– Created 2017-05-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4634

set aStr to “戦場の絆”

set reqURLStr to “https://api.cognitive.microsoft.com/bing/v5.0/search” –Microsoft Project Oxford
set myAPIkey to retAPIkey() of me –API Primary Key

set aRec to {q:aStr, |count|:“5″, |offset|:“0″, mkt:“ja-JP”}
set bURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPI(bURL, myAPIkey) of me
set aRESTres to json of aRes
set aRESCode to responseCode of aRes
set aRESHeader to responseHeader of aRes

return (webPages of aRESTres)

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestGETAPI(aURL, anAPIkey)
  –Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:anAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
if dRes = missing value then
    set resCode to -1
    
set resHeaders to {}
  else
    set resCode to (dRes’s statusCode()) as integer
    
–Get Response Header
    
set resHeaders to (dRes’s allHeaderFields()) as record
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPI

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “XxXxXXxXxXxXXxXXxXXXxXXXxxxxXxxX” –API Key 1
end retAPIkey

★Click Here to Open This Script 

2017/05/04 Analyze Imageで画像認識

MicrosoftのCognitive ServicesのREST APIのひとつ、Computer Vision APIの「Analyze Image」を呼び出して画像認識するAppleScriptです。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に5,000回まで無料で使用できます(1分間に20回までという制限もあります)。

アカウント登録を行い、Analyze ImageをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

指定した画像のカテゴリ(Categories)、タグ情報(Tags)、詳細内容(Description)の情報を取得しています。

精度については、実際にテスト画像と得られた認識データを見比べていただくしかありません。カタツムリとか誕生日ケーキとか夕日や花火、ワインボトルなど(!)、驚くほど正確に認識されています。夕日の手前にあるのは飛行機ではないのですが、そのぐらいの誤差はあります(イルカを「犬」と判定されたりもしました)。

ためしに、この結果(Description)をそのままGoogle Tlanstlate APIに渡して日本語訳させ画像のFinderコメント欄に入れる実験などもやってみたところ、面白い結果が得られました(実用性はまだまだですが、言いたいことはわかる、というレベル。個人的には英語のままのほうがわかりやすいかも)。

こうしたサービスを利用して、写真.app(Photos.app)内の写真にすべてタグ付けして、タグをもとに風景の写真だけを新規アルバムに入れて分類するといった使い方はできそうです(すでにやってるし)。そして、AppleのアプリケーションとMicrosoftやGoogleのWebサービスを連携させるといった使い方はAppleが提供するわけがないので、こういう用途こそAppleScriptで勝手に連携させるべきだと思います。

r0010704_resized.png
{{metadata:{width:2592, |format|:”Jpeg”, height:1944}, tags:{{|name|:”fireworks”, confidence:0.998994648457}, {|name|:”outdoor object”, confidence:0.998748064041}}, categories:{{|name|:”dark_fireworks”, score:0.99609375}}, |description|:{tags:{”fireworks”, “object”}, captions:{{|text|:“a close up of fireworks”, confidence:0.233880494749}}}, requestId:”ce5e5c15-f68f-4647-9694-5fb8ede9cd31″}}

r0015213_resized.png
{{metadata:{width:2592, |format|:”Jpeg”, height:1944}, tags:{{|name|:”ground”, confidence:0.999917149544}, {|name|:”animal”, confidence:0.993541717529}, {|name|:”invertebrate”, hint:”animal”, confidence:0.952201843262}, {|name|:”outdoor”, confidence:0.887740492821}, {|name|:”mollusk”, hint:”animal”, confidence:0.814713895321}, {|name|:”snail”, hint:”animal”, confidence:0.649717211723}}, categories:{{|name|:”abstract_texture”, score:0.6015625}}, |description|:{tags:{”animal”, “outdoor”, “shellfish”, “snail”, “piece”, “laying”, “food”, “sitting”, “top”, “surface”, “lying”, “banana”, “close”, “fruit”, “bird”, “beach”, “plate”, “board”, “street”}, captions:{{|text|:“a snail on the ground”, confidence:0.480042590526}}}, requestId:”b290dc83-0e33-41b0-8a39-76c067bcffb8″}}

r0015918_resized.png
{{metadata:{width:2048, |format|:”Jpeg”, height:1536}, tags:{{|name|:”grass”, confidence:0.99923813343}, {|name|:”outdoor”, confidence:0.997150361538}, {|name|:”ground”, confidence:0.972813665867}, {|name|:”standing”, confidence:0.93672734499}, {|name|:”animal”, confidence:0.917161226273}, {|name|:”bird”, confidence:0.824012756348}}, categories:{{|name|:”animal_horse”, score:0.98046875}}, |description|:{tags:{”grass”, “outdoor”, “standing”, “bird”, “animal”, “water”, “field”, “white”, “small”, “walking”, “front”, “sitting”, “parrot”, “brown”, “large”, “grassy”, “green”, “body”, “dirt”, “red”, “river”}, captions:{{|text|:“a bird that is standing in the grass”, confidence:0.859258793309}}}, requestId:”bc5b8778-cf96-4d3c-8ba9-be22b38c36dd”}}

img_2502_resized.png
{{metadata:{width:3264, |format|:”Jpeg”, height:2448}, tags:{{|name|:”sky”, confidence:0.999066531658}, {|name|:”outdoor”, confidence:0.99786490202}, {|name|:”sunset”, confidence:0.880310297012}, {|name|:”distance”, confidence:0.229086950421}}, categories:{{|name|:”outdoor_”, score:0.0078125}, {|name|:”outdoor_waterside”, score:0.5234375}}, |description|:{tags:{”outdoor”, “sunset”, “airplane”, “plane”, “sitting”, “building”, “runway”, “large”, “front”, “water”, “pier”, “top”, “sun”, “track”, “orange”, “light”, “road”, “bridge”, “standing”, “train”, “man”, “jet”, “white”, “city”, “riding”, “red”, “flying”, “bird”, “blue”, “night”, “river”, “tower”, “tarmac”}, captions:{{|text|:“a plane that is in front of a sunset”, confidence:0.493561860724}}}, requestId:”49705b7d-99cb-4234-9af6-b30a32f02ad5″}}

img_0313_resized.png
{{metadata:{width:4032, |format|:”Jpeg”, height:3024}, tags:{{|name|:”indoor”, confidence:0.929384291172}, {|name|:”candle”, confidence:0.877458691597}, {|name|:”birthday”, confidence:0.817751348019}, {|name|:”lit”, confidence:0.53784763813}}, categories:{{|name|:”others_”, score:0.01171875}}, |description|:{tags:{”table”, “indoor”, “cake”, “birthday”, “food”, “sitting”, “lit”, “plate”, “front”, “small”, “top”, “bowl”, “fruit”, “filled”, “chocolate”, “holding”, “man”, “woman”}, captions:{{|text|:“a birthday cake with lit candles”, confidence:0.8700279282}}}, requestId:”62b58a19-2d3a-4f9a-bd54-5aa4460d8385″}}

img_2187_resized.png
{{metadata:{width:3264, format:”Jpeg”, height:2448}, tags:{{name:”bottle”, confidence:0.999316096306}, {name:”table”, confidence:0.987863183022}, {name:”indoor”, confidence:0.968602716923}, {name:”wine”, confidence:0.928377449512}, {name:”alcohol”, confidence:0.846504926682}, {name:”food”, confidence:0.785823404789}, {name:”glass”, confidence:0.782037138939}, {name:”beverage”, confidence:0.776733756065}}, categories:{{name:”drink_”, score:0.90234375}}, description:{tags:{”bottle”, “table”, “indoor”, “wine”, “sitting”, “alcohol”, “food”, “glass”, “beverage”, “cup”, “banana”, “empty”, “top”, “sandwich”, “counter”, “beer”, “phone”, “orange”, “wooden”, “computer”, “laying”}, captions:{{text:“a bottle of wine”, confidence:0.858091829408}}}, requestId:”d2db3a05-fcb0-43bb-8191-89707904918a”}}

r0020016_resized.png
{{metadata:{width:1280, format:”Jpeg”, height:960}, tags:{{name:”sky”, confidence:0.999883055687}, {name:”outdoor”, confidence:0.997511506081}, {name:”person”, confidence:0.910886585712}, {name:”toy”, confidence:0.878364562988}}, categories:{{name:”others_”, score:0.00390625}, {name:”outdoor_”, score:0.01171875}}, description:{tags:{”outdoor”, “person”, “toy”, “thing”, “man”, “white”, “grass”, “standing”, “people”, “field”, “riding”, “jumping”, “doing”, “air”, “holding”, “group”, “trick”, “board”, “statue”, “dog”, “player”, “ramp”}, captions:{{text:“a statue of a man jumping in the air”, confidence:0.153422169506}}}, requestId:”ec5dc466-76c5-47df-b858-2e418ce891a8″}}

r0016164_resized.png
{{metadata:{width:2048, format:”Jpeg”, height:1536}, tags:{{name:”water”, confidence:0.999519586563}, {name:”animal”, confidence:0.993490874767}, {name:”outdoor”, confidence:0.992024421692}, {name:”aquatic mammal”, hint:”animal”, confidence:0.978799343109}, {name:”mammal”, hint:”animal”, confidence:0.914148926735}, {name:”ocean”, confidence:0.804374277592}, {name:”whale”, hint:”animal”, confidence:0.533892810345}, {name:”wave”, confidence:0.527211129665}, {name:”dolphin”, hint:”animal”, confidence:0.340564578772}}, categories:{{name:”outdoor_”, score:0.00390625}}, description:{tags:{”water”, “animal”, “outdoor”, “mammal”, “ocean”, “wave”, “laying”, “brown”, “riding”, “surfing”, “top”, “body”, “lying”, “board”, “large”, “beach”, “floating”, “swimming”, “standing”, “dog”, “young”, “white”, “man”}, captions:{{text:“a dog swimming in the ocean”, confidence:0.604725984086}}}, requestId:”161b6c0f-f3fd-4e06-b4be-d5baa9516e53″}}

AppleScript名:Analyze Image APIで画像認識
– Created 2017-05-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4625

set imgFilePath to POSIX path of (choose file of type {“public.jpeg”} with prompt “Select an Image to Analyze”)
set aRes to recogImage(imgFilePath) of me

on recogImage(imgFilePath)
  set reqURLStr to “https://api.projectoxford.ai/vision/v1.0/analyze”
  
set myAPIkey to retAPIkey() of me –API Key 1
  
  
set aRec to {visualFeatures:“Categories,Tags,Description”, details:“Celebrities”, language:“en”}
  
set bURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestPOSTAPIAndParseResultsWithImage(bURL, imgFilePath, myAPIkey) of me
  
set aRESTres to json of aRes
  
set aRESCode to responseCode of aRes
  
set aRESHeader to responseHeader of aRes
  
  
return (aRESTres as list)
end recogImage

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestPOSTAPIAndParseResultsWithImage(aURL, imgFilePath, myAPIkey)
  –Get Image Contents from file
  
set imgPathStr to current application’s NSString’s stringWithString:imgFilePath
  
set imgData to current application’s NSData’s dataWithContentsOfFile:imgPathStr
  
set postBody to current application’s NSMutableData’s |data|()
  
postBody’s appendData:imgData
  
  
–Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setHTTPBody:postBody
  
aRequest’s setValue:“application/octet-stream” forHTTPHeaderField:“Content-Type”
  
aRequest’s setValue:myAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
if dRes = missing value then
    set resCode to -1
    
set resHeaders to {}
  else
    set resCode to (dRes’s statusCode()) as integer
    
–Get Response Header
    
set resHeaders to (dRes’s allHeaderFields()) as record
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResultsWithImage

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString())
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “xXXxXXXXxXXxXXXxXXXXXXXXxXXXXXXx” –API Key 1
end retAPIkey

★Click Here to Open This Script 

2017/04/30 Computer Vision APIで文字認識(OCR)

MicrosoftのCognitive ServicesのREST APIのひとつ、Computer Vision APIを呼び出すAppleScriptです。

MicrosoftのAPIは割とシンプルでオンラインドキュメントも読みやすいため、書くのに3分ぐらいしかかかっていません(既存の別のAPI用のものをend pointとAPI keyとパラメータを書き換えただけ)。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に5,000回まで無料で使用できます(1分間に20回までという制限もあります)。アカウント登録を行い、OCR ServiceをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

cognitive_key.png
▲ここの「Key 1」

Vision APIでは、対応画像ファイル形式はJPEG、PNG、GIF、BMP(!)。ファイル容量は4MB以下、50×50ピクセル以上の大きさである必要があります。

img_0320_resized.png

こんな「戦場の絆」のリプレイIDをゲームセンターで撮影して、このIDをもとにYouTubeのリプレイムービーのURLを検索することになるわけですが、このリプレイIDのOCRにComputer Vision APIを使ってみました。

この用途(ランダムに近い英数字の固定桁の組み合わせ、画面上のイメージの認識)については、かなり使える印象です。認識用に使用した画像は、iPhone 7で撮影して写真.appにiCloud経由で転送された写真をそのまま利用しており、ファイルサイズは2.6MB、画像の大きさは4032×3024ピクセルありました。このぐらいだと、認識のためにMicrosoftのクラウドにアップロードするのに少々待たされます。

{boundingBox:”929,1171,746,134″, text:”fy57nt2f“}
{boundingBox:”920,1438,770,135″, text:”3kt5y72v“}

この用途においては、とても誤認識が少ないと感じています。本来取り出したい目的のデータが日本語(ja)ではないので、言語を英数字(en)に指定して呼び出しています。

縦書きの日本語の印刷物の認識だと、もう少し結果が異なってくると思います。一応、この画面についても当初は日本語(ja)で認識していましたが、書体の問題もあり(ちょっと太すぎ?)日本語の文字については誤認識されまくっていました。

AppleScript名:Computer Vision APIで文字認識(OCR)
– Created 2017-04-30 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4622

set imgFilePath to POSIX path of (choose file of type {“public.image”} with prompt “Select an Image to OCR”)

set reqURLStr to “https://api.projectoxford.ai/vision/v1.0/ocr” –Microsoft Cognitive Services OCR endpoint
set myAPIkey to retAPIkey() of me –API Primary Key

–Request parameters
set aRec to {|language|:“en”, detectOrientation:“true”}
set bURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestPOSTAPIAndParseResultsWithImage(bURL, myAPIkey, imgFilePath) of me
set aRESTres to json of aRes

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false
set aRESHeader to responseHeader of aRes

return ((regions of aRESTres) as list)
–>  {{language:”en”, regions:{{boundingBox:”728,366,1497,2042″, lines:{{boundingBox:”1655,366,186,28″, words:{{boundingBox:”1655,366,186,28″, text:”pea-a-r”}}}, {boundingBox:”1223,962,467,89″, words:{{boundingBox:”1223,962,467,89″, text:”UäLCOD”}}}, {boundingBox:”753,1134,922,171″, words:{{boundingBox:”753,1134,64,156″, text:”1″}, {boundingBox:”929,1171,746,134″, text:”fy57nt2f”}}}, {boundingBox:”1761,1167,458,64″, words:{{boundingBox:”1761,1175,80,53″, text:”20″}, {boundingBox:”1862,1169,169,62″, text:”17/0″}, {boundingBox:”2045,1167,174,60″, text:”4/26″}}}, {boundingBox:”2005,1231,215,52″, words:{{boundingBox:”2005,1231,215,52″, text:”17:55″}}}, {boundingBox:”742,1403,948,170″, words:{{boundingBox:”742,1403,86,160″, text:”2″}, {boundingBox:”920,1438,770,135″, text:”3kt5y72v”}}}, {boundingBox:”1765,1432,459,65″, words:{{boundingBox:”1765,1441,81,53″, text:”20″}, {boundingBox:”1866,1434,170,63″, text:”17/0″}, {boundingBox:”2050,1432,174,61″, text:”4/26″}}}, {boundingBox:”2010,1495,215,54″, words:{{boundingBox:”2010,1495,215,54″, text:”17:47″}}}, {boundingBox:”736,1672,88,176″, words:{{boundingBox:”736,1672,88,176″, text:”3″}}}, {boundingBox:”728,2240,96,168″, words:{{boundingBox:”728,2240,96,168″, text:”5″}}}}}, {boundingBox:”2817,1217,186,320″, lines:{{boundingBox:”2817,1217,175,53″, words:{{boundingBox:”2817,1217,175,53″, text:”6vs6″}}}, {boundingBox:”2826,1483,177,54″, words:{{boundingBox:”2826,1483,177,54″, text:”6vs6″}}}}}}, textAngle:0.0, orientation:”Up”}}

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestPOSTAPIAndParseResultsWithImage(aURL, anAPIkey, imgFilePath)
  
  
–Get Image Contents from file
  
set imgPathStr to current application’s NSString’s stringWithString:imgFilePath
  
set imgData to current application’s NSData’s dataWithContentsOfFile:imgPathStr
  
set postBody to current application’s NSMutableData’s |data|()
  
postBody’s appendData:imgData
  
  
–Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setHTTPBody:postBody
  
aRequest’s setValue:“application/octet-stream” forHTTPHeaderField:“Content-Type”
  
aRequest’s setValue:anAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to (dRes’s statusCode()) as integer
  
  
–Get Response Header
  
set resHeaders to (dRes’s allHeaderFields()) as record
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResultsWithImage

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) –as text
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “xXXXXxXxXxXXXXxxxXXxXXXxxXxxXxxx” –API Primary Key
end retAPIkey

★Click Here to Open This Script 

2017/03/31 簡単ツイート収集_search

Apitoreの「簡単ツイート収集」REST APIを呼び出して、指定キーワードによるTweet内容の検索を行うAppleScriptです。

実行前にApitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

また、Twitterのサービスとの連携をオンにするために、Apitoreのマイページから「Twitterと連携する」をクリックし、ApitoreをTwitterとアプリ連携してください。

AppleScript名:簡単ツイート収集_search
– Created 2017-03-29 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4572

set aQuery to “#07Itakura”
set aResList to twitterSearchByQuery(aQuery) of me
–> {tweets:{{sentiment:missing value, favoritedCount:409, source:”Echofon“, retweetCount:392, userId:371790917, userName:”機動戦士ガンダム第07板倉小隊”, userScreenName:”07itakura”, text:”【お知らせ】本日放送した板倉小隊特番のアーカイブを明日26日の昼12時から板倉小隊のHPにて1ヵ月限定で配信決定!見逃してしまった方、もう一度見たいという方、ぜひお楽しみに! https://t.co/Q9q6VhiNGU #07itakura”, statusId:8.45606316664021E+17, sentimentScore:missing value, retweeted:false, favorited:false, createdAt:1.490443231E+12, userProfileImageURL:”http://pbs.twimg.com/profile_images/2578804231/hmzlhl1aaxaen8nf8i5b_normal.png”},….

on twitterSearchByQuery(aQuery)
  set reqURLStr to “https://api.apitore.com/api/23/twitter/search”
  
set accessToken to retAccessToken() —Access Token
  
set aRec to {access_token:accessToken, q:aQuery, sinceId:“-1″, maxId:“-1″, iter:“1″}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
set aRESCode to (responseCode of aRes) as integer
  
set aRESTres to (json of aRes) as record
  
  
return aRESTres
end twitterSearchByQuery

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXXXxxxX-xxxx-XXXx-xxxX-XxxxXxXxxXXx” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2017/01/05 XPathQuery4ObjCのじっけん

オープンソースのXML/RSSのパーサー「XPathQuery for Objective-C」(By Satoshi Konno)をCocoa FrameworkにビルドしてAppleScriptから呼び出してみました。

いくつも試してはみたものの、なかなかしっくりくるRSSのパーサーがなかったのですが、これまで試した中では一番いい感じです(当社比)。

とりあえず、OS X 10.10以降で動作するようにXPathQuery4ObjCをFramework化してビルドしたバイナリを用意しておきました。自己責任でframeworkを~/Library/Frameworksフォルダ以下にアーカイブを展開してコピーしたうえで本Scriptをおためしください。

–> Download Framework Binary

AppleScript名:XPathQuery4ObjCのじっけん
– Created 2017-01-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XPathQueryKit”
–https://github.com/cybergarage/XPathQuery4ObjC
–http://piyocast.com/as/archives/4376

set rssURL to current application’s |NSURL|’s URLWithString:“http://rss.news.yahoo.com/rss/topstories”
set xpathQuery to current application’s CGXPathQuery’s alloc()’s initWithContentsOfURL:rssURL

if ((xpathQuery’s parse()) as boolean) = true then
  set aTitle to xpathQuery’s valueForXPath:“/rss/channel/title”
  
set entriesList to xpathQuery’s objectsForXPath:“/rss/channel/item”
  
  
repeat with xpathObject in entriesList
    
    
set entryTitle to (xpathObject’s valueForXPath:“title”)
    
log {entryTitle as string}
    
    
set linkURL to (current application’s |NSURL|’s URLWithString:(xpathObject’s valueForXPath:“link”))
    
log {linkURL’s absoluteString() as string}
    
    
set mediaContent to (xpathObject’s objectForXPath:“media:content”)
    
    
if mediaContent is not equal to missing value then
      set anAttr to mediaContent’s attributes()
      
set anImageURL to (current application’s |NSURL|’s URLWithString:((mediaContent’s attributes())’s valueForKey:“url”))
      
log {anImageURL’s absoluteString() as string}
    end if
    
  end repeat
  
end if

★Click Here to Open This Script 

AppleScript名:XPathQuery4ObjCのじっけん2
– Created 2017-01-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XPathQueryKit”
–https://github.com/cybergarage/XPathQuery4ObjC
–http://piyocast.com/as/archives/4376

set rssURL to current application’s |NSURL|’s URLWithString:“http://piyocast.com/as/feed”
set xpathQuery to current application’s CGXPathQuery’s alloc()’s initWithContentsOfURL:rssURL

if ((xpathQuery’s parse()) as boolean) = true then
  set entriesList to xpathQuery’s objectsForXPath:“/rss/channel/item”
  
  
repeat with itemObject in entriesList
    set aChild to itemObject’s children()
    
log aChild
    
    
set aTitle to (itemObject’s valueForXPath:“title”)
    
log aTitle as string
    
    
set aLink to (itemObject’s valueForXPath:“link”)
    
log aLink as string
    
    
set aComments to (itemObject’s valueForXPath:“comments”)
    
log aComments as string
    
    
set aPubdate to (itemObject’s valueForXPath:“pubDate”)
    
log aPubdate as string
    
    
set aCreator to (itemObject’s valueForXPath:“dc:creator”)
    
log aCreator as string
    
    
set aCategory to (itemObject’s valuesForXPath:“category”)
    
log aCategory as list
    
    
set aGUID to (itemObject’s valuesForXPath:“guid”)
    
log aGUID as string
    
    
set aDesc to (itemObject’s valueForXPath:“description”)
    
log aDesc as string
    
    
set aCommentURL to (itemObject’s valueForXPath:“wfw:commentRss”)
    
log aCommentURL as string
  end repeat
  
end if

★Click Here to Open This Script 

2016/12/09 gooの形態素解析APIを呼び出す

gooラボが公開している各種APIのうちの、形態素解析APIを呼び出して日本語をparseするAppleScriptです。


supported by goo

利用にあたっては、gooラボAPI利用方法のページからのリンクで、Githubのアカウントを用いて(先に、Githubのアカウントを取得してある必要がある)ひもづけして(リンクをクリックするとひもづけ完了)、Application IDを取得。取得したIDをScript中に記入しておく必要があります。

gooのAPIは見た限りではみんなPOST methodを使って情報をAPIに受け渡す必要があり、少々クセがあります。ただし1つ攻略できてしまえば全部攻略できたも同じ。難易度はそれほど高くはありません(全API攻略済み)。

gooが提供しているAPIの機能レベルについては「他より頑張っている」印象。

 キーワード抽出API:面白いけど、固有名詞辞書の内容次第

 ひらがな化API:ただしい「読み」のひらがなにできるかは固有名詞辞書次第

 固有表現抽出API:

 語句類似度算出API:ウィンドウズとWindows程度の類似度は分かるが、実際に関連性がありそうな単語同士の関連性がそれほど高く評価されない。Apitoreの「単語ベクトル変換【新語対応】」(word2vec)の方が使える印象

 時刻情報正規化API:10年以上前にAppleScriptで作った気がする。ただ、こうして提供されているのは便利だし意義深い

 商品評判要約API:実用性の高そうな要約エンジンは必要。どの程度の長文の要約に耐えられるかがみどころ。実用性は未知数

当の形態素解析APIについては、Webサービスの常で、イレギュラーな人名などの固有名詞を正確に認識することは困難なようです。逆に、Yahoo!の形態素解析辞書に「長野谷」という名前が登録されていること自体が異例といえるでしょう。

AppleScript名:Gooの形態素解析APIを呼び出す
– Created 2016-12-08 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–https://labs.goo.ne.jp/api/jp/morphological-analysis/
–http://piyocast.com/as/archives/4354

set anID to retAPIKey() of me

set reqURLStr to “https://labs.goo.ne.jp/api/morph”
set aPostData to {request_id:“record001″, sentence:“私の名前は長野谷です。”, app_id:anID}
set aRes to callRestPOSTAPIAndParseResults(reqURLStr, aPostData) of me
set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then return false

set aRESHeader to responseHeader of aRes
set aRESTres to json of aRes
–>  (NSDictionary) {request_id:”record001″, word_list:{{{”私”, “名詞”, “ワタシ”}, {”の”, “格助詞”, “ノ”}, {”名前”, “名詞”, “ナマエ”}, {”は”, “連用助詞”, “ハ”}, {”長野”, “名詞”, “ナガノ”}, {”谷”, “名詞接尾辞”, “ダニ”}, {”です”, “判定詞”, “デス”}, {”。”, “句点”, “$”}}}}

–POST methodのREST APIを呼ぶ
on callRestPOSTAPIAndParseResults(aURL, aPostData)
  set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestPOSTAPIAndParseResults

on retAPIKey()
  return “XXxXXxXXxXxxxXXXxXXxXxXxXXxXxXXXxXxXxxxXxXXXXxXxxXXXxXXXxXXXXxXX” –Goo API Key
end retAPIKey

★Click Here to Open This Script 

2016/11/30 Livedoorお天気WebサービスAPIで指定地点の天気予報を取得する

livedoorのお天気WebサービスAPIを呼び出して、指定地点コードで指定した地点の天気予報を取得するAppleScriptです。

本サービスを呼び出すのに、開発者登録を行う必要はありません。

例として東京都の東京地方のコード(130010)を指定しています。

現在の緯度・軽度情報を取得して、その情報から住所コードを取得する(逆住所ジオコーディング)と、このようなサービスを利用して「現在位置の天気予報を取得する」という処理ができます。

AppleScript名:Livedoorお天気WebサービスAPIで指定地点の天気予報を取得する
– Created 2016-10-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://weather.livedoor.com/weather_hacks/webservice
–http://piyocast.com/as/archives/4337

set reqURLStr to “http://weather.livedoor.com/forecast/webservice/json/v1″

set aRec to {city:“130010″}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then error “Server Error”

set aRESHeader to responseHeader of aRes

set aRESTres to (json of aRes) as record
return aRESTres
(*
{location:{prefecture:”東京都”, city:”東京”, area:”関東”}, publicTime:”2016-11-30T17:00:00+0900″, title:”東京都 東京 の天気”, copyright:{provider:{{link:”http://tenki.jp/”, |name|:”日本気象協会”}}, title:”(C) LINE Corporation”, image:{height:26, title:”livedoor 天気情報”, width:118, |url|:”http://weather.livedoor.com/img/cmn/livedoor.gif”, link:”http://weather.livedoor.com/”}, link:”http://weather.livedoor.com/”}, link:”http://weather.livedoor.com/area/forecast/130010″, forecasts:{{|date|:”2016-11-30″, dateLabel:”今日”, temperature:{min:missing value, max:missing value}, image:{title:”曇り”, width:50, |url|:”http://weather.livedoor.com/img/icon/8.gif”, height:31}, telop:”曇り”}, {|date|:”2016-12-01″, dateLabel:”明日”, temperature:{min:{celsius:”6″, fahrenheit:”42.8″}, max:{celsius:”14″, fahrenheit:”57.2″}}, image:{title:”曇時々雨”, width:50, |url|:”http://weather.livedoor.com/img/icon/10.gif”, height:31}, telop:”曇時々雨”}, {|date|:”2016-12-02″, dateLabel:”明後日”, temperature:{min:missing value, max:missing value}, image:{title:”晴時々曇”, width:50, |url|:”http://weather.livedoor.com/img/icon/2.gif”, height:31}, telop:”晴時々曇”}}, |description|:{|text|:” 高気圧が三陸沖を東へ移動しています。一方、黄海付近は気圧の谷となっ
ています。

【関東甲信地方】
関東甲信地方は、気圧の谷や湿った空気の影響で曇りとなっています。

30日は、曇りで、気圧の谷や湿った空気の影響で、夜には雨の降る所が
あるでしょう。

12月1日は、気圧の谷の影響により、曇りで、朝にかけて雨の所が多い
見込みです。伊豆諸島では朝にかけて雷を伴う所があるでしょう。

関東近海では、30日から12月1日にかけて波が高いでしょう。船舶は
高波に注意してください。

【東京地方】
30日は、曇りで、夜遅くは雨の降る所があるでしょう。
12月1日は、曇りで、明け方まで雨となる見込みです。”, publicTime:”2016-11-30T16:34:00+0900″}, pinpointLocations:{{link:”http://weather.livedoor.com/area/forecast/1310100″, |name|:”千代田区”}, {link:”http://weather.livedoor.com/area/forecast/1310200″, |name|:”中央区”}, {link:”http://weather.livedoor.com/area/forecast/1310300″, |name|:”港区”}, {link:”http://weather.livedoor.com/area/forecast/1310400″, |name|:”新宿区”}, {link:”http://weather.livedoor.com/area/forecast/1310500″, |name|:”文京区”}, {link:”http://weather.livedoor.com/area/forecast/1310600″, |name|:”台東区”}, {link:”http://weather.livedoor.com/area/forecast/1310700″, |name|:”墨田区”}, {link:”http://weather.livedoor.com/area/forecast/1310800″, |name|:”江東区”}, {link:”http://weather.livedoor.com/area/forecast/1310900″, |name|:”品川区”}, {link:”http://weather.livedoor.com/area/forecast/1311000″, |name|:”目黒区”}, {link:”http://weather.livedoor.com/area/forecast/1311100″, |name|:”大田区”}, {link:”http://weather.livedoor.com/area/forecast/1311200″, |name|:”世田谷区”}, {link:”http://weather.livedoor.com/area/forecast/1311300″, |name|:”渋谷区”}, {link:”http://weather.livedoor.com/area/forecast/1311400″, |name|:”中野区”}, {link:”http://weather.livedoor.com/area/forecast/1311500″, |name|:”杉並区”}, {link:”http://weather.livedoor.com/area/forecast/1311600″, |name|:”豊島区”}, {link:”http://weather.livedoor.com/area/forecast/1311700″, |name|:”北区”}, {link:”http://weather.livedoor.com/area/forecast/1311800″, |name|:”荒川区”}, {link:”http://weather.livedoor.com/area/forecast/1311900″, |name|:”板橋区”}, {link:”http://weather.livedoor.com/area/forecast/1312000″, |name|:”練馬区”}, {link:”http://weather.livedoor.com/area/forecast/1312100″, |name|:”足立区”}, {link:”http://weather.livedoor.com/area/forecast/1312200″, |name|:”葛飾区”}, {link:”http://weather.livedoor.com/area/forecast/1312300″, |name|:”江戸川区”}, {link:”http://weather.livedoor.com/area/forecast/1320100″, |name|:”八王子市”}, {link:”http://weather.livedoor.com/area/forecast/1320200″, |name|:”立川市”}, {link:”http://weather.livedoor.com/area/forecast/1320300″, |name|:”武蔵野市”}, {link:”http://weather.livedoor.com/area/forecast/1320400″, |name|:”三鷹市”}, {link:”http://weather.livedoor.com/area/forecast/1320500″, |name|:”青梅市”}, {link:”http://weather.livedoor.com/area/forecast/1320600″, |name|:”府中市”}, {link:”http://weather.livedoor.com/area/forecast/1320700″, |name|:”昭島市”}, {link:”http://weather.livedoor.com/area/forecast/1320800″, |name|:”調布市”}, {link:”http://weather.livedoor.com/area/forecast/1320900″, |name|:”町田市”}, {link:”http://weather.livedoor.com/area/forecast/1321000″, |name|:”小金井市”}, {link:”http://weather.livedoor.com/area/forecast/1321100″, |name|:”小平市”}, {link:”http://weather.livedoor.com/area/forecast/1321200″, |name|:”日野市”}, {link:”http://weather.livedoor.com/area/forecast/1321300″, |name|:”東村山市”}, {link:”http://weather.livedoor.com/area/forecast/1321400″, |name|:”国分寺市”}, {link:”http://weather.livedoor.com/area/forecast/1321500″, |name|:”国立市”}, {link:”http://weather.livedoor.com/area/forecast/1321800″, |name|:”福生市”}, {link:”http://weather.livedoor.com/area/forecast/1321900″, |name|:”狛江市”}, {link:”http://weather.livedoor.com/area/forecast/1322000″, |name|:”東大和市”}, {link:”http://weather.livedoor.com/area/forecast/1322100″, |name|:”清瀬市”}, {link:”http://weather.livedoor.com/area/forecast/1322200″, |name|:”東久留米市”}, {link:”http://weather.livedoor.com/area/forecast/1322300″, |name|:”武蔵村山市”}, {link:”http://weather.livedoor.com/area/forecast/1322400″, |name|:”多摩市”}, {link:”http://weather.livedoor.com/area/forecast/1322500″, |name|:”稲城市”}, {link:”http://weather.livedoor.com/area/forecast/1322700″, |name|:”羽村市”}, {link:”http://weather.livedoor.com/area/forecast/1322800″, |name|:”あきる野市”}, {link:”http://weather.livedoor.com/area/forecast/1322900″, |name|:”西東京市”}, {link:”http://weather.livedoor.com/area/forecast/1330300″, |name|:”瑞穂町”}, {link:”http://weather.livedoor.com/area/forecast/1330500″, |name|:”日の出町”}, {link:”http://weather.livedoor.com/area/forecast/1330700″, |name|:”檜原村”}, {link:”http://weather.livedoor.com/area/forecast/1330800″, |name|:”奥多摩町”}}}
*)

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/21 HTMLReaderでWeb上のHTMLからリンクを取得する

オープンソースのフレームワーク「HTMLReader」を用いて、指定のURLのHTMLをダウンロードして指定要素を取り出すAppleScriptです。

実行にあたっては、HTMLReaderをダウンロードしてビルドし、~/Library/Frameworksにインストールしておく必要があります。

AppleScript名:HTMLReaderでWeb上のHTMLからリンクを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4325

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

set conType to headerRes’s valueForKeyPath:“Content-Type”
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType

set aTextArray to ((aHTML’s nodesMatchingSelector:“a”)’s textContent) as list –リンク文字
set aLinkArray to ((aHTML’s nodesMatchingSelector:“a”)’s attributes’s valueForKeyPath:“href”) as list –URL
–> {”#start-of-content”, “https://github.com/”, “/personal”, “/open-source”, “/business”, “/explore”, “/join?source=header-repo”, “/login?return_to=%2Fnolanw%2FHTMLReader”, “/pricing”, “/blog”, …….}

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

AppleScript名:HTMLReaderでWeb上のHTMLから画像リンクを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4325

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

set conType to headerRes’s valueForKeyPath:“Content-Type”
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType

set aLinkArray to ((aHTML’s nodesMatchingSelector:“img”)’s attributes’s valueForKeyPath:“src”) as list –画像リンクURLを取得
–> {”https://avatars2.githubusercontent.com/u/177228?v=3&s=40″, “https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif”, “https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif”, ….

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

2016/11/21 HTMLReaderでWeb上のHTMLを取得する

オープンソースのフレームワーク「HTMLReader」を用いて、指定のURLのHTMLをダウンロードして指定要素を取り出すAppleScriptです。

実行にあたっては、HTMLReaderをダウンロードしてビルドし、~/Library/Frameworksにインストールしておく必要があります。

Github上のページで紹介されているサンプルを翻訳したもので、そのうち「Load a web page.」をblocks構文を使わずに大幅に書き換えてみました。

AppleScript名:HTMLReaderのじっけん
– Created 2016-06-10 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4324

–Parse a string and find an element
set aMarkUp to current application’s NSString’s stringWithString:

ぴよぴよ!ぴよー


set aDocument to current application’s HTMLDocument’s documentWithString:aMarkUp
set anArray to ((aDocument’s nodesMatchingSelector:“b”)’s textContent) as list
–>  {”ぴよぴよ!”, “ぴよー”}

–Wrap one element in another.
set b to aDocument’s firstNodeMatchingSelector:“b”
set childArray to b’s parentNode()’s mutableChildren()
set aWrapper to current application’s HTMLElement’s alloc()’s initWithTagName:“div” attributes:{|class|:“special”}
childArray’s insertObject:aWrapper atIndex:(childArray’s indexOfObject:b)
b’s setParentNode:aWrapper
set htmlRes to (aDocument’s rootElement’s serializedFragment()) as string
–>  ”

ぴよぴよ!

ぴよー

★Click Here to Open This Script 

AppleScript名:HTMLReaderでWeb上のHTMLを取得する
– Created 2016-11-21 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4324

set aStr to “https://github.com/nolanw/HTMLReader”
set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
if exRes = false then return –エラー発生時に処理打ち切り

–>  (NSDictionary) {Content-Encoding:”gzip”, X-Runtime:”0.074184″, Set-Cookie:”_gh_sess=eyJz…..”,…….. X-Frame-Options:”deny”, Content-Type:”text/html; charset=utf-8″, X-Content-Type-Options:”nosniff”, X-UA-Compatible:”IE=Edge,chrome=1″}

set conType to headerRes’s valueForKeyPath:“Content-Type”
–>  (NSString) “text/html; charset=utf-8″

set aHome to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
set htmlSource to aHome’s rootElement()’s serializedFragment() –HTMLソース文字列

–(処理対象部分のみ掲載)
–>

set aDiv to aHome’s firstNodeMatchingSelector:“.repository-meta-content”
set aWhiteSpace to current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()
set dRes to (aDiv’s textContent()’s stringByTrimmingCharactersInSet:aWhiteSpace) as string
–>  ”A WHATWG-compliant HTML parser in Objective-C.”

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

2016/11/21 じゃらんAPIで宿情報を検索する

リクルートの旅行情報サイト「じゃらん」の「じゃらん宿表示API アドバンス」で宿情報を検索するAppleScriptです。

実行前にあらかじめじゃらんWebサービスの開発者登録を行い、「じゃらんAPIキー」を入手してください(登録無料)。

「じゃらん宿表示API アドバンス」はRESTful API(JSONを返す)ではなく、割としっかりとした論理構造を持つXMLを返してきます。

また、リストが割と長くなっていますが、掲載にあたって普段使っているXMLLibの内容を本Script内に展開したことが原因です(ありあわせのコードのつぎはぎで、さほどコードは書いておらず、動作確認を含めてあまり時間をかけていません)。

本Scriptで指定した検索条件は、

  横浜みなとみらい地区で2016年11月21日宿泊の1室、大人2名で宿泊可能な施設

です。テスト実行にあたっては、日付情報を実行時点での未来に設定しておく必要があります。

空室の詳細な状況確認については、別途「空室検索API」を。エリアコードの検索については「エリア検索API」を、温泉の検索については、「温泉検索API」を呼び出すようになるようです。ただ、一般公開されているAPIはこれらの情報検索系のみで、実際の宿泊予約についてはWebブラウザから行うことになるようです。

AppleScript名:じゃらんAPIで宿情報を検索する
– Created 2016-11-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4323

–http://www.jalan.net/jw/jwp0100/jww0102.do

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set reqURLStr to “http://jws.jalan.net/APIAdvance/HotelSearch/V1/”

set aKey to retAccessKey() of me

–横浜みなとみらい地区で2016年11月21日宿泊の1室、大人2名で宿泊可能な施設
set aRec to {|key|:aKey, s_area:“140202″, stay_date:“20161121″, room_count:“1″, adult_num:“2″, sc_num:“0″}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me
set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false
set aRESHeader to responseHeader of aRes
set aXMLres to (xml of aRes)
set aNameRes to (aXMLres’s valueForKeyPath:“Results.Hotel.HotelName.contents”) as list
–>  {”ホテルルートイン横浜馬車道”, “エスカル横浜”, “ホテル エディット 横濱”, “ホテルパセラの森 横浜関内(2015年NEWオープン)”, “ヨコハマプラザホテル”, “横浜 マンダリンホテル”, “ホテルモントレ横浜”, “ヨコハマホステルヴィレッジ 林会館”, “フレックステイイン横浜”, “東横イン横浜スタジアム前1(旧東横イン横浜スタジアム前本館)”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set aXmlRec to my makeRecordWithXML:resStr
  
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {xml:aXmlRec, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessKey()
  return “xxxXXXXXXXXXxX” –じゃらんAPIキー
end retAccessKey

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

——–XMLParse Lib

on makeRecordWithXML:xmlString
  set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
theNSXMLParser’s setDelegate:me
  
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) –as record
  else
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  set parentDict to my dictStack’s lastObject()
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
theArray’s addObject:childDict
  else
    parentDict’s setObject:childDict forKey:elementName
  end if
  
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/05 XMLをNSDictionaryに

オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScriptです。

XMLをNSDictionaryにするのには、なかなか手こずらされており、依然としてSatimageのXMLLib OSAXが手放せない状況ですが、OSAXを使わずになんとかする方法についてはつねに模索しておりました。

「XML Parser」などをキーワードにGithub上でいろいろ物色していたところ、そんなに気合の入ったキーワードでなくても、「XML NSDictionary」ぐらいでいろいろ見つかりました。

それらを人気順でソートし上から順番に物色。条件が合って手軽にフレームワーク化に成功したのがこの「XMLDictionary」です。

XMLをNSDictionaryに変換し、さらにNSDictionaryをrecordまで変換すればAppleScriptで簡単に取り扱えます(NSDictionaryの属性値ラベルに空白などが入っていなければ)。

フレームワークについては、例によってOS X 10.10をターゲットにしてビルドしてみました。~/Library/Frameworksに入れてご利用ください(あくまで自己責任で)。

XMLそのものよりも、RSSのParseが割と手間だったので、RSSを手軽に扱えるようになったことのメリットが大きいと感じています。

→ FrameworkのZipアーカイブのダウンロード(30KB)

AppleScript名:XMLをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://www.ibiblio.org/xml/examples/shakespeare/all_well.xml”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:XMLをDictionaryに(local file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aFile to POSIX path of (choose file)
set aURL to current application’s |NSURL|’s fileURLWithPath:aFile
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record

★Click Here to Open This Script 

AppleScript名:RSSをDictionaryに(remote file)
– Created 2016-11-05 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “XmlToDictKit” –https://github.com/nicklockwood/XMLDictionary
–http://piyocast.com/as/archives/4304

set aURL to current application’s |NSURL|’s alloc()’s initWithString:“http://piyocast.com/as/feed”
set xmlString to current application’s NSString’s alloc()’s initWithContentsOfURL:aURL encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)
if xmlString = missing value then return false
set xmlDoc to (current application’s NSDictionary’s dictionaryWithXMLString:xmlString) as record
set aChannel to |item| of channel of xmlDoc
set aDoc1 to first item of aChannel
–>  {category:{”Application Control”, “10.10 savvy”, “10.11 savvy”, “10.12 savvy”}, dc:creator:”maro”, comments:”http://piyocast.com/as/archives/4304#comments”, title:”XMLをNSDictionaryに”, link:”http://piyocast.com/as/archives/4304″, pubDate:”Sat, 05 Nov 2016 21:21:17 +0900″, description:”オープンソースのXMLDictionary(By Nick Lockwood)をFramework化した「XmlToDictKit」を呼び出して、XMLをNSDictionaryにするAppleScr…”, guid:{__text:”http://piyocast.com/as/archives/4304″, _isPermaLink:”false”}, wfw:commentRss:”http://piyocast.com/as/archives/4304/feed/”}

★Click Here to Open This Script 

2016/11/04 Wikipedia APIで記事の問い合わせを行う

WikipediaのAPIを呼び出して、指定記事の内容を取得するAppleScriptです。

自分で書いた記事の本文を取得して、本文中のURLを抽出し、それらが継続して存在しているかどうかを自動でチェックする、といった用途に使えると思います。

AppleScript名:Wikipedia APIで記事の問い合わせを行う
– Created 2016-11-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4302

–set reqURLStr to "https://en.wikipedia.org/w/api.php"–English Version
set reqURLStr to "https://jp.wikipedia.org/w/api.php" –Japanese Version

set aRec to {action:"query", titles:"AppleScript", |prop|:"revisions", rvprop:"content", |format|:"json"}
–set aRec to {action:"query", titles:"AppleScript|Mac OS X|Objective-C", |prop|:"revisions", rvprop:"content", |format|:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–> {query:{pages:{2954:{pageid:2954, title:"AppleScript", revisions:{{contentformat:"text/x-wiki", *:"{{Infobox プログラミング言語|名前 = AppleScript ……., contentmodel:"wikitext"}}, ns:0}}}, batchcomplete:""}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/02 connpassイベントサーチAPIで検索を行う

connpassイベントサーチAPIで検索を行うAppleScriptです。

エンジニアをつなぐIT勉強会支援プラットフォームであるconnpassのサイトでは、イベントサーチAPIを用意しており、

  event_id(イベントID)
  keyword(キーワード (AND))
  keyword_or(キーワード (OR))
  ym(イベント開催年月)
  ymd(イベント開催年月日)
  nickname(参加者のニックネーム)
  owner_nickname(管理者のニックネーム)
  series_id(グループID)

などの検索キーをもとにイベントを検索できます。あー、自分の登録したイベントが見つかってよかったー、という確認です。

# 先日、connpassイベントサーチAPIがhttps経由でのアクセスに変更されたため、本リストも修正しておきました

AppleScript名:connpassイベントサーチAPIで検索を行うv2
– Created 2016-10-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–http://connpass.com/about/api/
–http://piyocast.com/as/archives/4300

set reqURLStr to "https://connpass.com/api/v1/event/"

set aRec to {keyword:"AppleScript", ym:"201611"} –サーチクエリーと対象月
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>
(*
{results_available:1, results_start:1, |events|:{{place:"マイ・スペース MS&BB 池袋西武横店 1号室", event_url:"http://ashole.connpass.com/event/44103/", accepted:2, title:"AppleScript本フィードバック会", limit:7, event_type:"participation", owner_id:64136, ended_at:"2016-11-26T20:30:00+09:00", updated_at:"2016-11-01T09:22:15+09:00", lon:"139.711383200000", waiting:0, event_id:44103, hash_tag:"AppleScript,Mac,macOS,Mac OS X", owner_nickname:"Piyomaru", lat:"35.726486900000", started_at:"2016-11-26T18:30:00+09:00", owner_display_name:"Piyomaru", catch:"「AppleScript最新リファレンス」「AppleScript最新10大技術」についての解説", series:{|url|:"http://ashole.connpass.com/", |id|:3041, title:"AppleScriptの穴"}, address:"〒171-0022 東京都豊島区南池袋1-16-20(ぬかりやビル2階)", |description|:"<p>macOS標準装備で、GUIアプリケーションを操作できるマクロ言語「AppleScript」、その20年以上の歴史をまとめ、最新情報を盛り込んだ電子書籍「AppleScript最新リファレンス」「AppleScript 最新10大技術」を発行いたしました。</p>\n<p>・電子書籍オンライン販売URL\n<a href=\"https://piyomarusoft.booth.pm\" rel=\"nofollow\">https://piyomarusoft.booth.pm</a></p>\n<p>これらの本について、分からない点やもっと知りたい点について、筆者本人と直接お話できる場を設けました。</p>\n<p>参加資格は、AppleScriptを実際に使っている方、興味を持っている方で、筆者の書籍を実際に購入した、あるいは購入しようと考えている方です。事前に内容を読んであることが望ましいです。</p>\n<p>筆者Blog「AppleScriptの穴」\n<a href=\"http://piyocast.com/as/\" rel=\"nofollow\">http://piyocast.com/as/</a></p>"}}, results_returned:1}
*)

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/01 日本語形態素解析【新語対応】

Apitoreの「日本語形態素解析【新語対応】」REST APIを呼び出して、指定の日本語の形態素解析を行うAppleScriptです。使用している形態素解析エンジンはJavaベースの「Kuromoji」とのこと。

IPADICを辞書に使うバージョンと、IPADIC NEologdを辞書に使うバージョンの2つのAPIがあり、それぞれ同じ文章で形態素解析を行っています。NEologdのほうは「きゃりーぱみゅぱみゅ」などの新しめの固有名詞が登録されており、単語として認識します。

その一方で、やはり人名などの固有名詞(例:自分のなまえ)が正しく単語として認識されないと困るので、形態素解析についてはローカルの日本語入力IMやアドレスブックに登録してある人名との連携は欠かせないと思うものであります(IMの学習は「間違っている」ケースもあるので難しいところですが)。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

AppleScript名:日本語形態素解析【新語対応】_ipadic
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4299

set reqURLStr to “https://api.apitore.com/api/7/kuromoji-ipadic/tokenize”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:“私の名前はきゃりーぱみゅぱみゅです。”}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {startTime:”1477627346754″, tokens:{{partOfSpeechLevel1:”名詞”, baseForm:”私”, pronunciation:”ワタシ”, position:0, partOfSpeechLevel3:”一般”, reading:”ワタシ”, surface:”私”, known:true, allFeatures:”名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ”, conjugationType:”*”, partOfSpeechLevel2:”代名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “代名詞”, “一般”, “*”, “*”, “*”, “私”, “ワタシ”, “ワタシ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”の”, pronunciation:”ノ”, position:1, partOfSpeechLevel3:”*”, reading:”ノ”, surface:”の”, known:true, allFeatures:”助詞,連体化,*,*,*,*,の,ノ,ノ”, conjugationType:”*”, partOfSpeechLevel2:”連体化”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “連体化”, “*”, “*”, “*”, “*”, “の”, “ノ”, “ノ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”名前”, pronunciation:”ナマエ”, position:2, partOfSpeechLevel3:”*”, reading:”ナマエ”, surface:”名前”, known:true, allFeatures:”名詞,一般,*,*,*,*,名前,ナマエ,ナマエ”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “名前”, “ナマエ”, “ナマエ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”動詞”, baseForm:”はく”, pronunciation:”ハキャ”, position:4, partOfSpeechLevel3:”*”, reading:”ハキャ”, surface:”はきゃ”, known:true, allFeatures:”動詞,自立,*,*,五段・カ行イ音便,仮定縮約1,はく,ハキャ,ハキャ”, conjugationType:”五段・カ行イ音便”, partOfSpeechLevel2:”自立”, conjugationForm:”仮定縮約1”, allFeaturesArray:{”動詞”, “自立”, “*”, “*”, “五段・カ行イ音便”, “仮定縮約1”, “はく”, “ハキャ”, “ハキャ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助動詞”, baseForm:”り”, pronunciation:”リ”, position:7, partOfSpeechLevel3:”*”, reading:”リ”, surface:”り”, known:true, allFeatures:”助動詞,*,*,*,文語・リ,基本形,り,リ,リ”, conjugationType:”文語・リ”, partOfSpeechLevel2:”*”, conjugationForm:”基本形”, allFeaturesArray:{”助動詞”, “*”, “*”, “*”, “文語・リ”, “基本形”, “り”, “リ”, “リ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”*”, pronunciation:”*”, position:8, partOfSpeechLevel3:”一般”, reading:”*”, surface:”ー”, known:false, allFeatures:”名詞,固有名詞,一般,*,*,*,*,*,*”, conjugationType:”*”, partOfSpeechLevel2:”固有名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “固有名詞”, “一般”, “*”, “*”, “*”, “*”, “*”, “*”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”*”, pronunciation:”*”, position:9, partOfSpeechLevel3:”*”, reading:”*”, surface:”ぱみゅぱみゅです”, known:false, allFeatures:”名詞,一般,*,*,*,*,*,*,*”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “*”, “*”, “*”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”記号”, baseForm:”。”, pronunciation:”。”, position:17, partOfSpeechLevel3:”*”, reading:”。”, surface:”。”, known:true, allFeatures:”記号,句点,*,*,*,*,。,。,。”, conjugationType:”*”, partOfSpeechLevel2:”句点”, conjugationForm:”*”, allFeaturesArray:{”記号”, “句点”, “*”, “*”, “*”, “*”, “。”, “。”, “。”}, partOfSpeechLevel4:”*”}}, endTime:”1477627346755″, log:”", processTime:”1″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json;charset=UTF-8″, Access-Control-Allow-Origin:”*”, Pragma:”no-cache”, X-XSS-Protection:”1; mode=block, 1; mode=block”, Server:”nginx/1.10.1″, Transfer-Encoding:”Identity”, Expires:”0″, Cache-Control:”no-cache, no-store, max-age=0, must-revalidate”, Date:”Thu, 27 Oct 2016 03:19:17 GMT”, Strict-Transport-Security:”max-age=31536000; includeSubDomains;”, Connection:”keep-alive”, X-Content-Type-Options:”nosniff, nosniff”, X-Frame-Options:”DENY, SAMEORIGIN”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

AppleScript名:日本語形態素解析【新語対応】_ipadic_neologd
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4299

set reqURLStr to “https://api.apitore.com/api/7/kuromoji-ipadic-neologd/tokenize”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:“私の名前はきゃりーぱみゅぱみゅです。”}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {startTime:”1477998343747″, tokens:{{partOfSpeechLevel1:”名詞”, baseForm:”私”, pronunciation:”ワタシ”, position:0, partOfSpeechLevel3:”一般”, reading:”ワタシ”, surface:”私”, known:true, allFeatures:”名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ”, conjugationType:”*”, partOfSpeechLevel2:”代名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “代名詞”, “一般”, “*”, “*”, “*”, “私”, “ワタシ”, “ワタシ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”の”, pronunciation:”ノ”, position:1, partOfSpeechLevel3:”*”, reading:”ノ”, surface:”の”, known:true, allFeatures:”助詞,連体化,*,*,*,*,の,ノ,ノ”, conjugationType:”*”, partOfSpeechLevel2:”連体化”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “連体化”, “*”, “*”, “*”, “*”, “の”, “ノ”, “ノ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”名前”, pronunciation:”ナマエ”, position:2, partOfSpeechLevel3:”*”, reading:”ナマエ”, surface:”名前”, known:true, allFeatures:”名詞,一般,*,*,*,*,名前,ナマエ,ナマエ”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “名前”, “ナマエ”, “ナマエ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”は”, pronunciation:”ワ”, position:4, partOfSpeechLevel3:”*”, reading:”ハ”, surface:”は”, known:true, allFeatures:”助詞,係助詞,*,*,*,*,は,ハ,ワ”, conjugationType:”*”, partOfSpeechLevel2:”係助詞”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “係助詞”, “*”, “*”, “*”, “*”, “は”, “ハ”, “ワ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”きゃりーぱみゅぱみゅ”, pronunciation:”キャリーパミュパミュ”, position:5, partOfSpeechLevel3:”一般”, reading:”キャリーパミュパミュ”, surface:”きゃりーぱみゅぱみゅ”, known:true, allFeatures:”名詞,固有名詞,一般,*,*,*,きゃりーぱみゅぱみゅ,キャリーパミュパミュ,キャリーパミュパミュ”, conjugationType:”*”, partOfSpeechLevel2:”固有名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “固有名詞”, “一般”, “*”, “*”, “*”, “きゃりーぱみゅぱみゅ”, “キャリーパミュパミュ”, “キャリーパミュパミュ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助動詞”, baseForm:”です”, pronunciation:”デス”, position:15, partOfSpeechLevel3:”*”, reading:”デス”, surface:”です”, known:true, allFeatures:”助動詞,*,*,*,特殊・デス,基本形,です,デス,デス”, conjugationType:”特殊・デス”, partOfSpeechLevel2:”*”, conjugationForm:”基本形”, allFeaturesArray:{”助動詞”, “*”, “*”, “*”, “特殊・デス”, “基本形”, “です”, “デス”, “デス”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”記号”, baseForm:”。”, pronunciation:”。”, position:17, partOfSpeechLevel3:”*”, reading:”。”, surface:”。”, known:true, allFeatures:”記号,句点,*,*,*,*,。,。,。”, conjugationType:”*”, partOfSpeechLevel2:”句点”, conjugationForm:”*”, allFeaturesArray:{”記号”, “句点”, “*”, “*”, “*”, “*”, “。”, “。”, “。”}, partOfSpeechLevel4:”*”}}, endTime:”1477998343748″, |log|:”", processTime:”1″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json;charset=UTF-8″, Access-Control-Allow-Origin:”*”, Pragma:”no-cache”, X-XSS-Protection:”1; mode=block, 1; mode=block”, Server:”nginx/1.10.1″, Transfer-Encoding:”Identity”, Expires:”0″, Cache-Control:”no-cache, no-store, max-age=0, must-revalidate”, Date:”Thu, 27 Oct 2016 03:19:17 GMT”, Strict-Transport-Security:”max-age=31536000; includeSubDomains;”, Connection:”keep-alive”, X-Content-Type-Options:”nosniff, nosniff”, X-Frame-Options:”DENY, SAMEORIGIN”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2016/10/29 同義語辞書【新語対応】

Apitoreの「同義語辞書【新語対応】」APIを呼び出して、指定の日本語の単語の同義語(っぽいもの)を返すAppleScriptです。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

一般的なシソーラス的な動作を行うわけではなく、固有名詞に特化しているとのこと。そこで、「ガンダム」を渡してみたところ、

{input:”ガンダム”, endTime:”1477742279389″, processTime:”1019″, log:”Success.”, distances:{{word:”ζガンダム”, distance:0.819100677967}, {word:”rx-78″, distance:0.798426866531}, {word:”ζζガンダム”, distance:0.794555306435}, {word:”ザク”, distance:0.783695220947}, {word:”zガンダム”, distance:0.776543617249}, {word:”フルアーマー”, distance:0.757733106613}, {word:”gアーマー”, distance:0.75621265173}, {word:”νガンダム”, distance:0.754522383213}, {word:”ガンキャノン”, distance:0.752012193203}, {word:”モビルスーツ”, distance:0.744036734104}}, startTime:”1477742278370″, num:”10″}

やや、ゴミ(ζガンダム、ζζガンダム)が入っているようです。また、「グフ・カスタム」を入力してみると、

{input:”グフ・カスタム”, endTime:”1477742501687″, processTime:”934″, log:”Success.”, distances:{{word:”sideral”, distance:0.922877013683}, {word:”北海道道104号網走端野線”, distance:0.922159075737}, {word:”北海道道966号十勝岳温泉美瑛線”, distance:0.921318411827}, {word:”kuttner”, distance:0.916471898556}, {word:”北海道道102号網走川湯線”, distance:0.915675938129}, {word:”北海道道42号八雲北檜山線”, distance:0.915441274643}, {word:”三浦隆”, distance:0.914838135242}, {word:”桧山泰浩”, distance:0.914380848408}, {word:”北海道道120号美深中頓別線”, distance:0.914329528809}, {word:”knipex”, distance:0.913379848003}}, startTime:”1477742500753″, num:”10″}

と、関係ない言葉が返ってくるようです。Wikipediaに項目が単独で存在しているような単語と、その関連語では本サービス上での扱いが異なるように感じられます。

AppleScript名:同義語辞書【新語対応】
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4294

set reqURLStr to “https://api.apitore.com/api/9/word2vec-neologd-jawiki/distance”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |word|:“ガンダム”, num:“10″}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {input:”ガンダム”, endTime:”1477742279389″, processTime:”1019″, log:”Success.”, distances:{{word:”ζガンダム”, distance:0.819100677967}, {word:”rx-78″, distance:0.798426866531}, {word:”ζζガンダム”, distance:0.794555306435}, {word:”ザク”, distance:0.783695220947}, {word:”zガンダム”, distance:0.776543617249}, {word:”フルアーマー”, distance:0.757733106613}, {word:”gアーマー”, distance:0.75621265173}, {word:”νガンダム”, distance:0.754522383213}, {word:”ガンキャノン”, distance:0.752012193203}, {word:”モビルスーツ”, distance:0.744036734104}}, startTime:”1477742278370″, num:”10″}

–>  {input:”グフ・カスタム”, endTime:”1477742501687″, processTime:”934″, log:”Success.”, distances:{{word:”sideral”, distance:0.922877013683}, {word:”北海道道104号網走端野線”, distance:0.922159075737}, {word:”北海道道966号十勝岳温泉美瑛線”, distance:0.921318411827}, {word:”kuttner”, distance:0.916471898556}, {word:”北海道道102号網走川湯線”, distance:0.915675938129}, {word:”北海道道42号八雲北檜山線”, distance:0.915441274643}, {word:”三浦隆”, distance:0.914838135242}, {word:”桧山泰浩”, distance:0.914380848408}, {word:”北海道道120号美深中頓別線”, distance:0.914329528809}, {word:”knipex”, distance:0.913379848003}}, startTime:”1477742500753″, num:”10″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
set bLen to length of aValList
  
if aLen is not equal to bLen then return false
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/10/27 感情推定【極性判定】

Apitore「感情推定」APIを呼び出して、指定の日本語の文章がPositive(肯定的)かNegative(否定的)かを判定するAppleScriptです。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

用途は割といろいろあって、ちょっと考えただけでも、

「メールの文面をチェックして、日本語であれば(NSLinguisticTaggerを使って判定)感情推定APIを呼び出し、Negativeと判定されたらメールの一覧に赤く色をつける」

とかいうAppleScriptはかんたんに書けますし、便利だと思います(色付きメールを読みたくなくなる、とかいう心理的なフィードバックが発生することについてはさておき)。

また、

Safariの閲覧履歴を取得して、各URLにアクセスして本文のみ取得。読んだ内容がPositveかNegativeかの内訳をグラフで表示」

とかいうのも面白そうです。

テストしてみたところ、たまたまサーバー側で問題を起こしていた最中のようで、サンプルプログラム中に記述してある「かならずPositive(肯定的)と判定される例文」がNegative(否定的)と出てきて焦りましたが、じきに安定することでしょう。

AppleScript名:感情推定【極性判定】
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4289

set aText to "おめでとう!カブスが4勝2敗でドジャースを下し、2016年ナ・リーグの覇者に!次は水曜日(10月26日)に行われるワールドシリーズでア・リーグの覇者インディアンスと対決!"

set reqURLStr to "https://api.apitore.com/api/11/sentiment/predict"
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:aText}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {endTime:"1477617363491", processTime:"44", text:"おめでとう!カブスが4勝2敗でドジャースを下し、2016年ナ・リーグの覇者に!次は水曜日(10月26日)に行われるワールドシリーズでア・リーグの覇者インディアンスと対決!", log:"Success.", predict:{score:0.520746946335, sentiment:"positive"}, startTime:"1477617363447", sentimens:{{score:0.520746946335, sentiment:"positive"}, {score:0.479253053665, sentiment:"negative"}}}

–set aRESCode to (responseCode of aRes) as integer
–>  200

–set aRESHeader to (responseHeader of aRes) as record
–>  {Content-Type:"application/json;charset=UTF-8", Access-Control-Allow-Origin:"*", Pragma:"no-cache", X-XSS-Protection:"1; mode=block, 1; mode=block", Server:"nginx/1.10.1", Transfer-Encoding:"Identity", Expires:"0", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Date:"Thu, 27 Oct 2016 03:22:11 GMT", Strict-Transport-Security:"max-age=31536000; includeSubDomains;", Connection:"keep-alive", X-Content-Type-Options:"nosniff, nosniff", X-Frame-Options:"DENY, SAMEORIGIN"}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retAccessToken()
  return "xxxXxxXx-XXXX-XXXX-XxXX-XXxXXxxXXxxx" –API Tore Access Token
end retAccessToken

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

2016/10/18 URLにリソースが存在するかチェック v3

指定したURLにリソース(ファイル)が存在するかを確認するAppleScriptの改良版です。

Webサーバーなどのリモート環境を指しているURLをファイル名までフルに記述して、画像ファイルなどの存在確認を行います。「存在確認だけ」を行うことができないので、確認すると実際にデータも取得します。

  結果:{存在確認(true/false), レスポンスヘッダー(NSDictionary), データ本体(NSData)}

v3では、Webサーバーに問い合わせを行ってエラーになったときの対応を追加しています。

AppleScript名:URLにリソースが存在するかチェック v3
– Created 2016-10-18 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4279

set aStr to “http://piyocast.com/as/wp-content/uploads/2016/10/xfinder2.jpg.pagespeed.ic.jFDNAr4CuI.jpg”

set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me

exRes
–> true / false

headerRes
–> (NSDictionary) {Connection:”Keep-Alive”, Content-Type:”image/jpeg”, Server:”Apache”, Last-Modified:”Mon, 10 Oct 2016 03:43:53 GMT”, Expires:”Tue, 10 Oct 2017 03:43:53 GMT”, Cache-Control:”max-age=31536000″, Accept-Ranges:”bytes”, Date:”Mon, 10 Oct 2016 05:24:38 GMT”, Keep-Alive:”timeout=1, max=100″, Content-Length:”4383″, Etag:”W/”0″”}

aData
–> (NSData)

set conType to (headerRes’s valueForKey:“Content-Type”) as string
–>  ”image/jpeg”

set modDate to (headerRes’s valueForKey:“Last-Modified”) as string
–>  ”Mon, 10 Oct 2016 03:43:53 GMT”

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

★Click Here to Open This Script 

2016/09/13 並列ダウンロードのじっけん

複数のURLをリストで渡すと、Webからダウンロードを実行するAppleScriptです。

downloaded.jpg

実行すると、デスクトップにファイルを作成します。強制的に最前面(foreground)で実行する必要はありません。

本当に並列処理できているのか確証が持てなかったので、わりと大きなデータ(書籍のサンプルPDF)を指定してみました。たぶん、並列ダウンロードしていると思うんですが、、、、

オリジナルのObjective-Cのプログラムでは、毎回同じファイル名で保存するようになっていたので、URLからファイル名を求め(lastPathComponent())、その名前で保存するようにしてみました。

NSURLのlastPathComponent()は本当に便利です。DropboxのURLとかのパラメータつきURLから普通にPure AppleScriptでファイル名を抽出するのは面倒くさい処理ですが、Cocoaの機能を利用すると楽勝です。

ダウンロードしたデータ(NSData)を直接変数に代入できるので、「いったんファイルに書き込む処理をしたくない」ような場合に本ルーチンを書き換えてファイル保存しないようにすることで、目的を達成できます。

AppleScript名:並列ダウンロードのじっけん
– Created 2016-09-13 by Takaaki Naganoya
– 2016 Piyomaru Software

– オリジナル: HTTP からファイルをダウンロードして、ローカルに保存する方法(shintarou_horiのブログ)
–http://shintarou-hori.hatenablog.com/entry/2014/03/15/193604

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set urlList to {“http://piyocast.com/as/wp-content/uploads/2016/08/book1_20_sampler.pdf”, “http://piyocast.com/as/wp-content/uploads/2016/08/book2_20_sampler.pdf”}

set fileCount to 1

repeat with i in urlList
  set j to contents of i
  
set aURL to (current application’s |NSURL|’s URLWithString:j)
  
set aQueue to current application’s NSOperationQueue’s new()
  
set aOperation to (current application’s NSInvocationOperation’s alloc()’s initWithTarget:me selector:“execDL:” object:aURL)
  (
aQueue’s addOperation:aOperation)
end repeat

on execDL:theURL
  set imageData to current application’s NSData’s alloc()’s initWithContentsOfURL:theURL
  
set aFileName to theURL’s |lastPathComponent|()
  
my performSelectorOnMainThread:“saveData:” withObject:{imageData, aFileName} waitUntilDone:false
end execDL:

on saveData:theDataArray
  copy theDataArray to {theData, saveFileName}
  
set documentsDirectory to current application’s NSString’s stringWithString:(POSIX path of (path to desktop folder))
  
set dataPath to documentsDirectory’s stringByAppendingPathComponent:saveFileName
  
set fileManager to current application’s NSFileManager’s defaultManager()
  
  
set success to (fileManager’s fileExistsAtPath:dataPath) as boolean
  
if success then
    set retData to current application’s NSData’s dataWithContentsOfFile:dataPath
  else
    theData’s writeToFile:dataPath atomically:true
  end if
end saveData:

★Click Here to Open This Script 

2016/05/11 ShangriLa Anime API V1のデータを取得する

技術書典の一般向けサイトで、参加サークルの詳細な説明文が見られるようになったので、ひととおり見てみました。すると、ノーマークだったサークルが面白いことをやっていることが分かったので、ちょっと見てみました。

ブースB-11の「秋葉原IT戦略研究所」さんが「SNSデータ解析で見る2016年アニメ界の展望」という本を出されるそうで、それだけだと何のことかわからなかったのですが、Twiter上でハッシュタグをつけてつぶやいている膨大なデータを分析してみた、という話だと理解しました。

その活動の一環として、「秋葉原IT戦略研究所」さんではRESTful APIで呼べるアニメ情報データベースを公開されており、とくに認証も何もかかっていないので気楽に呼べそうです。

そこで、実際にAppleScriptから呼んでみることにしました。実行にはShane StanleyのAppleScript Libraries「Bridge Plus」のインストールを必要とします。

まずは、このデータベースが対象にしているクール(1クール13話、1年を52週と仮定したときに1年は4クール)の情報を取得してみました。各クールが何年何月何日から何年何月何日までなのか、という情報は提供してくれないため、各自でカレンダー計算を行う必要がありそうです(4/1なのに前クールの最終回を放映していたというパターンもあるので、そのあたりどうなるのかルールが少々不明)。

AppleScript名:GET method REST API_Anime API_get cours
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/cours”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSDictionary) {7:{id:7, year:2015, cours:3}, 3:{id:3, year:2014, cours:3}, 8:{id:8, year:2015, cours:4}, 4:{id:4, year:2014, cours:4}, 9:{id:9, year:2016, cours:1}, 5:{id:5, year:2015, cours:1}, 1:{id:1, year:2014, cours:1}, 6:{id:6, year:2015, cours:2}, 10:{id:10, year:2016, cours:2}, 2:{id:2, year:2014, cours:2}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–Request  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
–Parse Results
  
set resList to aRes as list
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

次に、指定した年のアニメ作品に関する情報を取得。

AppleScript名:GET method REST API_Anime API_getInfo_in_a_year
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/” & “2016″

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSArray) {{id:281, title:”機動戦士ガンダム サンダーボルト”}, {id:282, title:”プリンス・オブ・ストライド オルタナティブ”}, {id:283, title:”無彩限のファントム・ワールド”}, {id:284, title:”ハルチカ〜ハルタとチカは青春する〜”}, {id:285, title:”ノルン+ノネット”}, {id:286, title:”アクティヴレイド −機動強襲室第八係−”}, {id:287, title:”少女たちは荒野を目指す”}, {id:288, title:”僕だけがいない街”}, {id:289, title:”おじさんとマシュマロ”}, {id:290, title:”ファンタシースターオンライン2 ジ アニメーション”}, {id:291, title:”だがしかし”}, {id:292, title:”暗殺教室(第2期)”}, {id:293, title:”ディバインゲート”}, {id:294, title:”おしえて!ギャル子ちゃん”}, {id:295, title:”石膏ボーイズ”}, {id:296, title:”霊剣山 星屑たちの宴”}, {id:297, title:”GATE 自衛隊 彼の地にて、斯く戦えり(2期)”}, {id:298, title:”昭和元禄落語心中”}, {id:299, title:”紅殻のパンドラ”}, {id:300, title:”ブブキ・ブランキ”}, {id:301, title:”ラクエンロジック”}, {id:302, title:”デュラララ!!×2 結”}, {id:303, title:”ナースウィッチ小麦ちゃんR”}, {id:304, title:”虹色デイズ”}, {id:305, title:”大家さんは思春期!”}, {id:306, title:”Dimension W”}, {id:307, title:”灰と幻想のグリムガル”}, {id:308, title:”シュヴァルツェスマーケン”}, {id:309, title:”最弱無敗の神装機竜(バハムート)”}, {id:310, title:”赤髪の白雪姫(第2期)”}, {id:311, title:”てーきゅう(第7期)”}, {id:312, title:”魔法少女なんてもういいですから。”}, {id:313, title:”蒼の彼方のフォーリズム”}, {id:314, title:”この素晴らしい世界に祝福を!”}, {id:315, title:”亜人”}, {id:316, title:”FAIRY TAIL ZERO”}, {id:317, title:”ももくり”}, {id:318, title:”この男子、魔法がお仕事です。”}, {id:319, title:”SUSHI POLICE”}, {id:320, title:”血液型くん!4″}, {id:321, title:”迷家‐マヨイガ‐”}, {id:322, title:”宇宙パトロールルル子”}, {id:323, title:”機動戦士ガンダムユニコーン RE:0096″}, {id:324, title:”影鰐-KAGEWANI-承”}, {id:325, title:”ぼのぼの”}, {id:326, title:”フューチャーカード バディファイト トリプルディー”}, {id:327, title:”逆転裁判”}, {id:328, title:”学戦都市アスタリスク 2nd SEASON”}, {id:329, title:”僕のヒーローアカデミア”}, {id:330, title:”マクロス”}, {id:331, title:”コンクリート・レボルティオ〜超人幻想〜THE LAST SONG”}, {id:332, title:”くまみこ”}, {id:333, title:”怪盗ジョーカー(シーズン3)”}, {id:334, title:”ばくおん!!”}, {id:335, title:”聖戦ケルベロス 竜刻のファタリテ”}, {id:336, title:”ハンドレッド”}, {id:337, title:”薄桜鬼〜御伽草子〜”}, {id:338, title:”ジョーカー・ゲーム”}, {id:339, title:”双星の陰陽師”}, {id:340, title:”SUPER LOVERS”}, {id:341, title:”鬼斬”}, {id:342, title:”文豪ストレイドッグス”}, {id:343, title:”あんハピ♪”}, {id:344, title:”クロムクロ”}, {id:345, title:”ネトゲの嫁は女の子じゃないと思った?”}, {id:346, title:”甲鉄城のカバネリ”}, {id:347, title:”少年メイド”}, {id:348, title:”坂本ですが?”}, {id:349, title:”田中くんはいつもけだるげ”}, {id:350, title:”キズナイーバー”}, {id:351, title:”はいふり”}, {id:352, title:”ふらいんぐうぃっち”}, {id:353, title:”とんかつDJアゲ太郎”}, {id:354, title:”三者三葉”}, {id:355, title:”うさかめ”}, {id:356, title:”マギ シンドバッドの冒険”}, {id:357, title:”Re:ゼロから始める異世界生活”}, {id:358, title:”うしおととら(第3クール)”}, {id:359, title:”ワガママハイスペック”}, {id:360, title:”ジョジョの奇妙な冒険 ダイヤモンドは砕けない”}, {id:361, title:”テラフォーマーズ リベンジ”}, {id:362, title:”プリパラ(3rdシーズン)”}, {id:363, title:”エンドライド”}, {id:364, title:”ビッグオーダー”}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–Request  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
–Parse Results
  
set resList to aRes as list
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults