Instagram の写真や動画を保存するブックマークレット
Instagram の写真や動画をサクッと保存したいなと思い、ブックマークレットを作ってみた。
対象とするのは、以下のような1つの投稿を表示するページ。
写真と動画を複数まとめてアップした投稿ページについては後述する。
写真の保存方法
ブラウザの開発者ツールで調べてみると、写真が投稿されているページの場合、div._jjzlb
の配下に img#pImage_0
という要素があり、この src
属性値が表示されている画像 = フルサイズの画像ということが分かる。そこで、この src
属性値を location.href
に設定すれば、画像を直接ブラウザで表示し、保存することができる。
// これで動く
javascript:(function() { location.href = document.getElementById('pImage_0').src; })();
javascript:(function(){location.href=document.getElementById('pImage_0').src})()
// スペースや書かなくても良いセミコロンを除去
で、最近のブラウザ (Firefox 54 で検証) だと、ネイティブで ES2015 の記法も解釈できるので、さらにブックマークレットを短く書ける。
javascript:(() => {location.href=document.getElementById('pImage_0').src})()
javascript:(()=>{location.href=document.getElementById('pImage_0').src})()
javascript:{location.href=document.getElementById('pImage_0').src}
無名関数はアロー関数にできる。さらに、引数を取らないアロー関数は単に {}
で囲むだけで良いようだ。
…と、ここまで書いておいてアレだが、location.href
なら直接叩けば良かった。
javascript:location.href=document.getElementById('pImage_0').src
とりあえずこれで動作する。
動画の保存方法
写真の場合は img#pImage_0
という要素があったが、動画のページの場合は、video._c8hkj
という要素で動画を表示している。クラス名はランダム文字列なのかと思っていたが、どうもこれで固定のようだ。同じように location.href
にして動画を直接表示してみる。
javascript:location.href=document.getElementsByClassName('_c8hkj')[0].src
動画の場合、リンクを作って右クリックから「リンク先を保存」した方が楽かな?と思い、リンクをページ左上に追加するようにしてみた。
javascript:((d)=>{d.body.innerHTML+=`<a href="${d.getElementsByClassName('_c8hkj')[0].src}" style="position:absolute;top:0">Video</a>`})(document)
ES2015 の記法である、アロー関数、テンプレートリテラル、プレースホルダー (テンプレートリテラル内の ${}
) を使ってみたがちゃんと動いた。
複数の写真・動画を投稿したページの場合
複数の写真・動画をまとめて1つに投稿したページの場合、画像と動画はカルーセルで1つずつ表示するようになっていて、カルーセルを移動する度に表示欄の要素をまるごと取り替えている。よって、実際にその画像を表示するまでは画像の URL が分からない。手間だが、保存したい画像を表示してはブックマークレットを実行、次の画像を表示してはブックマークレットを実行、としないといけなさそう。
そして、画像の場合は img#pImage_0
という ID なのは1枚目だけで、2枚目以降は #pImage_1
・#pImage_2
…と ID が変わっていく。つまり先程の #pImage_0
を特定するやり方では取得できない画像があるのだ。そこで、画像の場合に必ず img
要素を囲んでいる div._jjzlb
から img
要素を特定するように書き換えた。つまり、複数投稿にも対応した画像保存のブックマークレットは以下のようになる。
javascript:location.href=d.getElementsByClassName('_jjzlb')[0].getElementsByTagName('img')[0].src
複数投稿にも対応・写真でも動画でもリンクを作るブックマークレット
そんなこんなで、写真の場合でも動画の場合でも、さらには複数投稿の場合でも対応している1つのブックマークレットを作ってみた。これを投稿ページで使うと、画面左上に「Image」もしくは「Video」のリンクを追加する。
javascript:((d,c)=>{i=d[c]('_jjzlb')[0],v=d[c]('_c8hkj')[0];if(!i&&!v)return;d.body.innerHTML+=`<a href="${(i?i.getElementsByTagName('img')[0]:v).src}" style="position:absolute;top:0">${i?'Image':'Video'}</a>`})(document,'getElementsByClassName')
長いので整形して説明する。
javascript:((d,c) => {
i = d[c]('_jjzlb')[0],
v = d[c]('_c8hkj')[0];
if(!i && !v) return;
d.body.innerHTML += `<a href="${(i ? i.getElementsByTagName('img')[0] : v).src}" style="position:absolute;top:0">${i ? 'Image' : 'Video'}</a>`
})(document, 'getElementsByClassName')
- 即時関数の引数
d
とc
は、ショートコーディングでお馴染み、配列記法でdocument.getElementsByClassName
にアクセスするためのもの。 - 変数
i
は画像を囲むdiv._jjzlb
を、変数v
は動画のvideo._c8hkj
を取得している。このどちらかの DOM 要素が存在するかどうかでリンクを作り変えるという寸法。 if(!i && !v) return;
は、どちらの要素もなかったらそこで中止、という処理。if(!i && !v) { alert('写真も画像もありません'); return; }
と丁寧にアラートを出しても良いかも。d.body.innerHTML +=
で、ページ中にリンクを追加している。このやり方をすると、以降そのページでのカルーセル等が効かなくなってしまうので、更新して再表示してやる必要があるが、仕方なし…。- リンクの HTML はテンプレートリテラルを使って書いている。
position:absolute;top:0
と指定することでページ左上に表示させている。 - リンク先 URL は
(i ? i.getElementsByTagName('img')[0] : v).src
という三項演算子で作っている。分解してみるとこうだ。
let href;
if(i) {
href = i.getElementsByTagName('img')[0].src;
}
else {
href = v.src;
}
- 写真と動画のどちらが存在しているか、変数
i
の存在を確認して分岐している。 - 写真の場合は変数
i
(div._jjzlb
) 配下からimg
要素を取得し、そのimg
要素のsrc
属性値を取得してリンクに設定している。 - 動画の場合は変数
v
がvideo._c8hkj
という要素なので、このsrc
属性値を取得している。 ${i ? 'Image' : 'Video'}
部分、これは必須ではないが、リンクの文言を「Image」か「Video」で切り替えるために書いている。不要なら適当に「Link」とでもすればより短くできる。
以上。これでサクッと写真や動画が保存できる。