AniList API でアニメ情報を取得|Python 手順とレート制限の注意点

  • URLをコピーしました!
目次

はじめに

AniList は、アニメやマンガの作品情報、放送スケジュール、声優、ユーザー評価などを網羅した大規模なデータベースサイトです。同サイトが提供する AniList API はクエリ言語に GraphQL を採用しており、プログラムから必要なデータだけを効率的に取得できます。本記事では、Python を使って放送予定のアニメや人気キャラクターの情報を取得し、グラフ化して分析する手順を解説します。

この記事でわかること
  • AniList API の仕組み: REST API とは異なる GraphQL の基本的な使い方
  • 任意の年の取得: Python で放送予定アニメの情報を年指定で取得する方法
  • キャラ情報の取得: 作品だけでなく、人気キャラクターや声優のデータを抽出する方法
  • データの可視化: 取得したデータを Matplotlib でグラフ化する手順
  • 実運用上の注意: レート制限・エラー応答・代替 API との使い分け

AniList API は、公開データであれば認証なしで利用でき、GraphQL によって必要なフィールドだけを 1 回のリクエストで取得できます。本記事のコード例で、放送予定アニメやキャラクター人気の取得から Matplotlib での可視化までを再現できます。ただし API は現在レート制限が縮退している点など、実運用で押さえておきたい注意点も後半で整理します。

AniList API の特徴と GraphQL の基礎

AniList API は、Meta(旧 Facebook)が開発したクエリ言語 GraphQL を採用しています。REST API では 1 つのエンドポイントから固定的なデータ一式が返るのに対し、GraphQL では必要なフィールドだけを指定して取得できる点が大きな違いです。

認証不要で始められる

公開データ(作品情報やキャラクター情報など)の取得であれば、アカウント登録や API キーの発行なしでリクエストを送れます。 すべてのリクエストは単一のエンドポイントhttps://graphql.anilist.coに対する POST で完結します。ログイン中のユーザーの非公開リストの読み取りや進捗更新など、ユーザー個人に紐づく操作を行う場合のみ OAuth2 が必要になります。

レート制限と現在の縮退状態

無償で利用できますが、サービス維持のためのレート制限が設けられています。公称値は 90 リクエスト/分ですが、本記事の執筆時点では API が縮退状態にあり、30 リクエスト/分に制限されています。 これは API 復旧までの一時的な措置とされています。分あたりの上限とは別に、短時間に過剰なリクエストを送ることを防ぐバーストリミッターも設けられている点に注意が必要です。

参考: AniList API Docs(Rate Limiting)
“currently in a degraded state and is limited to 30 requests per minute”
(現在は縮退状態にあり、30 リクエスト/分に制限されています。)
https://docs.anilist.co/guide/rate-limiting

レスポンスにはX-RateLimit-LimitX-RateLimit-Remainingヘッダーが含まれ、残りリクエスト数を確認できます。超過時の挙動と対処は本記事後半の制約セクションで詳しく扱います。

充実したドキュメントと GraphiQL

公式ドキュメントが整理されており、ブラウザ上でクエリを試せる GraphiQL(インタラクティブエディタ)も提供されています。クエリの構造を確かめながら開発を進められます。

GraphQL クエリの基本構造

GraphQL では、欲しいデータをリクエスト時に定義します。たとえばアニメのローマ字タイトルと人気度だけが必要な場合、次のように記述します。

query {
  Media (id: 1) {
    title {
      romaji  # ローマ字タイトル
    }
    popularity # 人気度
  }
}

REST API では作品 ID を指定すると、放送日・あらすじ・スタッフ情報まで一式が返り、不要なデータまで受け取ってしまいます(オーバーフェッチ)。GraphQL では必要なフィールドだけを指定するため、通信量を抑えられ、レスポンスも軽くなります。

実践: 任意の年の放送予定アニメを取得する

