Chromeの拡張機能を自作してスクレイピングを行う一番簡単な例(v3対応)
Chromeを自動操縦してWebページをスクレイピングする方法については過去にPuppeteerを使う例※を紹介してきました。
余計なソフトを入れるのが面倒だなぁ、と食わず嫌いしていましたが、実際やってみるとNodejsとpuppeteerのインストールはとても簡単。
おかげさまで、IE11のサポートが終了した後もVBScriptでIEの自動操作をしていた部分をChrome+Puppeteerへ切り替えることができ、大変満足しています。
がしかし。
完全に自動操縦するほどではないんだけどなぁ…。ほんのちょぉ~っとだけ、現在表示しているWebページから必要な項目だけ抽出する操作をしたいなぁ~と思うことがあります。Puppeteerは意外と動作が重いし。
頻繁に使わないのならデベロッパーツールでも十分
Chrome(もしくはEdge Chromium)には便利なデベロッパーツールが付いているので、[F12]キー(もしくはCtrl+Shift+I)でツールを起動し、コンソールに直接JavaScriptを書くことができます。
これは自分のブログを開き、コンソールで「document.querySelectorAll('article');」を実行した例ですが、articleタグの一覧をサクっと取得できています。
ここから更にタイトルだけを抽出したり、逆にタイトルを変更することもできますし、appendChildを使って新しい要素を追加することも自由自在。
あくまで画面上の操作だけなので、実際の記事が編集されるわけではありませんが、タイトルの一覧を取得してCSVにしたり、デベロッパーツールでのDOM操作で管理が楽になる例は枚挙に暇がありません。
特にPuppeteerなどとの違いは、認証の有無だと思っています。自動操縦ツールで操作する場合、スクリプト内にログイン用のIDとパスワードを設定しておかなければならないので管理に気を使う必要があります。
一方で、普段使っているChromeでデベロッパーツールを開くだけなら、(自動ログインに対応しているサイトなら)常にログイン状態ですから、認証部分を気にせず、いきなりマイページの解析などが出来るわけです。
こんな便利なデベロッパーツールですが、毎回ソースコードを貼り付けるのはさすがに面倒くさい…。
以前紹介した「楽天アフィリエイトレポートの注文明細からリンクを生成するブックマークレット」のようにスクレイピング作業をブックマークレット化するのもひとつの手ではありますが、今回はせっかくなので(?)Chromeの拡張機能で実装してみたいと思います。
自作のChrome拡張で現在開いているページをスクレイピングする
Chromeの拡張機能を作るのはとても簡単。3つのテキストファイルを用意するだけです。
- manifest.json (拡張機能の定義ファイル)
- HTMLファイル (UI用)
- JavaScriptファイル (HTML解析用)
適当なディレクトリにこれら3つのファイルをほおりこんで、拡張機能の管理画面(chrome://extensions/)の「パッケージ化されていない拡張機能を読み込む」ボタンからそのディレクトリを指定するだけ。
※画面右側のデベロッパーモードをONにしないとこれらのボタンは表示されないので注意
※また、パッケージ化する場合は秘密鍵が指定ディレクトリのひとつ上に生成されるため、Cドライブの直下などに作らないよう注意
manifest.jsonの記入例
{
"name": "My Extension",
"description": "My Extension",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
},
"permissions": [ "activeTab","scripting" ]
}
name | 拡張機能の名前 |
---|---|
description | 拡張機能の説明 |
version | 拡張機能のバージョン |
manifest_version | 拡張機能で使うmanifest.jsonのバージョン※1 |
action | 起動時に開くポップアップのHTMLファイル |
permissions | 拡張機能で要求する権限※2 |
※1
v2は2022年1月でChrome Webストアへの登録ができなくなっており、2023年1月には既存のv2拡張機能も使用不可になる予定なので、これから作る場合はv3で作ったほうが良いです。
※2
アクティブなタブに表示されているページを読み取る(スクレイピングする)ため、"activeTab"権限を使います。
また、そのページ内でJavaScriptを実行してDOM構造を読み取るので"scripting"権限も要求しています。
popup.htmlの例
<!DOCTYPE html>
<html>
<body>
<h1>スクレイピングテスト</h1>
<textarea id="result"></textarea>
<script src="popup.js"></script>
<style>
h1 { font-size:1.2em; }
</style>
</body>
</html>
普通のHTMLなので、特に難しいこともないですが、メインとなるページ読み取り処理はJavaScriptで実行されるため、popup.jsの読み込みをここでしています。
popup.jsの例
main();
async function main()
{
//アクティブなタブを取得
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
//アクティブなタブでJavaScript(parseDOM)を実行
chrome.scripting.executeScript({
target:{tabId:tab.id},
func:parseDOM
}).then(function (r) {
//実行結果をポップアップウィンドウへ表示
document.getElementById('result').innerHTML = r[0].result;
});
}
function parseDOM()
{
return document.body.innerHTML;
}
普通にDOM操作するかと思いきや、見慣れない関数が出てきて面喰いましたかね? ぼくははじめて見たときなんじゃこりゃ…と驚きました。
でも、落ち着いて見ると実は単純で、ここで使っている特殊な関数は下記の2つだけ。
- chrome.tabs.query
- chrome.scripting.executeScript
chrome.tabs.queryはChromeブラウザのタブを検索する関数で、active: true, currentWindow: true というオプションを渡すことで、現在のカレントウィンドウの、アクティブなタブを検索しています。
非同期関数なので、awaitを使って実行完了を待つ作りにしています。それが気に食わなければ .then などを使って非同期処理にすると良いと思います。参考までにその次のexecuteScriptではthenを使っています。せっかくasync mainにしたので、このexecuteScriptもawaitで実行しても良いんですけどね。
このexecuteScriptは芸達者で、指定したタブに対してJavaScriptのコードを直接送り込むこともできれば、関数名を指定して、それを丸ごとそのページ内で実行させることもできます。
今回の例では func:parseDOM というパラメーターを渡しているとおり、parseDOMという自作の関数をまるごと送り込んで実行させる作りになっています。
肝心の parseDOM がreturn document.body.innerHTML;というようにbody内のHTMLコードをまるごと返しているだけなので、いまいちピンとこないかもですが、この parseDOM関数だけは指定したタブ内で実行されていることを理解するとわかりやすいのではないでしょうか。
そう、拡張機能のボタンをポチっと押したときに表示されるpopup.htmlはあくまで別のタブ(あるいは別ドメインと言ったほうが良いか)なので、本来はpopup.html側から、現在のアクティブタブを直接操作することはできないんですね。
なので、executeScript関数で、アクティブタブ内で操作したいコードを送り込んでやる形になります。
そしてその結果はreturnで返すことができるのですが、ここでも注意点がひとつ。
executeScriptの戻り値でオブジェクトを返そうとも、文字列を返そうとも、必ず配列になって返されます。
本来なら、下記のドキュメントどおり、for文を使って取り出すべきなのですが、どうせ必ず配列の最初に入ってくるんだから r[0].result で良いんじゃね?と思って、そのように書いています。真面目な人はドキュメントどおりにやりましょう。
https://developer.chrome.com/docs/extensions/reference/scripting/
そして、最後に document.getElementById('result').innerHTML = r[0].result; で、ポップアップウィンドウ内にあるテキストエリアにHTMLタグを丸っとほおりこんでいます。
まとめ
管理ページ等で、繰り返し同じような作業をするケースがあるものの、Puppeteerで自動化するほどではない……………という微妙な作業用にデベロッパーツールを使っていました。
具体的には(通じる人がいるかわかりませんが)レンタルサーバーのcPanelの電子メールフィルターにインポート/エクスポート機能がないため、自前でJavaScriptを書いて抽出処理をしていたんですね。
そんなにしょっちゅう使うわけではなく、たまのことなので、JavaScriptをべたべたっと貼って実行してきたのですが、うーん、なんとも美しくない。
そういった事情で、今回Chromeの拡張機能で実装してみようと思ったんです。それでちょっと調べてみたところ、v2の記事ばかりがヒットして、そもそも動作しないコードだらけだったため、まずはv3での拡張機能の一番簡単なコード例として、この記事を投稿しておこうと思いました。
忘れなければ、この後にcPanelの操作例を投稿する…と思います…たぶん。