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が採れれば良いんだけどと思い
@node = uniq @node;
@node = sort { $a <=> $b } @node;

で$node[2]とかやってみたけどなんか気持ち悪いしなんか常に正しい値では無いように思える

  • その2
my @nodes = $xml->findnodes( '//IsCategoryRoot/../../preceding-sibling::BrowseNodeId[last()]' );
    • 欲しいノードの最後に必ずIsCategoryRootという要素があるのに着目し、また、軸の向きを途中から逆に出来る事が分かったので試してみた*2
      いい感じに採れた!
  • その3
my @nodes = $xml->findnodes( '/descendant::IsCategoryRoot[1]/../../../../preceding-sibling::BrowseNodeId' );

問題:次のXPath式の違いを説明してください。
//div[1]
/descendant::div[1]

というのを読んでいるうちにああそういう事か!とようやく気がついた。
XPath式スゴイ!そして面白い!

宿題

あとは抽出したBrowseNodeIdからTopSellersのASINを取得して、ASINからそれぞれの書像や書名等を取得してJSONでクライアントに返して、クライアント側でjQueryのPluginのjCarouselでゴニョれば出来る筈

*1:もちろん最初に情報を取得する時にBrowseNodeを取得しておく必要がありますが

*2:親の親の親の兄であるBrowseNodeId