AniList API は、まだ放送されていない制作決定済みの作品もデータベースに登録されています。これを利用すると、翌年以降の注目作を正確なデータに基づいて早めに把握できます。

ここでは、指定した年に放送予定のアニメのうち、すでに期待度(人気度)が高い作品トップ 5 を取得します。API のエンドポイントにリクエストを送り、結果を JSON 形式で受け取るシンプルな構成です。

import requests

# AniList API のエンドポイント
url = "https://graphql.anilist.co"

# 取得したい年を動的に指定(ここを変えるだけで他の年も取得可能)
target_year = 2027

# GraphQL クエリの定義
# $seasonYear(年)を引数として受け取り、人気順(POPULARITY_DESC)で並べ替える
query = '''
query ($seasonYear: Int, $page: Int, $perPage: Int, $sort: [MediaSort]) {
  Page (page: $page, perPage: $perPage) {
    media (seasonYear: $seasonYear, sort: $sort, type: ANIME) {
      id
      title {
        romaji
        english
        native
      }
      popularity
      status
    }
  }
}
'''

# クエリに渡す変数
variables = {
    "seasonYear": target_year,
    "page": 1,
    "perPage": 5,                 # 上位 5 件を取得
    "sort": ["POPULARITY_DESC"]   # 人気順(期待度順)
}

# API リクエストを実行
response = requests.post(url, json={"query": query, "variables": variables})
data = response.json()

# 結果の表示
if "errors" in data:
    print(data["errors"])
else:
    print(f"--- {target_year} 年 放送予定・期待度ランキング ---")
    for i, anime in enumerate(data["data"]["Page"]["media"], 1):
        title = anime["title"]["native"] or anime["title"]["romaji"]
        print(f"{i} 位: {title}(期待度: {anime['popularity']})")

コード解説: seasonYear を動的に指定する

このスクリプトのポイントは、variables辞書の中で定義している"seasonYear": target_yearです。GraphQL クエリ側では$seasonYear: Intと記述して整数の年を受け取る枠を用意し、リクエスト時に Python の変数target_yearを渡しています。

この仕組みにより、クエリ本体を書き換えることなく、target_yearの値を変えるだけで対象年を切り替えられます。2026にすれば今年の作品、2010にすれば過去の作品を取得できます。この変数の分離が、GraphQL をプログラムから扱う際の利点です。

なお、sort[MediaSort]型のため、本記事では["POPULARITY_DESC"]とリスト形式で渡しています。単一の文字列を渡しても動作しますが、リスト形式に統一しておくと型の意図が明確になります。

参考: AniList API Docs
“ask for exactly the fields you need in one request”
(必要なフィールドだけを 1 回のリクエストで要求できます。)
https://docs.anilist.co/

実践: アニメキャラクターの人気データを抽出する

AniList API では、作品情報からさらに掘り下げて、その作品に登場するキャラクターや、演じている声優のデータまで一括で取得できます。これを使うと、作品内で最もお気に入り登録が多いキャラクターや、主役と脇役の人気差などを数値で分析できます。

キャラクター情報を取得するクエリの構造

キャラクター情報を取得するには、Media(作品)クエリの中にcharactersフィールドを入れ子にします。ここで重要になるのがedgesです。GraphQL では、作品とキャラクターの間にある関係性(Edge)を通じてデータにアクセスします。これにより、キャラクター名だけでなく、その作品における役割(主役・脇役)や担当声優といった、文脈に紐づく情報をまとめて取得できます。

声優情報を含めて取得する

例として、人気作『SPY×FAMILY』のキャラクター人気ランキングを、担当声優名とあわせて取得します。sort: FAVOURITES_DESCを指定し、ユーザーからのお気に入り登録数が多い順に抽出します。

import requests
import matplotlib.pyplot as plt
import matplotlib_fontja  # 日本語表示に対応(後述)

# AniList API のエンドポイント
url = "https://graphql.anilist.co"

