YouTubeAPIでLIKEを自動化とEC2とS3でのログ同期とSLACKへのWEBHOOK

2019/4/11

全体的にどんな事が必要なのかがわかる程度を目標にしています。コードはちゃんと動きますが、あまり期待しないでください。

(1)概要

YouTubeで特定のキーワードにマッチする動画に対して自動的にライクをつける。(なぜそのようなことが必要なのかはここでは議論しない。)

YouTubeが提供しているAPIサンプルがPython2.7なのでPython2.7を使用する。

https://developers.google.com/youtube/v3/code_samples/python?hl=ja

サンプルは以下の2つを使用する。

・キーワードの検索 ・動画の評価

 

(2)準備

①認証情報の準備

Google Developers Consoleにログインしてプロジェクトを作成し、APIキーとOAuth 2.0 クライアント IDの認証情報を生成する。

 

https://console.developers.google.com/getting-started?hl=ja

OAuthクライアントIDは「その他」で作成する。

APIキーは控えておき、OAuthクライアントIDはJSONをダウンロードしておく

 

②実行環境の準備

今回はMAC上で作成したものをAWSにデプロイした。

Python2.7とPIPは別件でインストール済だったのでPIPを使って以下をインストール

sudo pip install –upgrade google-api-python-client
sudo pip install –upgrade oauth2client

 

(3)実装の検討

今回はキーワード「vocaloid」をキーワードにして検索にかかる動画を片っ端からライクをつける処理にしたいので、方針を以下のように整理した。

①1時間に1回、一定期間(1時間)分のアップロードされた動画を検索して、検索結果(動画IDの一覧)をファイルに出力する。

②1時間に1回、検索処理で出力したファイルを読み込んで一覧にある動画IDに対して1つづつライクをつける。

①はAPIキーだけで処理できるが、②はOAuthのフローが必要。また、1時間に1回実行するという処理についてはcronで実装する。

 

(4)サンプルの修正

①キーワードの検索

時間を指定してアップロードされた動画の一覧を検索したいので、クエリにpublishedAfterとpublishedBeforeを追加する。ここでは一日遅らせてライクする処理にしている。主な追加は以下のあたり。

todayx=datetime.today()
today1=todayx – timedelta(days=1)
today2=today1 + timedelta(minutes=60)
stdate=today1.strftime(‘%Y-%m-%dT%H:%M:%SZ’)
endate=today2.strftime(‘%Y-%m-%dT%H:%M:%SZ’)

def youtube_search(options):
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
developerKey=DEVELOPER_KEY)

