mcpField
I
instagram-post-downloader

instagram-post-downloader

github.com/mohitagw15856クリエイティブ・メディア1.5k48 回インストール5 日前に更新
リポジトリ

instagram-post-downloader について

instagram-post-downloaderは、公開Instagram投稿の画像を原寸で保存するスキルです。単一画像、カルーセル、複数URLに対応し、カルーセルをPDFにまとめられます。Reelsはカバー画像のみが対象です。

主な機能

  1. 01単一画像とカルーセル画像のダウンロード
  2. 02投稿URLの一括処理
  3. 03取得可能な最高解像度の選択
  4. 04カルーセル画像のPDF化
  5. 05投稿メタデータの保存

活用シーン

  1. 01公開投稿の高解像度画像の保存
  2. 02複数のInstagram投稿の一括アーカイブ
  3. 03カルーセル画像を個別画像とPDFで整理
  4. 04Reelのカバー画像のダウンロード

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitagw15856/pm-claude-skills --skill instagram-post-downloader

インストール後、Claude Code で/instagram-post-downloaderを入力して読み込まれたか確認します。

詳細

Instagram 投稿ダウンローダー スキル

Instagram の CDN から Instagram の投稿をフル解像度でダウンロードします。スクリーンショットも圧縮もありません。単一画像、カルーセル(複数スライドの投稿)、Reel のカバー画像に対応しています。カルーセルの場合は、各スライドの個別ファイルと、スライドを結合した PDF を作成します。1 回の実行で複数の URL をまとめて処理できます。


前提条件 — ドメイン許可リスト

このスキルでメディアを取得する前に、Claude Code の許可リストに Instagram の CDN ドメインを追加する必要があります。

設定 → 機能 → ドメイン許可リスト → 追加:

*.cdninstagram.com

これを設定しないと、CDN へのすべての取得リクエストがブロックされます。Claude が cdninstagram.com への取得を試みたときに権限エラーが表示された場合は、これを設定してください。


必要な入力

事前に指定されていない場合、Claude は以下を確認します。

入力必須備考
Instagram 投稿 URLはい1 行に 1 つ、またはカンマ区切り。形式は https://www.instagram.com/p/XXXX/ または https://www.instagram.com/reel/XXXX/
出力ディレクトリいいえデフォルトは現在の作業ディレクトリ内の ./instagram-downloads/
カルーセルの PDF 結合いいえデフォルトははい — 各スライドに加えて carousel.pdf を作成
ファイル名のプレフィックスいいえスライドのファイル名の前に追加する任意のプレフィックス。例:brand_ → brand_slide_01.jpg

複数 URL の入力例:

https://www.instagram.com/p/ABC123/
https://www.instagram.com/p/DEF456/
https://www.instagram.com/p/GHI789/

出力構成

処理した URL ごとに、Claude は投稿のキャプション(先頭 40 文字。サニタイズし、スペースはアンダースコアに置き換え、特殊文字は削除)を名前にしたフォルダーを作成します。キャプションが取得できない場合は、投稿のショートコードをフォルダー名にします。

単一画像の投稿
instagram-downloads/
└── this_is_the_caption_first_40_chars/
    ├── image.jpg
    └── metadata.txt
カルーセルの投稿
instagram-downloads/
└── carousel_caption_first_40_chars/
    ├── slide_01.jpg
    ├── slide_02.jpg
    ├── slide_03.jpg
    ├── slide_04.jpg
    ├── carousel.pdf          ← all slides stitched in order
    └── metadata.txt
複数 URL の一括実行(3 URL)
instagram-downloads/
├── first_post_caption_sanitised/
│   ├── image.jpg
│   └── metadata.txt
├── second_post_carousel_caption/
│   ├── slide_01.jpg
│   ├── slide_02.jpg
│   ├── carousel.pdf
│   └── metadata.txt
└── third_post_caption_here/
    ├── image.jpg
    └── metadata.txt
metadata.txt の形式
Post URL:       https://www.instagram.com/p/XXXX/
Shortcode:      XXXX
Type:           carousel | single_image | reel
Slide count:    4  (carousel only)
Caption:        [full caption text]
Username:       @username
Fetched at:     2026-05-27T14:32:00Z
CDN URLs:
  slide_01.jpg  https://scontent.cdninstagram.com/v/...
  slide_02.jpg  https://scontent.cdninstagram.com/v/...