# 検索したいアニメのタイトル(英語・ローマ字表記が確実)
search_title = "SPY×FAMILY"

# 作品(Media)を検索し、その中のキャラクター(characters)を人気順で取得する
query = '''
query ($search: String) {
  Media (search: $search, type: ANIME) {
    title {
      romaji
      native
    }
    # キャラクター情報を人気順(FAVOURITES_DESC)で取得
    characters (sort: FAVOURITES_DESC, perPage: 10) {
      edges {
        role  # 役割(MAIN / SUPPORTING)
        node {
          name {
            full
            native
          }
          favourites # キャラクターのお気に入り登録数
        }
        # そのキャラクターを担当する声優(日本語)を取得
        voiceActors (language: JAPANESE, sort: FAVOURITES_DESC) {
          name {
            full
            native
          }
        }
      }
    }
  }
}
'''

variables = {"search": search_title}

response = requests.post(url, json={"query": query, "variables": variables})
data = response.json()

if "errors" in data:
    print(data["errors"])
else:
    media = data["data"]["Media"]
    title = media["title"]["native"] or media["title"]["romaji"]
    print(f"--- 『{title}』 キャラクター人気ランキング ---")

    char_names = []
    char_favs = []

    for i, edge in enumerate(media["characters"]["edges"], 1):
        char_node = edge["node"]
        role = edge["role"]
        favs = char_node["favourites"]
        name = char_node["name"]["full"]

        # 声優情報(リストで返るため先頭の 1 人を取得)
        va_name = "情報なし"
        if edge["voiceActors"]:
            va_name = edge["voiceActors"][0]["name"]["native"]

        print(f"{i} 位: {name}(CV: {va_name})- favourites {favs} [{role}]")

        char_names.append(name)
        char_favs.append(favs)

    # 取得したデータで横棒グラフを作成
    plt.figure(figsize=(10, 6))
    plt.barh(char_names, char_favs, color="skyblue")
    plt.xlabel("お気に入り登録数")
    plt.title(f"『{title}』 人気キャラクター上位")
    plt.gca().invert_yaxis()  # 1 位を上に表示
    plt.tight_layout()
    plt.show()

コードのポイント

edgesnodeの使い分けが要点です。edgesの中のroleで主役(MAIN)か脇役(SUPPORTING)かを取得し、nodeの中でキャラクター自身の名前や人気度を取得します。voiceActorslanguage: JAPANESEでフィルタリングし、日本語の声優のみを抽出しています。favouritesはキャラクター単体への人気度を示す数値で、作品自体の人気度(popularity)とは別の指標です。

参考: AniList API Docs
“query Character, Staff, Studio and AiringSchedule types”
(Character・Staff・Studio・AiringSchedule の各タイプを取得できます。)
https://docs.anilist.co/

取得したデータを可視化する(Matplotlib)

数値の羅列では見えにくい人気度の差も、グラフにすると把握しやすくなります。AniList API から返るデータは JSON 形式(辞書とリストの入れ子構造)のため、Matplotlib でグラフ化するには X 軸用と Y 軸用のリストに分解します。リスト内包表記を使うと簡潔に書けます。

# JSON データからリストを作成する(例: タイトルと人気度)
titles = [anime["title"]["native"] for anime in data["data"]["Page"]["media"]]
popularity = [anime["popularity"] for anime in data["data"]["Page"]["media"]]

plt.barh(titles, popularity, color="cornflowerblue")
plt.xlabel("人気度(Popularity)")
plt.title("放送予定アニメ 期待度ランキング")
plt.gca().invert_yaxis()  # ランキング 1 位を上に表示
plt.tight_layout()
plt.show()

日本語フォントの文字化け対策

Matplotlib は標準設定のままでは日本語が文字化け(いわゆる豆腐表示)します。これを防ぐ手段としてかつてはjapanize-matplotlibが広く使われていましたが、Python 3.12 系で動作しない事象が報告されており、更新も停滞しています。現在は後継として、Python 3.7〜3.13 に対応し継続的にメンテナンスされているmatplotlib-fontjaの利用をおすすめします。

