XMLデータ内容取得 - xml_contents

Last update: 2008/11/14

 昨今では、自身のホームページやブログなどに広告を載せて収益を得る「アフィリエイト」が盛んです。単に出来合いのアフィリエイトリンクを貼るだけでは物足りないアフィリエイターの中には、より高度で効果的な広告展開を求めて、自分自身で楽天や Amazon などのウェブサービス(API)を利用して XMLデータを取り扱う機会が増えています。

 しかし、例えば Perl なら XML::Parser、PHP なら expat などのモジュールを使って XMLデータを処理するのは結構面倒です。Web上の多くの API はごくシンプルな形式の XMLデータを返してくるだけなので、これらのような大層なモジュールは不必要なことがほとんどです。

 関数 xml_contents は、XMLデータの内容を簡単に取り出すために便利です。

# -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
# %|$   xml_contents( xmldata[, tagnames...]) 2008/11/03
# -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
sub xml_contents
{
    my($xml, @tags) = @_; 
    my %ret = (); 
    if(@tags)
    {
        for my $tag(@tags)
        { 
            if($xml =~ m@<$tag(?:\s+[^>]*)?>(.*?)</$tag>@is)
            { $ret{$tag} = $1; }
        }
    }
    else
    {
        while($xml =~ m@<([\w\:]+)(?:\s+[^>]*)?>(.*?)</\1>@isg)
        {
            my($tag, $val) = ($1, $2); 
            if(exists $ret{$tag})
            {
                $ret{$tag} = [$ret{$tag}] unless ref $ret{$tag}; 
                push @{$ret{$tag}}, $val; 
            }
            else
            { $ret{$tag} = $val; }
        }
    }
    return %ret ? \%ret : $xml; 
}

 この関数は特定の形式に依存しないので、あらゆる種類の XMLデータに利用することが出来ます。ただし、今のところ要素の属性は捨ててしまう仕様になっています。

# 最初に現れる ProductName という名前の要素を得る(電脳)
my $ret = xml_contents($data, "ProductName"); 
print $ret->{ProductName}; 

# レスポンスがエラーでないか確かめる(楽天)
my $ret = xml_contents($data, "Status", "StatusMsg"); 
if(lc($ret->{Status}) ne "success")
{ return "failure response($ret->{Status}: $ret->{StatusMsg})"; }

# 最初に現れる Item 以下のツリーを得る(Amazon)
my $item = xml_recursive(xml_contents($data, "Item")); 
print $item->{Item}{ItemAttributes}{Title}; 

 この関数には PHP版もあります。(実はこちらがオリジナル)
 PHP では正規表現が自然な形で使えないため、このような処理は Perl のほうが向いていると考えています。

//-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
// xml_contents(xmldata[, tags]) 2008-06-26
//-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
function xml_contents($xmldata, $tags = null)
{
    $ret = array(); 
    if(isset($tags))
    {
        if(! is_array($tags)) settype($tags, "array"); 
        foreach($tags as $tag)
        { 
            if(preg_match("@<$tag(?:\s+[^>]*)?>(.*?)</$tag>@is", $xmldata, $matchs))
                $ret[$tag] = $matchs[1]; 
        }
    }
    else
    {
        preg_match_all('@<([\w\:]+)(?:\s+[^>]*)?>(.*?)</\1>@is', $xmldata, $matchs); 
        for($i=0; isset($matchs[1][$i]); $i++)
        {
            $tag = $matchs[1][$i]; 
            $val = $matchs[2][$i]; 
            if(isset($ret[$tag]))
            {
                if(! is_array($ret[$tag])) settype($ret[$tag], "array"); 
                $ret[$tag][] = $val; 
            }
            else
                $ret[$tag] = $val; 
        }
    }
    return count($ret) ? $ret : $xmldata; 
}

 関数についてお気づきの点などありましたらご連絡くださると幸いです。