Instagram のページから投稿 URL を一括取得するブックマークレットを作った
Instagram の検索結果や「保存済み一覧」などのページを Web ブラウザで開いている時に、そのページにある投稿ページへのリンクを一括取得するブックマークレットを作った。収集した URL はクリップボードにコピーする。
javascript:(d=>{e=d.createElement('textarea');e.textContent=Array.from(d.querySelectorAll('a'),a=>a.href.match(/(?!.*liked_by).*(?=\/p\/)/)&&a.href).filter(a=>a).join('\n');d.body.appendChild(e);e.select();d.execCommand('copy');d.body.removeChild(e)})(document);
何件の URL を取得したか alert()
するバージョンはコチラ。
javascript:(d=>{e=d.createElement('textarea');l=Array.from(d.querySelectorAll('a'),a=>a.href.match(/(?!.*liked_by).*(?=\/p\/)/)&&a.href).filter(a=>a);e.textContent=l.join('\n');d.body.appendChild(e);e.select();d.execCommand('copy');d.body.removeChild(e);alert(l.length+'件')})(document);
拙作の @neos21/igsv で一括ダウンロードするために取得するのであれば、igtv 【URL】
となるようコピーさせても良いだろう。コピーさせたままターミナルに貼り付ければ igsv
でダウンロードができる。
javascript:(d=>{e=d.createElement('textarea');l=Array.from(d.querySelectorAll('a'),a=>a.href.match(/(?!.*liked_by).*(?=\/p\/)/)&&'igsv '+a.href).filter(a=>a);e.textContent=l.join('\n')+'\n';d.body.appendChild(e);e.select();d.execCommand('copy');d.body.removeChild(e);alert(l.length+'件')})(document);
コードの説明
普通のコードに直すとこんな感じ。
const textarea = document.createElement('textarea');
const links = Array.from(document.querySelectorAll('a'), a => a.href.match(/(?!.*liked_by).*(?=\/p\/)/) && a.href).filter(a => a);
textarea.textContent = links.join('\n');
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert(links.length + '件');
テキストエリアを生成してクリップボードにコピーする部分は、以前紹介したブックマークレットと同じ。
Array.from()
を使うと、document.querySelectorAll()
を配列化し、ついでに map()
相当のことができる。querySelectorAll()
の結果は通常の配列ではないので、forEach()
以外を使う時は何らかの方法で配列化する必要がある。
- 参考 : querySelectorAll()の結果はNodeListだけどforEach()が使える仕様です。(配列とかおれおれAdvent Calendar2018 – 11日目) | Ginpen.com
a.href.match(/(?!.*liked_by).*(?=\/p\/)/)
部分の正規表現がミソ。基本的には、href
属性値に /p/
を含んでいればその値を返すつもりで書いている。コレは Instagram の投稿ページの URL が、
https://www.instagram.com/p/XXXXXXXXXXX/
となっているので、間にある /p/
部分を探るようにした。ただ、投稿一覧などのページだと
https://www.instagram.com/p/XXXXXXXXXXX/liked_by/
といった URL が混じることがあったので、それを除外するために /(?!.*liked_by).*(?=\/p\/)/
こんな正規表現になった。
より正確に取得するなら改良の余地はあるだろうが、ブックマークレットの文字数を削りたいのでコレだけ。
match()
しなかった場合は undefined
が返り、Array.from()
が生成する配列に空 (undefined
) の要素が混じってしまう。コレを削るのが .filter(a => a)
というイディオムだ。コレだけで、Falsy な値を除外できる。
この方法は undefined
・null
・''
(空文字)・false
の他に、数値の 0
も除外されてしまうので、数値を扱う配列の場合は注意。
コレで、投稿ページの URL の配列ができたので、あとはテキストエリアに突っ込んでコピーしたりとかすれば終わり。
注意点
このブックマークレットで投稿 URL を一括取得や〜、といきたいところだったが、一つ大きな注意点が。
Instagram には Virtual Scroll という技術が導入されている。スクロールバーは一見長くなっているのだが、DOM 要素的には画面上に表示されている要素しか配置されていない、という状態になっているのだ。
- 参考 : 【Angular】Virtual Scrollingの実装 - 開発覚書はてな版 … Virtual Scroll について
つまり、数十件、数百件分の投稿が画面に表示されているかのように、ページ最下部までのスクロールを何度も繰り返した後にこのブックマークレットを使っても、先頭の方の投稿の DOM 要素はなくなっており、2・30件分くらいの URL しか取得できないのだ。
一括でゴッソリ取れるかと思ったが、なかなか上手く行かず残念。諦めよう〜