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

'''
Python
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

# 環境変数または事前に入力された情報からアクセストークンとアカウント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

# 'comments'フィールドが存在しない要素にデフォルト値を割り当てます。
for result in results:
if "comments" not in result:
result["comments"] = []

df = pd.json_normalize(
results,
record_path='comments',
meta=[
'id', 'media_type', 'media_url', 'thumbnail_url',
'permalink', 'caption', 'timestamp', 'like_count',
'comments_count', 'insights'
],
errors='ignore' # エラーを無視し、サムネイル画像が存在しない場合には NaN を設定
)

return df

df = get_instagram_data()

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

if choice == "Content":
selected_id = st.sidebar.selectbox("Select Post", df["id"].unique())
selected_data = df[df["id"] == selected_id].iloc[0]
image_url = selected_data["media_url"] if selected_data["media_type"] == "IMAGE" else selected_data["thumbnail_url"]
image_response = requests.get(image_url)
image = Image.open(BytesIO(image_response.content))

likes_follows_percentage = (float(selected_data["like_count"]) / float(selected_data["insights"][0]['values'][0]['value'])) * 100

st.image(image, use_column_width=True)
st.write(f"Likes: {selected_data['like_count']} ({likes_follows_percentage:.2f}%)")
st.write(f"Comments: {selected_data['comments_count']}")

comments_df = df[df["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":
# インプレッションデータを使ったグラフの作成
...
elif selected_category == "Engagement":
# エンゲージメントデータを使ったグラフの作成
...

'''




そして上記のコードを実行すると、下記のエラーが表示されて動作しません。エラーへの対処をコードに組み込んで、行頭のインデントを付与した状態で表示して？？



