Corredor

ウェブ、プログラミングの勉強メモ。

Instagram の写真や動画を保存するブックマークレット

できる100の新法則 Instagram マーケティング 写真1枚で「欲しい」を引き出す技術

できる100の新法則 Instagram マーケティング 写真1枚で「欲しい」を引き出す技術

  • 作者: 株式会社オプト,山田智恵,小川由衣,石井リナ,できるシリーズ編集部
  • 出版社/メーカー: インプレス
  • 発売日: 2016/03/18
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (1件) を見る

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')
  • 即時関数の引数 dc は、ショートコーディングでお馴染み、配列記法で 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 属性値を取得してリンクに設定している。
  • 動画の場合は変数 vvideo._c8hkj という要素なので、この src 属性値を取得している。
  • ${i ? 'Image' : 'Video'} 部分、これは必須ではないが、リンクの文言を「Image」か「Video」で切り替えるために書いている。不要なら適当に「Link」とでもすればより短くできる。

以上。これでサクッと写真や動画が保存できる。