ラベル API の投稿を表示しています。 すべての投稿を表示
ラベル API の投稿を表示しています。 すべての投稿を表示

2023年1月16日月曜日

InstagramグラフAPIとPythonで指定ハッシュタグ付きの最新投稿を取得する

InstagramグラフAPIを使うとInstagramの投稿情報などを取得できる。ただ、APIの使用に必要なアクセストークンの取得にやや手間がかかる。Pythonで指定したハッシュタグ付きの最新投稿を取得してみたので、アクセストークンの取得も含めて手順をまとめておく。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
$ python3 -V
Python 3.8.10


APIアクセストークンの取得

InstagramグラフAPIを使うため、FacebookグラフAPIのアクセストークンを取得する。FacebookとInstagramのアカウントはあるとする。Instagramアカウントはプロアカウントに切り替えておく。


FacebookページとInstagramプロアカウントの紐づけ

アクセストークン取得前に、FacebookページとInstagramプロアカウントを紐づけておく。Facebookページがなければ、Facebookのメニューの[ページ]を開いて、[新しいページを作成]から作成できる。

InstagramプロアカウントのFacebookページへの紐づけは、Facebookページの画面の[Instagram投稿を宣伝]>[アカウントをリンク]で行う。


Facebook for Developersでアプリの作成

次に、Facebook for Developersアカウントを作成する。Meta  for Developersにアクセスして、アカウントを作成する。アカウントを作成したら、マイアプリの画面でアプリを作成する。今回は以下の設定で作成。

アプリタイプ:ビジネス
アプリ名:テストアプリ(任意名)
アプリの連絡先メールアドレス:登録メールアドレス(デフォルト)
ビジネスマネージャーアカウントをお持ちですか?:ビジネスマネージャアカウントが設定されていません(デフォルト)

アプリを作成したら、[アプリに製品を追加]からInstagramグラフAPIの[設定]ボタンを押す。これで、メニューの商品欄にInstagramグラフAPIが追加される。ここまでやったら、画面右下の[変更を保存]ボタンを押す。


permissonの追加

Facebook for Developersのトップメニューの[ツール]>[グラフAPIエクスプローラー]を開く。[Metaアプリ]で作成したアプリを選択し、[ユーザーまたはページ]で[ページアクセストークン]からFacebookページを選択してから、permissionを追加する。▼をクリックし、さらにOtherを開くとInstagramのpermissionが表示される。今回は以下5つを追加。

  • instagram_basic
  • instagram_content_publish
  • instagram_manage_comments
  • instagram_manage_insights
  • instagram_manage_messages



permissionを追加し、[Generate Access Token]をクリックすると、Facebookでログインのポップアップ画面が表示される。この画面ではFacebookユーザーとして続行をクリック。

次に表示される画面では、今回は[現在のページのみにオプトイン]を選択。Facebookページを選択してから[続行]ボタンを押す。

続く画面では、今回は[現在のInstagramアカウントのみにオプトイン]を選択。Instagramプロアカウントを選択してから[続行]ボタンを押す。

アプリが要求するアクセス許可の確認画面が表示されるので、[続行]で先に進む。次の画面で[OK]を押すと、アクセストークンが表示される。


無期限アクセストークンの取得

ここまでで取得できたアクセストークンには期限があるので、無期限のアクセストークンを取得する。[ツール]>[グラフAPIエクスプローラー]でグラフAPIエクスプローラを開いてて、[Metaアプリ]で対称のアプリを選択し、アクセストークン左のiアイコンをクリックするとアクセストークン情報が表示される。


画面右下の[アクセストークンツールで開く]をクリックして、アクセストークンツールを開き、[アクセストークンを延長]をクリックすると、期限のないアクセストークンが表示される。


[デバッグ]ボタンを押すと無期限のアクセストークン情報が表示される。有効期限が「受け取らない」になっていればOK。



InstagramビジネスアカウントID の取得

アクセストークンのほかにAPI使用に必要なInstagramビジネスアカウントIDを取得する。Facebook for Developersのトップメニューの[ツール]>[グラフAPIエクスプローラー]を開く。クエリに「me?fields=accounts{instagram_business_account}」を指定して、[送信]ボタンを押して表示されるレスポンスのinstagram_business_accountがInstagramビジネスアカウントID。



指定ハッシュタグ付きの最新投稿を取得

無期限アクセストークンとInstagram ビジネスアカウントIDが取得できたので、指定したハッシュタグが付いた最新の投稿をAPIで取得してみる。

はじめに、APIのエンドポイントアクセスに使うRequestsをインストールしておく。

$ pip3 install requests

指定ハッシュタグ付きの最新投稿を取得する手順としては、IG Hashtag SearchでハッシュタグIDを取得して、そのIDをもとにIGハッシュタグ付きの最近のメディアで最新投稿を検索する。以下コードでは、ハッシュタグ「スイーツ」が付いた最新の投稿を取得する。

from pprint import pprint

import requests

API_VER = 'v15.0'
INSTA_ACCOUNT_ID = 'xxxxxxxxxx' # Instagram ビジネスアカウントID
INSTA_ACCESS_TOKEN = 'xxxxxxxxxxx' # 無期限アクセストークン


# ハッシュタグ「スイーツ」のIDを取得
query = 'スイーツ'
ig_hashtag_search_url = f'https://graph.facebook.com/{API_VER}/ig_hashtag_search?user_id={INSTA_ACCOUNT_ID}&q={query}&access_token={INSTA_ACCESS_TOKEN}'
response = requests.get(ig_hashtag_search_url)
res = response.json()['data']
hashtag_id = res[0]['id']
print('hashtag_id=', hashtag_id)