STEP
ライブラリのインストール
pip install matplotlib-fontja
STEP
コード内でのインポート

スクリプトの冒頭でインポートするだけで、日本語フォントが自動的に適用されます。

import matplotlib.pyplot as plt
import matplotlib_fontja  # インポートするだけで日本語表示に対応

plt.title("放送予定アニメ 期待度ランキング")  # 日本語が使える

ライブラリを追加せず、Matplotlib の標準機能でフォントを指定する方法もあります。任意の日本語フォントファイルを読み込んでfont.familyに設定すれば、外部ライブラリへの依存を避けられます。

import matplotlib
import matplotlib.pyplot as plt

# 任意の日本語フォントファイルを読み込む
matplotlib.font_manager.fontManager.addfont("/path/to/NotoSansJP-Regular.ttf")
matplotlib.rc("font", family="Noto Sans JP")

plt.title("放送予定アニメ 期待度ランキング")

参考: matplotlib-fontja(PyPI)
“Python :: 3.7 … Python :: 3.13”
(Python 3.7 から 3.13 までに対応。)
https://pypi.org/project/matplotlib-fontja/

実運用で押さえておきたい制約とエラー対処

公開データを認証なしで扱える手軽さがある一方、AniList API には実運用で意識しておきたい制約があります。導入前に把握しておくと、安定したアプリケーション設計につながります。

レート制限の超過(429)への対処

レート制限を超過すると、API は 429(Too Many Requests)を返し、1 分間のタイムアウトが課されます。このときレスポンスにはRetry-After(待機すべき秒数)、X-RateLimit-Reset(再試行可能になる Unix タイムスタンプ)、X-RateLimit-Remainingなどのヘッダーが含まれます。これらを参照してリトライを制御すると、タイムアウトの連鎖を避けられます。

import time
import requests

url = "https://graphql.anilist.co"