完了サマリー(ターミナルに表示)
Instagram Post Downloader — Batch Complete
==========================================
URLs processed:   3
Posts saved:      3
Total files:      11  (9 images + 2 PDFs)
Skipped:          0
Output dir:       /Users/you/project/instagram-downloads/

Results:
  ✓ this_is_the_caption_first_40_chars/     1 image
  ✓ carousel_caption_first_40_chars/        4 slides → carousel.pdf
  ✓ third_post_caption_here/                1 image

Claude がこのスキルを実行する方法

ステップ 1 — 入力を収集して検証する
  1. ユーザーから URL を受け取ります。ユーザーがカンマ区切りのリストを貼り付けた場合は、カンマで分割します。1 行に 1 つずつ貼り付けた場合は、改行で分割します。
  2. 各 URL が instagram.com/p/、instagram.com/reel/、または instagram.com/tv/ に一致することを検証します。形式が正しくない URL があれば、処理を続ける前に知らせます。
  3. 出力ディレクトリを確認します。指定がない場合は ./instagram-downloads/ を使用し、そのことをユーザーに伝えます。
  4. PDF の結合については、ユーザーが希望を指定していない場合にのみ確認します。デフォルトは「はい」です。
ステップ 2 — URL ごとに投稿ページを取得する

Instagram 投稿ページの HTML を取得します。

GET https://www.instagram.com/p/{shortcode}/?__a=1&__d=dis

Instagram は API の仕様を頻繁に変更します。次のフォールバックを順番に試してください。

試行 A — JSON エンドポイント:

https://www.instagram.com/p/{shortcode}/?__a=1&__d=dis

JSON レスポンスを解析します。graphql.shortcode_media または data.shortcode_media を探します。

試行 B — 埋め込みページ(最も信頼性が高い):

https://www.instagram.com/p/{shortcode}/embed/captioned/

このページの HTML を取得し、<script> タグに埋め込まれた og:image メタタグと、window.__additionalDataLoaded または window.__StaticData の JSON データを抽出します。

試行 C — oEmbed エンドポイント:

https://api.instagram.com/oembed/?url=https://www.instagram.com/p/{shortcode}/&omitscript=true

thumbnail_url が返されます。単一画像には便利ですが、カルーセルでは最初のフレームしか取得できません。

すべてのリクエストに含めるヘッダー:

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Accept-Language: en-US,en;q=0.9
Accept: text/html,application/xhtml+xml,application/json
ステップ 3 — CDN 画像 URL を抽出する

取得したデータから、高解像度の CDN URL をすべて抽出します。Instagram の CDN URL は次の形式です。

https://scontent.cdninstagram.com/v/...jpg?...
https://scontent-lax3-1.cdninstagram.com/v/...jpg?...
https://instagram.fXXX1-1.fbcdn.net/v/...jpg?...

単一画像の投稿の場合:

  • 単一の display_url、または display_resources の中で最も大きいエントリー(config_width が最大のもの)を抽出します。

カルーセルの投稿の場合:

  • JSON 内の edge_sidecar_to_children.edges[] を探します。各エッジには、それぞれの node.display_url と node.display_resources[] が含まれます。
  • すべてのエッジを順番に処理します。これによってスライド番号が決まります。
  • 各スライドの display_resources 配列から、最も高解像度のバリエーションを選択します。

Reel の場合:

  • カバー画像は、単一画像と同じ方法で抽出できます。
  • 動画ファイル自体の取得にはサードパーティ製ツールが必要です(「ボーナス」セクションを参照)。

JSON の抽出に失敗した場合は、ページ HTML から <meta property="og:image"> タグをスクレイピングします。これにより、少なくとも 1 つの画像 URL(最初のスライド、または唯一の画像)を取得できます。

ステップ 4 — フォルダー名のサニタイズ

投稿キャプションからフォルダー名を作成します:

  1. キャプションの先頭から40文字を取得します。
  2. 英数字、スペース、ハイフン以外の文字をすべて削除します。
  3. スペースとハイフンをアンダースコアに置き換えます。
  4. 結果を小文字にします。
  5. 先頭と末尾のアンダースコアを削除します。
  6. 結果が空の場合(例:キャプションが絵文字のみ)、代わりに投稿のショートコードを使用します。
import re

def sanitise_folder_name(caption: str, shortcode: str) -> str:
    truncated = caption[:40]
    cleaned = re.sub(r'[^a-zA-Z0-9 \-]', '', truncated)
    underscored = re.sub(r'[\s\-]+', '_', cleaned).strip('_').lower()
    return underscored if underscored else shortcode
