郭立 (leeguoo)

# デスクトップアプリは実はWebページだ:これを理解すると、操作はもはや「デスクトップ自動化」ではなくなる

初めて chrome-use で Slack デスクトップ版につなぎ、`snapshot` で名前付きのボタンが大量に出てきたとき、少し驚いた。あとで納得した——Slack デスクトップ版はそもそもWebページなのだ。Electron アプリは Chromium レンダリングエンジンにネイティブの殻をかぶせたもので、内部には構造のはっきりした DOM がある。この記事では、なぜこの事実によって、ずっと泥臭い作業だった「デスクトップ自動化」がWebページ操作へと単純化されるのか、そしてブラウザの場合と比べて必ず知っておくべきトレードオフについて説明する。

2026年7月2日 · 記事 · 公開

このページの目次

はじめて chrome-use で Slack デスクトップばんにつなぎ、snapshotてきた名前付なまえつきのボタンのやまたとき、すこおどろいた。これはWebページを操作そうさするのとまったくわらない。あとで納得なっとくした。Slack デスクトップばんは、そもそもWebページなのだ。

Electron アプリは、Chromium レンダリングエンジンに Node.js の外殻がいかくしたものだ。Slack、VS Code、Discord、Figma、Notion を「デスクトップソフト」として使つかっているつもりでも、その画面がめんはすべて Chromium がレンダリングした HTML で、外側そとがわにネイティブウィンドウのからをかぶせているだけだ。この事実じじつれると、もともとかなりむずかしかったことが、くずれるように簡単かんたんになる。デスクトップアプリの操作そうさは、Webページの操作そうさへと退化たいかする。

Slack/VS Code などの Electron アプリの<ruby>殻<rt>から</rt></ruby>をめくると、<ruby>中身<rt>なかみ</rt></ruby>は<ruby>実<rt>じつ</rt></ruby>は Chromium のWebページであり、chrome-use は CDP のリモートデバッグポートを<ruby>通<rt>つう</rt></ruby>じて<ruby>接続<rt>せつぞく</rt></ruby>し、ボタンをスクリーンショット上の<ruby>座標<rt>ざひょう</rt></ruby>ではなく @e1/@e2 の<ruby>要素参照<rt>ようそさんしょう</rt></ruby>で<ruby>特定<rt>とくてい</rt></ruby>する

デスクトップ自動化はもともとどれほど扱いにくかったか

まず、このみちがなかったとき、プログラムにデスクトップアプリを操作そうささせるのがどれほどむずかしかったかをはなそう。

ひとつの方法ほうほうは、スクリーンショットをり、ボタンのピクセル座標ざひょうさがし、そこをクリックすることだ。解像度かいぞうどわり、テーマがわり、ウィンドウがうごいただけで、座標ざひょうはすべてずれる。しかもプログラムは画面がめんなにがあるのかを「理解りかい」できず、ただ1まい画像がぞうているだけだ。もうひとつの方法ほうほうは、システムのアクセシビリティ API、つまり macOS の Accessibility や Windows の UIAutomation を使つかうことだ。これならコントロールツリーを取得しゅとくできるが、プラットフォームごとに API がべつで、しかも Electron アプリがこの API に公開こうかいする情報じょうほうは、しばしば不完全ふかんぜんだ。なぜなら、その「コントロール」は実際じっさいにはネイティブコントロールではなく div だからだ。

どちらの方法ほうほう手間てまがかかるし、どちらもひとつの事実じじつ迂回うかいしている。このアプリの内部ないぶには、構造こうぞうがはっきりしていて意味いみもある DOM があるのに、外側そとがわからピクセルし、あるいは変換へんかんされたそうしに、それへばしているのだ。

Chromium には最初から扉がある

レンダリングエンジンが Chromium なので、Electron アプリは自然しぜんに Chrome DevTools Protocol をはなせる。起動時きどうじ--remote-debugging-portけると、CDP ポートがひらき、内部ないぶの DOM がそのまま露出ろしゅつする。

$ bash
open -a "Slack" --args --remote-debugging-port=9222
chrome-use connect 9222
chrome-use snapshot -i

(アプリごとに衝突しょうとつしないポートをてればよい。ぜんプラットフォームの起動きどうコマンドはドキュメントサイトにあるので、ここではくわしく展開てんかいしない。)

