XPath式が便利すぎる件
所蔵マップでISBNで検索する時にAWSを使って情報を持って来ているのだけれど、折角だからAmazonの「この商品を買ったひとはこんな商品も買っています」みたいな見せ方で、そのジャンルのトップセールが表示したらどうだろうか?なんてことをふと思った
ざっくり調べるとBrowseNodeLookupという検索方法でResponseGroupがTopSellersを指定すると取得出来る事が分かった*1
早速リクエストを組み立ててブラウザに投げてXMLを表示させてみたら
〜略〜 <Items> <Item> <ASIN>4873113008</ASIN> <BrowseNodes> <BrowseNode> <BrowseNodeId>515826</BrowseNodeId> <Name>Perl</Name> <Ancestors> <BrowseNode> <BrowseNodeId>492352</BrowseNodeId> <Name>プログラミング</Name> <Ancestors> <BrowseNode> <BrowseNodeId>466298</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>465610</BrowseNodeId> <Name>ジャンル別</Name> <IsCategoryRoot>1</IsCategoryRoot> <Ancestors> <BrowseNode> <BrowseNodeId>465392</BrowseNodeId> <Name>本</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> <BrowseNode> <BrowseNodeId>547670</BrowseNodeId> <Name>コンピュータ・インターネット 全般</Name> <Ancestors> <BrowseNode> <BrowseNodeId>466298</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>465610</BrowseNodeId> <Name>ジャンル別</Name> <IsCategoryRoot>1</IsCategoryRoot> <Ancestors> <BrowseNode> <BrowseNodeId>465392</BrowseNodeId> <Name>本</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> <BrowseNode> <BrowseNodeId>548102</BrowseNodeId> <Name>オライリージャパン</Name> <Ancestors> <BrowseNode> <BrowseNodeId>548098</BrowseNodeId> <Name>出版社別</Name> <Ancestors> <BrowseNode> <BrowseNodeId>466298</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>465610</BrowseNodeId> <Name>ジャンル別</Name> <IsCategoryRoot>1</IsCategoryRoot> <Ancestors> <BrowseNode> <BrowseNodeId>465392</BrowseNodeId> <Name>本</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> <BrowseNode> <BrowseNodeId>15749771</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>15749671</BrowseNodeId> <Name>なか見!検索</Name> <Ancestors> <BrowseNode> <BrowseNodeId>515742</BrowseNodeId> <Name>ストア</Name> <Ancestors> <BrowseNode> <BrowseNodeId>465392</BrowseNodeId> <Name>本</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> <BrowseNode> <BrowseNodeId>3135681</BrowseNodeId> <Name>インターネット・Web開発</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1197778</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1060414</BrowseNodeId> <Name>ユーズドブック(和書)</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1058424</BrowseNodeId> <Name>jp-used</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> <BrowseNode> <BrowseNodeId>3135691</BrowseNodeId> <Name>プログラミング</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1197778</BrowseNodeId> <Name>コンピュータ・インターネット</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1060414</BrowseNodeId> <Name>ユーズドブック(和書)</Name> <Ancestors> <BrowseNode> <BrowseNodeId>1058424</BrowseNodeId> <Name>jp-used</Name> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </Ancestors> </BrowseNode> </BrowseNodes> </Item> </Items>
めちゃめちゃネストが深い、そして深さが不定
これでは何時ものようにXML::TreePPを使ってこんな風に書く事が出来ない
my $BrowseNodeId = $tree->{ItemLookupResponse}->{Item}->{BrowseNodes}->{BrowseNode}->{BrowseNodeId};
これはあれだなXPath式って奴をそろそろ覚えないとダメなんだなと思い色々調べて試してみた
- その1
〜略〜 my $response_string = get($uri); my $xml = XML::XPath->new( xml => $response_string ); my @nodes = $xml->findnodes( '//BrowseNodeId[last()]' ); my @node; for my $vals ( @nodes ) { push @node, int($vals->getFirstChild->getNodeValue); };
-
- 全部のBrowseNodeIdが採れてしまった
一番子孫のBrowseNodeIdから遡って三代前のBrowseNodeIdが採れれば良いんだけどと思い
- 全部のBrowseNodeIdが採れてしまった
@node = uniq @node; @node = sort { $a <=> $b } @node;
で$node[2]とかやってみたけどなんか気持ち悪いしなんか常に正しい値では無いように思える
- その2
my @nodes = $xml->findnodes( '//IsCategoryRoot/../../preceding-sibling::BrowseNodeId[last()]' );
-
- 欲しいノードの最後に必ずIsCategoryRootという要素があるのに着目し、また、軸の向きを途中から逆に出来る事が分かったので試してみた*2
いい感じに採れた!
- 欲しいノードの最後に必ずIsCategoryRootという要素があるのに着目し、また、軸の向きを途中から逆に出来る事が分かったので試してみた*2
- その3
my @nodes = $xml->findnodes( '/descendant::IsCategoryRoot[1]/../../../../preceding-sibling::BrowseNodeId' );
-
- 一番最初のノードのだけ採れれば一番いいんだけどなぁと悩んでいて、のid:amachangさんの記事で
問題:次のXPath式の違いを説明してください。
//div[1]
/descendant::div[1]
というのを読んでいるうちにああそういう事か!とようやく気がついた。
XPath式スゴイ!そして面白い!