def post_with_retry(query, variables, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(url, json={"query": query, "variables": variables})

        if response.status_code == 429:
            # Retry-After(秒)を参照し、なければ 60 秒待機
            retry_after = int(response.headers.get("Retry-After", 60))
            print(f"レート制限に達しました。{retry_after} 秒待機します。")
            time.sleep(retry_after)
            continue

        response.raise_for_status()
        return response.json()

    raise RuntimeError("リトライ上限に達しました。")

公称値は 90 リクエスト/分ですが、執筆時点では縮退状態で 30 リクエスト/分に制限されている点に留意してください。バーストリミッターもあるため、短時間に集中してリクエストを送らず、一定の間隔を空ける設計を推奨します。

API が利用できない場合(403)

API 側の都合で一時的に利用できない状態になると、403 が返り、安定性の問題で一時的に無効化された旨の GraphQL エラーメッセージが添えられます。この場合は時間を置いて再試行するほかなく、アプリケーション側ではエラーメッセージをそのまま表示せず、ユーザー向けに「現在 API が利用できない」旨を案内する設計が望ましいといえます。

参考: AniList API Docs(Considerations)
“temporarily disabled due to severe stability issues”
(深刻な安定性の問題により一時的に無効化されています。)
https://docs.anilist.co/guide/considerations

IP ブロックと成人向けコンテンツの扱い

単一の IP から過剰なリクエストが続くと、手動で IP ブロックされる場合があります。サーバー上で動かす定期バッチなどでは、リクエスト間隔とキャッシュの設計を特に意識するとよいでしょう。

また、AniList のエントリには成人向け(18+)のコンテンツが含まれる場合があります。App Store・Google Play・Microsoft Store などへアプリを配信する場合は、各ストアの規約に注意が必要です。クエリ側ではisAdultフィールドでフィルタリングできますが、公式も判定の完全性は保証していない点を踏まえ、配信先に応じた追加のフィルタリングを検討してください。

代替 API との比較: AniList と Jikan の使い分け

アニメ・マンガのデータを扱える無償 API は AniList だけではありません。代表的な代替として、MyAnimeList(MAL)の非公式 API である Jikan があります。両者は設計思想が異なるため、用途に応じた使い分けが有効です。

AniList と Jikan の違い

AniList は GraphQL を採用した公式 API で、必要なフィールドだけを 1 回のリクエストで取得できます。作品・キャラクター・声優・スタジオといった関連エンティティを横断して取得しやすい点が特長です。

一方の Jikan は、MyAnimeList を対象とした非公式の REST API です。API キーは不要で、エンドポイントはhttps://api.jikan.moe/v4/anime/{id}/seasons/{year}/{season}のようにパスで対象を指定します。MAL のデータをキャッシュして配信するため応答は軽快ですが、新規追加されたタイトルは反映までに時間差が生じる場合があります。レート制限はおおむね毎秒数リクエスト・分あたり 60 程度です。

比較軸AniList APIJikan(MyAnimeList)
方式GraphQLREST
提供元公式非公式(MAL をキャッシュ)
認証公開データは不要(個人操作は OAuth2)不要
データ取得必要なフィールドのみを 1 リクエストで取得エンドポイント単位で固定的に取得
レート制限公称 90/分(現在は縮退で 30/分)おおむね 60/分・毎秒数リクエスト
データの鮮度データベースに直接アクセスキャッシュ由来で反映に時間差が生じうる

参考: Jikan REST API(FreeAPIHub)
“Jikan is simple for straightforward lookups, while AniList’s GraphQL gives finer control”
(Jikan は単純な検索に向き、AniList の GraphQL はより細かな制御が可能です。)
https://freeapihub.com/apis/jikan-rest

どちらを選ぶべきか

MyAnimeList のスコアやランキング、ユーザーリストを基準に考えるアプリケーションでは Jikan が適しています。MAL の評価軸をそのまま扱えるためです。一方、本記事で扱ったような、作品からキャラクター、キャラクターから声優へと関連データを横断して柔軟に取得したい場合は、AniList の GraphQL が適しています。取得するフィールドを細かく制御できるため、通信量を抑えつつ目的のデータだけを集められます。

AniList が縮退状態で安定性に懸念がある期間は、用途によっては Jikan を併用・フォールバックとして検討する選択肢もあります。

まとめ

本記事では、AniList API と Python を使って、放送予定のアニメ情報やキャラクター人気を取得し、可視化する手順を解説しました。あわせて、実運用で意識したいレート制限やエラー対処、代替 API との使い分けも整理しました。

  • AniList API は認証不要で利用でき、GraphQL で必要なデータだけを取得できる
  • seasonYearを指定して、放送予定アニメを年単位で先取りして取得できる
  • 作品だけでなくキャラクターや声優のデータも紐づけて抽出できる
  • 取得した JSON はリストに分解し、Matplotlib でグラフ化できる
  • 日本語表示には、現在も保守されるmatplotlib-fontjaの利用が無難
  • レート制限は公称 90/分・現在は縮退で 30/分。429 と 403 への対処が必要
  • MAL 基準なら Jikan、柔軟な横断取得なら AniList という使い分け

以上、最後までお読みいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

関西を拠点に活動する、現役インフラエンジニア。経験20年超。

大手通信キャリアにて、中〜大規模インフラ(ネットワーク・サーバ・クラウド・セキュリティ)の設計・構築およびプロジェクトマネジメントに従事。現場で直面した技術課題への対処や、最新の脆弱性情報への実務対応を、一次情報として発信しています。

保有資格
CCIE Lifetime Emeritus(取得から20年以上)/ VCAP-DCA / Azure Solutions Architect Expert

▶ 運営者プロフィール(詳細)

目次