Electronで表示するwebviewの中の要素を操作する

Electronではwebviewを使って、簡単にwebページを表示することができるけど、そのページを構成しているhtmlってどうやって操作するんだろう・・・と思ったのでメモ。

Electronのメインプロセスとレンダラープロセスを理解しないとダメだったようです。

electronでは、以下のようにすれば、指定したwebページを表示することができる。

 <webview>
    src="https://qiita.com/trend">
</webview>

f:id:utr066:20180507153454p:plain

だけど、この中の要素を扱おうと思っても扱うことができない。JavaScriptで要素を取得して出力しようと思ってもコンソールに出すことはできません。

f:id:utr066:20180507153702p:plain

要素の検証をしてみても、表示しているものはwebviewになっていますね。このwebviewで表示しているページの中の要素をいじりたい。

preloadを使って表示するページの要素を取得する

ここで表示しているページの要素を取得してコンソールに出したい、とするならpreloadを使えば可能。

 <webview
    id="foo"
    preload="foo.js"
    src="https://qiita.com/trend">
</webview>

preloadで指定したfoo.jsでそのページの要素をいじることができる。

hatenaブログからおすすめ記事をコンソールに出力する

Qiitaのtrendの記事なんかを表示しようと思ったけど、ログイン処理が必要になってきそうので、はてなブログのトップページを表示させ、そこからおすすめ記事のタイトルを取ってきます。

f:id:utr066:20180507165524p:plain

今回で言えば、「正義の報復を受けよ」ですね。

index.htmlに以下のように書く。

 <webview
    id="foo"
    preload="foo.js"
    src="http://hatenablog.com/">
</webview>

これでfoo.jsの中ではてなブログのトップページの要素をいじることができます。その前にメインプロセス側でjsをいじる。

<script>
  var webview = document.getElementById('foo');
  // webviewのロードが終了したら呼ばれるイベント
  webview.addEventListener("did-finish-load", function(){
    webview.send("getContent");
  });

  // プロセス間通信を行う
  webview.addEventListener('ipc-message', function(event) {
    console.log(event["args"][0]);  // content.innerTextが出力される
  });

</script>

webviewのロードが終了したら、webview.sendでgetContentを指定しています。

レンダリングプロセスにチャネル経由で非同期メッセージを送信すると、任意の引数を送信することもできます。 レンダラープロセスは、ipcRendererモジュールを使用してチャネルイベントをリッスンすることによってメッセージを処理できます。

sendの引数でチャネルイベントを指定して、レンダラープロセス側では、そのイベントを指定することで受け取ることが可能なようですね。

electronjs.org

こっちはfoo.jsです。

const {ipcRenderer} = require('electron')

ipcRenderer.on('getContent', function(){
  title = document.getElementsByClassName('serviceTop-entry-title')[0].firstElementChild.innerText;
  ipcRenderer.sendToHost('hogehoge', title);
});

ipcRendererのイベント名はさっきメインプロセス側で指定したチャネル名getContentですね。ここでは、はてなのトップページからおすすめ記事のタイトルを取得し、それをメインプロセス側に送信しています。

それを受け取るときには、ipc-messageというものが使えそうです。

electronjs.org

  webview.addEventListener('ipc-message', function(event) {
    console.log(event["args"][0]);
  });

これでコンソールに表示されます。

ちなみにrendererの方で指定したhogehogeという名前のchannelは受け取り側のeventの中に入っています。

ipcRenderer.sendToHost('hogehoge', title);
  webview.addEventListener('ipc-message', function(event) {
    console.log(event.channel);  // hogehogeが出力される
  });

表示されますが、ちょっと面倒ですね。

f:id:utr066:20180507165115p:plain