ターミナル / シェルスクリプトから JSON をダンプする. ずっと欲しかったので今日仕事をサボって作ってた.
シェルスクリプトからJSONを吐くことがある. 例えば Slack の incoming webhook は JSON データをPOSTで送る. Slack じゃなくても今どきの WebAPI ではよくある. シェルスクリプトから雑に curl で投げたいときに JSON データだけどうにかしてダンプする必要がある. 例えば,
cat <<EOM > payload.json
{
"channel": "#hoge",
"text": "$TEXT"
}
EOM
みたいにすれば簡単なテンプレート言語として機能してくれる. のだけど, $TEXT
に改行が入っていたら, クオーテーション文字が入っていたら, これはおそらく正しくJSONとして機能しない(改行は大丈夫かな?どうだろう?).
これは別に JSON というデータフォーマットに限ったことじゃない. シェルスクリプトに優しいデータフォーマットなんてもはやない. YAML なら安全に吐けるというなら YAML を吐いて JSON に変換してもいいが, それも難しい.
というわけで作った. jq
という JSON からデータをフィルタ(射影)するコマンドがあるが, おおよそそれの逆をするという気持ちから qj
とした.
README の例だが, こんな感じ:
qj -e .=3
3
qj -e .x=1 -e .y=2 -e .z[0]=3
{"x":1,"y":2,"z":[3]}
qj -e '.hello="world"'
{"hello":"world"}
qj -e '.persons[1].name="Alice"'
{"persons":[null,{"name":"Alice"}]}
jq 方式の値一個をフィルタする式と, その値のペアを =
で区切って指定してく. 値の数だけこれを並べないといけないので, 組み立てる JSON の割にかなり冗長に書いていかないといけない.
だから本当は jq がそうであるようにパイプを使って上手いこと
qj -e '.x | (.y=2, .z=3)'
{
"x": {"y": 2, "z": 3}
}
このくらい書けるのがいいんだけど, そういうことまでは書けない.
また, このコマンドが欲しかったモチベーションの全てが, 文字列のエスケープが面倒くさいということだったので, 値は特にパースできなかったときは丸々全て文字列ということにしている.
qj -e '.=hoge'
"hoge"
qj -e '.=ho"ge'
"ho\"ge"
qj -e ".=$(seq 3)"
"1\n2\n3"
クオーテーションが入ってても改行が入ってても出来るだけ文字列として解釈するようにする.
シェルスクリプトから Slack の incoming webhook に POST は次のように出来る
qj -e .channel=#channel .username=bot .text=hello! .icon_emoji=:ghost: |
curl -XPOST --data @- https://hooks.slack.com/services/XXXXXXXXXXXX
このくらいの単純な JSON なら記述量も減るし, エスケープのこと考えなくていいし楽.
これは中の実装に関してはまじでただの tanakh/easy-scraper のラッパーです.
それはともかくめちゃくちゃ便利. web スクレイピングといえば, 私は次の二つを取っていた.
1 で十分ということもある. これでいいときはこれでいい. 2 を選ぶより楽だし都合がいいこともある. そうでない場合は 2 をする必要がある. 問題は所望のデータの場所を指定するパスを保守するのが面倒だということ. 最初に必死こいてそのパスを探すのに, あるとき簡単に変わってしまう.
easy-scaper は XML に関するパターンマッチを実現してくれる. web-grep ではプレースホルダーとして {}
を用いて例えば次のようにパターンを記述する.
<a class="link">{}</a>
これは link
というクラス属性を持つ a
タグに包まれた中のテキストを表現する. HTML 自体を取得したいことはないからテキストにしか {}
はマッチしない.
{}
は属性にも使える.
<a class="{}"></a>
これは a
タグのクラス名を取得する.
このパターンマッチはHTML中の唯一つにマッチすることを初めから仮定していないのもいい. パスでデータの場所を指定する方法はちょうど一つを指しているが, このパターンで指定する方法はマッチしなければゼロ個だし, 複数にマッチすればその全てを列挙してくれる. 例えば <li>
で列挙されたデータを全部取得したいというときに便利だ.
seq 3 | sed 's#.*#<li>&</li>#' | web-grep '<li>{}</li>'
1
2
3
Webで連載されてる漫画の更新情報をチェックする. master ブランチには載ってないけど, 更新されてたら Slack に通知するのまで裏で実装されている.
更新チェックするのには, 適当な web ページの適当な箇所を見つければいい. 例えば作品ページがあって, そのどこかに最終更新日が書かれてることがある. その該当の innerText を見ればいい. 作品ごとのページが与えられてない場合は, 最新話へのリンクがどこかに貼られてるのを探してそのリンクを見る, とか. ここにさっき言った web-grep を使ってる.
web ページ丸々の差分を見る, ヘッダにある ETag を見る, とかはたいてい通用しない. 出版社がきちんと運営するような web ページはたいてい毎日, 漫画更新とは関係ない箇所の変更をしているものだ. 別な漫画の新着情報が載ってたりね.
web-manga-check は徹底してシェルスクリプトとして書かれている. そこで Slack への通知にはさっき言った qj を使って組み立てた JSON を curl でポストしている. テキスト中に "
が含まれているばっかりに, という経験を何度も味わっているが, これなら安心.
投票率は区の民度(なんだかんだ言っても)
結果は以下の表のとおり. 生データは https://gist.github.com/cympfh/508d88c532b3edc59c4fb42c5b2b6cb7 として置いておきます.
rank | 地区 | 投票率 (%) | 参考 URL |
---|---|---|---|
1 | 千代田区 | 64.65 | https://www.city.chiyoda.lg.jp/koho/kurashi/senkyo/tochiji/tohyo.html |
2 | 文京区 | 62.98 | https://www.city.bunkyo.lg.jp/kusejoho/senkyo/r02tochiji/r2tochiji.html |
3 | 練馬区 | 61.14 | https://www.city.nerima.tokyo.jp/kusei/senkyo/kekka/totijisenkyo/tohyo.html |
4 | 台東区 | 61.07 | https://www.city.taito.lg.jp/index/kusei/senkyo/totiji/2chijitouhyo.html |
5 | 世田谷区 | 58.26 | https://www.city.setagaya.lg.jp/mokuji/kusei/007/002/d00186300.html |
6 | 中央区 | 58.03 | https://www.city.chuo.lg.jp/smph/kurasi/senkyo/sokuho/r0207tochijisen/r2tochijisenkyo_tohyosokuho.html |
7 | 北区 | 57.69 | http://www.city.kita.tokyo.jp/senkan/kuse/senkyo/kekka/kekka/190721_sangi-tohyo.html |
8 | 杉並区 | 57.61 | https://www.city.suginami.tokyo.jp/senkyo/r2tochiji/1059073/index.html |
9 | 江東区 | 56.93 | https://www.city.koto.lg.jp/610102/tochiji2020/tohyo.html |
10 | 品川区 | 56.21 | https://www.city.shinagawa.tokyo.jp/PC/kuseizyoho/kuseizyoho-sensei/kuseizyoho-senkyo/tochijisenkyo/hpg000028789.html |
11 | 渋谷区 | 56.02 | https://www.city.shibuya.tokyo.jp/kusei/senkyo/20200705to_tou.html |
12 | 中野区 | 55.76 | https://www.city.tokyo-nakano.lg.jp/dept/711000/d029097.html |
13 | 江戸川区 | 55.36 | https://www.city.edogawa.tokyo.jp/documents/18856/2115.pdf |
14 | 墨田区 | 55.08 | https://www.city.sumida.lg.jp/senkyo_sokuho/tohyo.html |
15 | 豊島区 | 54.82 | https://www.city.toshima.lg.jp/361/kuse/senkyo/2006191019.html |
16 | 目黒区 | 54.62 | https://www.city.meguro.tokyo.jp/smph/gyosei/senkyo/tochiji/sokuhou.html |
17 | 新宿区 | 54.59 | http://www.city.shinjuku.lg.jp/senkyo/tochiji_20ji_output.html |
18 | 荒川区 | 54.45 | https://www.city.arakawa.tokyo.jp/a004/gikaisenkyo/senkyo/tochijisokuho.html |
19 | 板橋区 | 53.5 | https://www.city.itabashi.tokyo.jp/kusei/senkyo/1016101/1023614.html |
20 | 大田区 | 53.44 | https://www.city.ota.tokyo.jp/touhyou/t_r2tochiji_2000.html |
21 | 葛飾区 | 51.89 | http://www.city.katsushika.lg.jp/information/1000080/1020036/1011705/1023858.html |
22 | 足立区 | 49.58 | https://www.city.adachi.tokyo.jp/juyo/200705senkyoall/touhyousaisyu.html |
23 | 港区 | 49.32 | https://www.city.minato.tokyo.jp/senkan/tochijisenkyo/kaihyou.html |
いかがでしたか?
Annictはbooklog, filmarksのアニメ版. APIを充実してユーザーに提供してくれていて, これでユーザーのレビューをアニメ作品に紐付いた状態で取得することが出来る. ユーザー \(i\) がアニメ \(j\) のレビューを書いてるという行列 \(A_i^j\) を作って, ありがちな感じで行列分解して次元圧縮とか推薦が出来る.
できた:
相変わらずUIデザインはなんも分からん.
業務でいつもやってることを趣味でやっただけ. 行列分解は implicit がオススメ. 重み付きで行列分解したりロジスティック回帰してくれたりする. データを取得するのにAnnictが提供してるGraphQLを使った. GraphQL生まれてはじめて触るので何もわからんが, グラフでもなんでもないものを強引にグラフとしてるようにしか見えない.
fastAPI 便利
Vue.js 触ってみた. 便利だ.
友人が作ってるのに感化されて自分でも試してみた.
私がレシピとして第一に参照してるのはこれ:
自分が組み立てたレシピは以下の通り.
いくつかの材料工程は不要だと思ってる. 例えば肉をヨーグルトにつける作業は無駄だと思う. 臭み消しと柔らかくするための操作だと思うのだが, まず日本のスーパーで手に入る肉は普通臭くない. 柔らかさは…よくわかんないです. 柔らかく煮込んだりビーフシチューみたいな作り方するわけでもなし.
玉ねぎ なんでもいい. 具材 お好きに. 肉をヨーグルトに漬ける操作, しなくていい. ただ乳製品を入れたまろやかさが好きなら牛乳入れるといい. 普通の日本のカレーでも牛乳入れたりするしね. トマト 自分で粉砕する工程が面倒だから初めからそのようなものを買えばいい. トマトピューレとかもあるけど, 飲む用のトマトジュースを初めから買うのが一番安価だと思う. ただ適当にトマトジュース買うと塩分とか添加しまくってるのが普通だから何にも添加されてないものを選ぶこと. 探せばそういうのもあります. スパイス ターメリックとガラムマサラだけあれば割とカレー. クミンシードくらいはあったほうがいいとは思うけど, 無くてもちゃんとカレーはカレー.
工程をいくつか省いただけでほぼほぼ同じ.
すればいい.
最後まで作ってから保存してもいいけど, トマト(及び牛乳)を入れる手前が一番体積が小さいので, このタイミングで保存しておくのが便利だと思う.
本当にペーストくらいになるまで刻むのがインド風らしい, が, 結構扱いに困る. ペーストになった玉ねぎはどんどん油を吸うし粘度が高いと単純にヘラにくっついて炒めにくい. 私は5ミリ角くらいに留めておいてる.
ぶんぶんチョッパー なるものを使ってる. これは要するに手動のフードプロセッサー. 確かに便利だけど包丁で十分困ってないならそれでいい. ただペーストになるまで刻む(もはやミキサーする)ということを試したい場合はこういったなにか道具があると便利.
玉ねぎは3個くらいまとめて売られてることが多い. 予め全部刻んでおいてジップロックに入れて冷凍保存してる. 料理するときにまな板と包丁が要らないように準備しておくことは料理の美徳.