0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

競合のInstagramアカウントを監視して構造化データを取得したいですか?

0
Posted at

はじめに:Instagramデータ収集はブランドのソーシャル競争における核心的なニーズ

グローバルなソーシャルマーケティングの勢力図において、Instagramは20億人を超える月間アクティブユーザー、強力な視覚的伝播属性、そして成熟したECエコシステムを備えています。これにより、ブランドが海外市場を展開し、ユーザーの心理を掴み、競合の動向を監視するための核心的な陣地となっています 。新鋭ブランドの台頭、成熟ブランドの維持、あるいはクロスボーダーECの精細な運用において、Instagramプラットフォームのデータ深掘りは不可欠です 。

競合のプロフィールメタデータの正確な取得、投稿やエンゲージメントデータのリアルタイム追跡、コンテンツ戦略とフォロワーの嗜好分析こそが、ブランドがデータ駆動型の意思決定を行い、マーケティング手法を最適化し、市場競争力を高めるための鍵となります 。

しかし、Instagramプラットフォームのアンチスクレイピングメカニズムは日に日に厳格化しており、公式APIの権限縮小やデータ取得の制限、独自スクレイパーによるアカウント凍結のリスクなど、大規模かつ安定的なデータ収集はブランドが直面する核心的な難題となっています 。

ソーシャルリスニングと競合分析に焦点を当てるブランドにとって、データ収集は「プロフィールのメタデータの完全性」「投稿情報の包括性」「エンゲージメント指標の正確性」「ページネーションによる一括収集のスムーズさ」「データの鮮度の高さ」という5つの核心的なニーズを満たす必要があります 。単一の収集方法ではビジネスシーンに適応しにくいため、コンプライアンスを遵守した効率的かつ安定的なAPIツールの選択が、この状況を打破する鍵となります 。

ブランド向けInstagramデータ収集の核心的ニーズの分解

プロフィールメタデータ: アカウントID、プロフィール文、アイコン、フォロワー数/フォロー数、投稿数、認証ステータス、アカウントタイプ、外部リンク、連絡先を収集し、競合の価値と運用フェーズを判断します 。
投稿全量データ: 画像、Reels、カルーセルなど全ての投稿タイプをカバー。投稿ID、投稿時間、キャプション、ハッシュタグ、素材リンク、位置情報などのフィールドを含み、コンテンツ戦略を再現します 。
エンゲージメント指標データ: いいね数、コメント数、シェア数、保存数、再生回数、コメントのホットワードを取得し、コンテンツの効果を数値化してバズった投稿を特定します 。
ページネーションによる一括収集: 複数アカウントや過去のコンテンツの大規模な遡及をサポートし、データの漏れがない完全な収集を保証します 。

主要なInstagramデータ収集APIの全方位比較

現在、市場でInstagramのデータ収集を実現できるAPIは主に3つのカテゴリーに分けられます:Meta公式Graph API、サードパーティ製プライベートAPI、そしてBright Data Web Scraper APIです 。これら3種類は、権限、安定性、コンプライアンス、実用性において顕著な差があります 。ブランドの核心的なニーズに基づき、重要な次元から正確に比較します :

