オライリー本の表紙に描かれている全動物をシェル芸でリスト化してみる
(2022-04-27追記)
元サイトの構成変更などにより、本記事のコードは動作しなくなってしまったため、修正版を作成しました。
オライリー本の表紙に描かれている全動物をシェル芸でリスト化してみる(修正版)
O’Reilly Media, Inc.(日本法人: オライリー・ジャパン、以下ひと纏めにしてオライリー社とします)は、コンピュータ技術書籍の出版や、ウェブサイト作成、IT関連のカンファレンスの開催等を手がけている、メディア企業です。
このオライリー社の出版物として、表紙に動物の木版画が描かれているコンピュータ技術書、通称「アニマルシリーズ」があります。
このアニマルシリーズは、詳細な解説や情報量の多さに定評がある事で有名ですが、僕のような動物好きな輩には、表紙にリアルで多様な動物が描かれている点も、非常に魅力的です。可愛くてつい購入してしまう、なかなかアコギな商法だぜ!
ある時、オライリー社の出版物一覧を眺めていて、アニマルシリーズにはどんな動物が登場したのだろうかと、ふと疑問に感じました。
そこで、今回、アニマルシリーズに描かれている全動物を、シェル芸で調べることが出来ないか、考えてみました。
本記事では、オライリー社の「アニマルシリーズ」の表紙に描かれている全動物をワンライナーでリスト化する方法を、記します。
実行環境
Arch Linux 4.4.39-1-lts
GNU bash 4.4.5
GNU coreutils 8.26-1
grep (GNU grep) 2.27
sed (GNU sed) 4.2.2
gawk 4.1.4
nkf 2.1.4
curl 7.51.0
方針
オライリー社のWebサイトでは、次のページにて、アニマルシリーズで使用された動物を調べることが出来ます。
http://www.oreilly.com/animals.csp - Animal Menagerie
今回は、このWebページの内容を利用します。
ただし、上記のWebページでは、20件ずつしか、書籍情報を出力できません。
そこで、URLにクエリを与えてやる事によって、全ての書籍情報が得られるまで、ページの取得を続けます。
ページの取得が完了したら、シェル芸ではお馴染みのフィルタコマンドを用いて、HTMLソースから必要な情報を、少しずつ削り出していきます。
最後に出力を整形すれば、アニマルシリーズで使用された全動物のリストが得られます。
シェル芸コード
以下、シェル芸による解答です。
#!/bin/sh
seq 0 20 inf | # URLクエリの生成
awk '{print "\
curl -s http://www.oreilly.com/animals.csp?x-o="$0" \
|nkf -Lu \
|sed '\''s/Regul.* kurz & gut/Regular Expression Pocket Reference, 2nd Edition/'\'' \
|grep -E '\''class=\"(book-title|animal-name)\"'\'' \
|| exit 0\
"}' | # コマンド列の生成
## curl: URLクエリ毎にHTMLソースを取得
## nkf: 文字エンコーディングをUTF-8に変換(Regular Expression Pocket Reference, 2nd Edition用)
## sed: 独語タイトルを英語タイトルに修正(Regular Expression Pocket Reference, 2nd Edition用)
## grep: 書籍名および動物名レコードの抽出
## || exit: 番兵コマンド
sh | # コマンドの実行
tr -d '\n' | # 改行の除去
sed 's;</h2>;&\n;g' | # <h2>タグの後に改行を挿入
sed 's;</h1>;&{FS};g' | # <h1>タグの後にフィールドセパレータ{FS}を挿入
sed 's/<[^>]*>//g' | # HTMLタグの除去
sed 's/\s\{2,\}//g' | # 2個以上続くスペースの除去
### 1.書籍名 2.動物名
awk -F'{FS}' '$0=$2' | # 動物名フィールドの抽出
### 1.動物名
awk -vFS= 'OFS=""; $1=toupper($1)' | # 頭文字を大文字に変換
sort | # ソート
uniq # 重複レコードの除去
以下、コピペ実行用のワンライナーコードです。
seq 0 20 inf | awk '{print "curl -s http://www.oreilly.com/animals.csp?x-o="$0" |nkf -Lu |sed '\''s/Regul.* kurz & gut/Regular Expression Pocket Reference, 2nd Edition/'\'' |grep -E '\''class=\"(book-title|animal-name)\"'\'' || exit 0"}' | sh | tr -d '\n' | sed 's;</h2>;&\n;g' | sed 's;</h1>;&{FS};g' | sed 's/<[^>]*>//g' | sed 's/\s\{2,\}//g' | awk -F'{FS}' '$0=$2' | awk -vFS= 'OFS=""; $1=toupper($1)' | sort | uniq
コードの解説
以下、コード中のコメントでは説明しきれていない部分や、補足が必要な箇所について、解説をします。
04行目の awk
ここでは、データを取得・加工する為のコマンドを生成しています。
今回、URLにクエリを与えてやる事によって、書籍情報を順次取得していきます。
しかし、ここでは、どこまでクエリを与えてやれば良いのか分からない、終了条件が不明という問題点があります。
そこで、今回、終了条件として、 grep
の終了ステータスを用いる事にしました。
seq
で生成したクエリを curl
にURLとして与えてやり、HTMLソースを取得する事にします。
ここで、表示する書籍情報が無い場合、HTMLソースに class="book-title"
もしくは class="animal-name"
の文字列が含まれていない事を利用します。
通常、 grep
は、パターンに一致するレコードが見つからなかった場合、非ゼロの終了ステータスを返します。
そこで、 grep
の後に、直前に実行したコマンドの終了ステータスが非ゼロの場合のみに実行する処理として、 || exit
を追記します。
こうする事によって、書籍情報が無いページが得られた場合、 grep
が非ゼロの終了ステータスを返し、その結果 exit
が実行され、一連のコマンド列の実行が終了されます。
また、ここでは nkf
による文字エンコーディングの変換と、 sed
による文字列の置換をおこなっています。
その理由は、書籍『Regular Expression Pocket Reference, 2nd Edition』の参照先が不正である事にあります。
この書籍だけ、何故か参照先が英語版ではなく、下記URLのドイツ語版になっています(指定ミス??)。
https://www.oreilly.de/buecher/120244/-regul%C3%A4re-ausdr%C3%BCcke---kurz-%26-gut.html
ドイツ語版のタイトルには、非ASCIIの文字が含まれているため、そのままでは書籍名のレコード全体が、 grep
側ではバイナリ列と判定されてしまいます。
そこで、 nkf
を使って文字エンコーディングを変換することで、 grep
で書籍名レコードを抽出出来るようにします。
また、抽出しただけでは書籍タイトルがドイツ語版のままであるため、 sed
で英語タイトルに修正しています。
19行目の sed
フィールドの区切り文字、フィールドセパレータには、一般的に半角スペースや ,
カンマ等が用いられます。
しかし、今回扱うデータでは、各フィールドの値に半角スペースやカンマが使用されています。
ですが、 awk
では、フィールドセパレータは文字だけでなく、文字列や正規表現も利用可能です。
そこで、今回、フィールド値として用いられる可能性が低い文字列、 {FS}
を、フィールドセパレータとして使いました。
19行目の sed
では、このフィールドセパレータ {FS}
を挿入する処理をおこなっています。
また、23行目の awk
では、実際にフィールドセパレータに {FS}
を指定しています。
参照Webサイト
http://www.oreilly.com/animals.csp (Sat Jan 7 19:16:16 JST 2017 アクセス)
→ O’Reilly MediaのWebサイト内にある、アニマルシリーズで使用した動物を調べられるページ。
雑記
今回得られたリストを用いて、技術カテゴリ毎、もしくは動物の分類(属・科)毎に集計してみると、傾向が見えてきて面白いかもしれない。
→ ただ… 書籍のトピックを技術毎にカテゴリ化するのは、元データには該当する情報がないので、難しいかもしれない。
→ また、動物の分類毎にカテゴリ化するのも、動物名を学名に変換する必要があるので、こっちも難しいかも…動物の英語名称を学名に変換するのって、シェル芸で上手く出来ないのかな…
今回、オライリーの書籍をリスト化してみて気づいた。
→ 日本語版が出版されていないオライリー書籍って、けっこう多いみたい。日本語版が存在しない書籍のうち、以下の物が面白そう。
Regular Expression Pocket Reference, 2nd Edition
正規表現ポケットリファレンス 第2版。BREからERE、そしてPerl等の拡張実装も扱っているので、純粋に便利そう。
まさかの
grep
のポケットリファレンス! 内容がすごく気になるぞ…!!
Effective awk Programming, 4th Edition
GNU awk
の公式ドキュメント、『Effective awk Programming』の紙バージョン。紙版が存在する事と、紙版をオライリー社が出していた事は、ちょっと意外だった。
GNUの公式ドキュメントなので、GNUのWebサイトからでも閲覧可能。 https://www.gnu.org/software/gawk/manual/
(追記)
Twitterにて @Heliac1999さんに教えていただいたテクニック。curl
では、URLを特定の数値範囲として展開したり、更に数値の増分の指定等も出来るとの事。
@gin_135 curlの場合、
— ゾンビプログラマ (@Heliac1999) January 7, 2017
$ curl -s https://t.co/rN8bQ192YK"[0-$max:20]"
なんて書き方ができるので、
@gin_135
— ゾンビプログラマ (@Heliac1999) January 7, 2017
max=$(curl -s https://t.co/qxtBramzEl | grep -Po '<div class="pagination pagination--top".+of\s+\K\d+(?!:.+$)')
なんてしてもいいかも。
このテクニックを利用すれば、ややこしいコマンド列を生成するなんて事をしなくても、もっとシンプルに実現できそう!
…てか、ページ内に最大レコード数が書いてあったのねorz
curl -s http://www.oreilly.com/animals.csp?x-o="[0-$(curl -s http://www.oreilly.com/animals.csp |grep 'class="pagination pagination--top"' |grep -o 'of [0-9]*' |cut -d ' ' -f 2):20]" |
tr -d '\n' |
sed 's;</h2>;&\n;g' |
sed 's;</h1>;&{FS};g' |
sed 's/<[^>]*>//g' |
sed 's/\s\{2,\}//g' |
awk -F'{FS}' '$0=$2' |
awk -vFS= 'OFS=""; $1=toupper($1)' |
sort |
uniq
コマンド置換を使って無理やりワンライナー化したので、1行目の curl
が長ったらしくなってしまったけれど…
それでも、元のコードよりはシンプル&分かりやすくなったはず。感謝!