PowerShellからはてなAPIを呼び出す 2

昨日作ったはてなAPIを呼び出すスクリプトから、XML-RPCの部分を抜き出して汎用的なスクリプト(Get-XmlRpc.ps1)にした。

Get-XmlRpc.ps1

param([string]$reqURL, [string]$methodName, [object[]]$arguments=@())

if($reqURL.Length -eq 0 -or $methodName.Length -eq 0 -or $args[0] -eq "-?") {
    $commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
	Write-Host -foregroundColor Yellow @"

Name:
    $commandName

Description:
    Requests to Web site by XML-RPC form, and the response is returned by XML form.

Usage:
    $commandName [[-reqURL] <string>] [[-methodName] <string>] [[-arguments] <object[]>]

    -reqURL <string>
        The Web site URL to request.
    
    -methodName <string>
        The Method name of XML-RPC.
        
    -arguments <object[]>
        The list of argument for XML-RPC.
    
"@
	exit 1
}
# .NETの型とXML-RPCの型のマッピング
$xmlTypeMapping = @{
	[string]="string";
	[int]="int"; [long]="int";
	[double]="double"; [float]="double";
	[bool]="boolean";
	[DateTime]="dateTime.iso8601";
}
# 指定した値をXML-RPC用文字列に変換する。
function ConvertTo-XmlRpcString([object]$value) {
	if($value -is [Hashtable]) {
		# Hashtableの場合
		return [string]::Join([Environment]::NewLine,
(&{ @"
<struct>
"@
$value.GetEnumerator() | % { $name=$_.Key; $value=(ConvertTo-XmlRpcString $_.Value); @"
  <member>
    <name>$name</name>
    <value>
      $value
    </value>
  </member>
"@ }
@"
</struct>
"@ })
)
	} elseif($value -is [Array]) {
		# 配列の場合
		return [string]::Join([Environment]::NewLine,
(&{ @"
<array>
  <data>
"@
$value | % { $value=(ConvertTo-XmlRpcString $_); @"
    <value>
      $value
    </value>
"@ }
@"
  </data>
</array>
"@ })
)
	} else {
		# それ以外の場合
		return "<{0}>{1}</{0}>" -f $xmlTypeMapping[$value.GetType()], (& {
			if($value -is [DateTime]) { return $value.ToString("yyyyMMddTHH:mm:ss") }
			else {
				return $value
			}
		})
	}
}

# XML-RPC用リクエストXML
$reqXml = &{ @"
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>$methodName</methodName>
  <params>
"@
$arguments | % { $value=(ConvertTo-XmlRpcString $_); @"
    <param>
      <value>
        $value
      </value>
    </param>
"@ }
@"
  </params>
</methodCall>
"@ }; Write-Debug [string]$reqXml

$content = [Text.Encoding]::UTF8.GetBytes($reqXml)

$httpReq = [Net.HttpWebRequest]::Create($reqURL)
$httpReq.Method = "POST"
$httpReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)"
$httpReq.ContentType = "text/xml"
$httpReq.ContentLength = $content.Length

$reqStream = $httpReq.GetRequestStream()
$reqStream.Write($content, 0, $content.Length)
$reqStream.Close()

$httpRes = $httpReq.GetResponse()
if($httpRes.StatusCode -ne "OK") {
	Write-Error "The problem occurred while acquiring the response."
	exit 1
}
$resStream = $httpRes.GetResponseStream()
$resXml = [xml]((New-Object IO.StreamReader($resStream)).ReadToEnd())
$resStream.Close()

$resXml

使い方は、サイトのURLとXML-RPCのメソッド名、そのメソッドに引き渡す引数を指定するだけ。

これを使って昨日のスクリプトを書き直すと、

param([string[]]$siteURLs=@())

if($siteURLs.Length -eq 0 -or $args[0] -eq "-?") {
$commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
    Write-Host @"
説明:  指定したURLのサイトのはてなブックマーク数を取得します。
用法:  $commandName siteURLs

注意:  New-PSObject, Get-XmlRpc が必要です。
"@ -foregroundColor Yellow
    exit 1
}
$resXml = Get-XmlRpc "http://b.hatena.ne.jp/xmlrpc" "bookmark.getCount" $siteURLs
$resXml.methodResponse.params.param.value.struct.member | % { New-PSObject @{ Name=$_.name; Value=$_.value.int } }

だいぶすっきりした。レスポンスのXMLもオブジェクトにマッピングしたかったけど、それはめんどくさいからやめた。いつかやるかもしれん。

で、調子に乗ってもう一個作ってみた。

はてなダイアリーキーワード連想語API」を呼び出すスクリプト(Get-Rensougo.ps1)

param([string[]]$words=@())

if($words.Length -eq 0 -or $args[0] -eq "-?") {
$commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
    Write-Host @"
説明:  指定した用語から連想されるはてなキーワードの一覧を取得します。
用法:  $commandName words=@()

注意:  New-PSObject, Get-XmlRpc が必要です。
"@ -foregroundColor Yellow
    exit 1
}
$resXml = Get-XmlRpc "http://d.hatena.ne.jp/xmlrpc" "hatena.getSimilarWord" @{ wordlist=$words }
$resXml.methodResponse.params.param.value.struct.member.value.array.data.value | % {
    New-PSObject @{ Name=$_.struct.member.name; Value=$_.struct.member.value.string }
}

使い方はこんな感じ

PS > Get-Rensougo C#

Value                                                  Name
-----                                                  ----
結城浩                                                 word
CLR                                                    word
 .NET                                                  word
存在                                                   word
プラットフォーム                                       word
イメージ                                               word
Java                                                   word
Common                                                 word
2000年                                                 word
発表                                                   word
プログラミング言語                                     word
オープンソース                                         word
MicroSoft                                              word
Delphi                                                 word
C++                                                    word
ヨーロッパ                                             word
コンパイラ                                             word
Mono                                                   word
ECMA                                                   word

はぁ疲れた。