まちカドまぞくのプロマイド一覧をシェル芸で作る

# やること
- [まちカドまぞくプロマイド一覧ページ](https://gist.github.com/eggplants/5a77b98aa678e3ce0111d5bf417e66d1)をシェル芸で作る.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278919/c323dbdb-5b11-6798-3b62-60ae5cefd0c3.png)



# 動機
- この度ローソンプリントからアニメ「まちカドまぞく」の素晴らしい各シーンを切り取ったプロマイドが公開された.

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">TVアニメ「まちカドまぞく」のキャラクター・1話～12話の名シーンをブロマイドとして店内マルチコピー機サービス「ローソンプリント」で販売開始♪<br>詳しくは→<a href="https://t.co/ByNiJOMOHY">https://t.co/ByNiJOMOHY</a><a href="https://twitter.com/hashtag/%E3%83%AD%E3%83%BC%E3%82%BD%E3%83%B3?src=hash&amp;ref_src=twsrc%5Etfw">#ローソン</a> <a href="https://twitter.com/hashtag/%E3%83%AD%E3%83%BC%E3%82%BD%E3%83%B3%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88?src=hash&amp;ref_src=twsrc%5Etfw">#ローソンプリント</a> <a href="https://twitter.com/hashtag/%E3%81%BE%E3%81%A1%E3%82%AB%E3%83%89%E3%81%BE%E3%81%9E%E3%81%8F?src=hash&amp;ref_src=twsrc%5Etfw">#まちカドまぞく</a> <a href="https://t.co/2VeUvwnQpz">pic.twitter.com/2VeUvwnQpz</a></p>&mdash; ローソンマルチコピー機 (@lawsonmulticopy) <a href="https://twitter.com/lawsonmulticopy/status/1232138192423464960?ref_src=twsrc%5Etfw">February 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

- しかしカットの枚数が**全576枚**と多く, 20枚/1pで**28+1p(16枚)**にページネーションされており, お気に入りの画像を探すのが大変だった.
- 「じゃあMarkdownで一覧を作ろう！」と思った.

# やった
## 結論
- 結局**1行**のワンライナーになった.

```bash
curl -s 'https://lawson-print.com/products/categories/machikado?page=[1-29]'|egrep -B8 '<span>まちカドまぞく__[^<]+'|sed -r 's/^\t+//g;/^$/d;s/^--/@/g;1s/^/\n/'|awk -F\\n '{print substr($2,34,43)"\n"substr($8,10,29)"\n"substr($9,7)}' RS=@|ruby -e 'u="https://lawson-print.com";`dd`.split(?\n).each_slice(3).to_a.uniq.map{|a,b,c|puts"## [#{c.scan(/^[^<]+/)[0]}](#{u+b})\n![img](#{u+a})"}'>mazoku.md
```

## 分解
- きれいにしたもの

```bash
#!/bin/bash
curl -s 'https://lawson-print.com/products/categories/machikado?page=[1-29]'  |
egrep -B8 '<span>まちカドまぞく__[^<]+'  |  sed -r 's/^\t+//g;/^$/d;s/^--/@/g;1s/^/\n/'  |
awk -F\\n '{print substr($2,34,43)"\n"substr($8,10,29)"\n"substr($9,7)}' RS=@ |
ruby -e 'u = "https://lawson-print.com"
`dd`.split(?\n).each_slice(3).to_a.uniq.map { | a, b, c |
  puts "## [#{ c.scan(/^[^<]+/)[0] }](#{ u+b })\n![img](#{ u+a })"
}' > mazoku.md
```

# やったこと
- 以下, ワンライナーは目がつかれるので分解して解説する

## 1行目: クロール
```bash:1行目
curl -s 'https://lawson-print.com/products/categories/machikado?page=[1-29]' |
```
- 29ページある[プロマイド一覧ページ](https://lawson-print.com/products/categories/machikado)のソースを取得.
- ここで得られる出力は, Chromeなら`view-source:https://lawson-print.com/products/categories/machikado`で確認できる.
- `curl`コマンドの`-s`は進捗状況やエラーを表示せず, 取得したページソースだけを出力するやつ.
- 引数URLの一部が連番なら`[START-END]`で指定できる.(ここでは`1-29`)

## 2行目: プロマイド名, プロマイド詳細ページURL, 画像URL部分の切り出し
```bash:2行目
egrep -B8 '<span>まちカドまぞく__[^<]+' | sed -r 's/^\t+//g;/^$/d;s/^--/@/g;1s/^/\n/' |
```
- `grep`コマンドの`-B <int>`は, 検索に引っかかった行の**n行前**を一緒に表示する.(**n行後**は`-A <int>`)
- `egrep`コマンドは`grep -E`と同じく, 検索語での拡張正規表現を有効にする.
- ここでは`画像リンク行~プロマイド詳細ページリンク行~プロマイド名行`を切り出している.

```html:切り出し結果(※インデントは削除)
<img class="img-responsive" src="/media/23eb44c3-94e2-4f7b-8bd6-9829e262fb92" />

</div>
<!-- Item details -->
<div class="item-details">
<!-- Name -->
<h5>
<a href="/products/describe/1017190001">
<span>まちカドまぞく__01　L</span><br />
--
<img class="img-responsive" src="/media/e90a3d50-2557-478f-8088-bcec9e4418a2" />

</div>
<!-- Item details -->
<div class="item-details">
<!-- Name -->
<h5>
<a href="/products/describe/1017190002">
<span>まちカドまぞく__01　2L</span><br />
--
(略)
```

- `sed`コマンドの`-r`は拡張正規表現を有効化し, `+`, `\t`, `(`, `)`, `|`をエスケープ無しで使用できる.
- ここでは:
  - ページソースのインデントを削除(`s/^\t+//g`)
  - 空行削除(`/^$/d`)
  - `grep`のレコードセパレータ`--`を`<-- -->`と区別するため`@`に置換(`s/^--/@/g`)
  - 後続の`awk`で1行目を`$2`に入れるため改行を挿入する(`1s/^/\n/`)
 
```html:sed後
(空行)
<img class="img-responsive" src="/media/23eb44c3-94e2-4f7b-8bd6-9829e262fb92" />
</div>
<!-- Item details -->
<div class="item-details">
<!-- Name -->
<h5>
<a href="/products/describe/1017190001">
<span>まちカドまぞく__01　L</span><br />
@
<img class="img-responsive" src="/media/e90a3d50-2557-478f-8088-bcec9e4418a2" />
</div>
<!-- Item details -->
<div class="item-details">
<!-- Name -->
<h5>
<a href="/products/describe/1017190002">
<span>まちカドまぞく__01　2L</span><br />
@
(略)
```

## 3行目: 不要行の削除, 必要部分の抽出
```bash:3行目
awk -F\\n '{print substr($2,34,43)"\n"substr($8,10,29)"\n"substr($9,7)}' RS=@ |
```

- `awk`コマンドのフィールドセパレータを改行(`\n`), レコードセパレータ(`RS`)を`@`に設定.
- こうすると`@`までの各行が変数`$1~$9`までに順に入る.
- `substr(str,N,L)`は文字列の`N`文字目から`L`文字切り出す.

```html:awk後
/media/23eb44c3-94e2-4f7b-8bd6-9829e262fb92
/products/describe/1017190001
まちカドまぞく__01　L</span><br />
/media/e90a3d50-2557-478f-8088-bcec9e4418a2
/products/describe/1017190002
まちカドまぞく__01　2L</span><br />
(略)
```

## 4~7行目: Markdownに整形(Ruby)
```Ruby:4~7行目(これを引数にrubyの-eで実行)
u = "https://lawson-print.com"
`dd`.split(?\n).each_slice(3).to_a.uniq.map { | a, b, c |
  puts "## [#{ c.scan(/^[^<]+/)[0] }](#{ u+b })\n![img](#{ u+a })"
}
```
- 標準入力を`dd`で受け取って行ごとに配列に入れ, `each_slice(3).to_a`で3要素ずつで配列内配列を作る.
- `uniq`は新着リリース欄に表示されたものを削除している.
- 各要素を以下のMarkdown形式にして出力.

```md:整形
## [プロマイド名](詳細ページリンク)
![img](画像リンク)
```
- `> mazoku.md`で出力して完成！

```md:mazoku.md
## [まちカドまぞく__01　L](https://lawson-print.com/products/describe/1017190001)
![img](https://lawson-print.com/media/23eb44c3-94e2-4f7b-8bd6-9829e262fb92)
## [まちカドまぞく__01　2L](https://lawson-print.com/products/describe/1017190002)
![img](https://lawson-print.com/media/e90a3d50-2557-478f-8088-bcec9e4418a2)
(略)
```

# まとめ
- シェル上でクローリングからスクレイピングまでをやった.
- 実行しながらデバッグしやすく, 感覚的にパイプで処理を繋いで書けて気持ちいい.
- まちカドまぞく1~5巻[発売中！](https://www.amazon.co.jp/kindle-dbs/entity/author/B0761TM2C5?_encoding=UTF8&node=86141051&offset=0&pageSize=12&searchAlias=stripbooks&sort=date-desc-rank&page=1&langFilter=default#formatSelectorHeader)