| @映画/ドラマ/テレビ

テヘラン

5 月からテヘランシーズン 2 が毎週 1 エピソードずつ公開されてて毎週楽しみに見てた。 Homeland もだけど、自分はどうもイランが絡むスパイものドラマが好きみたいだ。テヘランは面白かったが最後の終わり方が何それ感があった。あれでシーズン 3 なかったら納得がいかない。ショーン・トーブももっと活躍して欲しい。シーズン 3 が作られて欲しいので Apple TV+ に入ってる人は見てください。

ザ・モーニングショー

コロナに罹患してスーパー具合が悪いときにザ・モーニングショーを一気見した。いわゆる #MeToo 的な内容だが、主人公アレックス・レヴィ役のジェニファー・アニストンが 50 代とは思えぬ美しさで輝いていた。ミッチ・ケスラー役のスティーブ・カレルも中年男性の魅力を遺憾なく発揮していて良かった。同じ人物が「40歳の童貞男」の主人公を演じているとは思えないかっこよさだった。

#MeToo 的な話は映画業界などを中心に最近日本でも問題になっていたが、ザ・モーニングショーで取り上げられているのはもうちょい悪質度が低い問題な気がする。いわゆる飲み会でお持ち帰りした的なやつだ。多分裁判したら女性の方は勝てない気がする。少なくともいまの日本では。女性の方が抵抗しなかったという意味で合意の上での出来事っぽいんだけど、いまのアメリカのリベラルな基準で言うとそれはアウトで、男性側は社会的に抹殺されるようだ。そう遠くない将来に日本でもそういう空気感に変わっていくのかも知れない。村上春樹のノルウェイの森に出てくる永沢さんなんかは外務省クビになると思う。プレイボーイの人はザ・モーニングショーを見て認識を改めた方が良さげ。

Apple TV+ 、テッド・ラッソはじめカバー写真が惹かれなくてなかなか見てこなかったけど、見始めると面白い。次はフォー・オール・マンカインドを見たい。

| @ブログ

グローバルナビゲーション(右上の白い領域)内の検索ボタンを押したら Alfred 風のモーダル検索フォームが開いて、そこにキーワードを入力するとインクリメンタルサーチが実行されて逐次検索結果の記事が表示されるようにした。

これまでだと検索すると Archives ページの絞り込み検索に飛ばすだけだったが、 Archives ページに遷移せずに検索できるようになった。また Archives ページだと時系列順でしか検索結果が表示されないが、インクリメンタルサーチではマッチ度順に関連度の高いものを表示するようにしている。ただし表示するのは上位 10 件だけにして、それ以上は Archives ページで時系列順の検索に飛ばしている。

昔ながらのブログの検索 UI には不満がある。ページネーションで何ページも辿って検索結果を見ていくのは大変だし、大抵並び順が時系列順で自分が最も用事がありそうな記事に辿り着くのに時間がかかる。自分のブログの検索はタイトルのみ表示されればよくて本文のプレビューは不要だし(著者だからタイトルを見ただけでどんな記事なのか大体わかる)、何ページもページネーションせずに一覧でガッと検索結果を見たい。それに結果は時系列順ではなく関連度が高い順に並んでいて欲しい。キーワードを一部だけかすってるような最近の記事が最も関連度が高い記事を差し置いて最上位に表示されるのはいまいちだ。

今回作った Alfred 風インクリメンタルサーチではこれらの問題が解消されていて非常に満足。自分にとって自分のブログが世の中の情報の中で一番参照頻度が高いし、そのブログで効率的に情報を取り出せるのは大切なことだと思う。

| @ブログ

ダークモードとライトモードの切り替えをグローバルナビゲーション(上の方の半透明の白い領域)から簡単に行えるようにした(これまではこのサイトについてのページで切り替える必要があった)。デフォルトは OS の設定準拠で、 OS がダークモードであればダークモードに、ライトモードであればライトモードで表示される。手動でテーマを切り替えると Cookie に設定値を保存する。機能と見た目は MDN Web Docs を参考にした。

