Webスクレイピングのコードを書いていて気づいたこと

この3日くらい暇なときに Web スクレイピングをする javascript コードを書いているんだが、処理が複雑になってくるにつれてコードの見通しが悪くなってきた。コードが汚いという理由によってやる気がなくなるレベルだったので、今日はリファクタリングに費やそうと思ってコードに色々と手を入れてみた結果、コードの治安が川崎市から文京区くらいになった。以下、スクレイピングのコードを書くときに気をつけるべきだと思ったこと。

  • htmlをwebからフェッチする関数とhtmlをスクレイピングして情報を抽出する関数を分ける
    • 1つの関数に1つの役割を持たせるという当たり前の原則ですが、できてないところがあった
  • 関数の名前に命名規則を設ける
    • 関数の名前はgetHogePageみたいな感じで{動詞}{名詞}みたいな感じになると思うが、この動詞と名詞それぞれについてちゃんと命名規則を考えてからコードを描き始めた方が良い。雰囲気でgetとかfetchとかscrapeとかを使い分けてると後で見返したときに悲しくなってくる。名詞についても、htmlページに階層がある場合は、この階層のページはIndexPageで、この階層はItemPageで...などと事前に決めておくと調子よくコードが書ける
  • async/awaitを使う
  • 例外を使う
    • 最近よくgo言語を書いていて、例外を使わずいちいちエラーチェックをした方がわかりやすいという考えになっているんだけど、大量の web ページをスクレイピングするとエッジケース的なエラーが数種類出てしまう場合が多いので、よく発生するエラーだけを手で頑張って処理して残りは例外としてまとめてcatchして処理する方が良さそう

街の風景の切り替わりと自転車

街の景色の移り変わりを見たくて、昔からたまに散歩をしている。ぼーっとしながら歩いていて、住宅街にいたはずがいつの間にか大通りに出ていたり、人が多い駅前からちょっと歩いたところでスッと伸びた誰もいない緑道を発見すると大変嬉しい気分になる。

同じ住宅街でも、もちろん場所によって雰囲気が違う。例えば、自由が丘近辺だとイメージ通り完全に金持ちの家が多い感じがするが、そこから目黒/五反田方面に歩いていくと意外とぼろぼろの家や意味不明な人形が窓に大量に飾ってある家が多くなって庶民的になる。そういうことに気づくと、人それぞれの暮らしがあるということに思い至って、人それぞれの暮らしがあるんだなと思ったとき専用の感情になる。

似たような話で、低い家が立ち並ぶ完全な住宅街から高層ビルが見え隠れする街もなんとなく良い。例としては、西新宿から見た新宿とか要町から見た池袋とかがある。なぜ良いかは説明が難しいが、人はみんな生活があり、低い家から高いビルに働きに行くこともあるんだろうと勝手にに想像できるのが良いのかもしれない。そういう街に行くと、FF7でスラム街の中に神羅カンパニーが急に立っているのを思い出してFF7がやりたくなるという効果もある。

最近、会社の自転車通勤制度という存在に気づいたので、わざわざ自転車を買って会社まで30分くらい漕いで通っているのだが、自転車は徒歩よりも速く移動できて良いということがわかった。速く移動できて良いというのは、速く移動できて良いということも当然あるが、街の風景切り替わり見太郎として徒歩と同じ時間移動しても風景の変化が多いところがとくに良い。30分の通勤でも何個も街を通れるのでお得な気分になる。これからも、自転車で(徒歩でも)いろんな風景を見たい。

TOEICを受ける

会社の制度でTOEICがタダ&業務時間内で受けられるらしいので、来月受けることにした。TOEICは今まで3回申し込んで、

  • 1回目(5年前くらい):なんとなく受けてみて850点くらい
  • 2回目(3年前くらい):試験直前でTOEICを受ける必要性が1ミリもないことに気づいて休養
  • 3回目(2年前くらい):就活のために受けて970点

だったので、今回は満点をとりたい。TOEICの点が良くてもまったく意味がないと思いきや、何かの拍子にTOEICの話になると意外とすごい人みたいになり、その他の能力が0でも許されの閾値が少しだけ高くなるというメリットがある気がする。本当に英語が話せる人からするとTOEICが何点とかいう話は1ミリたりとも意味をなさないと思うが、日本の普通の会社だとそうでもないということが就職してみてわかった。

とりあえず単語とかフレーズを覚えるかと思って↓の本をやってます。

ディープラーニングの基本を思い出した

就活するときにデータサイエンスを勉強していた一環でディープラーニングを少しやっていたのだが、その後興味が持続せず放っておいていた。最近興味が復活してきたので、もう一回やってみようと思って今日ディープラーニングの基礎を思い出していた。

とりあえず、前に通読した、

をぱらぱらと読み返したのと、新しく

Deep Learning with Python

Deep Learning with Python

を2章まで読んだ。

後者の本は著者が Keras の作者なんだけど、隙あらば Keras の自慢を入れてくるところと、今後の AI の発展について急に熱く語り出すところが良かった。

思い出したこと