比較次元 Meta公式 Graph API サードパーティ製プライベートAPI(Instagrapi / Data365等) Bright Data Web Scraper API
データ権限 権限が厳格。認証済みのビジネス/クリエイターアカウントのみ、かつアカウント連携が必要。自社アカウントのデータのみ取得可能で、競合の公開データは収集不可 。 公式連携不要で公開データを収集可能。ただしインターフェース権限が不安定で、プラットフォームのポリシー変更に影響されやすく、一部フィールドが取得不可 。 権限のハードルなし。Instagramの全公開アカウントデータを収集可能。全アカウントタイプをカバーし、連携制限もなく、競合分析に最適 。
データの完全性 フィールドの欠落が激しく、過去の投稿や深いエンゲージメントデータの取得に制限あり。ページネーション機能がなく、大規模なニーズに対応不可 。 基本フィールドは比較的完全だが、マイナーな指標や深い階層のデータが欠落。大量収集時にフォーマットの混乱やデータの漏れが発生しやすい 。 プロフィール、投稿、エンゲージメント指標の全フィールドをカバー。構造化出力され、深いページネーションや複数アカウントの一括収集もスムーズ 。
凍結リスク 公式インターフェースのため凍結はないが、呼び出し回数やデータ量に厳格な制限があり、超過すると即停止 。 アンチスクレイピング回避能力が極めて低く、高頻度の収集でIPやアカウントが凍結されやすい。安定性は60%未満 。 世界規模の住宅用IPプール、自動ローテーション、JSレンダリングによりアンチスクレイピングを完全に回避。99.99%の安定稼働で凍結リスクなし 。
データの鮮度 データの更新が数時間から数日遅れる。リアルタイム/定時収集をサポートせず、ソーシャルリスニングに適さない 。 基本的なリアルタイム収集は可能だが、高頻度だと制限がかかりやすい。定時実行機能が不完全 。 カスタム定時タスク(毎日/毎時)をサポート。準リアルタイム収集でプラットフォームと同期し、最高の鮮度を維持 。
技術的ハードル Meta開発者ドキュメントの熟知、認証、連携、デバッグが必要。技術的ハードルと運用コストが高い 。 スクリプトの自社開発、凍結対応、データ修正が必要。運用が煩雑で業務中断が起きやすい 。 標準化されたAPIインターフェースで即利用可能。ノーコード/ローコード設定をサポートし、プロキシや回避策の運用が不要 。
コンプライアンス 完全に合法だが、データの使用範囲が制限され、競合分析などの商用利用は不可 。 グレーゾーン。Instagramの利用規約に違反し、法的リスクが存在。データは商用不可 。 公開データを合法的に収集。GDPRやCCPA等の法規制を遵守し、プラットフォームのルールに従っているため商用利用も安心 。
実用・適応性 自社アカウントのデータ管理のみに適応。競合監視には全く使用できない 。 個人の小規模・低頻度テストにのみ適しており、企業レベルの大規模ニーズには対応不可 。 あらゆるシーンに適応。ソーシャルリスニング、競合分析、大規模データ収集、BI連携を完璧にサポート 。

実用的な結論: Meta公式APIは権限に制限があり、サードパーティAPIはリスクが高い。いずれもブランドの必須ニーズを満たせません。Bright Data Web Scraper APIは、コンプライアンス、安定性、全量取得、実用性を兼ね備えた唯一の選択肢であり、企業レベルの全プロセスデータ収集ニーズに適応します 。

👉 Bright Dataの体験クレジットを無料で受け取るhttps://get.brightdata.com/hwj7ya

よくある質問 (FAQ)

Q: Instagramデータ収集APIとプロキシサーバーの違いは何ですか?
プロキシサーバーはIPアドレスのローテーションを提供するだけで、JavaScriptレンダリング、CAPTCHA(画像認証)、アンチスクレイピングの指紋(Fingerprint)を処理する複雑なクローラーロジックを自分で書く必要があります 。一方、Bright Data Web Scraper APIはすぐに使えるソリューションであり、プロキシ、ブラウザ自動化、パーサーを内蔵しています。低レイヤーのコードを保守することなく、構造化されたJSONデータを直接取得できます 。
Q: Instagramの公開データを収集するのは合法ですか?GDPRに違反しませんか?
公開されているデータ(Public Data)の収集は、通常、法的規範に合致しています 。Bright DataはGDPRやCCPAなどのプライバシー法規制を厳格に遵守しており、ユーザーが自発的に公開している情報のみを収集し、プライベートなデータには触れません 。そのコンプライアンス性は、独自に収集するグレーゾーンのツールよりも遥かに高く、企業の商用利用に適しています 。
Q: Bright Data Web Scraper APIがサポートするInstagramデータの種類は?
以下の全量公開データをサポートしています :
プロフィール: フォロワー数、フォロー数、プロフィール文、アイコン、認証ステータス 。
投稿内容: 画像、Reels動画、カルーセル、キャプション、ハッシュタグ 。
エンゲージメント: いいね数、コメント数、再生回数、シェア数 。
メタデータ: 投稿時間、位置情報、_shortcode_など 。
Q: スクレイピング時のIP凍結を避けるには?
Bright Dataは世界最大級の住宅用IPプール(7,200万以上)を保有しており、自動でIPをローテーションし、本物のブラウザ指紋を組み合わせて人間の行動をシミュレートします 。Web Scraper APIレイヤーでCAPTCHAやアンチスクレイピングの課題も自動処理するため、凍結リスクはほぼゼロ(成功率99.99%)です 。
Q: 小規模チームで予算が限られている場合、Bright Dataはお得ですか?
非常にお得です 。Web Scraper APIは成功したリクエストごとに課金されるモデルで、月額固定費のハードルがありません 。自社でクローラーチームを構築する人件費、サーバー維持費、IP購入費用と比較して、Bright DataのROI(投資対効果)はより高いです 。新規ユーザー向けの無料クレジットでビジネス価値を検証することも可能です 。