CSS がぐちゃぐちゃなのでかなり難儀した( 3 年前にも同じことを書いている)。ダークモードとライトモードの切り替えなんて自分しかしていないと思うがかなり満足度の高い休日プログラミングだった。

| @技術/プログラミング

Rust 製の全文検索システム Tantivy を Ruby から使える Tantiny を導入したことを書いた。

結構手軽に使えるのだがやはり日本語のトークナイズ(形態素解析)ができないのでいまいちなところがあった。 Tantivy には lindera-tantivy というものがあって、 Lindera は kuromoji のポートなので、これを使うと日本語や中国語、韓国語の形態素解析ができる。 Tantiny に導入できないか試してみたが、自分の Rust 力では到底無理だった。

ちなみに関連記事の表示でも日本語の形態素解析は行っている。

MeCab に neologd/mecab-ipadic-neologd を組み合わせてナウな日本語に対応させつつ形態素解析している。

この仕組みを作ってトークナイズは Ruby で自前で行い、 Tantiny および Tantivy にはトークナイズ済みの配列を食わせるだけにした( Tantiny はトークナイズ済みのテキストを受け付けることもできる)。トークナイズを自前で行うことで辞書ファイルで拾いきれないような固有名詞もカバーできる。例えば 山と道 なんかは MeCab と mecab-ipadic-neologd にトークナイズさせると に分割されてしまう。自前のトークナイザーで単語として認識させていている。おかげで「山と道」をちゃんと検索できるようになっている

なお、自前のトークナイザーはこんなコードになっている。

class Tokenizer
  attr_reader :text

  class << self
    def run(text)
      self.new(text).tokenize
    end
  end

  def initialize(text)
    @text = text
  end

  def cleansed_text
    @cleansed_ ||= text.
      gsub(/<.+?>/, '').
      gsub(/!?\[(.+)?\].+?\)/, '\1').
      gsub(%r{(?:```|<code>)(.+?)(?:```|</code>)}m, '\1')
  end

  def words_to_ignore
    @words_to_ignore ||= %w[
      これ こと とき よう そう やつ とこ ところ 用 もの はず みたい たち いま 後 確か 中 気 方
      頃 上 先 点 前 一 内 lt gt ここ なか どこ まま わけ ため 的 それ あと
    ]
  end

  def preserved_words
    @preserved_words ||= %w[
      山と道 ハイキング 縦走 散歩 プログラミング はてブ 鐘撞山 散財 はてなブックマーク はてな
    ]
  end

  def nm
    require 'natto'
    @nm ||= Natto::MeCab.new
  end

  def words
    @words ||= []
  end

  def tokenize
    preserved_words.each do |word|
      words << word if cleansed_text.match?(word)
    end

    nm.parse(cleansed_text) do |n|
      next unless n.feature.match?(/名詞/)
      next if n.feature.match?(/(サ変接続|数)/)
      next if n.surface.match?(/\A([a-z][0-9]|\p{hiragana}|\p{katakana})\Z/i)
      next if words_to_ignore.include?(n.surface)
      words << n.surface
    end

    words
  end
end

preserved_words が手製の辞書だ。 はてなはてブ も辞書登録しておかないと MeCab だとバラバラに分割されてしまって検索できなかった。

難点としては記事更新後に自動でインデックスの更新が行われず、 cron によるバッチ処理でインデックス更新を行っている1。なので検索インデックスにデータが反映されるまでにタイムラグがある。 Tantiny でやれれば記事作成・更新時のコールバックとして処理できるのでリアルタイムに変更を検索インデックスに反映させることができるが、個人の日記なのでタイムラグありでも大きな問題にはならない。