ステップ 5 — 出力フォルダー構成の作成
import os

base_dir = "./instagram-downloads"
folder_name = sanitise_folder_name(caption, shortcode)
post_dir = os.path.join(base_dir, folder_name)
os.makedirs(post_dir, exist_ok=True)

同じ名前のフォルダーがすでに存在する場合(例:同じURLを2回実行した場合)、衝突を避けるためにショートコードを追加します:folder_name_SHORTCODE。

ステップ 6 — 各画像ファイルのダウンロード

各CDN URLについて、ストリーミングGETリクエストでファイルをダウンロードします:

import requests

def download_file(url: str, dest_path: str) -> bool:
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
        "Referer": "https://www.instagram.com/",
    }
    response = requests.get(url, headers=headers, stream=True, timeout=30)
    response.raise_for_status()
    with open(dest_path, "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    return True

ファイル名:

  • 画像が1枚の場合:image.jpg
  • カルーセルのスライド:slide_01.jpg、slide_02.jpg、...(2桁のゼロ埋め。スライドが99枚を超える場合は3桁)

Content-TypeヘッダーまたはURLの拡張子からファイル形式を判定します。Instagramは写真にはJPEGを使用し、場合によってはWebPを使用することもあります。実際の拡張子を保持してください。

ステップ 7 — カルーセルをPDFに結合(該当する場合)

すべてのスライドをダウンロードした後、Pillowを使用して1つのPDFに結合します:

from PIL import Image

def stitch_to_pdf(image_paths: list[str], output_path: str) -> None:
    """
    Combine a list of image files into a single multi-page PDF.
    Each image becomes one page. Page size matches the image dimensions.
    """
    images = []
    for path in sorted(image_paths):  # sort ensures slide_01, slide_02, ... order
        img = Image.open(path).convert("RGB")
        images.append(img)

    if not images:
        return

    first = images[0]
    rest = images[1:]
    first.save(
        output_path,
        format="PDF",
        save_all=True,
        append_images=rest,
        resolution=150.0,
    )

投稿フォルダーにcarousel.pdfとして保存します。Pillowがインストールされていない場合は、先にpip install Pillowを実行するか、ユーザーに実行を案内してください。

スキル開始時の依存関係の確認:

try:
    from PIL import Image
except ImportError:
    print("Pillow not installed. Run: pip install Pillow")
    print("PDF stitching will be skipped. Individual slides will still be downloaded.")
    skip_pdf = True
ステップ 8 — metadata.txt の書き込み

抽出したすべてのメタデータを含むmetadata.txtファイルを投稿フォルダーに書き込みます:

from datetime import datetime, timezone

def write_metadata(post_dir, post_url, shortcode, post_type, caption, username, cdn_urls):
    lines = [
        f"Post URL:       {post_url}",
        f"Shortcode:      {shortcode}",
        f"Type:           {post_type}",
    ]
    if post_type == "carousel":
        lines.append(f"Slide count:    {len(cdn_urls)}")
    lines += [
        f"Caption:        {caption}",
        f"Username:       @{username}",
        f"Fetched at:     {datetime.now(timezone.utc).isoformat()}",
        "CDN URLs:",
    ]
    for filename, url in cdn_urls.items():
        lines.append(f"  {filename:<16} {url}")

    with open(os.path.join(post_dir, "metadata.txt"), "w", encoding="utf-8") as f:
        f.write("\n".join(lines) + "\n")
ステップ 9 — 完了サマリーの表示

すべてのURLの処理後、ターミナルにサマリーテーブルを表示します(上記の「出力構成」セクションに記載の形式)。以下を含めてください:

  • 試行したURLの合計数
  • 正常に保存された投稿数
  • 書き込んだファイルの合計数(画像とPDFは別々に集計)
  • スキップしたURLとその理由
ステップ 10 — エラーを適切に処理
エラーの状況対応
URLがInstagramのURLではないメッセージを表示してスキップします:"Skipped — not an Instagram URL: [url]"
投稿が非公開、またはログインが必要メッセージを表示してスキップします:"Skipped — post is private or login required: [url]"
CDNの取得で403/404が返される利用可能であれば代替CDN URLを試します。代替URLがない場合はスライドをスキップし、その旨をメタデータに記録します
PillowがインストールされていないPDFへの結合をスキップし、スライドのみを保存して、サマリーにその旨を記録します
ネットワークタイムアウト5秒後に1回再試行します。それでも失敗する場合はスキップしてログに記録します
フォルダー名の衝突フォルダー名にショートコードのサフィックスを追加します
レート制限(429)10秒待ってから再試行します。再試行も失敗した場合はログに記録します

ボーナス — Instagram Reels(動画)のダウンロード

このスキルの対象は画像とカルーセルPDFです。Instagramの動画CDNは署名付きURLと追加の認証トークンを使用するため、サードパーティ製ツールなしではClaude CodeがReelsの動画を直接ダウンロードできません。

Reelsでの推奨方法:

メンテナンスされているオープンソースツールのyt-dlpを使用します:

# Install
pip install yt-dlp

# Download a Reel
yt-dlp "https://www.instagram.com/reel/XXXX/" -o "%(title)s.%(ext)s"

# Download to a specific folder
yt-dlp "https://www.instagram.com/reel/XXXX/" \
  -o "./instagram-downloads/%(uploader)s_%(id)s.%(ext)s"

# Download best quality
yt-dlp -f "bestvideo+bestaudio" "https://www.instagram.com/reel/XXXX/"

ユーザーから依頼があれば、ClaudeはBash経由でこのコマンドを実行できます。yt-dlpは公開されているReelsの認証トークン抽出を自動で処理します。


スクリプト全体のテンプレート

Claudeは、ユーザーが単独で実行できるスタンドアロンスクリプト(instagram_downloader.py)として作成することを提案してください:

#!/usr/bin/env python3
"""
Instagram Post Downloader
Fetches high-res images from public Instagram posts and carousels.
Requires: pip install requests Pillow
"""

import os
import re
import sys
import json
import time
import requests
from datetime import datetime, timezone
from pathlib import Path

try:
    from PIL import Image
    PILLOW_AVAILABLE = True
except ImportError:
    PILLOW_AVAILABLE = False
    print("Warning: Pillow not installed. PDF stitching disabled. Run: pip install Pillow")


HEADERS = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept-Language": "en-US,en;q=0.9",
    "Referer": "https://www.instagram.com/",
}