snapshotかえすのは、その DOM のアクセシビリティ視点してんだ。すべてのボタンや入力欄にゅうりょくらん名前なまえ要素ようそであり、@ref特定とくていできる。あなたは「送信そうしんボタンはたぶん右下みぎしたあたりのピクセルにある」と推測すいそくしているのではない。button "Send" そのものを取得しゅとくしている。ウィンドウがうごいても、テーマがわっても影響えいきょうしない。むすびついているのは座標ざひょうではなく要素ようそだからだ。agent にとって、このちがいは本質的ほんしつてきだ。いまや agent は、このデスクトップアプリがどんな姿すがたをしているかをれる。スクリーンショットを相手あいててずっぽうでクリックしているのではない。

必ず知っておくべきトレードオフ

ここには、ブラウザの場面ばめんとはちがてんがある。おもみやすいので、べつはなしておく価値かちがある。

普段ふだん使つかっている Chrome を操作そうさするとき、chrome-use はブラウザ拡張かくちょうと native messaging を使つかう。デバッグポートにはれないので、「リモートデバッグを許可きょかするか」という確認かくにんダイアログもなく、Runtime.enable のようなプロトコルそう痕跡こんせきもない。しかし Electron アプリには Chrome ウェブストアの拡張かくちょうれられない。だから、このみちでは --remote-debugging-port使つかうしかない。つまり、ブラウザの場面ばめんにあった「痕跡こんせきゼロ」のような利点りてんは、Electron ではられない。前提ぜんていは、自分じぶんでデバッグポートをひらいて、そこへ接続せつぞくすることだ。

Slack や VS Code のような「自分じぶんのアプリ」を自動化じどうかするなら、これはまったく問題もんだいではない。だれかをだまそうとしているのではなく、すでにログインしている自分じぶんのツールを自動化じどうかしたいだけだからだ。ただし、ブラウザでの「検出けんしゅつされない」という期待きたいったまま Electron を操作そうさしにるなら、まず期待値きたいち調整ちょうせいする必要ひつようがある。これは「自分じぶんのアプリへ接続せつぞくする」ことであって、「他人たにんのサイトをかくれて操作そうさする」ことではない。

おもしろい細部:アプリの中にさらにアプリを入れられる

Electron アプリの内部ないぶには、しばしば <webview> がさらにまれている。たとえば Slack でひらいた第三者だいさんしゃアプリのパネルは、本質的ほんしつてきにはもうひとつの独立どくりつしたWebページだ。これらは CDP では独立どくりつした target になる。接続後せつぞくごにまず chrome-use tab一覧いちらんすと、メインウィンドウ、設定せっていウィンドウ、そしてそれらの webview が、それぞれ target になっているのがえる。個別こべつえて操作そうさできる。

だから、ひとつの Electron アプリは「ひとつのWebページ」ではない。「ネイティブのからなかかさなったWebページのたば」だ。これを理解りかいすると、マルチウィンドウやパネルのような、もともとあたまなやませる場面ばめんは、すべて「ただしい target にえる」だけになる。

境界

この方法ほうほうは、Chromium 内核ないかくのアプリにだけつ。Swift/AppKit でかれた純粋じゅんすいなネイティブアプリには CDP ポートがなく、接続せつぞくできない。その場合ばあいは、システムのアクセシビリティにもどるか、べつのツールを使つか必要ひつようがある。iPhone 上のネイティブ app を操作そうさするとき、私はべつ仕組しくみを使つかっている。判断はんだん簡単かんたんだ。Slack、VS Code、Discord、Figma、Notion、Spotify のような「ひとつのコードでマルチプラットフォームに対応たいおうする」デスクトップアプリは、基本的きほんてきに Electron だ。

「デスクトップアプリはじつはWebページだ」と理解りかいしてから、私はデスクトップツールをると、まずそれが Electron かどうかを確認かくにんするようになった。Electron なら、それを操作そうさすることは、もはや私がずっと泥臭どろくさいとかんじていた「デスクトップ自動化」ではない。すでによくっている、Webページをひらいてあつかうためのあの方法ほうほうになる。

← 前の記事
agentにWebページのデータを取らせるために、3つの不器用な方法を試して、最後に1つだけ残した
次の記事 →
本物の Chrome がボット判定されない理由:CreepJS 0% はどう生まれるのか

コメント

コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。

最大 1000 文字。