本当は Tantiny で lindera-tantivy を使えるようにして Pull Request がカッチョイイのだが、とりあえずは自分は目的が達成できたので満足してしまった。 5 年くらい前から Rust 勉強したいと思っているが、いつまでも経っても Rust を書けるようにはならない。


  1. mecab-ipadic-neologd を VPS 上でインストールできず(めっちゃメモリを使う)、手元の Mac で Docker コンテナ化して Docker Hub 経由でコンテナイメージを Pull して VPS 上で Docker 経由で動かしている(その辺について書いてる記事: ブログのコンテナ化を試みたけどやめた) 

| @雑談

ららぽーとのニューガンダム

コロナにかかってしまった。いまはもうだいぶ回復して明日で自宅待機も終わるが、正直かなりしんどかった。

時系列

自分がいきなり感染したわけではなく最初は家族(嫁さん)だった。次に子ども、四日遅れで自分も感染した。

  • 7/10
    • 嫁さんの具合悪くなる
  • 7/11
    • 嫁さんを病院に連れて行こうと試みるが近所の病院は電話つながらず OR 予約満杯 |
    • 少し離れたところにある老人ホーム併設の病院で診てもらえ、嫁さんの陽性判明
  • 7/12
    • 子どもは家からオンライン授業
    • 夜、子どもの具合も悪くなる
  • 7/13
    • 子どもを小児科に連れて行き検査
  • 7/14
    • 子どもの陽性判明
    • 夜から自分の具合悪くなる
  • 7/15
    • 病院に連れて行ってもらうが近所の病院満杯で少し離れた病院に
    • 自分の陽性判明
  • 7/15 ~ 7/18
    • 39度の熱が出ては解熱剤で下げを 4 日間繰り返す
  • 7/19
    • 平熱に戻るが体調は万全とは言えず(座ってるだけで汗をかく)

自分がかかるまで

自分が発症するまでが結構きつくて、感染しないように部屋を分けて過ごそうと試みたがそもそも家が狭くトイレは一箇所しかないのでどうしても生活動線が重なり感染してしまった。本当はダメなのだが、家族の感染がわかったときに食料がなかったのでスーパーに買い出しに行って雑炊の素やレトルト食品、野菜などを沢山買ってきた。嫁さんのために雑炊を作り、仕事をして、洗濯をし、子どもにも食事させてなど家事育児を一人でこなさないといけないのがとにかくしんどかった。家が狭いので自分の机で子どもにリモート授業を受けさせて、自分はキャンプ用の小さい椅子とテーブルで仕事してた。そうこうしているうちに子どもも症状が出てきて、病院に連れて行って検査を受けたところ陽性だった。

自分が感染

自分はなぜか症状が出ず、すでに感染してるのかもだけどワクチンが効いてて症状が出てないのかもな〜、なんて思ってたら四日遅れで症状が出て、 38 度の熱が出てぶっ倒れた。この頃には嫁さんが回復していたので今度は自分が病院に連れて行ってもらって検査を受けた。

この病院に行くというのが難易度が高い。家の近くの病院は電話がつながらないかつながったとしても予約で一杯で診てもらえず、普段行かない離れた病院に行って診てもらった。具合が悪い状態で診てもらえる病院を探すのがハチャメチャにしんどい。嫁さんのときは自分が病院に電話して回って、自分のときには嫁さんが病院に電話して回った。病院は診てもらえるところ見つかったとしてもちょっと遠いところだったりして、車がなかったら病院に行くこともできない。首都圏は人口多くて車を持ってない人も多いだろうのでもっと大変そうだ。

インフルエンザよりもしんどい

正直コロナを舐めていて、ワクチンを三回受けてるしきっと軽い風邪の症状で済むと思っていたが、そんなことはなくて 39 度以上の熱が 4 日間出てハチャメチャにしんどかった。鼻づまり、鼻水、咳の症状は熱が下がったあとも続いた。弱毒化していると言われるが普通の風邪なんかよりも断然やっかいだ。

陽性判明後、保健所からは SMS しか来ない

嫁さん、子ども、自分のときも保健所からは電話などはない。厚労省と市役所から SMS が届くだけ。別に大した情報は書いてないし病状の報告などを求められることもない。いまは陽性者は二グループに分けられてて、基礎疾患があったり高齢だと電話がかかってきて、それ以外は SMS のみで自分で治せやということのようだ。

