Instagramプロフィール自動化|Playwrightで実装

AI業務効率化

※ この記事にはアフィリエイトリンクが含まれています。リンク経由で購入しても読者の皆さんに追加費用は発生しません。収益は本サイトの運営費に充てています。

Instagram Graph APIではプロフィールの変更ができません。読み取りと投稿はできるのに、自己紹介文やアイコン画像の設定だけはAPI対象外。僕はこの壁をPlaywrightのモバイルエミュレーションで突破しました。この記事では、実際に動いたコードと5回失敗した過程を全部書きます。

こんな方に読んでほしい記事です:

  • Instagram APIの「できること・できないこと」を体系的に知りたい
  • Playwrightでモバイルサイトを操作する具体的なコードが欲しい
  • page.evaluate()でオーバーレイを突破する実装パターンを知りたい
  • 前回のアフィリリンク自動化の続きを追いたい

Instagram/Threads APIの「できること・できないこと」完全整理

まず、APIでどこまでやれるのかをハッキリさせておきます。公式ドキュメントを読んで整理した結果がこの表です。

操作 Instagram Graph API Threads API Playwright
プロフィール情報の取得
プロフィール画像の変更
自己紹介文の変更
プロフィールリンクの変更
フィード投稿(画像必須)
テキスト投稿
ストーリーズ投稿
カルーセル投稿
コメント管理
インサイト取得
DM送信
フォロー/アンフォロー ✅(非推奨)

要するに、APIは「投稿」と「分析」に特化していて、「アカウント設定」は一切対象外です。プロフィール変更、DM、フォロー操作はPlaywrightでブラウザを操作するしかありません。

⚠️ Playwrightでの自動操作に関する注意事項

PlaywrightによるInstagramの自動操作は、利用規約に抵触する可能性があります。今回は「初期設定を1回だけ自動化する」用途です。自分のアカウント限定、操作頻度は最小限、自動いいね・自動フォローはやらない。日常的にPlaywrightでInstagramを操作し続けることは推奨しません。対象サービスの利用規約を都度確認してください。

Playwrightのモバイルエミュレーションでログインする

Instagramのデスクトップ版は、PlaywrightでアクセスしてもReactのSPAがうまくレンダリングされず、ログインフォームが表示されない問題があります。解決策はモバイルエミュレーションです。

contextの設定コード

const context = await browser.newContext({
  userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...",
  viewport: { width: 390, height: 844 },
  isMobile: true,
  hasTouch: true,
  ignoreHTTPSErrors: true,
});

iPhoneとして接続するだけで、モバイル版のログインフォームがレンダリングされます。さらに重要なのが、デスクトップとモバイルでフォームのinput name属性が違う点です。

環境 ユーザー名フィールド パスワードフィールド
デスクトップUI name="email" name="pass"
モバイルUI name="username" name="password"

公式ドキュメントにはこの違いは書かれていません。実際のDOMを確認するしかないのが現実です。Playwrightでログインフォームを操作するときは、まず全inputのname属性を出力するデバッグを入れるのが鉄則です。

ログイン実装のポイント

// フィールド名が環境で異なるので両方対応
await page.locator('input[name="username"], input[name="email"]')
  .first().fill(username);
await page.locator('input[name="password"], input[name="pass"]')
  .first().fill(password);

// ログインボタン(モバイルUIではdiv[role="button"]の場合あり)
await page.locator(
  'button[type="submit"], div[role="button"]:has-text("ログイン")'
).first().click();

ログイン後は「情報を保存しますか?」「通知を許可しますか?」のダイアログが出るので、ループで「後で」ボタンを押して閉じます。

page.evaluate()でオーバーレイを突破する

ログインできたら/accounts/edit/に遷移して自己紹介を設定します。ここで最大のハマりポイントが登場します。

問題:オーバーレイがクリックをブロックする

Instagramのプロフィール編集ページでは、Cookie同意バナーやログイン確認ダイアログがオーバーレイとして表示されます。Playwrightのclick()は「要素は見えているが別の要素がポインターイベントを遮っている」とエラーになります。

解決策:page.evaluate()で直接DOM操作

前回のA8.net操作でも使ったpage.evaluate()パターンです。ブラウザ内のJavaScriptとして実行するので、オーバーレイの影響を受けません。

// テキストエリアに自己紹介文を入力
const bioField = page.locator("textarea").first();
await bioField.evaluate((el) => {
  el.focus();
  el.value = "";
});
await bioField.fill(bioText);

// 「送信する」ボタンをevaluateで押す
await page.evaluate(() => {
  const buttons = document.querySelectorAll('div[role="button"]');
  for (const btn of buttons) {
    if (btn.textContent?.includes("送信")) {
      btn.click();
      return;
    }
  }
});

Playwrightのlocatorでは「要素を見つけて→クリックする」までに複数のチェックが入ります。しかしevaluate()はブラウザ内で直接実行されるので、オーバーレイの存在を無視できます。業務系WebアプリやSNSのようにUIが複雑なサイトでは、evaluate()が最も安定する操作方法です。