Bright Dataの利用開始

1.公式サイトで無料登録: Bright Data公式サイトhttps://get.brightdata.com/hwj7ya にアクセスし、登録・ログインしてコントロールパネルに入ります 。
2.APIキーの作成: アカウント設定からAPIキーを作成し、コピーしておきます 。
1.png

3.スクレーパーライブラリ: 左側のナビゲーションメニューから「スクレーパー」を選択すると、スクレーパーライブラリが表示されます 。
2.png

4.サンプルコード: 公式が提供するサンプルコードを直接コピーして使用できます 。
3.png

実践コード:Reelsバズりコンテンツ複製システム(汎用版)

ファッション業界において、Instagram Reelsはトラフィック誘導とコンバージョンの核心陣地です 。例えばZARAのようなトップブランドのReelsは、その流量とコンバージョン力の両面でベンチマークとなります 。本実践では、Bright Data Web Scraper APIを使用して、特定のアカウントの全Reelsデータをターゲット収集し、バズるロジックを分析します 。

第一段階:収集タスクのトリガーと結果の取得
この段階のスクリプトは、データ収集の核心です 。ZARAの全Reels収集タスクをトリガーし、ストリーミングダウンロード、無効データの自動クレンジングを行い、最終的にparquetとcsv形式のクリーンなデータセットを生成します 。

import requests
import json
import time
import pandas as pd
import os
from datetime import datetime
from io import StringIO

# ================= 設定エリア:ここを修正してください =================
API_KEY = "あなたのBright Data API Key"  # 自身のAPIキーに置き換えてください
DATASET_ID = "あなたのDataset ID"  # 自身のデータセットIDに置き換えてください
TARGET_URL = "https://www.instagram.com/zara/"
NUM_POSTS = 20000  # 最大取得件数(必要に応じて調整可能)

# 出力ファイルパス
OUTPUT_DIR = "data"
OUTPUT_FILE_PARQUET = os.path.join(OUTPUT_DIR, "zara_reels_clean.parquet")
OUTPUT_FILE_CSV = os.path.join(OUTPUT_DIR, "zara_reels_clean.csv")

# API トリガーエンドポイント
# 注:このインターフェースはWeb Unlocker技術に基づいてアンチスクレイピングを回避します。
# Web Scraper APIがその上位で構造化出力をカプセル化し、JSONを直接返します。
TRIGGER_URL = f"https://api.brightdata.com/datasets/v3/scrape?dataset_id={DATASET_ID}&notify=false&include_errors=true&type=discover_new&discover_by=url_all_reels"


# ================= コアロジック =================
def trigger_scrape():
    """ZARAリールのスクレイピングタスクをトリガーする"""
    print(f"🚀 スクレイピングタスクを開始します: {TARGET_URL} (上限: {NUM_POSTS} 件)")

    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }

    payload = {
        "input": [{
            "url": TARGET_URL.strip(),
            "num_of_posts": NUM_POSTS,
            "start_date": "",
            "end_date": ""
        }]
    }

    try:
        # プロキシ設定を無効化してリクエストを送信
        response = requests.post(TRIGGER_URL, headers=headers, json=payload, proxies={"http": None, "https": None})
        response.raise_for_status()
        result = response.json()

        if "snapshot_id" in result:
            snapshot_id = result["snapshot_id"]
            print(f"✅ タスクのトリガーに成功しました! Snapshot ID: {snapshot_id}")
            return snapshot_id
        else:
            print(f"❌ トリガー失敗。snapshot_idが見つかりません。レスポンス: {result}")
            return None

    except Exception as e:
        print(f"❌ リクエスト送信中に異常が発生しました: {e}")
        return None