物資支援は受けられず

体調的なつらさのほかには外出制限的なやつがきつい。家族が発症したら同居家族は濃厚接触者となり外出禁止となる。とはいえ普通は買い置きしていないのでいきなり外出するなと言われても困ると思う。支援物資的なものがもらえるのかと思って役所に電話すると、この制度はパソコンが使えない高齢者か一人親家庭の人が対象、家族が動けるなら支援対象外、家族が買い出しに行くかネットスーパーで何とかしろと言われた。陽性者が家庭内にいる場合は家族も外出禁止ではないのか。ちゃんと税金を払っているのにパソコンが使えるというだけで行政の支援を受けられないのは不公平だと思った。結局自分は登山用に買ってたウィダーインゼリーやカロリーメイト、カルパスなどを食べて凌いだ。

検査は症状が出るまでは受けられない

濃厚接触家族の検査についても難しかった。自治体がやっている無料検査所のルールを見ると、無症状者が対象で症状がある人や濃厚接触者は検査対象外とある。なので発症前の無症状の状態で検査してもらおうと病院に電話すると無症状者は検査できないと言われる。濃厚接触者で無症状の人は自治体の検査は受けられず、保険の効かない自由診療で全額自己負担で検査を受けるか、発症して症状が出てから一般の病院で検査してもらうしかなさそうだ。

経済的なダメージ

ちなみに自分の場合は海の日の連休を使って北アルプスに登山をしに行こうとしていて飛行機や山小屋、ホテルを予約していたが全部キャンセルしなければならなくなった。山小屋はキャンセル料かからなかったものの、飛行機と下山後に後泊する予定だったホテルはキャンセル料がかかった。コロナにかかりつつ合計 20000 円くらいが飛んで行って厳しかった。清水の舞台から飛び降りる思いで準備をしていたので正直悲しい。

入っててよかった映像配信サービス

自分は Netflix と Amazon Prime ビデオと Apple TV+ に入ってる。療養中はしんどいので映画やドラマを見て気を紛らわせてた。 Apple TV+ のザ・モーニングショー面白かった。ドラマかなんかでも見てないとやってられないくらいしんどかったのでコロナ陽性と判明した瞬間、なんかの動画配信サービスに登録した方がよいと思う。

まとめ

世間的にはコロナに打ち克ったムードがあふれていて近所のスーパーでもマスクしてないおじさんなんかをよく見かけるようになったが、やっぱりコロナはなるとしんどい。インフルエンザよりもきついと思う。経済的なダメージもある。なのでかからないで済むならかからないに越したことはない。幸運にもまだコロナにかからずに済んでいる人はワクチンを三回打ってたとしても油断しない方がよい。すでに身の回りで感染した人沢山でてると思うけど、体感的に今回のやつはめっちゃ簡単にうつるので注意して下さい。

| @登山/ランニング

継続的に走り始めて一年以上が経った。マイペースでのろのろ走りをしていたつもりだったが昨年の秋くらいはたまに 6:00/km を切るくらいのペースで走っていて、 VO2max が 41 くらいになっていた。しかし持久力を高めるため1には高負荷の状態で走るより、最大心拍数2の 60% ~ 70% くらいが良いと聞いたので 7:00/km くらいのゆっくりペースで走ることにした。

花粉の季節もあって週 3 回くらい走ってたのが週 1, 2 回くらいの頻度になって、しかもペースも落としたところ、昨年 11 月頃に VO2max が 41 だったのがここ最近は 38 になり、つい先日 38 を下回ってしまった。 40 代男性だと 38 未満は平均より心肺能力が低いという判定となる( iOS のヘルスケアアプリ上)。

これはまずいと思って昨年秋くらいのペース( 5:55/km くらい)で走ろうとしてみるがかなりきつく、 5km 走り終えたときの平均ペースは 6:25/km くらいになってしまう。ちょっとサボるとみるみる走力は落ちてしまうみたいだ。