# Call the search.list method to retrieve results matching the specified
# query term.
search_response = youtube.search().list(
q=options.q,
part=’id,snippet’,
maxResults=options.max_results,
order=’date’,
publishedAfter=endate,
publishedBefore=stdate

動画一覧をファイルに書き出したいのでlist.txtに書き出す。

for search_result in search_response.get(‘items’, []):
if search_result[‘id’][‘kind’] == ‘youtube#video’:
videos.append(‘%s’ % (search_result[‘id’][‘videoId’]))
elif search_result[‘id’][‘kind’] == ‘youtube#channel’:
channels.append(‘%s’ % (search_result[‘id’][‘channelId’]))
elif search_result[‘id’][‘kind’] == ‘youtube#playlist’:
playlists.append(‘%s’ % (search_result[‘id’][‘playlistId’]))

print ‘Videos:\n’, ‘\n’.join(videos), ‘\n’
print ‘Channels:\n’, ‘\n’.join(channels), ‘\n’
print ‘Playlists:\n’, ‘\n’.join(playlists), ‘\n’

f = open(‘./list.txt’, ‘w’)
for x in videos:
f.write(str(x) + “\n”)
f.close()

②動画の評価

動画一覧のファイルを読み込んで処理したいので以下を追加。

if __name__ == “__main__”:
argparser.add_argument(“–videoid”, default=”L-oNKK1CrnU”,
help=”ID of video to like.”)
args = argparser.parse_args()

youtube = get_authenticated_service(args)

path = ‘./list.txt’
with open(path) as myf:
while True:
s_line = myf.readline()
likevideoid=s_line.rstrip(‘\n’)
if len(likevideoid) !=11:
break

try:
like_video(youtube, likevideoid)
except HttpError, e:
print “An HTTP error %d occurred:\n%s” % (e.resp.status, e.content)
else:
print “%s has been liked.” % likevideoid

if not s_line:
break

 

(5)ローカルでテスト

Google Developers ConsoleのOAuth 2.0 クライアント IDの認証情報でダウンロードしたファイルはclient_secrets.jsonというファイル名にしてpythonと同じディレクトリに配置して、検索のほうを実行する。

動画一覧のファイルができている事を確認したら評価のほうを実行する。

たまにライクを拒否する設定になっている動画がありエラーを出すが、処理は継続するので問題ない。

(6)AWSのEC2にデプロイ

EC2にAmazonLinux2をデプロイした。Python2.7はインストールされているが、PIPはインストールされていなかったのでインストールして、ローカル同様にgoogle-api-python-clientとoauth2clientもインストール。

sudo easy_install pip
sudo pip install –upgrade google-api-python-client
sudo pip install –upgrade oauth2client

評価のほうのpythonは、一度実行すると認証用のファイルが同じディレクトリに生成される。ファイル名は、pythonファイル名-oauth2.json(今回でいうとファイル名がlike_video_new2.pyなので、like_video_new2.py-oauth2.json)。

そのファイルと、検索と評価の2つのpythonファイルと、client_secrets.jsonをEC2上に持ち込んで、cronでそれぞれ1時間毎に実行する。

が、quota(APIの実行数制限)の問題があって、動画の量によっては全ての検索された動画を全てライクすることはできない可能性があるので、その場合は検索条件をもう少し変えるか、cronで時間を絞るか、ライクできない分はエラーになるが、良しとするか。という選択になる。

quotaはユニットと呼ばれる値で制限されていて、ユニットは操作によって重さが変わります。
以下でサーチとレイティングでどれくらい使うか調べてみたところ、
search:100ユニット、rate:51ユニットとなっていました。
https://developers.google.com/youtube/v3/determine_quota_cost?hl=JA

また、以下を確認したところ、自分のアカウントでは1日あたりの上限値は10,000ユニットとなっていました。
https://console.developers.google.com/cloud-resource-manager?hl=ja

今は16時~25時にcronでレーティングを実行しているのですが、(15時から24時の投稿分が対象)
Qouta制限のエラーが出たのでログからどれくらいレーティングできたかを調べてみたところ。
16時から25時までで、164本のビデオをレイティングして、最後の処理ではそれ以降がquota制限でエラーになりました。

16時 14本
17時 13本
18時 12本
19時 14本
20時 18本
21時 20本
22時 20本
23時 20本
24時 21本
25時 12本
合計 164本

サーチの回数は10回なので、10*100=1000ユニット
レーティングの本数は164本なので、164*51=8364ユニット
合計で9,364ユニット。
だいたいあっているような気がします。正直倍くらいにして欲しいです。

 

また、ここが一番重要だと思うのですが、Likeが追加されるのは所有者アカウントであって、ブランドアカウントではありません。YouTubeコンテンツパートナーはアカウントを指定できるのかもしれないが自分は違うので未確認。

 

以下は実際にやっていないので推測も混ざっています。

図でいうところの、KAOR MUSIXというアカウントが所有者アカウント。
KAOR というアカウントがブランドアカウント。

通常、拡散したいと思うようなチャンネルはブランドアカウントで作成していると思いますが、APIでライクがつくのは所有者アカウントのほうです。
ただ、チャンネルは移動する事ができます。一般的には所有者アカウントで作ってしまったチャンネルをブランド化していくほうが多いように思いますが、今回の場合は逆に所有者アカウントにチャンネルを移動することでライクがつくほうのアカウントにチャンネルをもっていくことができます。
ただ、制限があります。所有者アカウントはチャンネル名は名前と苗字です。チャンネル名は変わってしまいます。また、コメントは引き継げないようです。

所有者アカウントについていたライクもいったんリセットされるようです。
それでも移動したい気持ちになってしまうのですが、やはりそれは本筋では無い気がします。
所有者アカウントのチャンネルはあくまでも1利用者として振る舞うのが良いのかもしれません。APIは動かしていますが、自分はそうしました。

 

(参考)ミクネットのYoutube関連のシステム構成。CloudFrontでは動画関連コンテンツを配信。

 

(参考)S3からEC2にデータを同期するスクリプト(python)

import subprocess
import os

res = subprocess.check_output(“aws s3 sync s3://kaor-log/clowdfront /home/ec2-user/kaor-log/s3sync”,shell=True) ※S3バケット内のデータをEC2のディレクトリに同期

print(res)

directory = os.listdir(‘s3sync’)
for item in directory:
if item[-3:] !=’.gz’:
continue
cmd2 = ‘gzip -d \”./s3sync/’+item+’\”‘
cmd3 = ‘aws s3 rm s3://kaor-log/clowdfront/’+item
res2=subprocess.check_output(cmd2,shell=True)
res3=subprocess.check_output(cmd3,shell=True)

(参考)SLACKにWEBHOOKするスクリプト(python)

※Slackの以下ページであらかじめ該当チャンネルのWEBHOOKを有効にしておく。

https://slack.com/services/new/incoming-webhook

 

import subprocess
import os

res = subprocess.check_output(“cat ./s3sync/* | grep -c AKANE”,shell=True)
res1 = subprocess.check_output(“cat ./s3sync/* | grep -c seventeen”,shell=True)
res2 = subprocess.check_output(“cat ./s3sync/* | grep -c Solist”,shell=True)

※単純にgrepでキーワードの出現回数を累計しているだけだが、grepの結果が0だとエラーになる。今回はそんなことにはならないので処理していないが、必要があればエラー処理をする。

req1=”curl -X POST –data-urlencode \’payload={\”channel\”: \”#post_from_ec2\”,\”username\”: \”webhookbot\”,\”text\”: \”AKANE:”+res+”\\r\\nseventeen:”+res1+”\\r\\nSolist:”+res2+”\\r\\n\”
, \”icon_emoji\”: \”:ghost:\”} \’ \”https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxx\””

res3 = subprocess.check_output(req1,shell=True) ※req1(slackへの投稿)を実行

以上