def poll_and_process(snapshot_id):
    """タスクの状態をポーリング(監視)し、ストリーミングでデータをクレンジングする"""
    snapshot_url = f'https://api.brightdata.com/datasets/v3/snapshot/{snapshot_id}'
    headers = {"Authorization": f"Bearer {API_KEY}"}

    print("⏳ タスクの状態を確認中 (2分ごとにチェックします)...")

    while True:
        try:
            response = requests.get(snapshot_url, headers=headers, stream=True)
            response.raise_for_status()
            content_type = response.headers.get('Content-Type', '').lower()

            # タスク未完了の場合、待機を継続
            if "application/jsonl" not in content_type and "text/plain" in content_type:
                print(f"   現在の状態: データ生成を待機中...")
                time.sleep(120)
                continue

            # データ準備完了、処理を開始
            if "application/jsonl" in content_type:
                print("✅ データ準備完了! ストリーミングダウンロードとクレンジングを開始します...")
                return process_stream(response)

        except requests.exceptions.RequestException as e:
            print(f"⚠️ ネットワークエラー: {e}。1分後に再試行します...")
            time.sleep(60)


def process_stream(response):
    """JSONLをストリーミング解析し、エラーデータを除外して標準データセットを構築する"""
    clean_records = []
    error_count = 0
    success_count = 0

    # メモリ溢れを防ぐため、1行ずつ読み込んで処理
    for line in response.iter_lines():
        if line:
            try:
                data = json.loads(line.decode('utf-8'))

                # エラーデータのフィルタリング
                if "error" in data or "error_code" in data:
                    error_count += 1
                    continue

                # 無効なリールデータのフィルタリング(URLまたは再生数・視聴数がないもの)
                if "url" not in data or ("video_play_count" not in data and "views" not in data):
                    continue

                # データフィールドの標準化
                record = {
                    "url": data.get("url"),
                    "shortcode": data.get("shortcode"),
                    "caption": data.get("description", ""),
                    "hashtags": data.get("hashtags", []),
                    "play_count": data.get("video_play_count") or data.get("views") or 0,
                    "like_count": data.get("likes") or 0,
                    "comment_count": data.get("num_comments") or 0,
                    "share_count": 0,
                    "save_count": 0,
                    "duration": float(data.get("length", 0)) if data.get("length") else 0.0,
                    "date_posted": data.get("date_posted"),
                    "timestamp_scraped": data.get("timestamp"),
                    "followers": data.get("followers"),
                }

                clean_records.append(record)
                success_count += 1

            except json.JSONDecodeError:
                continue

    print(f"🧹 クレンジング完了!")
    print(f"   ✅ 解析成功: {success_count} 件")
    print(f"   ❌ エラー/無効により除外: {error_count} 件")

    if not clean_records:
        print("❌ 有効なデータが取得できなかったため、保存を中止します。")
        return None

    # DataFrameに変換し、日付フォーマットを整形
    df = pd.DataFrame(clean_records)
    print("   ⏳ データのクレンジングと整形を実行中...")

    # 投稿時間フィールドの処理
    if 'date_posted' in df.columns:
        df['post_date'] = pd.to_datetime(df['date_posted'], errors='coerce', utc=True)
        df['post_date'] = df['post_date'].dt.tz_localize(None)
        valid_time_count = df['post_date'].notna().sum()
        print(f"   ✅ 時間解析完了: {valid_time_count}/{len(df)} 件のデータが有効です。")

    # クレンジング後のデータを保存
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)
    
    df.to_parquet(OUTPUT_FILE_PARQUET, index=False)
    df.to_csv(OUTPUT_FILE_CSV, index=False, encoding="utf-8-sig")

    print(f"💾 データが保存されました:")
    print(f"   - {OUTPUT_FILE_PARQUET} (分析用推奨)")
    print(f"   - {OUTPUT_FILE_CSV} (プレビュー確認用)")
    return df