iPhone のヘルスケアアプリ内の解説記事によると、 VO2max が平均を下回ると糖尿病や心臓病、アルツハイマー病のリスクが高まるらしいので再び 40 近くまで戻したいが、一度落ちた走力を取り戻すのは年齢もあってなかなか難しい。

先週金曜日のランニングのログとヘルスケアアプリ内の VO2max についての解説記事

  1. 自分の走る動機は山に行ったときにすぐにバテないための体力作り。 

  2. 最大心拍数は 220 - 年齢 で計算できる。 41 歳の自分の場合は 179 。 

| @WWW

以前、以下の記事でこのウェブサイトへのアクセス元 User Agent について書いた。

そのとき Hatena::Russia::Crawler というのが謎だということを書いた。最近のアクセスログを見ても相変わらずこの User Agent からのアクセスが多い。またアクセス頻度も高く、同一の URL に対して何度もアクセスしている。

これはやはりはてなの名を騙った怪しいクローラーなのではないかと思い調べてみた。

まず Hatena::Russia::Crawler という User Agent からのアクセスの IP アドレスを調べてみたところ以下だった。

cat log/access.log | grep 'useragent:Hatena::Russia::Crawler/0.01' | cut -f2 | sort | uniq -c | sort -nr
    434 remote_addr:52.68.0.227
    419 remote_addr:54.249.85.140
    417 remote_addr:54.92.97.59
    379 remote_addr:54.250.227.185

whois してみると AWS で運用されているものであることがわかるが、はてなのものかは断定できない。

もしこの IP からはてなブックマークやはてなアンテナなどの User Agent でのアクセスもあれば Hatena::Russia::Crawler ははてなのクローラーであると断定できるだろう。ということで調べてみたところこんな感じだった。

zcat -f log/access.log* | grep -E 'remote_addr:(52\.68\.0\.227|54\.249\.85\.140|54\.92\.97\.59|54\.250\.227\.185)' | cut -f13,2 | sort | uniq -c | sort -nr
  16687 remote_addr:54.250.227.185      useragent:Hatena::Russia::Crawler/0.01
  16448 remote_addr:54.92.97.59 useragent:Hatena::Russia::Crawler/0.01
  16370 remote_addr:54.249.85.140       useragent:Hatena::Russia::Crawler/0.01
  16272 remote_addr:52.68.0.227 useragent:Hatena::Russia::Crawler/0.01
     73 remote_addr:54.249.85.140       useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     60 remote_addr:54.250.227.185      useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     56 remote_addr:52.68.0.227 useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     50 remote_addr:54.92.97.59 useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     31 remote_addr:54.92.97.59 useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     31 remote_addr:54.250.227.185      useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     31 remote_addr:54.249.85.140       useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     26 remote_addr:52.68.0.227 useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     19 remote_addr:54.92.97.59 useragent:Hatena::Scissors/0.01
     19 remote_addr:54.250.227.185      useragent:Hatena::Scissors/0.01
     16 remote_addr:52.68.0.227 useragent:Hatena::Scissors/0.01
      9 remote_addr:54.249.85.140       useragent:Hatena::Scissors/0.01

なんと、 IP アドレスで検索してはてなのその他のクローラーもヒットしてしまった。つまり Hatena::Russia::Crawler ははてなのクローラーということだ。

ただしググっても一切情報が出てこない。 Hatena::Russia::Crawler で検索してトップヒットするのは自分のブログだ。

改めて Hatena::Russia::Crawler による直近 30 日間のアクセス状況を調べてみるとこんな感じだ。