'''
TypeError Traceback (most recent call last)
Cell In [9], line 51
38 df = pd.json_normalize(
39 results,
40 record_path='comments',
(...)
46 errors='ignore' # エラーを無視し、サムネイル画像が存在しない場合には NaN を設定
47 )
49 return df
---> 51 df = get_instagram_data()
53 menu = ["Content", "Analytics"]
54 choice = st.sidebar.radio("Menu", menu)

Cell In [9], line 38, in get_instagram_data()
35 if "comments" not in result:
36 result["comments"] = []
---> 38 df = pd.json_normalize(
39 results,
40 record_path='comments',
41 meta=[
42 'id', 'media_type', 'media_url', 'thumbnail_url',
43 'permalink', 'caption', 'timestamp', 'like_count',
44 'comments_count', 'insights'
45 ],
46 errors='ignore' # エラーを無視し、サムネイル画像が存在しない場合には NaN を設定
47 )
49 return df

File ~/.config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/io/json/_normalize.py:518, in _json_normalize(data, record_path, meta, meta_prefix, record_prefix, errors, sep, max_level)
515 meta_vals[key].append(meta_val)
516 records.extend(recs)
--> 518 _recursive_extract(data, record_path, {}, level=0)
520 result = DataFrame(records)
522 if record_prefix is not None:

File ~/.config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/io/json/_normalize.py:500, in _json_normalize.<locals>._recursive_extract(data, path, seen_meta, level)
498 else:
499 for obj in data:
--> 500 recs = _pull_records(obj, path[0])
501 recs = [
502 nested_to_record(r, sep=sep, max_level=max_level)
503 if isinstance(r, dict)
504 else r
505 for r in recs
506 ]
508 # For repeating the metadata later

File ~/.config/jupyterlab-desktop/jlab_server/lib/python3.8/site-packages/pandas/io/json/_normalize.py:430, in _json_normalize.<locals>._pull_records(js, spec)
428 result = []
429 else:
--> 430 raise TypeError(
431 f"{js} has non list value {result} for path {spec}. "
432 "Must be list or null."
433 )
434 return result

TypeError: {'id': '18067298371371308', 'media_type': 'CAROUSEL_ALBUM', 'media_url': 'https://scontent-nrt1-2.cdninstagram.com/v/t51.29350-15/339821895_1888798984838613_4119965049625769436_n.jpg?_nc_cat=109&ccb=1-7&_nc_sid=8ae9d6&_nc_ohc=esMyr91RNvsAX_al98n&_nc_ht=scontent-nrt1-2.cdninstagram.com&edm=AM6HXa8EAAAA&oh=00_AfAHHHy1924N4_gvsc8Gl1hYkcTglZJzKF1FAeOAZ_DY-w&oe=64379EFE', 'permalink': 'https://www.instagram.com/p/CqnLOtGvT3-/', 'caption': '. 10pics →\n. \n[Description] 長い商店街の町 / Long shopping street town\n\n[Equip] SONY #ILCE7RM2 (α7R 2nd) + #Leica #Summilux50 mmF1.4 (2nd) + k&f #blackmist 1/8\n\n[Develop] RAW(Slog-2[S-Gamut]) + #PixelmatorPhoto(for iOS) \n\n[Tags] #ig_japan #instagramjapan #キリトリセカイ #ファインダー越しの私の世界 #streetsnap #ig_street #streetgrammers #streetphotography #leicaphotography #lomography #デジタルでフィルムを再現したい #LikeAFilm #filmstyle #FilmSimulation #cinematic #VintageLens #OldLens #oldlens_japan #写真好きな人と繋がりたい #ファインダーは私のキャンパス #ダレカニミセタイケシキ #オールドレンズに恋をした #ストリートスナップ', 'timestamp': '2023-04-04T11:12:32+0000', 'like_count': 402, 'comments_count': 17, 'comments': {'data': [{'username': 'wanderer_j3', 'text': '🔥🔥', 'id': '17992260277873411'}, {'username': 'charliejmilner', 'text': 'Love this', 'id': '17996528422768831'}, {'username': 'tanerder', 'text': '✌', 'id': '18270351787193268'}, {'username': 'hfphotography', 'text': 'Lovely images in your Instagram World my friend , beautifully captured moments. I also love portrait and street photography.\n•\nI use a mixture of Sony and Leica Cameras , at the moment the Sony A1 , Leica SL2-S & Leica Q2 Monochrom 📷 but more important than the gear is the person unsing the camera and I love your style 👍', 'id': '17980521167033995'}, {'username': 'simonhiltonphoto', 'text': '👏', 'id': '18269368639193036'}, {'username': 'mykhailobogdanov', 'text': 'Great photo', 'id': '17975083253162785'}, {'username': 'mokyu_3', 'text': 'ステキですね🤩👍', 'id': '18264490783131172'}]}, 'insights': {'data': [{'name': 'impressions', 'period': 'lifetime', 'values': [{'value': 823}], 'title': 'インプレッション', 'description': 'メディアオブジェクトの合計ビュー数です', 'id': '18067298371371308/insights/impressions/lifetime'}, {'name': 'engagement', 'period': 'lifetime', 'values': [{'value': 420}], 'title': 'エンゲージメント', 'description': 'メディアオブジェクトの「いいね！」とコメントの合計数です', 'id': '18067298371371308/insights/engagement/lifetime'}]}} has non list value {'data': [{'username': 'wanderer_j3', 'text': '🔥🔥', 'id': '17992260277873411'}, {'username': 'charliejmilner', 'text': 'Love this', 'id': '17996528422768831'}, {'username': 'tanerder', 'text': '✌', 'id': '18270351787193268'}, {'username': 'hfphotography', 'text': 'Lovely images in your Instagram World my friend , beautifully captured moments. I also love portrait and street photography.\n•\nI use a mixture of Sony and Leica Cameras , at the moment the Sony A1 , Leica SL2-S & Leica Q2 Monochrom 📷 but more important than the gear is the person unsing the camera and I love your style 👍', 'id': '17980521167033995'}, {'username': 'simonhiltonphoto', 'text': '👏', 'id': '18269368639193036'}, {'username': 'mykhailobogdanov', 'text': 'Great photo', 'id': '17975083253162785'}, {'username': 'mokyu_3', 'text': 'ステキですね🤩👍', 'id': '18264490783131172'}]} for path comments. Must be list or null.
'''