通勤のバス事情
今現在、職場までバス通勤をしています。
私は現在沖縄に住んでいて、沖縄には電車がありません。沖縄は那覇以外だと車通勤が主流ですが、個人的に運転が嫌いで、通勤の時間帯は渋滞も相まってかなりストレスです。それ以外にもいくつか理由があり、現状はバスで通勤しています。
ただ、バス通勤もいくつか辛みがあります。
職場近くのバス停は比較的バスが通る頻度は多いのですが、自宅近くのバス停まで行くバスは本数が限られており、乗り遅れると次のバスまでそこそこ待ち時間が発生してしう。また、バスは交通渋滞の影響を受けるので、通勤ラッシュの時間帯や天気が悪い日だと予定時刻があてにならず、バス停に到着する時間が日によって変動する。
一応、沖縄だけのバスに対応したバス現在位置検索サイトがあります。
このサイトで目的のバスの現在地を確認しておけば乗り過ごすことはほぼないですが、タブを開いた状態にしてサイトを定期的に確認しなきゃいけないのはちょっと面倒。目的のバスが、乗りたいバス停に近づいてきた時に、通知が来るような仕組みがあると嬉しい。
ということで、作ってみることにした。
スマホに通知が来るようにすれば出勤と帰宅の両方で使えて便利ですが、いきなりスマホアプリにするにはちょっとハードルが高そうだったので、一旦帰宅のバスだけをターゲットにして、職場のPC(Windows)に通知が来る仕組みを実装してみることに。
調査
バス現在位置検索サイトが公式でAPIを提供してくれていれば楽に実装できるので、APIがないか調べて見ましたが、サイトを見る限りAPIは提供していなさそうでした。
サイト内をブラウザのDevToolsでも軽く調査して見ましたが、APIとして利用できそうなものはなかったので、とりあえずはブラウザ自動操作によってほしい情報を取得する方向で実装してみることに。
技術選定
あまりこだわりはありませんでしたが、なんとなくnode.jsで実現するのが楽しそうだなと持ったので、直感を信じてnode.jsを選択。
ライブラリ選定
ブラウザ操作は、Playwrightを使ってみたいなーと前々から思っていたので、Playwrightで実装。
OSへの通知はnode-notifier
というライブラリで簡単に実現できそうだったので、通知はこのライブラリを使うことに。
定期実行に関しては、実装を始める前段階での構想は、OSのタスクスケジューラを使うか、プログラムを常時起動させておいてJavaScriptのsetTimeout
関数などを使って定期実行する想定でした。
ただ、nodeで定期実行を良い感じにできるライブラリがないか調べたところ、node-cron
というライブラリでが良さげでした。cronのようにスケジューラを設定して定期実行させることができそうだったので、node-cron
を使うことに。
ということで、今回使用したライブラリは以下。
- Playwright:ブラウザ操作用
- node-notifier:通知用
- node-cron:定期実行用
仕様
今回ほしい情報は、乗りたいバスが近くのバス停まで来ているかどうか。
ということで、まずは「乗りたいバスの系統(番号)」と「バス停」の2つをパラメータとして用意する。
「乗りたいバスの系統」が「対象のバス停」に近い状態(そのバス停に向かっている、そのバス停で停車している、そのバス停から次のバス停に向かっている、の3パターン)のときに、OS側通知を利用して通知メッセージが表示されるようにする。
※事務所からバス停までは数分歩く必要があるので、乗りたいバス停を指定してしまうと、通知が来たタイミングでは間に合わなくなってしまう。そのため、実際にパラメータとして渡すバスは乗りたいバス停は、乗りたいバス停の3~4程度手前のバス停を指定する。
完成系
実装はAIの力を借り、数時間ほどで完了しました。
実際にプログラムを実行し、バスが近づいてきたときの様子がこちら。
OSの通知(Windows11の、画面右下に出てくる通知)としてメッセージが通知されました。
これで、ブラウザを起動して定期的に確認しなくても、バスが近づいてきたことを知れるようになりました。
やったね!
※Playwrightはヘッドレスで起動するようにしているため、定期実行してもブラウザはGUIでは起動しません。
実装してみての所感
環境構築、ライブラリの導入に関してそれほど苦労はしませんでした。
実装に関しては公式サイトのサンプルを見たり、AIのコード補完機能である程度実装できましたが、Playwrightでの要素の取得方法には意外と苦戦しました。
要素の取得に苦戦
ブラウザのDevToolsを確認しながら要素の取得方法を探っていたのですが、想像以上にclass属性やid属性が指定されている要素が少なく、要素の取得方法はかなり試行錯誤しました。
サイトの特性上、時間帯によってはバス情報が全くないこともあったり、バスがあったとしてもリアルタイムで現在地が変わっていくので、そういう点でも要素を調査するのも少し手間取りました。
また、今回情報を取得するのに使ったバス現在位置検索サイトですが、系統を選んで検索しても、リクエストパラメータに検索条件が残りません。(URLに検索の条件が残らない)
そのため、毎回ブラウザ起動後にプルダウンで乗りたいバスの系統を選び、検索ボタンを押す操作も実行しなければいけません。
非同期で実行されるので、検索ボタン押下後に結果が表示されるまで待つ必要があったりなど、地味に色々と試行錯誤しました。
Playwrightが活躍
ブラウザ操作系のライブラリについては、SeleniumやCypressは触ったことがありましたが、Playwrightは今回初めて使用しました。
SeleniumやCypressはCSSセレクターを使って要素を取得しますが、PlaywrightはCSSセレクターに加えてxpathという概念で親要素や兄弟要素をさかのぼって要素を取得できることが強みの1つのようです。
今回情報を取得したWebサイトは属性のある情報が少なく、かつバス現在地の前後のバス停情報を取得したかったこともあり、兄弟要素をさかのぼって要素を取得する必要がありました。そのため、ブラウザ操作をPlaywright以外で実装していたらかなり大変だったかもしれません。
私が個人でき開発を始めるとき、いつもその時の気分とノリで技術選定をしてしまい、後になって後悔することが多いです。今回も同じようにノリで技術を選びましたが、今回に限ってはPlaywrightを選んだことが吉と出ました。
ただ、xpathやlocatorの概念や使い方はまだ理解できていないなーと思う部分も多く、もう少し使いながら慣れていきたいなと思います。
実装後に知った衝撃の事実
とりあえずスピード重視でサクッと実装しましたが、実装後、改めてAPIが用意されていないかとか、スクレイピングをしても問題ないかとか、諸々確認しておこうと思い、バス現在位置検索サイトのWebサイト内を細かく見てみました。
すると、近接情報なるページがあることに気づきます。
このページは、バス停を指定するとそのバス停に近づいているバスの一覧を見ることができるページでした。
しかも、あと何分で目的のバス停に到着するかの目安まで書かれている!(大体20分前くらいから対象のバスが表示されるようになるみたい。)
さらに、このページは検索したらバス停の情報がリクエストパラメータに乗っているので、バス停を指定した状態でブックマークもできる!!
つまり、バス現在地のサイトよりもかなり使い勝手がよい!!
なんというか、最初からこのページだけ開いておけばよかったのでは。。という感じ。
タブを開かずにOSに通知を送ろうと思うとやはりシステム化が必要にはなるけれど、とはいえ情報の取得もこのページを対象にすればもっと楽に、より詳細な情報を取得できた気がする。
ということで作り直した
知らなかったことを嘆いても仕方ないので、近接情報のページから情報を取得するように作り直しました。
作り直したと言っても、既に作った関数はそのままに、新しく近接情報のサイトから情報を取得する関数を別で作成し、定期実行の処理で呼び出している関数を切り替えました。
現在地情報よりも簡単に実装できるかと思いきや、この画面も要素の構造がなかなかに複雑で、うまく情報を取得するのに苦労しました。
今回は要素の情報をDevToolsで色々調査し、その結果をもとにGitHub Copilotのエージェントにほぼ作ってもらいました。
実装後の通知は以下。
プログラムの作りとしては、最初に作ったものよりもパラメータが多くなってしまい、必要な情報をハードコーディングしている個所も増えてしまいました。
そういう点では最初の実装に比べると汎用性の低いコードになってしまいましたが、実用性で考えると、こちらの方がメッセージの内容が非常に分かりやすくて良さげです。
せっかくなのでGUIを追加する
技術選定でnodeを選んだのは、後からElectromでGUIを追加できたら面白そうだな、と思って選んだ部分もあります。
とりあえずCLIのアプリとして最低限動くものを作れましたが、この段階では設定情報(定期実行の設定)や、アプリを動かすうえでのパラメータ情報(バス停の情報、乗りたいバス系統の情報)がハードコーディングされている状態。
どうせなら設定情報やパラメータ情報は設定ファイルから取得できるようにして、その設定情報をGUIで編できるようにしたら何かと便利そうだなと思ったので、Electronについては何の前提知識もありませんでしたが、AIエージェントに頼ってGUIを作ってもらいました。
そして出来上がったのがこちら。
監視対象のバスと、自動通知のスケジュールをGUI同で設定できるようにしました。
また、ボタン押下で即時実行できるようにしました。
これで、ブラウザでタブを開いておかなくても、バスの情報を確認できたり、OS側に通知が来るようにできます。
また、毎回コマンドで起動するのも面倒なので、exeファイルを作ってもらい、デスクトップから起動できるようにもしてもらいました。
これでもう、バスの現在地のサイトや、近接情報のサイトを監視しておく必要はなくなりました!
やったね!
細かいところでいうと、まだハードコーディングしているパラメータ情報がいくつかあり、他の人でも使える状態にしようと思うと、まだ汎用化が必要な部分もあるのですが、とりあえず今回自分だけが使うことを想定しているので、一旦ここ状態でしばらく使ってみることにします。
まとめ
使用した技術・ツールなどのまとめ。
- 言語:node.js
- ライブラリ・フレームワーク
- Playwright:ブラウザ操作用
- node-notifier:通知用
- node-cron:定期実行用
- Electron:GUI作成用
- 開発ツール
- VS Code
- GitHub Copilot
- Model:Claude Sonnet 4
- 情報取得に使用したサイト
改めて所感
途中、少し遠回りをしてしまいましたが、AIの助けもあって、トータルでも数時間レベルで実装することができました。特に、PlaywrightとElectronの実装はAIエージェントにかなり助けられました。
Playwrightに関しては、AIの助けを借りずとも、公式サイトを見たりネットの情報を参考にすればプラス数時間で実装できたような感覚はあります。
Electronに関しては、AIなしで同じクオリティのものを作ろうと思うと、学習コストも併せて数日以上かかっただろうと思われます。それを今回、ほぼ100%AIコーディングで、1時間もかからないくらいでexeの作成まで持っていけました。ここまでくるとAIエージェント様様という感じです。
このようなアプリを作ろうと思う、プログラミングやソフトウェアの知識が最低限必要となることは間違いないですが、とはいえ、使用するライブラリやフレームワークの知識がほぼない状態だったとしても、AIを活用すれば数時間レベルでそれなりに動くものが作れる時代になりました。
つまり、何かしらの問題解決をしようと思ったとき、それがソフトウェアで解決可能な問題であれば、最短数時間レベルで問題を解決するツールが作れるような時代になったということ。
仕事をしていく上で問題解決力はどの職種においても重要な能力ですが、ソフトウェアに関する問題解決力はAIを使いこなせるようになれば差はなくなっていくことでしょう。これからは、問題解決力よりも、解決すべき問題を発見する問題発見力の方が大事になりそうです。
自分の身近にある、些細な課題(今回の私の例で言うと、バスに乗り遅て時間を無駄にしたくない、バスの現在地をずっと監視するのが面倒、など)を見つけて、見つけた課題に対してソフトウェアによってどう解決できるかを考える習慣が大事になるのだろうと思います。
ただ、バスに乗り遅れたくないという課題の根本的な解決は、リモートワークにするか職場近くに引っ越すかのどちらかだろうなとは思います。。
あと、今回情報取得に使用したページがAPIを提供してくれるとうれしいです。