notebook

都内でWEB系エンジニアやってます。

ブックマークレットに関して

今までブックマークレット?ふーんみたいな感じだったのですが、最近フロント側もやっていて面白くなってきているのと、結構できることあるみたいなのでtipsとして残しておきます

画面での確認

いきなりブックマークレットのコードを書く前にまずは画面でdeveloper consoleを開いて1コマンドづつ操作したりします

ここで出来上がった操作の流れをコードに落とし込んでいくと効率よさそうな感じですね

そこで使えそうなtipsたちを紹介していきます

querySelector API

セレクタCSSセレクタなのでxpathとちょっと違うがまぁ少し調べれば思い出せる範囲だと思います

  • classname というクラスを適用されたdiv要素を集める
document.selectorAll('div.classname')
  • document.querySelectorAllで取得したエレメントたちに対して何かする

document.queryselectorAllで返ってくるリストはNodeListでArrayではないのでmapなどの関数が使えません(forEachしか用意されていない)

ので下記のような感じになるかと思います

var hoge = [];
document.selectorAll('div.css-selector').forEach(_ => {
  hoge.push(_.textContent);
});

mapとか使いたい場合はこんな感じで書けます

urls = Array.from(document.querySelectorAll('div.class-celector'), x => x.href)

詳しくは下記参照

[querySelectorAllしてmapしたいとき...すると短い - hitode909の日記

blog.sushi.money

他頻出しそうな使い方

  • 画像のURLを取得
document.querySelector('img.hoge').src;
  • 画像のURLを取得
document.querySelector('a.hoge').href;
  • コンテンツを取得

特にそのテキストがある要素の指定までしなくてもテキストを取ってこれます

<div class="hoge">
  <div class="fuga">
    contents
  </div
</div>
document.querySelector('div.hoge').textContent;

contentsが取得できます

  • 特定エレメントをクリック
document.querySelector('div.hoge').click();
  • スタイルを変える
document.querySelector('div.hoge').style = 'color: #FF0000;'

developer consoleでquerySelectorApiを実行する際に下記エイリアスが使えるのでタイプ数が減って楽です

code alias
document.querySelectorAll $$
document.querySelector $

ブックマークレット

本題のブックマークレットです

ウィンドウ開いてコンテンツを表示

新たにwindowを開いてその中に抜き出したデータを表示といったことができます

(function(){
  const d = window.open().document;
  d.writeln(
    `<h3>${document.title}</h3>`
  );
  d.close();
})();

CSVのダウンロード

特定のデータを抜き出してCSVにしてダウンロードしたりもできます

(function(){
  const csv_text = "a,b,c\nd,e,f";
  const filename = 'filename.csv';
  const a = document.createElement('a');
  const mimeType = 'text/plain';
  const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
  const blob = new Blob([bom, csv_text], {type: 'text/plain'});
  const url = window.URL;
  const blobUrl = url.createObjectURL(blob);
  a.download = filename;
  a.target = '_blank';
  a.href = blobUrl;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
})();

開発

外部ファイルに書き出して読み込む

メインのロジックなどは外部のファイルに書き出してそのファイルを読み込むだけのブックマークレットを設定するやり方ですね

これならアプリなどと一緒で配った内容を書き換えなくてもロジックを途中で変えることができるので安心

一旦配ってしまったけどバグがありましたーなんて時に対応しやすいやつですね

ブックマークレットに入れるのは下記のみ

javascript:(function(){ const s = document.createElement('script'); s.src = 'http://192.168.30.93:8082/bookmarklets/hoge'; document.body.appendChild(s); })();

この時点では開発サーバ用のipを指定していますが、適当にファイルをサーブするサーバを用意するだけで修正→実行のループが楽にできるようになります

HTTPS対応

上記のように外部ファイル化するとhttpsのページでは混在コンテンツとして読み込めないことがあります

開発環境もhttpsに対応させておかないといけない.......となるわけですね

問うことでrubyのthinというgemで簡単にhttps対応させてしまいます

  • Gemfile
source 'https://rubygems.org'

gem 'thin'
  • app.ru
# encoding: utf-8

class SimpleServer
  def call(env)
    contents = 'hello! world!'
    [
      200,
      {'Content-Type' => 'text/plain;charset=utf-8'},
      [contents]
    ]
  end
end

use Rack::Static, :urls => ["/bookmarklets"]

run SimpleServer.new
  • 起動
bundle exec thin start --ssl -R app.ru

Rack::Staticが静的ファイルの配信サーバの役割をしてくれてます

これで例えばhttps://192.168.30.11:3000/bookmarklets/hoge.jsに対してアクセスするとhttpshoge.jsを取得することができます

なんて簡単!

気を付けること

bookmarkletは一行で書くので一行の場合は下記のような項目に気を付けなくてはいけません(外部ファイルにして読みだしている場合は除く)

  • コメントは // コメント じゃなく /* コメント */で書く
  • 処理の終わりを認識させるためにセミコロンつける

1行にしてブックマークに突っ込むのでこれをやらないとエラーになってしまいます

変換

ファイル読み出しの方法を取る以前はこんな感じでjavascriptの部分は普通に書いて最後に1行にまとめて貼り付ける用のテキストを出力さて登録とかしてました

正直面倒なのでやることはないだろうけど...

cat bookmarklet.js | sed -e ':loop;N;$!b loop;s/\n/ /g' -e 's/ \+/%20/g' -e 's/^/javascript:/'

まとめ

地味に面倒なポチポチタスクを自動化するのもサクッとできそうです

スクレイピングでは割と面倒なログイン処理などはすっ飛ばして実行したいところでブックマークレットを実行すればいいだけなので気軽でいいですね

開発に関しても「書いて→一行にして→登録して→実行」から外部ファイルにすることで「修正→実行」で良くなりループが回しやすくなります

勉強がてら下記にブックマークレット作ってみたのでよかったら見てみてください

swfz/bookmarklets

github.com

無駄にS3 + Clouffrontでssl配信、circleciで自動デプロイまでやったりしてみました

circleciが簡単に動かせたのでおすすめですね、課金体系も1コンテナまでなら無料なようなので個人ユースなら巨大プロジェクトとかでCI回さない限りは大丈夫でしょう

これに関してはまた別途記事にするかもしれません