よかった話

  • AI の定義:普通は人間がやるような知的なタスクを自動化するための努力
  • 古典的なプログラミングはデータと規則を入力して答えを得るが、機械学習ではデータと答えを入力して規則を得る
  • 機械学習の文脈での学習とは:入力データの有用な表現手法を正解データからのフィードバックの助けを借りて探すこと
  • ディープラーニングは人間の脳とは全く関係ない数学的モデルと考えるべき
  • Kaggle では、構造化データを扱う問題では gradient boost が、知覚的な問題ではディープラーニングが勝ってるので、結局みんな XGBoost と Keras の使い方を覚える必要がある
  • ディープラーニングで扱うデータは、ほとんどの場合以下のカテゴリのいずれかになる
  • numpyのブロードキャストの仕組み
    • 2つの array のndimが異なる場合は次元の拡張が起きる
    • 次元が揃ったら、サイズの小さい方の array が繰り返されて大きい方の array のサイズに合わせられる
  • ディープラーニングは行列演算の連鎖なので、結局連続した幾何学的変換であると思って良い。青色と赤色の紙を重ね合わせてぐしゃぐしゃに丸めることを考える。この状態を入力データとして、この丸まりを解きほぐしていって2枚のまっすぐな別々の紙に戻すのがディープラーニングで分類問題を解くことに対応する
  • ディープラーニングは、例えば467238647273856次元くらいでの話なので、勾配降下法などにおいて、1次元とか2次元での直感が通用すると思わないことが大事

「リーダブルコード」を読んだ

読みました。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

読みやすいコードを書くための方法がまとめられた本。読みやすいコードを「他人(未来の自分も含む)が最短時間で理解できるコード」と定義し、そのようなコードを書くために何をすれば良いのかが書かれている。いろいろと気をつけるべき点はあるが、とにかく常識的に考えてコードの読み手が混乱したり嫌になったりしないことを徹底するべきということが永遠に述べられている。とりあえず、すぐにできそうなこととして、以下の3点に気をつけていこうと思った。

  • 明確に書く
    • 変数名を具体的で意味のわかりやすいものにする
    • 制御構文の順序や使い方を直感に沿うようにする
  • 短く書く
    • コードからわかること/コードを改善すればわかることをコメントしない
    • 未使用なコードは削除する
    • ライブラリに親しんでおく
  • 変数の値を把握しやすくする
    • 変数のスコープはできるだけ狭くする
    • イミュータブルな変数を使う

以下読書メモ。

1章 理解しやすいコード

  • 他人が最短時間で理解できるようなコードを書く
    • 「他人」には未来の自分も含まれる

2章 名前に情報を詰め込む

  • より明確な単語を選ぶ
    • 単にgetとするよりもfetch/download、stopとするよりもkill/pause
  • tmp/retvalのような意味のない名前は避ける
    • ただし、swapに使う一時変数など「意味がない」変数ではあえて使ったほうがわかりやすいことも
  • 変数名の中に不要な単語があれば捨てる。逆に、変数名が長くなっても意味を明確にできるなら付け足す

3章 誤解されない名前

  • 範囲を指定するときは変数名で境界値を含むか含まないかを明確にできる
    • max/minを使うと境界値を含むことを明示できる(limitでは不明瞭)
    • first/lastを使うと境界値を含むことを明示できる(start/stopでは不明瞭)
    • begin/endでは範囲の始まりは含んで終わりは含まないという慣例がある
  • 軽量なアクセサにはget、重い処理にはcompute/countを使うなど言葉が持つイメージや慣例を重視する

5章 コメントすべきことを知る

  • コメントする価値のあることだけコメントする
    • コードからすぐわかることをコメントしない
    • コメントする代わりに変数/メソッド名をわかりやすくできないか考える
  • コメントには自分の考えを記録する
    • 何を思ってそのコードを書いたのか
    • 特に平均的な読み手が驚きそうな処理にはそのように書いた理由を書く

6章 コメントは正確で簡潔に

  • 曖昧な代名詞を避けて、簡潔に分かりやすく書く
  • 入出力のコーナーケースについて書く場合、実例を使うと良い(ex: // Strip("abba/a/ba", "ab"は"/a/"を返す))

7章 制御フローを読みやすくする

  • if/else文の並び順に注意する。以下の方針をもとに判断
    • ifの条件は肯定形を使う
    • 単純な条件を先に書く
    • 関心を引く/目立つ条件を先に書く
  • 「ガード節」を使って関数から早くreturnすると分かりやすい場合が多い
    • ネストを浅くすることにも寄与

8章 巨大な式を分割する

  • 説明変数を使って式を分割することで式の意味を明確にする
if line.split(':')[0].strip() == "root":

# 説明変数 username により意味を明示
username = line.split(':')[0].strip()
if username == "root":

9章 変数と読みやすさ

  • 不要な変数は削除する
    • 意味を明確にすることに寄与しない説明変数は削除
    • 一度しか使わない要約変数は削除
    • フラグ変数はループを途中で抜ける/returnを使えば不要になることも
  • 変数のスコープは可能な限り縮める
  • 変数を操作する場所が増えると現在値の判断が難しくなるため、変数をイミュータブルにすると良い
  • 変数の定義は変数を使う直前に行う

10章 無関係の下位問題を抽出する

  • 下位の問題を関数に抽出することで、高レベルの目標に集中できるとともに、処理を再利用したり単体テストしたりできるようになる

13章 短いコードを書く

  • コードを常に軽量に維持する
    • 未使用のコードを削除する
    • プロジェクトをサブプロジェクトに分割する
  • ライブラリを利用する。たまには標準ライブラリを時間をかけて読んでみて、どんなことができそうか感じ取っておく