# ================= メインプログラム =================
if __name__ == "__main__":
    # 収集タスクをトリガー
    snapshot_id = trigger_scrape()
    if snapshot_id:
        # ポーリング + クレンジング + データ保存
        df = poll_and_process(snapshot_id)
        if df is not None:
            print("\n🎉 データの収集とクレンジングが完了しました。分析スクリプトを実行してレポートを作成できます!")
        else:
            print("\n❌ 有効なデータを取得できませんでした。")
    else:
        print("\n❌ タスクのトリガーに失敗したため、プロセスを開始できません。")

実行結果: 運営チームが直接確認できるCSVファイルと、次の段階の分析システム用のParquetファイルの両方を出力します 。

{
  "url": "https://www.instagram.com/reel/xxxxx/",
  "shortcode": "xxxxx",
  "caption": "New Summer Collection. Available now. #BrandName #Fashion #NewArrival",
  "hashtags": ["BrandName", "Fashion", "NewArrival"],
  "video_play_count": 1140000,
  "likes": 17100,
  "num_comments": 80,
  "length": 39.0,
  "date_posted": "2026-02-14T10:11:51Z",
  "followers": 62200000
}

第二段階:ZARA Reelsバズり投稿のインテリジェント分析
この段階のコードは、動画の長さ、投稿のタイミング、キャプションの内容、エンゲージメントの質、ヒット事例の5つの次元から分析を行います 。自動的に可視化されたHTMLレポートを生成し、バズる法則を直感的に提示します 。

import pandas as pd
import numpy as np
import os
from collections import Counter
import re
import datetime

# ================= 設定エリア =================
DATA_FILE = "data/zara_reels_clean.parquet"
OUTPUT_REPORT_TXT = "reports/zara_strategy_report.txt"
OUTPUT_REPORT_HTML = "reports/zara_strategy_report.html"


class ZaraViralAnalyzer:
    def __init__(self, filepath):
        if not os.path.exists(filepath):
            raise FileNotFoundError(f"❌ データファイルが存在しません: {filepath}")

        print(f"📂 データを読み込み中: {filepath}")
        self.df = pd.read_parquet(filepath)
        print(f"✅ 読み込み成功: {len(self.df)} 件のレコード")

        # 投稿日時カラムの変換確認
        if 'post_date' not in self.df.columns:
            if 'date_posted' in self.df.columns:
                print("⚠️ 未変換の日時列を検出しました。変換を試みます...")
                self.df['post_date'] = pd.to_datetime(self.df['date_posted'], errors='coerce', utc=True).dt.tz_localize(None)
            else:
                print("❌ エラー:日時データが見つかりません。時間分析をスキップします。")
                self.df['post_date'] = pd.NaT

        # Viral Score(バズり指数)の計算
        if 'viral_score' not in self.df.columns:
            self._calculate_viral_score()
        else:
            print("⚡ 計算済みの Viral Score をロードしました")

    def _calculate_viral_score(self):
        """再生数・いいね数・コメント数に基づき独自のバズり指数を算出"""
        df = self.df
        max_play = df['play_count'].max() or 1
        max_like = df['like_count'].max() or 1
        max_comment = df['comment_count'].max() or 1

        # 重み付け: 再生数(40%)、いいね(30%)、コメント(30%)
        df['viral_score'] = (
            ((df['play_count'] / max_play * 40) +
             (df['like_count'] / max_like * 30) +
             (df['comment_count'] / max_comment * 30))
        ) * 1000
        print("⚡ Viral Score (バズり指数) を再計算しました")

    def analyze_duration_strategy(self):
        """動画の長さ(秒数)ごとのパフォーマンスを分析"""
        df = self.df.copy()
        bins = [0, 10, 20, 40, 60, 90, float('inf')]
        labels = ['10秒未満', '10-20秒', '20-40秒', '40-60秒', '60-90秒', '90秒以上']
        df['duration_bucket'] = pd.cut(df['duration'], bins=bins, labels=labels, right=False)

        stats = df.groupby('duration_bucket', observed=True).agg({
            'viral_score': 'mean',
            'play_count': 'mean',
            'url': 'count'
        }).rename(columns={'url': '投稿数'})

        best_bucket = stats['viral_score'].idxmax()
        return {
            'best': best_bucket,
            'score': stats.loc[best_bucket, 'viral_score'],
            'data': stats.sort_values('viral_score', ascending=False),
            'labels': labels
        }

    def analyze_timing_strategy(self):
        """最適な投稿曜日と時間帯を分析"""
        df = self.df.copy()
        df = df.dropna(subset=['post_date'])

        if df.empty:
            return {'error': '時間データがありません'}

        df['hour'] = df['post_date'].dt.hour
        df['weekday'] = df['post_date'].dt.day_name()

        hour_stats = df.groupby('hour')['viral_score'].mean()
        weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
        weekday_stats = df.groupby('weekday')['viral_score'].mean().reindex(weekday_order)

        best_hour = hour_stats.idxmax()
        best_day = weekday_stats.idxmax()

        top_3_hours = hour_stats.nlargest(3)
        top_3_days = weekday_stats.dropna().nlargest(3)

        # 日本のユーザー向けに洞察をローカライズ
        if 5 <= best_hour <= 9:
            insight = "早朝(通勤・通学中)にパフォーマンスが高い傾向です。"
        elif 12 <= best_hour <= 14:
            insight = "ランチタイムの休憩中に視聴が集中しています。"
        elif 18 <= best_hour <= 22:
            insight = "夜のゴールデンタイムです。最もトラフィックが多く、主力投稿に適しています。"
        else:
            insight = "深夜・早朝のニッチな時間帯です。競合が少なくテスト投稿に有効です。"

        return {
            'best_day': best_day,
            'best_hour': int(best_hour),
            'top_days': list(top_3_days.index),
            'top_hours': [int(h) for h in top_3_hours.index],
            'insight': insight,
            'hour_data': hour_stats,
            'day_data': weekday_stats
        }

    def analyze_content_guide(self):
        """文案(キャプション)の長さやハッシュタグを分析"""
        df = self.df.copy()
        df['caption_len'] = df['caption'].apply(lambda x: len(str(x)) if x else 0)
        # 指数で加重平均
        avg_len = (df['caption_len'] * df['viral_score']).sum() / df['viral_score'].sum()

        all_tags = []
        for tags in df['hashtags']:
            if isinstance(tags, list):
                all_tags.extend([t.lower() for t in tags])

        tag_counts = Counter(all_tags)
        top_tags = [tag for tag, count in tag_counts.most_common(5)]

        return {'avg_len': int(avg_len), 'top_tags': top_tags}

    def analyze_engagement_quality(self):
        """エンゲージメント率の質を分析"""
        df = self.df.copy()
        df = df[df['play_count'] > 0]

        df['like_rate'] = df['like_count'] / df['play_count']
        df['comment_rate'] = df['comment_count'] / df['play_count']

        bins = [0, 10, 20, 40, 60, 90, float('inf')]
        labels = ['10秒未満', '10-20秒', '20-40秒', '40-60秒', '60-90秒', '90秒以上']
        df['duration_bucket'] = pd.cut(df['duration'], bins=bins, labels=labels, right=False)

        stats = df.groupby('duration_bucket', observed=True).agg({
            'like_rate': 'mean',
            'comment_rate': 'mean'
        })

        if stats.empty:
            return {'error': 'データがありません'}

        best_like = stats['like_rate'].idxmax()
        best_comment = stats['comment_rate'].idxmax()

        return {
            'best_like_bucket': best_like,
            'best_like_val': stats.loc[best_like, 'like_rate'],
            'best_comment_bucket': best_comment,
            'best_comment_val': stats.loc[best_comment, 'comment_rate'],
            'insight': "明確なアクション(CTA)の追加を推奨します" if stats['like_rate'].max() < 0.05 else "コンテンツが強い共感を得ています"
        }

    def analyze_keywords(self):
        """キャプション内のバズりキーワードを抽出"""
        df = self.df.copy()
        text_data = " ".join(df['caption'].dropna().astype(str))
        stopwords = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'is', 'are',
                     'was', 'were', 'new', 'now', 'watch', 'check', 'link', 'zara'}
        words = re.findall(r'\b[a-zA-Z]{4,}\b', text_data.lower())
        filtered_words = [w for w in words if w not in stopwords]
        word_counts = Counter(filtered_words)
        return [word for word, count in word_counts.most_common(5)]

    def get_top_cases(self, n=3):
        """最もバズった動画TOP3を取得"""
        top_df = self.df.nlargest(n, 'viral_score')
        cases = []
        for i, row in top_df.iterrows():
            cases.append({
                'rank': int(row.name),
                'score': row['viral_score'],
                'url': row['url'],
                'duration': row['duration'],
                'caption': str(row['caption'])[:60] + "..." if len(str(row['caption'])) > 60 else row['caption']
            })
        return cases

    def generate_html_report(self, duration_data, timing_data, content_data, engagement_data, keywords, top_cases):
        """日本語化されたビジュアルHTMLレポートを生成"""
        # (※ここではHTML内のテキストを日本語化したロジックが組み込まれます)
        # 冗長さを避けるため、構造は元のままで中身のラベルを日本語に差し替えています
        
        # ...(以下、HTMLテンプレートの日本語化処理:タイトルや見出しを日本語に変換)...
        return html_content # 変換後のHTML

    def run_full_analysis(self):
        print("\n" + "=" * 60)
        print("🤖 ZARA 爆款(バズり)複製システム - インテリジェント分析")
        print("=" * 60)

        # 分析実行
        duration_data = self.analyze_duration_strategy()
        timing_data = self.analyze_timing_strategy()
        content_data = self.analyze_content_guide()
        engagement_data = self.analyze_engagement_quality()
        keywords = self.analyze_keywords()
        top_cases = self.get_top_cases(3)

        # コンソールに概要を表示
        print(f"\n1. 🎬 黄金時間(尺): {duration_data['best']}")
        print(f"2. ⏰ 最適な投稿タイミング: {timing_data.get('best_day', 'N/A')} {timing_data.get('best_hour', 'N/A')}:00")
        print(f"3. 💎 最高いいね率の尺: {engagement_data.get('best_like_bucket', 'N/A')}")
        print(f"4. 🔑 主要キーワード: {keywords}")

        # HTML生成と保存
        html_content = self.generate_html_report(duration_data, timing_data, content_data, engagement_data, keywords, top_cases)

        if not os.path.exists("reports"):
            os.makedirs("reports")

        with open(OUTPUT_REPORT_HTML, "w", encoding="utf-8") as f:
            f.write(html_content)

        print(f"\n✅ 分析完了!")
        print(f"📄 可視化レポートが生成されました: {os.path.abspath(OUTPUT_REPORT_HTML)}")
        print("💡 ヒント: 生成されたHTMLファイルをブラウザで開くと詳細なグラフを確認できます。")


if __name__ == "__main__":
    try:
        analyzer = ZaraViralAnalyzer(DATA_FILE)
        analyzer.run_full_analysis()
    except Exception as e:
        print(f"❌ 分析中にエラーが発生しました: {e}")
        import traceback
        traceback.print_exc()

未来への展望

今回ご紹介したのは、Bright Dataの膨大なエコシステム能力の氷山の一角に過ぎません :
全プラットフォーム網羅: InstagramからTikTok、YouTube、Amazonなど世界中の主要プラットフォームへ拡張し、全チャネルの競合インテリジェンス網を構築できます 。
リアルタイムの洞察: 「事後分析」から「リアルタイム監視」へとアップグレードし、一瞬のトレンドや競合の動きを捉えます 。
AIとの融合: 収集したデータを大規模言語モデル(LLM)に直接連携させ、クリエイティブな台本の自動生成やバズり確率の予測など、「無人化」されたコンテンツ運用を実現します 。
今日から、データであなたのブランドのソーシャルメディア戦略を武装しましょう。
👉 【無料トライアルはこちらhttps://get.brightdata.com/hwj7ya

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?