# ハッシュタグ「スイーツ」がついた最新投稿を取得
recent_media_url = f'https://graph.facebook.com/{hashtag_id}/recent_media?user_id={INSTA_ACCOUNT_ID}&fields=id,media_type,media_url,comments_count,like_count,caption,permalink,timestamp&access_token={INSTA_ACCESS_TOKEN}'
response = requests.get(recent_media_url)
res = response.json()['data']
pprint(res)

上記コードを実行すると、以下のような結果が得られる。

hashtag_id= 17843787853011507
[{'caption': 'xxxxx\n'
             '#xxxxx\n'
             '#xxxxx',
  'comments_count': 0,
  'id': '11111111111111',
  'like_count': 0,
  'media_type': 'IMAGE',
  'media_url': ''https://xxxxx',
  'permalink': 'https://www.instagram.com/p/xxxxx/',
  'timestamp': '2023-01-14T07:30:42+0000'},
 {'caption': 'xxxxxxxxxxxxx\n'
             '\n'
             '#xxxxxxxxxxxxxxxxxx',
  'comments_count': 0,
  ...


2022年12月30日金曜日

PythonとTwitter API v2でリアルタイムにツイートを取得する

Twitter API v1.1のリアルタイムのツイート収集が使えなくなったので、API v2に移行した。PythonライブラリのTweepyとTwitter API v2でリアルタイムにツイート収集する方法をまとめておく。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
$ python3 -V
Python 3.8.10
$ pip3 show tweepy
Name: tweepy
Version: 4.12.1
...


Twitter API のBearer Tokenの取得

Twitter API v2でリアルタイムにツイートを収集するために、Twitterの開発者サイトでBearer Tokenを取得しておく。開発者サイトのProjects & Apps > Overviewの画面で、対象アプリの[Keys and tokens](鍵のアイコン)を開く。この画面でBearer Tokenを生成できる。


Tweepyでリアルタイムツイート取得

TweepyでTwitter API v2を使ってリアルタイムにツイートを収集するには、StreamingClientクラスを使う。v1.1のとき(Tweepy4でリアルタイムツイートを取得する)と同様に、このクラスを継承したクラスを作成する。以下のコードでは、Twitter API v2のsampleメソッドを使って、リアルタイムに1%のツイートを出力する。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けページで取得したBearer Tokenを使う
BEARER_TOKEN = 'Bearer Token'

class TwStream(tweepy.StreamingClient):
    def on_tweet(self, tweet):
        print('-------------------------')
        print(tweet.data)

def main():
    twstream = TwStream(BEARER_TOKEN)
    twstream.sample()

if __name__ == '__main__':
    main()

ただ、このコードで取得できるのはツイートのIDやテキストのみで、取得できるデータはv1.1のときと違ってかなり限定されている。v2では、取得する情報を指定する必要がある。


APIで取得対象のデータを指定する

Twitter API v2では、ExpansionsFieldsを指定することで、さまざまなデータを取得できる。以下のコードでは、ExpansionsとTweetフィールド、Userフィールドを指定。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けページで取得したBearer Tokenを使う
BEARER_TOKEN = 'Bearer Token'

class TwStream(tweepy.StreamingClient):
    def on_tweet(self, tweet):
        print('-------------------------')
        print(tweet.data)

def main():
    twstream = TwStream(BEARER_TOKEN)
    # 取得するExpansions
    expansions = ['author_id', 'referenced_tweets.id', 'attachments.media_keys']
    # 取得するTweetフィールド
    tweet_fields = ['author_id', 'created_at', 'attachments', 'lang', 'public_metrics']
    # 取得するUserフィールド
    user_fields = ['created_at', 'description', 'location', 'verified']
    twstream.sample(expansions=expansions, tweet_fields=tweet_fields, user_fields=user_fields)

if __name__ == '__main__':
    main()

上記のように取得データを指定できるが、v1.1のように言語の指定ができない。このままだと、英語やさまざまな言語のツイートが混ざって取得される。filterメソッドを使うと言語の指定ができるが、filterメソッドでは取得できるツイート数に制限があるので、大量のツイートを収集したい場合には使えない。日本語で絞りたい場合は次のようなコードになる。取得したTweetオブジェクトのlangフィールドを参照して、日本語(ja)のみに限定する。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けページで取得したBearer Tokenを使う
BEARER_TOKEN = 'Bearer Token'

class TwStream(tweepy.StreamingClient):
    def on_tweet(self, tweet):
        if tweet.lang == 'ja':
            print('-------------------------')
            print(tweet.data)

def main():
    twstream = TwStream(BEARER_TOKEN)
    # 取得するExpansions
    expansions = ['author_id', 'referenced_tweets.id', 'attachments.media_keys']
    # 取得するTweetフィールド
    tweet_fields = ['author_id', 'created_at', 'attachments', 'lang', 'public_metrics']
    # 取得するUserフィールド
    user_fields = ['created_at', 'description', 'location', 'verified']
    twstream.sample(expansions=expansions, tweet_fields=tweet_fields, user_fields=user_fields)

if __name__ == '__main__':
    main()



2022年9月26日月曜日

Twitter Streaming APIのfilterは日本語に対応していない

Twitter Streaming APIではリアルタイムにツイートを取得できる。sampleとfilterの2種類の機能があるが、filterではキーワードを指定することで、このキーワードを含むツイートを取得することができる。と思っていたのだが、実際にfilterを使ってツイートを収集してみると、期待通りのツイートが取得できていない。

実際にキーワードを指定して収集してみる。以下コードはTwitter Streaming APIで省略されていないツイートテキストを取得するのコードを変更して、filterでキーワード「スイーツ」を指定したもの。これにより、「スイーツ」を含むツイートが取得できることを期待している。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'

class TwStream(tweepy.Stream):
    def _get_text(self, status):
        # 拡張ツイートの場合
        if hasattr(status, 'extended_tweet'):
            return status.extended_tweet['full_text']
        else:
            return status.text

    def on_status(self, status):
        # status(APIで取得できるデータ)を受け取ったときにon_statusが実行される
        print('-------------------------')
        # リツイートの場合は「RT」+ メンション分だけ文字数オーバーするので省略されることがある
        if hasattr(status, 'retweeted_status'):
            text = self._get_text(status.retweeted_status)
        else:
            text = self._get_text(status)
        # スクリーンネームとツイートを表示
        print(f'@{status.author.screen_name} {text}')

def main():
    twstream = TwStream(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

    # キーワード「スイーツ」を指定してツイートを取得
    twstream.filter(track=['スイーツ'], languages=['ja'])

if __name__ == '__main__':
    main()

実際に取得できたツイートすべてに「スイーツ」が含まれるが、よくよくみるとハッシュタグ「#スイーツ」や「スイーツ」のあとに空白があるツイートのみしか取得できていない。Twitterで「スイーツ」で検索した結果と比べてみても、それが確認できる。

Twitter APIの仕様を確認すると、Standard streaming API request parametersには以下の一文がある。

Non-space separated languages, such as CJK are currently unsupported.

つまり、filterでは日本語などの単語がスペースで区切られていない言語は未サポートということ。キーワードとして「スイーツ」を指定した場合、スイーツの直後にスペースがあるハッシュタグは取得できるが、ツイートテキスト内に「スイーツ」があっても取得対象にならない。過去のツイートを取得できるSearch APIでは、キーワード「スイーツ」を指定して「スイーツ」を含むツイートを取得できるが、Streaming APIでは同様のことはできないようだ。

Stream APIのfilterで日本語を使う場合は、特定のハッシュタグを含むツイートを取得する場合など、限定的な使い方になりそう。


2022年9月18日日曜日

Twitter Streaming APIで省略されていないツイートテキストを取得する

Twitter Streaming APIでツイートテキストを取得するときに、省略されていないテキスト全文を取得する方法をまとめておく。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
$ python3 -V
Python 3.8.10


省略されていないテキストを取得する方法

Twitter Streaming APIでツイートテキストを収集する場合、レスポンスデータのtextフィールドに、最大140文字までのテキストが格納されている。

ただし、2017年に半角文字の言語では280文字までに文字数制限が拡張されたために、ツイートテキストが140文字を超える場合がある。ツイートテキストが140文字を超える場合、textフィールドの最後が三点リーダー(…)となり、テキストの一部が省略されてしまう。

拡張された文字数のツイートテキストを取得するには、extended_tweetフィールドを使う。このextended_tweetフィールドはツイートテキストの文字数が140文字を超えた場合に存在し、このフィールドのfull_textフィールドにツイートテキストが格納されている。

ただこれでもまだ省略されてしまう場合がある。それはリツイートの場合で、ツイートテキストにRTとメンションが付与されるので、元のツイートが文字数制限いっぱいだと、ツイートテキストは一部省略されてしまう。

リツイートの場合、レスポンスデータのextended_tweetフィールドに元ツイートの情報があるので、そのツイートテキストを取得することで、省略されていないテキストを取得することができる。


省略されていないテキストを取得するコード

上記をふまえて、PythonでTwitter Streaming APIのレスポンスから省略されていないツイートテキスト全文を取得するコードを作成する。Twitter Streaming APIのデータ取得には、PythonライブラリのTweepyを使う。TweepyでStreaming APIを使う方法はTweepy4でリアルタイムツイートを取得するを参照。以下はStreaming APIでランダムサンプリングを取得して、スクリーンネームとツイートテキストを表示するコード。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'

class TwStream(tweepy.Stream):
    def _get_text(self, status):
        # 拡張ツイートの場合
        if hasattr(status, 'extended_tweet'):
            return status.extended_tweet['full_text']
        else:
            return status.text

    def on_status(self, status):
        # status(APIで取得できるデータ)を受け取ったときにon_statusが実行される
        print('-------------------------')
        # リツイートの場合は「RT」+ メンション分だけ文字数オーバーするので省略されることがある
        if hasattr(status, 'retweeted_status'):
            text = self._get_text(status.retweeted_status)
        else:
            text = self._get_text(status)
        # スクリーンネームとツイートを表示
        print(f'@{status.author.screen_name} {text}')

def main():
    twstream = TwStream(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

    # 日本語ツイートに限定してランダムサンプリングを取得
    twstream.sample(languages=['ja'])

if __name__ == '__main__':
    main()


2022年5月29日日曜日

Spotify再生回数トップ50の曲を分類して可視化する

Spotify APIで再生回数トップ50の曲データを取得するでは、Spotify APIで再生回数トツプ50の曲一覧を取得した。Spotify APIでは、曲名やアーティスト名などの情報のほかに、曲の特徴を取得できる。曲の特徴とは、ダンスに適した曲か、エネルギッシュさなどといったもので、これらの情報を利用して曲の分類と可視化をしてみる。


環境

WSL2(Ubuntu20.04)とJupyter。

$ lsb_release -dr
Description:    Ubuntu 20.04.4 LTS
Release:        20.04
$ python3 -V
Python 3.8.10
$ jupyter --version
jupyter core     : 4.7.1
jupyter-notebook : 6.2.0
qtconsole        : 5.0.2
ipython          : 7.20.0
ipykernel        : 5.4.3
jupyter client   : 6.1.12
jupyter lab      : 3.0.9
nbconvert        : 6.0.7
ipywidgets       : 7.6.3
nbformat         : 5.1.2
traitlets        : 5.0.5

Spotify APIで再生回数トップ50の曲データを取得するの手順でspotipyをインストールしておく。

さらに、曲を特徴でクラスタリングして可視化するためのPythonライブラリをインストールする。

$ pip3 install scikit-learn pandas matplotlib seaborn


spotipyで曲の特徴を取得

APIで取得できる特徴の説明はGet Track's Audio Featuresにある。今回はacousticness(アコースティック度)、danceability(ダンスに適した曲か)、energy(エネルギッシュさ)、instrumentalness(ボーカルがない度合い)、liveness(ライブ感)、loudness(曲全体のボリューム)、speechiness(話し言葉の度合い)、valence(曲のポジティブ度)を使用する。

今回もSpotify APIで再生回数トップ50の曲データを取得すると同様にspotipyを使う。曲の特徴を取得するには、spotifyのaudio_featuresメソッドに引数としてトラックIDのリストを渡す。取得した特徴はPandasのDataFrameにしておく。
import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials

# language='ja'としないとアーティスト名が英語表記になる
spotify = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(), language='ja')

def get_tracks(playlist_id):
    results = spotify.playlist_items(
        playlist_id,
        fields=None,
        limit=50,
        offset=0,
        market='JP',
        additional_types=('track', 'episode'),
    )
    items = results['items']

    tracks = []
    for item in items:
        data = {}
        track = item['track']
        data['track_id'] = track['id']
        data['track_name'] = track['name']
        data['artists'] = [ar['name'] for ar in track['artists']]
        tracks.append(data)

    return tracks

def get_track_features(track_ids):
    results = spotify.audio_features(track_ids)
    # idと分類に使用する特徴
    features = ['id', 'danceability', 'energy', 'loudness', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo']
    track_features = []
    for re in results:
        track_features.append(
            {key: value for key, value in re.items() if key in features}
        )
    return track_features

# プレイリストの曲一覧取得
# トップ50 日本
# URLの最後の22文字がプレイリストID
playlist_id = '37i9dQZEVXbKXQ4mDTEBXq'
tracks = get_tracks(playlist_id)
track_info = {tr['track_id']: f'{tr["track_name"]} - {"|".join(tr["artists"])}' for tr in tracks}

# 曲の特徴を取得
track_ids = [tr['track_id'] for tr in tracks]
track_features = get_track_features(track_ids)
df_feature = pd.DataFrame([tf for tf in track_features])
df_feature.set_index('id', inplace=True)
df_feature.info()

次のようにDataFrameには50曲分の特徴が格納される。
Index: 50 entries, 4IfrM44LofE9bSs6TDZS49 to 3oQaOjaIYPsnJbGNzXcIID
Data columns (total 9 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   danceability      50 non-null     float64
 1   energy            50 non-null     float64
 2   loudness          50 non-null     float64
 3   speechiness       50 non-null     float64
 4   acousticness      50 non-null     float64
 5   instrumentalness  50 non-null     float64
 6   liveness          50 non-null     float64
 7   valence           50 non-null     float64
 8   tempo             50 non-null     float64


クラスタリングによる分類と可視化

まずはK平均法でクラスタリングする。クラスター数は今回はとりあえず5とする。そして、その結果をもとにクラスターごとの曲を表示する。

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

# language='ja'としないとアーティスト名が英語表記になる
spotify = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(), language='ja')

def get_tracks(playlist_id):
    results = spotify.playlist_items(
        playlist_id,
        fields=None,
        limit=50,
        offset=0,
        market='JP',
        additional_types=('track', 'episode'),
    )
    items = results['items']

    tracks = []
    for item in items:
        data = {}
        track = item['track']
        data['track_id'] = track['id']
        data['track_name'] = track['name']
        data['artists'] = [ar['name'] for ar in track['artists']]
        tracks.append(data)

    return tracks

def get_track_features(track_ids):
    results = spotify.audio_features(track_ids)
    # idと分類に使用する特徴
    features = ['id', 'danceability', 'energy', 'loudness', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo']
    track_features = []
    for re in results:
        track_features.append(
            {key: value for key, value in re.items() if key in features}
        )
    return track_features

def kcluster(df):
    X = StandardScaler().fit_transform(df)
    # とりあえずクラスター数5
    n_clusters = 5
    clusterer = KMeans(init='k-means++', n_clusters=n_clusters, random_state=0)
    kmeans = clusterer.fit(X)
    return kmeans.labels_

# プレイリストの曲一覧取得
# トップ50 日本
# URLの最後の22文字がプレイリストID
playlist_id = '37i9dQZEVXbKXQ4mDTEBXq'
tracks = get_tracks(playlist_id)
track_info = {tr['track_id']: f'{tr["track_name"]} - {"|".join(tr["artists"])}' for tr in tracks}

# 曲の特徴を取得
track_ids = [tr['track_id'] for tr in tracks]
track_features = get_track_features(track_ids)
df_feature = pd.DataFrame([tf for tf in track_features])
df_feature.set_index('id', inplace=True)
df_feature.info()

# K平均法でクラスタリング
df_feature['cluster'] = kcluster(df_feature.values)

# クラスターごとの曲名を表示
with pd.option_context('display.max_rows', None, 'display.max_columns', None):
    for cluster, rows in df_feature.groupby('cluster'):
        print(f'\ncluster {cluster}:')
        for id in rows.index:
            print(track_info[id])

結果は以下の通り。

cluster 0:
ミックスナッツ - Official髭男dism
喜劇 - 星野 源
Habit - SEKAI NO OWARI
FEARLESS - LE SSERAFIM
Dynamite - BTS
Butter - BTS
群青 - YOASOBI
一途 - King Gnu
常緑 - 大橋ちっぽけ
怪物 - YOASOBI
WA DA DA - Kep1er
The Feels - TWICE
Bye-Good-Bye - BE:FIRST
ELEVEN - IVE

cluster 1:
ベテルギウス - 優里
なんでもないよ、 - マカロニえんぴつ
水平線 - back number
残響散歌 - Aimer
STAY (with Justin Bieber) - The Kid LAROI|ジャスティン・ビーバー
Cry Baby - Official髭男dism
猫 - DISH//
I LOVE... - Official髭男dism
おもかげ (produced by Vaundy) - milet|Aimer|幾田りら

cluster 2:
M八七 - 米津玄師
ドライフラワー - 優里
カメレオン - King Gnu
シャッター - 優里
勿忘 - Awesome City Club
逆夢 - King Gnu
点描の唄 - Mrs. GREEN APPLE|井上苑子
魔法の絨毯 - 川崎 鷹也
YOKAZE - 変態紳士クラブ
ハート - あいみょん
Stand by me, Stand by you. - 平井 大

cluster 3:
W / X / Y - Tani Yuuki
シンデレラボーイ - Saucy Dog
CITRUS - Da-iCE
恋風邪にのせて - Vaundy
きらり - 藤井 風
LOVE DIVE - IVE
Permission to Dance - BTS
Mela! - 緑黄色社会
夜に駆ける - YOASOBI
三原色 - YOASOBI
115万キロのフィルム - Official髭男dism
ヨワネハキ - MAISONdes|和ぬか|asmi
Pretender - Official髭男dism
踊 - Ado
BOY - King Gnu

cluster 4:
踊り子 - Vaundy

さらに、特徴ごとにクラスター別の箱ひげ図を作成する。上記コードに以下を追記。

# 特徴別の箱ひげ図を作成
fig, ax =plt.subplots(len(df_feature.columns) - 1, 1, figsize=(12, 40), facecolor=(1, 1, 1))
for i, col_name in enumerate(df_feature):
    if col_name != 'cluster':
        sns.boxplot(x=df_feature[col_name], y=df_feature['cluster'], width=0.5, orient='h', ax=ax[i])
        sns.swarmplot(x=df_feature[col_name], y=df_feature['cluster'], color="r", orient='h', dodge=True, ax=ax[i])
plt.tight_layout()
plt.show()

結果は以下のような箱ひげ図が作成される。クラスター4に1曲だけ属する「踊り子」は、ほかの曲に比べてacousticness(アコースティック度)とinstrumentalness(ボーカルがない度合い)が高いことがわかる。



2022年5月8日日曜日

Spotify APIで再生回数トップ50の曲データを取得する

音楽ストリーミングサービスのSpotifyにはAPIが用意されていて、曲に関するさまざまなデータを取得できる。今回は、Spotify APIのPythonラッパーであるspotipyを利用して、プレイリストの曲一覧を取得してみる。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.4 LTS
Release:        20.04
$ python3 -V
Python 3.8.10


Spotify APIのCLIENT IDとCLIENT SECRETの取得

Spotify APIを使用するには、Spotifyの開発者サイトでアプリを作成してCLIENT IDとCLIENT SECRETを取得する必要がある。

アプリを作成するには、Spotify for DevelopersのDASHBOARDにログインして、「CREATE AN APP」をクリックする。アプリ名(App name)とアプリの説明(App description)を記入するだけで作成できる。アプリを作成すると、DASHBOARDでCLIENT IDとCLIENT SECRETが確認できるようになる。


spotipyのインストール

Spotify APIによるデータ取得には、Spotify APIのPythonラッパーであるspotipyを使う。pipでインストールできる。

$ pip3 install spotipy
$ pip3 show spotipy
Name: spotipy
Version: 2.19.0
...

インストールしたら、環境変数SPOTIPY_CLIENT_IDとSPOTIPY_CLIENT_SECRETにそれぞれCLIENT IDとCLIENT SECRETを設定しておく。

export SPOTIPY_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxx
export SPOTIPY_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxx


spotipyでプレイリストの曲一覧を取得

spotipyでプレイリスト「トップ50 日本」の曲一覧を取得してみる。spotipyにはプレイリストの曲情報を取得するためのplaylist_itemsメソッドがあり、引数としてプレイリストIDが必要になる。プレイリストIDは、プレイリストURLの最後の22桁。「トップ50 日本」のプレイリストIDは「37i9dQZEVXbKXQ4mDTEBXq」。

playlist_itmesメソッドで取得できるのは曲名、アーティスト名、曲が含まれるアルバム情報などいろいろある。ここでは曲名とアーティスト名を取得して、一覧表示してみる。

import spotipy
from spotipy.oauth2 import SpotifyClientCredentials

# language='ja'としないとアーティスト名が英語表記になる
spotify = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(), language='ja')

# プレイリスト「トップ50 日本」のプレイリストID
# SpotifyのプレイリストURLの最後の22文字がプレイリストID
playlist_id = '37i9dQZEVXbKXQ4mDTEBXq'

results = spotify.playlist_items(
    playlist_id,
    fields=None,
    limit=50,
    offset=0,
    market='JP',
    additional_types=('track', 'episode'),
)
items = results['items']

tracks = []
for item in items:
    data = {}
    track = item['track']
    data['name'] = track['name']
    data['artists'] = [ar['name'] for ar in track['artists']]
    tracks.append(data)

for i, tr in enumerate(tracks):
    print(i + 1, tr['name'], '-', '|'.join(tr['artists']))

上記コードを実行すると、以下のようにトップ50の曲一覧が取得できる。

1 W / X / Y - Tani Yuuki
2 シンデレラボーイ - Saucy Dog
3 ミックスナッツ - Official髭男dism
4 ベテルギウス - 優里
5 なんでもないよ、 - マカロニえんぴつ
6 カメレオン - King Gnu
7 水平線 - back number
8 喜劇 - 星野 源
9 ドライフラワー - 優里
10 きらり - 藤井 風
11 残響散歌 - Aimer
12 CITRUS - Da-iCE
13 LOVE DIVE - IVE
14 シャッター - 優里
15 Butter - BTS
16 Dynamite - BTS
17 群青 - YOASOBI
18 恋風邪にのせて - Vaundy
19 勿忘 - Awesome City Club
20 逆夢 - King Gnu
21 Permission to Dance - BTS
22 一途 - King Gnu
23 STAY (with Justin Bieber) - The Kid LAROI|ジャスティン・ビーバー
24 夜に駆ける - YOASOBI
25 点描の唄 - Mrs. GREEN APPLE|井上苑子
...
47 まつり - 藤井 風
48 もう少しだけ - YOASOBI
49 虹 - 菅田 将暉
50 FEARLESS - LE SSERAFIM


2021年12月30日木曜日

楽天APIとPythonで楽天市場のランキングを取得する

楽天はいくつかAPIを提供していて(API一覧)、楽天市場のランキングもAPIで取得できる。今回はPythonで楽天市場のランキングを取得してみる。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
$ python3 -V
Python 3.8.10
$ pip3 show requests
Name: requests
Version: 2.25.1
...


ランキングを取得する事前準備

APIの使用にはアプリケーションIDが必要なので事前に取得しておく。アプリケーションIDはRakuten Webserviceでアプリを登録することで取得できる。

今回は楽天市場のランキングを取得したいので、いくつかあるAPIのうち楽天商品ランキングAPIを使用する。このAPIではランキングのジャンルを指定できるので、チョコレートのランキングを取得してみる。

ジャンル指定にはジャンルIDが必要で、ジャンルIDは楽天市場ジャンル検索APIで取得できる。ここではAPIテストフォームを使ってジャンルIDを取得する。ジャンルの階層構造の最上位ジャンルIDが0になっているようで、テストフォームでgenreIdに0を指定してGETボタンを押すとJSON(デフォルト)で最上位のジャンル一覧を取得できる。


その一覧に「スイーツ・お菓子」があるので、そのジャンルIDである551167で再度検索するとチョコレートのジャンルIDが201136であることがわかる。このジャンルIDでランキングを取得する。



楽天商品ランキングを取得する

楽天商品ランキングAPIで楽天市場のチョコレートのランキングを取得する。Pythonコードは以下の通り。

import os
import json

import requests

# APIのエンドポイント
ENDPOINT = 'https://app.rakuten.co.jp/services/api/IchibaItem/Ranking/20170628'

RAKUTEN_APP_ID = 'Rakuten Webserviceで取得したアプリケーションID'


def show_ranking(api_res):
    """ランキング順位とその商品名を表示
    """
    res = json.loads(api_res)
    items = res.get('Items')
    for item in items:
        rank = item.get('rank')
        item_name = item.get('itemName')

        print(f'{rank}位 {item_name}\n')


def get_ranking(genre_id):
    params = {
        'applicationId': RAKUTEN_APP_ID,
        'format': 'json',
        'formatVersion': 2,
        'genreId': genre_id
    }

    r = requests.get(url=ENDPOINT, params=params)
    assert r.status_code == 200, f'status code: {r.status_code}'

    show_ranking(r.text)


def main():
    get_ranking(genre_id='551167')


if __name__ == '__main__':
    main()


以下のようにランキング一覧が取得できる。

1位 ポイント10倍 送料無料 お歳暮 御歳暮 ギフト クリスマス プレゼント お年賀 ヨックモック お菓子 スイーツ 詰め合わせYCD-30 サンク デリス(5種45個入り)洋菓子 クッキー 手土産 個包装 内祝い 焼き菓子 お祝い お返し お取り寄せ 誕生日 チョコ 帰省土産 のし 缶

2位 送料無料 リンツ Lindt チョコレート リンドール 23個入り テイスティングセット| ギフト 洋菓子ギフト かわいい お菓子 スイーツ プレゼント 可愛い 手土産 内祝い 内祝いお返し お礼 リンツチョコ 誕生日 クリスマス X'mas 冬ギフト 退職 職場

3位 お歳暮 ギフト HTA-30AWN 送料無料 タルト・フリュイ・アソート S 秋冬限定お礼 誕生日 プレゼント お菓子 洋菓子 手土産 焼き菓子 出産 内祝 贈り物 お返し 退職 引越し 入学 卒業 定年 退職 ご挨拶 御歳暮
...

取得できる商品の名称は一般的な商品名ではなく、楽天市場の各店舗が設定した名称になっている。APIでは他にも年代や性別も指定できるが、ジャンルIDと同時に指定できない。


2021年12月20日月曜日

Tweepy4でリアルタイムツイートを取得する

Twitter API v1.1でのリアルタイムツイート取得ができなくなっているので、この記事の方法は使えない。リアルタイムにツイートを取得するときは、PythonとTwitter API v2でリアルタイムにツイートを取得するのようにv2を使う。(2022.12.30追記)


 TweepyTwitter APIを利用してツイート検索などを行えるPythonライブラリ。バージョンが3から4になって仕様がけっこう変わっている。今回はTweepyの最新バージョン4でリアルタイムツイートを収集してみる。


環境

WSL2(Ubuntu20.04)。

$ lsb_release -dr
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
$ python3 -V
Python 3.8.10

Twitter APIの利用に必要なAPIキーやトークンなどが未取得であれば、Twitterの開発者向けサイト(Developer Platform)で取得しておく。


Tweepyのインストール

Tweepyはpipでインストールできる。

$ pip3 install tweepy
$ pip3 show tweepy
Name: tweepy
Version: 4.4.0
Summary: Twitter library for Python
...


Tweepyでリアルタイムにツイートを取得する

Tweepyバージョン3でリアルタイムにツイートを取得するには、StreamListenerを継承するクラスのインスタンスをStreamオブジェクトにわたすようにしていたが、バージョン4ではStreamListenerが廃止されている。TweepyドキュメントのFrequently Asked QuestionsにStreamListener廃止についての記載がある。

Tweepyバージョン4でリアルタイムにツイートを取得するには、Streamを継承するクラスを作成し、そのインスタンスを使う。以下のコードではリアルタイムに日本語ツイートのランダムサンプリングを表示する。

import tweepy

# Twitter APIの認証情報
# Twitterの開発者向けのページで取得したキーとトークンを使う
CONSUMER_KEY = 'Consumer Key'
CONSUMER_SECRET = 'Consumer Secret'
ACCESS_TOKEN = 'Access Token'
ACCESS_TOKEN_SECRET = 'Access Token Secret'

class TwStream(tweepy.Stream):
    def on_status(self, status):
        # status(APIで取得できるデータ)を受け取ったときにon_statusが実行される
        print('-------------------------')
        # スクリーンネームとツイートを表示
        print(f'@{status.author.screen_name} {status.text}')

def main():
    twstream = TwStream(
        CONSUMER_KEY,
        CONSUMER_SECRET,
        ACCESS_TOKEN,
        ACCESS_TOKEN_SECRET
    )

    # 日本語ツイートに限定してランダムサンプリングを取得
    twstream.sample(languages=['ja'])

if __name__ == '__main__':
    main()



2021年11月15日月曜日

Pythonで世界銀行のオープンデータを使ってバブルチャートを作成する

世界銀行が公開しているオープンデータ(World Bank Open Data)があって、データ取得用のAPIも用意されている。Pythonでこのオープンデータを取得してバブルチャートを作成してみる。


環境


WSL2(Ubuntu20.04)とJupyter Lab。


APIで世界銀行のオープンデータを取得

オープンデータの取得にはWBGAPIを使う。WBGAPIでは世界銀行のAPIを利用してPythonでオープンデータにアクセスできる。インストールはpipでできる。

世界銀行のオープンデータには人口や所得、消費電力などいろいろなデータがある。WBGAPIで「power」という語を含むデータを検索してみる。

import wbgapi as wb

display(wb.series.info(q='power'))

以下のような結果が得られる。データを取得するときにはこの結果にあるidを使う。


次にオープンデータに含まれる国や地域を確認してみる。オープンデータには国別データの他にいくつかの国をまとめた地域ごとのデータもある。

import wbgapi as wb

display(wb.economy.info())

次のような国IDや地域などの情報が確認できる。


先の結果にあるregionの情報は以下のようにして確認できる。

import wbgapi as wb

display(wb.region.info())

データを取得するときにはデータの種類、国または地域、年を指定できる。以下では2019年のアメリカの人口を取得できる。

import wbgapi as wb

display(wb.data.DataFrame(['SP.POP.TOTL'], ['USA'], [2019]))

オープンデータをもとにバブルチャートを作成する

WBGAPIで取得したデータをもとにバブルチャートを作成してみる。まずは必要なPythonライブラリのインストール。

続いて4種類のデータを取得してバブルチャートを作成する。すべての国だと数が多すぎて見づらくなるので人口上位50にしぼってある。使用した4種類のデータは以下の通り。

  • SP.POP.TOTL: 人口
  • NY.GDP.PCAP.CD: ひとりあたりGDP
  • SP.DYN.LE00.IN: 出生時平均寿命
  • BN.GSR.FCTY.CD: 一次純所得

%matplotlib inline

import pandas as pd
import wbgapi as wb
import matplotlib.pyplot as plt
import seaborn as sns

# 地域を除いた国のリストを作成
# regionが空白になっていないidのみにする 
economy_info = wb.economy.info().items
countries = [ei['id'] for ei in economy_info if ei['region'] != '']

# 2019年の国別の以下のデータを取得する
# SP.POP.TOTL: 人口
# NY.GDP.PCAP.CD: ひとりあたりGDP
# SP.DYN.LE00.IN: 出生時平均寿命
# BN.GSR.FCTY.CD: 一次純所得
df=wb.data.DataFrame(
    ['SP.POP.TOTL', 'NY.GDP.PCAP.CD', 'SP.DYN.LE00.IN', 'BN.GSR.FCTY.CD'],
    countries,
    [2019]
)

# データがない国を省いて人口上位50にしぼる
df = df.dropna().sort_values('SP.POP.TOTL', ascending=False).head(50)

plt.figure(figsize=(18, 12))

# バブルチャート作成
# NY.GDP.PCAP.CD: Y軸
# SP.DYN.LE00.IN: X軸
# SP.POP.TOTL: 円の大きさ
# BN.GSR.FCTY.CD: 色
ax = sns.scatterplot(
    data=df, 
    x='NY.GDP.PCAP.CD', 
    y='SP.DYN.LE00.IN', 
    size='SP.POP.TOTL', 
    c=df['BN.GSR.FCTY.CD'], 
    cmap='autumn_r', 
    alpha=0.9, 
    edgecolors='grey', 
    linewidth=1, 
    legend=False, 
    sizes=(20, 5000)
)

# 各円に国IDを表示する
def put_label(x, y, labels, ax):
    a = pd.concat({'x': x, 'y': y, 'label': labels}, axis=1)
    for i, point in a.iterrows():
        ax.text(point['x']+.02, point['y'], str(point['label']))

put_label(df['NY.GDP.PCAP.CD'], df['SP.DYN.LE00.IN'], df.index.to_series(), plt.gca())

plt.show()

次のようなバブルチャートが作成された。


2021年10月25日月曜日

PythonとAPIで論文検索をする

Semantic Scholarという論文検索サービスがあり、検索結果のランキングにはAIが使われているらしい(Building a Better Search Engine for Semantic Scholar)。ちなみにこのサービスを運営しているのはAllen Institute for AIで、マイクロソフトの共同創業者だった故ポール・アレン氏が設立した研究機関。このサービスにはAPIがあるので、このAPIを利用してPythonで論文検索をしてみる。


環境


WSL2(Ubuntu20.04)。


Semantic ScholarのAPIについて

Semantic ScholarのLiterature Graph Service (1.0)を使って論文を検索する。このAPIのsearchエンドポイントではキーワードで論文を検索できる。レートリミットは5分間で100リクエストで、登録すればレートリミットを上げることができるらしい。ここでは登録せずに使う。「python」というキーワードで検索するには次のURLにアクセスする。

https://api.semanticscholar.org/graph/v1/paper/search?query=python

ブラウザでアクセスすると、以下のような結果が表示される。paperIdはSemantic Scholarで使われる論文ごとのID。


PythonとAPIで論文を検索する

デフォルトで取得できる論文情報は論文タイトルとAPIで論文詳細を取得するときなどに使える論文ID(paperId)だが、fieldsパラメータを使うと他の項目も取得できる。ここでは次の項目を取得する。

  • title:タイトル
  • year:年
  • referenceCount:参照数
  • citationCount:引用数
  • influentialCitationCount:影響力のある引用数? 機械学習で算出されたものらしい(What are Highly Influential Citations
  • isOpenAccess:オープンアクセスかどうか
  • fieldsOfStudy:研究分野
  • authors:著者

キーワード「python」で検索するPythonコードは以下の通り。最大取得件数を10に制限してある。

import requests
import json
 
endpoint = 'https://api.semanticscholar.org/graph/v1/paper/search'
keyword = 'python'
fields = ('title', 'year', 'referenceCount', 'citationCount',
	'influentialCitationCount', 'isOpenAccess', 'fieldsOfStudy', 'authors')
params = {
    'query': keyword,
    'fields': ','.join(fields),
    'limit': 10
}
r = requests.get(url=endpoint, params=params)

r_dict = json.loads(r.text)
total = r_dict['total']
print(f'Total search result: {total}')

data = r_dict['data']
for d in data:
    print('---------------')
    for fi in fields:
        if fi == 'authors':
            print(f'{fi}: {list(map(lambda a: a["name"], d[fi]))}')
        else:
            print(f'{fi}: {d[fi]}')

以下のように論文情報が取得できた。


2021年9月30日木曜日

PythonでGitHubのGraphQL APIを使う

GitHubにはAPIがあってリポジトリの情報取得などができる。REST APIGraphQL APIが利用可能だが、ここではGraphQL APIをPythonで使ってみる。


環境


WSL2(Ubuntu20.04)。


Explorerでクエリの確認

GraphQLではクエリを使うが、ExplorerでGraphQL APIへのクエリを試せる。Pythonで実行する前にクエリの結果を確認するのに使える。

例としてGraphQLでの呼び出しの作成にあるクエリを実行してみる。このクエリではoctocat/Hello-Worldリポジトリのクローズ済み最新20のissueの情報を取得できる。

query {
  repository(owner:"octocat", name:"Hello-World") {
    issues(last:20, states:CLOSED) {
      edges {
        node {
          title
          url
          labels(first:5) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    }
  }
}

Explorer画面の左ペインに上記クエリをペーストし、三角矢印のアイコンをクリックするとクエリが実行され、右ペインに結果が表示される。



アクセストークンの生成

GraphQL APIの使用にはアクセストークンが必要なので生成する。GitHubにログインしたらSettings > Developer Settings > Personal access tokens > Generate new token でアクセストークンを生成する。この際にトークンの有効期限やスコープを設定する。生成したらコピーしておく。



PythonでGraphQL APIを使う

アクセストークンを取得したのでPythonでGraphQL APIを使ってみる。トークンは送信ヘッダーのAuthorizationにセットする。クエリの検索条件はsearchで指定する。以下のコードでは「pandas」というワードをリポジトリ名に含み、スターが500以上、言語がPythonというリポジトリを検索する。リポジトリの検索条件指定についてはリポジトリを検索するを参照。取得するデータは検索条件に合致したリポジトリの数と各リポジトリの情報。nodesに取得するリポジトリの情報を指定する。

import requests
import json

endpoint = 'https://api.github.com/graphql'
api_token = '生成したアクセストークン'
headers = {'Authorization': 'token ' + api_token}

query = """
{ 
  search(type: REPOSITORY, query: "pandas in:name stars:>=500 language:python sort:stars", first: 100)   {
    repositoryCount
    nodes {
      ... on Repository {
        id
        url
        name
        description
        stargazers {
          totalCount
        }
        forks {
          totalCount
        }
        createdAt
        updatedAt
      }
    }
  }
}
"""
r = requests.post(url=endpoint, json={'query': query}, headers=headers)

search_result = json.loads(r.text)['data']['search']

# リポジトリ数
repo_count = search_result['repositoryCount']
print('repositoryCount: ' + str(repo_count))

# リポジトリ名とスター数
nodes = search_result['nodes']
for n in nodes:
  print('\nname: ' + n.['name'])
  print('stargazers totalCount: ' + str(n['stargazers']['totalCount']))
以下のように検索条件に合致したリポジトリ数と各リポジトリの情報が取得できる。