zcat -f log/access.log* | grep 'useragent:Hatena::Russia::Crawler/0.01' | cut -f5 | sort | uniq -c | sort -nr
  15697 request_uri:/index.atom
   9913 request_uri:/2022/04/20/integrate-charts-category-with-select-boxs
   9373 request_uri:/2022/05/04/reputation-and-interpretation
   8422 request_uri:/2022/05/11/fly-to-kamikochi-from-fukuoka
   7716 request_uri:/2022/05/16/using-tantivy-over-tantiny
   5906 request_uri:/2022/04/17/quit-using-hey
   5139 request_uri:/2021/12/29/thoughts-on-manga-subscription
   1308 request_uri:/2021/12/13/keep-a-stack-books-whether-reading-them-or-not
    787 request_uri:/2022/06/23/each-entry-title-should-be-marked-up-with-h1
    741 request_uri:/2021/12/13/keep-stack-books-whether-reading-them-or-not
    456 request_uri:/2022/06/24/if-you-feel-apple-musics-recommendation-is-awful
    100 request_uri:/2022/06/14/thoughts-on-hatena-bookmark
     46 request_uri:/2015/12/07/thoughts-on-rural-life
     46 request_uri:/2015/12/02/thoughts-on-t-on-t
     46 request_uri:/2015/12/02/thoughts-on-christmas-song
     45 request_uri:/2019/12/02/stop-drinking-outside-frequently
     16 request_uri:/2020/11/08/where-i-went-in-2019
     11 request_uri:/2015/12/05/omm-writer-music-is-nice-to-listen-to-while-writing
      5 request_uri:/2022/05/04/the-golden-maintenance-week
      2 request_uri:/2022/06/24/
      2 request_uri:/2022/06/24

index.atom はフィードの URL なので除外するとして、特定の記事に対して数千回もアクセスがある。 30 日間で 9000 回ということは一日あたり 300 回だ。 1 時間あたり 12.5 回である。何のためにこんなに高頻度でクローリングしているのだろうか。

とここまで調べたところほかの Bot 系アクセスはどうなのかと改めて User Agent 毎のアクセス数を調べてみたらこんな感じだった。

zcat -f log/access.log* | cut -f13 | sort | uniq -c | sort -nr | head -10
 124894 useragent:Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)
  65794 useragent:Hatena::Russia::Crawler/0.01
  58274 useragent:Ruby
  31493 useragent:Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1
  29454 useragent:Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)
  21492 useragent:Tiny Tiny RSS/21.11-7cfc30a (https://tt-rss.org/)
  20351 useragent:Slackbot 1.0 (+https://api.slack.com/robots)
  18765 useragent:Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)
  18395 useragent:Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)
  17942 useragent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

bingbot からのアクセスの方が Hatena::Russia::Crawler からのアクセスの 2 倍近くあった。ただし bingbot は検索エンジンのクローラーらしく、サイト全体をまんべんなくクローリングするような挙動で、特定の URL に一ヶ月間で数千回アクセスするような感じではない。

zcat -f log/access.log* | grep 'useragent:Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)' | cut -f5 | sort | uniq -c | sort -nr | head -25
    571 request_uri:/robots.txt
    352 request_uri:/
    205 request_uri:/category/misc
    160 request_uri:/2007/01/13/732
    156 request_uri:/2005/10/28/129
    150 request_uri:/2009/03/23/1010
    148 request_uri:/category/music
    146 request_uri:/archives
    144 request_uri:/category/www
    143 request_uri:/2016/07/
    143 request_uri:/2009/08/31/1074
    139 request_uri:/2010/07/05/1140
    138 request_uri:/category/photo
    138 request_uri:/2009/02/
    137 request_uri:/2006/09/09/658
    136 request_uri:/tags/netatmo
    136 request_uri:/2010/07/17/1145
    136 request_uri:/2006/07/23/611
    135 request_uri:/2014/03/
    134 request_uri:/?page=32
    134 request_uri:/2007/02/09/747
    133 request_uri:/category/shopping
    133 request_uri:/2021/07/26/how-to-get-to-kamikochi-from-fukuoka
    133 request_uri:/2011/11/03/finally-got-hhkpro2
    132 request_uri:/2006/01/

Hatena::Russia::Crawler は同一 URL に数千回もアクセスして何をしているのだろう? 謎は深まるばかりだ。