プロフィール画像の自動設定——5回失敗した記録

自己紹介文は安定して動きました。問題はプロフィール画像です。正直に書くと、5回試行錯誤して最終的に手動に切り替えました。

うまくいった部分

// 「写真を変更」ボタンをevaluateで押す
await page.evaluate(() => {
  const allElements = document.querySelectorAll('div[role="button"], a, span');
  for (const el of allElements) {
    if (el.textContent?.trim() === "写真を変更") {
      el.click();
      return;
    }
  }
});

// ファイル選択(input[type="file"]が2つあるので.first()必須)
await page.locator('input[type="file"]').first()
  .setInputFiles(avatarPath);

ファイル選択までは安定して動きます。画像のプレビューも表示される。

うまくいかなかった部分:フィルター画面が不安定

ファイル選択後にフィルター編集画面が出ますが、このUIが実行のたびに異なる挙動をします。

  • 1回目:「閉じる | 保存する | Ludwig | Moon…」とフィルター画面のボタンが並ぶ
  • 2回目:フィルター画面をスキップしてプロフィール編集ページに戻る
  • 3回目:ボタンのテキストが「確定する」と「保存する」でブレる

全ボタンのテキストをpage.evaluate()で出力するデバッグを入れて調査しましたが、安定パターンが見つかりませんでした。5回試行して断念し、プロフィール画像だけは手動で設定しました。

この経験からの学び

Instagramの画像アップロード系UIはPlaywrightでの完全自動化が極めて難しい。フィルター画面やトリミング画面などの中間ステップが不安定で、毎回同じフローにならない。一方、テキスト入力系(自己紹介文)は安定して動く。「自動化できる部分」と「手動の方が早い部分」を見極めて、費用対効果の高いところだけ自動化するのが現実的です。

Threads/Instagram投稿APIの実装

プロフィール設定と並行して、投稿APIも実装しました。こちらはPlaywrightではなく正規のAPIです。

Threads APIのテキスト投稿(2ステップ必須)

// Step 1: メディアコンテナを作成
const createRes = await fetch(
  `https://graph.threads.net/v1.0/${userId}/threads`, {
  method: "POST",
  body: JSON.stringify({
    media_type: "TEXT",
    text: "投稿テキスト",
    access_token: token,
  }),
});
const { id: containerId } = await createRes.json();

// Step 2: 公開する
await fetch(
  `https://graph.threads.net/v1.0/${userId}/threads_publish`, {
  method: "POST",
  body: JSON.stringify({
    creation_id: containerId,
    access_token: token,
  }),
});

1ステップで投稿できないのがやや面倒ですが、画像付き投稿の場合はコンテナ作成後に5秒ほど待ってから公開APIを叩く必要があります。画像処理が完了していないと公開エラーになるためです。

Instagram Graph APIの画像投稿

InstagramはThreadsと違い、テキストだけの投稿ができません。必ず画像URLが必要で、かつローカルファイルは直接アップロードできない仕様です。先にWordPressのメディアライブラリに画像をアップロードし、その公開URLを指定する流れになります。

実際にThreadsに初投稿した結果

npx tsx src/publish/threads.ts "投稿テキスト"の1コマンドで投稿完了。構築した投稿APIで初投稿しました。

Threads初投稿のスクリーンショット

DALL-E 3でアイコン画像を自動生成した過程

プロフィール用のキャラクターアイコンはDALL-E 3 APIで生成しました。

人物→動物→フクロウに絞り込んだ判断基準

最初に人物イラスト3パターンを生成。しかしAI副業系のInstagramアカウントを20件以上調査すると、人物イラストのアイコンは飽和状態でした。差別化のために動物キャラクターに方向転換。タヌキ2パターン、フクロウ2パターンの計4パターンを追加生成し、「メガネをかけたフクロウがラップトップに向かっている」デザインに決定。

アイコン画像の生成で気をつけたのは以下の点です。

  • 1024x1024px正方形:丸くトリミングされるので中央にキャラ配置
  • quality: “hd”:standardより料金は倍だが、1回しか作らないので許容
  • 「No text」が効かない問題:4パターン中1パターンでテキスト混入。ぶっちゃけDALL-E 3のテキスト制御はまだ不完全なので目視確認は必須

技術スタック・まとめ

  • 言語:TypeScript(tsxで直接実行)
  • ブラウザ自動操作Playwright(モバイルエミュレーション + evaluate()パターン)
  • SNS API:Threads API(テキスト+画像投稿)、Instagram Graph API(画像+カルーセル投稿)
  • 画像生成DALL-E 3 API
  • ホスティングConoHa WINGの料金プランを確認する

関連書籍

今回の学びは、APIでできることとできないことの境界を知り、できない部分をPlaywrightで補完するアプローチ。ただし全部を自動化できたわけではなく、画像アップロードのUI操作は手動に切り替えました。「自動化できる部分だけ自動化する」が現実的な落としどころです。

進展があればまたこのブログで報告します。

コメント

タイトルとURLをコピーしました