キャンペーン開始前に、IGアカウントのリストをブリーフと自動照合し、確認が必要な行だけをCSVにまとめられます。スクリプトは各行についてプロフィールを取得し、username、非公開設定、認証済みステータスを想定値と比較して、利用可能なカウンターも保存します。繰り返し行っていた手作業を、キャンペーンを実行に移す前の明確な確認工程にできます。
目的:公開前に差異を見つける
キャンペーン用の表には通常、アカウント名といくつかの想定条件が記載されています。たとえば、プロフィールが公開されていること、usernameが合意したものと一致すること、認証済みの表示がブリーフの記載と一致することです。リストの作成から開始までの間に、アカウント名が変わったり、非公開になったり、似た名前の別プロフィールだったりする可能性があります。行ごとに手作業で確認すると、プロセスを再現したり、例外をすばやく見つけたりするのが難しくなります。

このような事前診断には、usernameでプロフィールを取得すれば十分です。公開されているjsonscraperのドキュメントには、usernameパラメーターを指定するGET /v1/user/by/usernameルートが記載されています。任意のcache_timeoutも利用できます。レスポンスには、username、氏名、privateとverifiedのフラグ、投稿数、フォロワー数、フォロー数、プロフィール文などが含まれる場合があります。具体的なフィールドは実際のレスポンスを確認してください。スキーマのスナップショットで確認できるのは観測されたレスポンスでの存在であり、値は異なる場合や、存在しない場合があります。
表と確認ルールを準備する
username、expected_private、expected_verifiedの列を含むprofiles.csvを作成します。この2つの期待値の列にはtrueまたはfalseを入力し、usernameは@記号なしで記載します。プロフィールによって期待値がわからない場合は、セルを空欄にしてください。期待値がないことをエラーとして扱わないようにします。
username,expected_private,expected_verified
examplebrand,false,true
anotheraccount,false,false
ここでの確認は、取得したユーザー名がリクエストしたものと一致するか、また非公開設定と認証済みステータスが記録済みの想定と異なるかを調べる、限定的な2つのタスクを行います。カウンターとプロフィール文は参考情報として保存するもので、オーディエンスの質やクリエイターのキャンペーン適性を示す証拠として扱うものではありません。
IG Scraper APIに接続する
アクセスキーは環境変数JSONSCRAPER_INSTAGRAM_LICENSE_KEYに保存します。シークレットをCSV、ソースコード、実行ログに書き込まないでください。公式のIGエンドポイントのドキュメントには、ルートとパラメーター名が記載されています。APIのベースURLはhttps://ig.jsonscraper.comで、キーのパラメーター名はlicense_keyです。
リスト全体を処理する前に、テスト用の行で1回リクエストを行い、JSONの構造を確認して、アカウントにアクセス権があることを確かめてください。エンドポイントはusernameを受け取るため、このシンプルなシナリオでは既知のプロフィールIDや追加ルートは必要ありません。
例:リクエスト、照合、差異を含むCSVの作成
次のスクリプトはPython 3.8以降で動作します。CSVを読み込み、プロフィールごとにGETリクエストを1回行い、HTTPレスポンスのステータスを確認して、重要な元データと照合結果を保存します。python -m pip install requestsで依存関係をインストールし、実行前にキーを環境変数として設定してください。

import csv
import os
import time
from pathlib import Path
import requests
BASE_URL = "https://ig.jsonscraper.com/v1/user/by/username"
API_KEY = os.environ["JSONSCRAPER_INSTAGRAM_LICENSE_KEY"]
INPUT_FILE = Path("profiles.csv")
OUTPUT_FILE = Path("preflight_results.csv")
def parse_expected(value):
value = (value or "").strip().lower()
if value == "true":
return True
if value == "false":
return False
return None
def profile_from_response(payload):
# The documented observed response wraps profile fields in "ig".
if payload.get("status") != "ok":
raise ValueError("API response status is not ok")
profile = payload.get("ig")
if not isinstance(profile, dict):
raise ValueError("Response has no profile object under 'ig'")
return profile
with INPUT_FILE.open(newline="", encoding="utf-8") as source:
rows = list(csv.DictReader(source))
results = []
for row in rows:
requested = (row.get("username") or "").strip().lstrip("@")
if not requested:
results.append({
"requested_username": "",
"result": "missing_username",
"error": "username is empty",
})
continue
try:
response = requests.get(
BASE_URL,
params={"username": requested, "license_key": API_KEY},
timeout=30,
)
response.raise_for_status()
profile = profile_from_response(response.json())
actual_username = profile.get("username")
actual_private = profile.get("is_private")
actual_verified = profile.get("is_verified")
expected_private = parse_expected(row.get("expected_private"))
expected_verified = parse_expected(row.get("expected_verified"))
checks = []
if actual_username and actual_username.casefold() != requested.casefold():
checks.append("username_mismatch")
if expected_private is not None and actual_private != expected_private:
checks.append("privacy_mismatch")
if expected_verified is not None and actual_verified != expected_verified:
checks.append("verification_mismatch")
results.append({
"requested_username": requested,
"username": actual_username,
"full_name": profile.get("full_name"),
"is_private": actual_private,
"is_verified": actual_verified,
"media_count": profile.get("media_count"),
"follower_count": profile.get("follower_count"),
"following_count": profile.get("following_count"),
"result": "review" if checks else "matched",
"checks": ";".join(checks),
"error": "",
})
except (requests.RequestException, ValueError, KeyError) as exc:
results.append({
"requested_username": requested,
"result": "request_or_response_error",
"error": str(exc),
})
# A small delay spaces requests; it is not a documented quota guarantee.
time.sleep(0.5)
fieldnames = sorted({key for item in results for key in item})
with OUTPUT_FILE.open("w", newline="", encoding="utf-8") as destination:
writer = csv.DictWriter(destination, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(results)
print("Saved", len(results), "rows to", OUTPUT_FILE)
コードでは、観測されたレスポンスのラッパー、すなわち外側のstatusとプロフィールオブジェクトのigを検証しています。プロバイダーが形式を変更した場合は、構造チェックによってそのレコードの処理を停止し、誤った一致を黙って作成する代わりにエラーを保存します。スクリプトはキーを表示せず、出力ファイルにも含めません。
結果の読み方
preflight_results.csvでmatchedとなっている行は、指定された照合条件を満たしています。reviewは1つ以上の項目がブリーフと異なることを意味し、checks列に具体的な項目が表示されます。リクエストやレスポンスのエラーは別に記録されるため、データを取得できなかった状態が、確認済みの一致に見えることはありません。
フォロワー数と投稿数は、申請内容と並べてプロフィールの現在の状態を人が確認するのに役立ちます。これらはリクエストに対する一時点の値です。時間の経過による変化を評価するには、実行日時を付けて別々に保存してください。1回の確認では履歴を取得できません。フィールドが空欄でも、本人確認や他の利用可能な特徴の確認には使えますが、存在しないカウンターとの比較はできません。
キャンペーン開始前の自動化
テスト用CSVを手動で確認した後、スクリプトをスケジュールやキャンペーン準備工程に追加します。再現可能な確認のため、元のリストは別に保管し、実行日を記録して、結果のCSVをアクセス制限のある業務用ストレージに保存してください。usernameが変わる可能性がある場合は、再処理しやすいように自社システムの安定した行識別子を使うと便利です。
リストが増えたら、並列処理の上限を設け、一時的なネットワークエラーに限って再試行し、シークレットを含めずにエラーコードを別のログへ記録してください。無効なキーや不正なusernameに対して、無限に再試行しないでください。cache_timeoutは必要な鮮度ポリシーに従って追加できます。ドキュメントによると、IGではデフォルトでキャッシュが無効になっており、0より大きい値を指定すると、その期間キャッシュが有効になります。
プロフィール確認によって、手作業での照合を具体的な例外リストに絞り込めます。キャンペーン開始前に、名前、公開設定、認証済みステータス、利用可能なカウンターを1つの表で確認できます。APIパラメーターと最新の連携方法については、IG Scraper APIのページとエンドポイントのドキュメントを参照してください。