def extract_shortcode(url: str) -> str:
    match = re.search(r"instagram\.com/(?:p|reel|tv)/([A-Za-z0-9_-]+)", url)
    if not match:
        raise ValueError(f"Cannot extract shortcode from URL: {url}")
    return match.group(1)


def fetch_post_data(shortcode: str) -> dict:
    """Try multiple endpoints to get post JSON data."""
    # Attempt A: JSON endpoint
    try:
        url = f"https://www.instagram.com/p/{shortcode}/?__a=1&__d=dis"
        r = requests.get(url, headers=HEADERS, timeout=15)
        if r.status_code == 200:
            data = r.json()
            media = (data.get("graphql", {}).get("shortcode_media") or
                     data.get("data", {}).get("shortcode_media"))
            if media:
                return media
    except Exception:
        pass

    # Attempt B: Embed page
    try:
        url = f"https://www.instagram.com/p/{shortcode}/embed/captioned/"
        r = requests.get(url, headers=HEADERS, timeout=15)
        html = r.text
        # Look for JSON blob in script tags
        matches = re.findall(r'window\.__additionalDataLoaded\([^,]+,(\{.+?\})\);', html)
        for blob in matches:
            try:
                data = json.loads(blob)
                media = (data.get("graphql", {}).get("shortcode_media") or
                         data.get("data", {}).get("shortcode_media"))
                if media:
                    return media
            except json.JSONDecodeError:
                continue
    except Exception:
        pass

    return {}


def get_cdn_urls(media: dict) -> list[tuple[str, str]]:
    """Return list of (filename, cdn_url) tuples."""
    results = []
    media_type = media.get("__typename", "")

    if media_type == "GraphSidecar":
        edges = media.get("edge_sidecar_to_children", {}).get("edges", [])
        for i, edge in enumerate(edges, start=1):
            node = edge.get("node", {})
            resources = node.get("display_resources", [])
            url = (max(resources, key=lambda r: r.get("config_width", 0)).get("src")
                   if resources else node.get("display_url", ""))
            if url:
                ext = "jpg" if "jpg" in url.lower() else "webp"
                filename = f"slide_{i:02d}.{ext}"
                results.append((filename, url))
    else:
        resources = media.get("display_resources", [])
        url = (max(resources, key=lambda r: r.get("config_width", 0)).get("src")
               if resources else media.get("display_url", ""))
        if url:
            ext = "jpg" if "jpg" in url.lower() else "webp"
            results.append((f"image.{ext}", url))

    return results


