Entry

蔵書インデクシングプロジェクト活動進捗(3)

2008年07月27日

前回は,バーコードリーダーを使って読んだ ISBN をもとにして,AWS にアクセスするための URL を取得するスクリプトを(なぜか PHP で)書いたのでした。前回までのお話はこちら。

今回は,作った URL から XML 形式の書籍データを取得して,自分のところのデータベースに登録するための SQL を作ることにします。もっとも,スキーマがいまいち決まらないので,ここでは取ってきたデータを一旦作業テーブルに収めることにします。作業テーブルに収めるのは,XML を解析しつつ,他のエントリと関連づける(正規化したレコードを作る)のが面倒臭い上に,本来 DB 上でやるはずの操作を自前で実装するのがアホくさいってのもあります。

ということで,作業テーブルは次のようなもの。MySQL 用のテーブルです。

DROP TABLE IF EXISTS tmp_book;
CREATE TABLE tmp_book (
       id INTEGER  PRIMARY KEY AUTO_INCREMENT,
       isbn CHAR(10),
       ean CHAR(13),
       title VARCHAR(256),
       edition VARCHAR(128),
       publisher VARCHAR(128),
       pubdate VARCHAR(10),  
       image_url VARCHAR(128),
       binding VARCHAR(64),
       price VARCHAR(32),
       currency_cd CHAR(3),
       pages VARCHAR(32)
);

DROP TABLE IF EXISTS tmp_author;
CREATE TABLE tmp_author (
       id INTEGER PRIMARY KEY AUTO_INCREMENT,
       isbn CHAR(10),
       name VARCHAR(64),
       role VARCHAR(32)
);

著者(author/creator)だけは,書籍と1対多の関係にあるので,別テーブル(tmp_author)に収めることにしました。また,primary key は,取得した値をそのまま使わず,別個に ID を割り当てています。これはいわゆるサロゲートキーってやつ……ではなくて,作業の最中に余計なエラー(主キー違反とか)の世話をしたくないから,無駄に primary key を振らなかっただけです。価格(price)カラムやページ(pages)のカラムを文字列として登録しているのも,同様の理由からです。

つことで,AWS のレスポンス(XML)から SQL を作ることにします。こゆもんの解析は,DOM や SAX のようなパーサを使ってやってるところが多いみたいですけれど,そんな低水準な層で扱うよりも XSLT を使った方がはるかに簡単です。ここでは libxslt に付属している xsltproc というプログラムを使うことにしました。

xsltproc のいいところは,引数に URL を指定すると,ネットワーク越しにファイルを引っ張ってきてくれるところです。つまり,ネットワークにアクセスするプログラムを書いたり,wget で一旦ダウンロードして……なんてことをしなくてもいいというわけ。あたしが使っている FreeBSD 上では libxslt を ports からインストールしただけで使えました。Windows 向けのバイナリも用意されているので,Windows からでも使えます(参照:xsltprocの使用方法)。

んなわけで,あたしがやることは XML→SQL の XSLT スクリプト書くことだけ。こんなの作りました。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2005-10-05">

  <xsl:output method="text" encoding="UTF-8"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//aws:ItemLookupResponse/aws:Items/aws:Item"/>
  </xsl:template>

  <xsl:template match="//aws:ItemLookupResponse/aws:Items/aws:Item">
    INSERT INTO tmp_book (
      isbn,
      ean,
      title,
      edition,
      publisher,
      pubdate,
      image_url,
      binding,
      price,
      currency_cd,
      pages
     ) VALUES (
      '<xsl:value-of select="./aws:ItemAttributes/aws:ISBN"/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:EAN"/>',
      '<xsl:value-of select='translate(./aws:ItemAttributes/aws:Title, "&apos;", "&apos;&apos;")'/>',
      '<xsl:value-of select='translate(./aws:ItemAttributes/aws:Edition, "&apos;", "&apos;&apos;")'/>',
      '<xsl:value-of select='translate(./aws:ItemAttributes/aws:Publisher, "&apos;", "&apos;&apos;")'/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:PublicationDate"/>',
      '<xsl:value-of select="./aws:LargeImage/aws:URL"/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:Binding"/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:ListPrice/aws:Amount"/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:ListPrice/aws:CurrencyCode"/>',
      '<xsl:value-of select="./aws:ItemAttributes/aws:NumberOfPages"/>'
    );
    <xsl:apply-templates select="./aws:ItemAttributes/aws:Author"/>
    <xsl:apply-templates select="./aws:ItemAttributes/aws:Creator"/>
  </xsl:template>

  <xsl:template match="aws:ItemAttributes/aws:Author">
    INSERT INTO tmp_author (
        isbn,
        name,
        role
      ) VALUES (
        '<xsl:value-of select="../aws:ISBN"/>',
        '<xsl:value-of select="."/>',
        '<xsl:value-of select="./@Role"/>'
      );
  </xsl:template>

  <xsl:template match="aws:ItemAttributes/aws:Creator">
    INSERT INTO tmp_author (
        isbn,
        name,
        role
      ) VALUES (
        '<xsl:value-of select="../aws:ISBN"/>',
        '<xsl:value-of select="."/>',
        '<xsl:value-of select="./@Role"/>'
      );
  </xsl:template>

</xsl:stylesheet>

このスタイルシートに,前回作った URL リストを食べさせてあげると,めでたく INSERT してくれる SQL ができあがるって寸法です。ということで,食べさせるスクリプト……。これはわざわざファイルにしなくても,シェルから入力すればいいと思います。request.txt っていうのは,前回作ったスクリプトで作った URL のリストです。

#!/bin/sh

while read line; do
    xsltproc awssql.xsl ${line} >> aws.sql
    sleep 1
done < request.txt

一応,アクセスが集中しないように,1秒ずつ取得するようにしています。ちゃんと取れたか確かめてみる……。

aian:~ % grep 'INSERT INTO tmp_book' aws.sql | wc -l
    1044
aian:~ % wc -l booklist.txt 
    1048 booklist.txt

booklist.txt ってのは,バーコードリーダーで読んだ ISBN が列記されているテキストファイルです。4つほど足りないですけど,これは,もともと Amazon に登録されていないものなので,問題はありません(確認済み)。4つはあとで手入力することになる……と。

SQL までできたので,あとはこれを作業テーブルに流して,それから「ちゃんとしたスキーマ」に移すことにします。上のスキーマを流したところ,エスケープしなくちゃいけない文字列がエスケープできてなくて,何度かコケてました。コケてたのは2箇所程度だったので,こゆのは,スクリプトを直さずに,結果の SQL ファイルを直接手で直します。1回しか流さないもんですしね。いちいち生成スクリプトを直すと,かえって効率が悪いです。

テーブルはおおむねできたので,今後登録するデータは,バッチではなく,上にかぶせるアプリケーションを経由して登録することにします。もちろん,バーコードリーダーを使って登録できるようにしたいところ。そろそろ画面も作り始めるか……。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN