ツールとして、InstagramのプロアカウントとFacebook APIやInstagram グラフAPIとPython3を用いる事ができる状況において、①自分がInstagramで投稿したコンテンツを任意でアップロードせずとも、分析対象のコンテンツ画像をInstagramから自動でダウンロードして表示するようにしたうえで、当該コンテンツに対する"いいね"数やフォロー数に加えてそれぞれインプレッションからの割合のパーセント表示と、コメントしたメンバーのIDとアイコンを表示する機能を1ペインで表示し、②各コンテンツのインプレッションやエンゲージメントなど取得できうる限りのアナリティクス情報のデータを取得して横断的に分析できるように、StreamlitとStreamlitShareとブラウザを利用してインタラクティブなグラフやチャート等で2ペイン目で表示できるようにし、③表示するグラフデータの要素を変更する場合にはコードを改変せずともブラウザのUI上でクリックして要素をインタラクティブに選択変更できるようにし、④アプリケーションが開く際に毎回IDやAPI利用に関する情報入力が不要なように事前に必要な情報はコードに埋め込んであるコードを作成しようとしています。

'''

import streamlit as st
import pandas as pd
import requests
import json
import plotly.express as px
from PIL import Image
from io import BytesIO
from collections import defaultdict

# 環境変数または事前に入力された情報からアクセストークンとアカウントIDを設定
access_token =""
account_id =""

def get_instagram_data():

    base_url = f'https://graph.facebook.com/v11.0/{account_id}/media'
    params = {
        'fields': 'id,media_type,media_url,thumbnail_url,permalink,caption,timestamp,like_count,comments_count,comments{username,profile_picture_url,text},insights.metric(impressions,engagement)',
        'access_token': access_token
    }

    results = []

    while base_url:
        response = requests.get(base_url, params=params)
        data = json.loads(response.text)

        results.extend(data['data'])

        if 'paging' in data and 'next' in data['paging']:
            base_url = data['paging']['next']
        else:
            base_url = None

    for result in results:
        if not result.get('comments'):
            result['comments'] = {'data': []}
        if not "insights" in result:
            result["insights"] = [{"values": []}]

    grouped_results = defaultdict(list)

    for result in results:
        grouped_results[result['timestamp'].split("T")[0]].append(result)

    output = []
    for timestamp in grouped_results.keys():
        for idx, data in enumerate(grouped_results[timestamp], 1):
            data["meta_date_id"] = f'{timestamp.replace("-", "")}{idx}'
            output.append(data)

    df = pd.json_normalize(
        output,
        record_path=['comments', 'data'],
        meta=[
            'id', 'media_type', 'media_url', 'thumbnail_url',
            'permalink', 'caption', 'timestamp', 'like_count',
            'comments_count', 'insights', 'meta_date_id'
        ],
        meta_prefix='meta',
        errors='ignore'
    )

    df['timestamp'] = pd.to_datetime(df['timestamp']).dt.strftime('%Y%m%d')

    return df

df = get_instagram_data()

menu = ['Content', 'Analytics']
choice = st.sidebar.radio('Select Menu', menu)

if choice == 'Content':
    selected_id = st.sidebar.selectbox('Select Post', df["meta_date_id"].unique())
    selected_data = df[df["meta_date_id"] == selected_id].iloc[0]
    image_url = selected_data['meta_media_url'] if selected_data['meta_media_type'] == 'IMAGE' else selected_data['meta_thumbnail_url']

    if pd.notna(image_url):
        image_response = requests.get(image_url)
        image = Image.open(BytesIO(image_response.content))
        st.image(image, use_column_width=True)
    else:
        st.write('Image not found')

    meta_insights = selected_data.get('meta_insights')
    try:
        if meta_insights and len(meta_insights) > 0 and len(meta_insights[0]["values"]) > 0:
            impressions_value = meta_insights[0]["values"][0].get("value", 0)
            like_percentage = (selected_data['meta_like_count'] / impressions_value) * 100
        else:
            like_percentage = 0
    except KeyError:
        like_percentage = 0

    st.write(f'Likes: {selected_data["meta_like_count"]} ({like_percentage:.2f}%)')
    st.write(f'Comments: {selected_data["meta_comments_count"]}')

    comments_df = df[df["meta_date_id"] == selected_id]
    st.write(comments_df[['username', 'text']])

elif choice == 'Analytics':
    categories = ['Impressions', 'Engagement']
    selected_category = st.selectbox('Select metric', categories)

    if selected_category == 'Impressions':
        pass
    elif selected_category == 'Engagement':
        pass

'''

上記コードを実行すると下記のエラーが発生します。行頭にPython用のインデントを付与した修正コードを表示してください。

'''

KeyError                                  Traceback (most recent call last)
File ~/.var/app/org.jupyter.JupyterLab/config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/core/indexes/base.py:3802, in Index.get_loc(self, key, method, tolerance)
   3801 try:
-> 3802     return self._engine.get_loc(casted_key)
   3803 except KeyError as err:

File ~/.var/app/org.jupyter.JupyterLab/config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/_libs/index.pyx:138, in pandas._libs.index.IndexEngine.get_loc()

File ~/.var/app/org.jupyter.JupyterLab/config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/_libs/index.pyx:165, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:5745, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:5753, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'timestamp'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[47], line 68
     64     df['timestamp'] = pd.to_datetime(df['timestamp']).dt.strftime('%Y%m%d')
     66     return df
---> 68 df = get_instagram_data()
     70 menu = ['Content', 'Analytics']
     71 choice = st.sidebar.radio('Select Menu', menu)

Cell In[47], line 64, in get_instagram_data()
     50         output.append(data)
     52 df = pd.json_normalize(
     53     output,
     54     record_path=['comments', 'data'],
   (...)
     61     errors='ignore'
     62 )
---> 64 df['timestamp'] = pd.to_datetime(df['timestamp']).dt.strftime('%Y%m%d')
     66 return df

File ~/.var/app/org.jupyter.JupyterLab/config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/core/frame.py:3807, in DataFrame.__getitem__(self, key)
   3805 if self.columns.nlevels > 1:
   3806     return self._getitem_multilevel(key)
-> 3807 indexer = self.columns.get_loc(key)
   3808 if is_integer(indexer):
   3809     indexer = [indexer]

File ~/.var/app/org.jupyter.JupyterLab/config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/core/indexes/base.py:3804, in Index.get_loc(self, key, method, tolerance)
   3802     return self._engine.get_loc(casted_key)
   3803 except KeyError as err:
-> 3804     raise KeyError(key) from err
   3805 except TypeError:
   3806     # If we have a listlike key, _check_indexing_error will raise
   3807     #  InvalidIndexError. Otherwise we fall through and re-raise
   3808     #  the TypeError.
   3809     self._check_indexing_error(key)

KeyError: 'timestamp'

'''