def sanitise_folder_name(caption: str, shortcode: str) -> str:
    truncated = caption[:40] if caption else ""
    cleaned = re.sub(r"[^a-zA-Z0-9 \-]", "", truncated)
    underscored = re.sub(r"[\s\-]+", "_", cleaned).strip("_").lower()
    return underscored if underscored else shortcode


def download_file(url: str, dest_path: str) -> bool:
    r = requests.get(url, headers=HEADERS, stream=True, timeout=30)
    r.raise_for_status()
    with open(dest_path, "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)
    return True


def stitch_pdf(image_paths: list[str], output_path: str) -> None:
    if not PILLOW_AVAILABLE:
        return
    images = [Image.open(p).convert("RGB") for p in sorted(image_paths)]
    if images:
        images[0].save(output_path, format="PDF", save_all=True,
                       append_images=images[1:], resolution=150.0)


def process_url(post_url: str, base_dir: str, stitch_pdf_flag: bool) -> dict:
    result = {"url": post_url, "status": "ok", "files": [], "error": None}
    try:
        shortcode = extract_shortcode(post_url)
        media = fetch_post_data(shortcode)

        caption = ""
        username = ""
        if media:
            caption_edges = media.get("edge_media_to_caption", {}).get("edges", [])
            caption = caption_edges[0]["node"]["text"] if caption_edges else ""
            owner = media.get("owner", {})
            username = owner.get("username", "")

        folder_name = sanitise_folder_name(caption, shortcode)
        post_dir = os.path.join(base_dir, folder_name)
        if os.path.exists(post_dir):
            post_dir = f"{post_dir}_{shortcode}"
        os.makedirs(post_dir, exist_ok=True)

        cdn_urls = get_cdn_urls(media) if media else []
        if not cdn_urls:
            # Fallback: oEmbed
            oembed_url = f"https://api.instagram.com/oembed/?url={post_url}&omitscript=true"
            r = requests.get(oembed_url, headers=HEADERS, timeout=10)
            if r.status_code == 200:
                thumb = r.json().get("thumbnail_url", "")
                if thumb:
                    cdn_urls = [("image.jpg", thumb)]
                    username = r.json().get("author_name", "")

        downloaded_paths = []
        cdn_map = {}
        for filename, url in cdn_urls:
            dest = os.path.join(post_dir, filename)
            download_file(url, dest)
            downloaded_paths.append(dest)
            cdn_map[filename] = url
            result["files"].append(filename)

        if stitch_pdf_flag and len(downloaded_paths) > 1 and PILLOW_AVAILABLE:
            pdf_path = os.path.join(post_dir, "carousel.pdf")
            stitch_pdf(downloaded_paths, pdf_path)
            result["files"].append("carousel.pdf")

        post_type = "carousel" if len(cdn_urls) > 1 else "single_image"
        write_metadata(post_dir, post_url, shortcode, post_type, caption, username, cdn_map)
        result["files"].append("metadata.txt")

    except Exception as e:
        result["status"] = "error"
        result["error"] = str(e)

    return result


def write_metadata(post_dir, post_url, shortcode, post_type, caption, username, cdn_map):
    lines = [
        f"Post URL:       {post_url}",
        f"Shortcode:      {shortcode}",
        f"Type:           {post_type}",
    ]
    if post_type == "carousel":
        lines.append(f"Slide count:    {len([k for k in cdn_map if 'slide' in k])}")
    lines += [
        f"Caption:        {caption}",
        f"Username:       @{username}",
        f"Fetched at:     {datetime.now(timezone.utc).isoformat()}",
        "CDN URLs:",
    ]
    for fn, url in cdn_map.items():
        lines.append(f"  {fn:<18} {url}")
    with open(os.path.join(post_dir, "metadata.txt"), "w", encoding="utf-8") as f:
        f.write("\n".join(lines) + "\n")


def main(urls: list[str], base_dir: str = "./instagram-downloads", stitch: bool = True):
    os.makedirs(base_dir, exist_ok=True)
    results = []
    for url in urls:
        url = url.strip()
        if not url:
            continue
        print(f"Processing: {url}")
        r = process_url(url, base_dir, stitch)
        results.append(r)
        time.sleep(1)  # polite delay between requests

    # Summary
    ok = [r for r in results if r["status"] == "ok"]
    err = [r for r in results if r["status"] == "error"]
    total_files = sum(len(r["files"]) for r in ok)
    print("\nInstagram Post Downloader — Batch Complete")
    print("==========================================")
    print(f"URLs processed:   {len(results)}")
    print(f"Posts saved:      {len(ok)}")
    print(f"Total files:      {total_files}")
    print(f"Errors:           {len(err)}")
    print(f"Output dir:       {os.path.abspath(base_dir)}\n")
    for r in results:
        if r["status"] == "ok":
            print(f"  OK  {r['url']}")
        else:
            print(f"  ERR {r['url']}  — {r['error']}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python instagram_downloader.py <url1> [url2] ...")
        sys.exit(1)
    main(sys.argv[1:])

品質チェック

タスクを完了とする前に、各項目を確認してください。

  • ドメイン許可リストを確認済み — フェッチを試みる前に *.cdninstagram.com を追加している
  • 処理を開始する前に、提供されたすべての URL が Instagram の URL であることを検証している
  • CDN URL は利用可能な最高解像度のものを使用している(最大の config_width を選択)
  • フォルダー名をサニタイズしている — 特殊文字や空白を含めず、キャプションから取得した最大 40 文字にしている
  • フォルダーの競合に対処している — フォルダーがすでに存在する場合はショートコードを追加している
  • カルーセルのスライドにゼロ埋めの連番を付けている(slide_01、slide_02、…)
  • PDF にすべてのスライドを正しい順序で含めている(アルファベット順ではなく、スライドのインデックス順)
  • すべての投稿フォルダーに、完全な CDN URL を含む metadata.txt を書き込んでいる
  • 起動時に Pillow 依存関係を確認している — 利用できない場合も適切にフォールバックする
  • バッチ完了時に、ファイル数とエラーを含む概要を表示している
  • 非公開投稿のエラーを捕捉して報告している — 黙ってスキップしていない
  • レート制限に対処している — リクエスト間に少なくとも 1 秒の遅延を設けている
  • 認証情報や Cookie を保存していない — このスキルは公開投稿のみを対象としている

避けるべきパターン

  • 非公開投稿やログインが必要なコンテンツをダウンロードしようとしないでください — このスキルは公開投稿のみを対象としています
  • 429 レート制限レスポンスを無視しないでください — 再試行する前に必ずバックオフ待機を実装してください
  • 複数のアカウントを処理する際、すべてのダウンロードを単一のフラットなフォルダーに保存しないでください — ソースごとに名前付きサブフォルダーを使用してください
  • カルーセル投稿の PDF 結合を省略しないでください — 個別のスライドだけを提供し、結合 PDF を作成しない場合は出力が不完全です
  • Instagram がログイン画面を返した場合は処理を続行しないでください — エラーを黙って返すのではなく、制限があることを明確に伝えてください

トリガーフレーズの例

  • 「この Instagram の投稿をダウンロードしてください:https://www.instagram.com/p/ABC123/」
  • 「そのカルーセルをダウンロードフォルダーに保存してください」
  • 「この Instagram の投稿からすべてのスライドを取得して、PDF を作成できますか?」
  • 「この 5 件の Instagram 投稿をダウンロードしてください」[URL のリストが続きます]
  • 「削除される前に、この IG 投稿をアーカイブしてください」
  • 「このカルーセルの高解像度画像が必要です」
  • 「この Instagram URL の画像をダウンロードして、PDF にまとめてください」
  • 「この Instagram 投稿を一括ダウンロードしてください」[URL が続きます]
  • 「この Instagram カルーセルのスライドを、個別の JPEG として保存してください」
  • 「この Instagram 画像の高解像度版を取得してください」

Instagram のスクレイピング対策に関する注意事項

Instagram はページ構造と API エンドポイントを頻繁に変更します。3 回のフェッチ試行すべてが失敗した場合は、次の手順に従ってください。

  1. 埋め込みページ方式(/embed/captioned/)はこれまで最も安定しているため、まず試してください。
  2. CDN URL には有効期限があります。フェッチ後すぐにダウンロードしてください — URL を保存して後からダウンロードしないでください。
  3. 技術的には公開されている投稿でも、Instagram がログイン画面を返すことがあります。この場合、認証なしではスキルを実行できません(認証は対象範囲外です)。
  4. Instagram が 429 を返した場合は、再試行する前に 10〜30 秒待ってください。大規模なリストではバッチサイズを小さくしてください。

このスキルは公開投稿のみを対象として設計されています。ログイン、セッション、非公開コンテンツには対応していません。


Frank and Diana Dovgopol(Write, Prompt, Scale)のスキルに着想を得て、当初作成されたものです — このライブラリ向けに適応・拡張されています。

コメント