【シェルと遊ぼう】はてなブログAPIのレスポンス解析 その3 指定した項目を指定した順番で取り出す

はじめに

前回は、はてなブログAPIのレスポンスを1行にまとめるところまでを作った。

今回は、そこから指定したタグの値を取り出すスクリプトを作成しよう。

つくるもの

使い方は、前回の出力をパイプで受け取る感じ。
(h-entry-getitemsが今回のスクリプト)

> cat SAMPLE | h-entry-pickup detail | h-entry-cleantags | h-entry-perline | h-getitems  

動作例

以下が加工前のデータ。1つの記事(entryタグ)が1行になっている。 (記事のコンテンツは無し)

> cat SAMPLE  
<entry><edit>https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/edit/2500000000</edit><alternate>http://{ブログID}/entry/2013/09/02/112823</alternate><author>{はてなID}</author><title>記事タイトル1</title><updated>2013-09-02T11:28:23+09:00</updated><published>2013-09-02T11:28:23+09:00</published><app:edited>2013-09-02T11:28:23+09:00</app:edited><summary> 記事本文 リスト1 リスト2 内容 </summary><category>カテゴリ1</category><category>カテゴリ2</category><app:draft>no</app:draft></entry>  
<entry><edit>https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/edit/2500000000</edit><alternate>http://{ブログID}/entry/2013/09/02/112823</alternate><author>{はてなID}</author><title>記事タイトル2</title><updated>2013-09-02T11:28:23+09:00</updated><published>2013-09-02T11:28:23+09:00</published><app:edited>2013-09-02T11:28:23+09:00</app:edited><summary> 記事本文 リスト3 リスト4 内容 </summary><category>カテゴリ3</category><category>カテゴリ4</category><app:draft>no</app:draft></entry>  

与えられた引数の項目(XMLタグ)の値を、与えられた順番で出力する。
区切り文字は>固定にしている。

> cat SAMPLE | h-entry-getitems title  
記事タイトル1  
記事タイトル2  

> cat SAMPLE | h-entry-getitems published title category  
2013-09-02T11:28:23+09:00>記事タイトル1>カテゴリ1  
2013-09-02T11:28:23+09:00>記事タイトル2>カテゴリ3  

指定した項目が存在しなかった場合、その項目は空文字で出力する。

> cat SAMPLE | h-entry-getitems title DUMMY category  
記事タイトル1>>カテゴリ1  
記事タイトル2>>カテゴリ3  

引数がない場合は、全項目を出力する。

> cat SAMPLE |  h-entry-getitems  
https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/edit/2500000000>http://{ブログID}/entry/2013/09/02/112823>{はてなID}>記事タイトル1>2013-09-02T11:28:23+09:00>2013-09-02T11:28:23+09:00>2013-09-02T11:28:23+09:00> 記事本文 リスト1 リスト2 内容 >カテゴリ1>カテゴリ2>no  
https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/edit/2500000000>http://{ブログID}/entry/2013/09/02/112823>{はてなID}>記事タイトル2>2013-09-02T11:28:23+09:00>2013-09-02T11:28:23+09:00>2013-09-02T11:28:23+09:00> 記事本文 リスト3 リスト4 内容 >カテゴリ3>カテゴリ4>no  

コード

今回のコードはこちら。
何か変なところがあればご指摘下さいまし。

ざっくり説明すると下記となる。

  • 2行目   出力の際の区切り文字
  • 4行目以降 引数なしの場合。全てのXMLタグを除去する
  • 14行目以降 引数ありの場合。引数で指定した項目を指定した順番で取り出して、1行ずつ出力する

ワンライナー以外のawkスクリプトを初めて書いた気がする。
他の方法が思い浮かばなかったときの最終兵器感がありますな。

awkを使った箇所は、ちょっと処理の補足をしておこう。

引数ありの場合の補足

    pick_tags=  
    for tag in "$@"; do  
        # ,<tag1>,<tag2>....  
        pick_tags="$pick_tags,<$tag>"  
    done  
    # ,<tag1>,<tag2>  
    # <tag1>,<tag2>  
    pick_tags=${pick_tags#,}  
    awk -v target="$pick_tags" -v target_num=$# \  
    'BEGIN { \  
        split(target, tags, ",");  
        for (i=1; i<=target_num; i++) {  
            len[i]=length(tags[i])  
            tags[i]=tags[i] "[^<]*";  
        }  
    }{  
        fields=""  
        for (i=1; i<=target_num; i++) {  
            where=match($0, tags[i]);  
            if (where == 0) {  
                fields=fields "'$PLANE_TEXT_SEP'"  
            }else {  
                fields=fields "'$PLANE_TEXT_SEP'" substr($0, RSTART+len[i], RLENGTH-len[i]);  
            }  
        }  
        sub(/^'"$PLANE_TEXT_SEP"'/, "", fields)  
        print fields  
    }'  

最初に、可変個の引数をawkに渡したいため、pick_tagsという1つの変数に引数をまとめている。
その際、各引数にタグの<>も追加しておく。

次に、awk起動する際に、引数と引数の個数を渡している。
awkではオプションの-v variable=valueで変数を起動時に定義できるっぽい。

次のBEGINブロックは、awkで入力の1行目を処理する前に実行されるブロック。
ここでは、引数を配列に変換して各行で探す検索パターンを構築している。
ここで変換した配列の数だけ、各入力行でループを回しながら検索する力技。

構築している検索パターンは、<タグ名>[^<]*
最終的に欲しいのは値だけなので、検索ヒット位置から値だけを取り出す際のオフセットとなる<タグ名>の文字数もlenに保存しておく。

BEGINの次のブロックが、各入力行に対して実行されるブロック。
BEGINで作った検索パターンにマッチしたものを、fieldsに入れている。

検索にはmatch関数を使い、戻り値が0ならマッチしなかったことを示すため、区切り文字だけセットしている。
マッチした場合は、タグの値だけ取り出す為、substr関数を使っている。
match関数は、RSTARTにマッチ開始位置、RLENGTHはマッチした文字数をセットしてくれるっぽい。

sub関数を使っているのは、先頭の余計な区切り文字を除去するため。

最後にprint関数で取得した1行分の項目を出力している。


うーん、実にシェルスクリプトっぽくない感じ。

ちなみに同じような処理を最初シェルスクリプトのループで書いてみたのだけれども、うるとらはいぱー遅かった。

awkすっごい。

最後に

今回のスクリプトで、はてなブログAPIのレスポンスから好きな項目を取り出せるようになった。
これで、記事のタイトルやカテゴリ、URLなど任意の項目の一覧表を簡単に作れるはずだ。
実際は、作ってきたスクリプト達の実行をまとめたラッパースクリプトを用意するといいかと思う。

さて、ここまでは記事のコンテンツ以外の項目を取り出すものを作ってきた。
次回は、取得した記事のコンテンツと一緒に、必要な情報をmdファイルに保存するスクリプトでも作ってみよう。

確認環境

PC Thinkpad X1 Carbon 2nd Gen
OS 12.0-RELEASE-p7

参考

以上。