hiyamgyyy

学生がやったことのメモとかいろいろ

YouTubeチャンネルの動画を自動で全保存してみよう

先日、僕が応援する某アイドルのシンガーソングライター名義でのYouTubeチャンネルが大人の事情で削除されるかもしれないという情報を聞きつけ、消される前に動画を全保存しなければ。という使命感に駆られました。
また最近では、そのアイドルグループのMVやライブ映像もメジャーデビューに先駆け(?)削除されたり、静岡朝日SunsetTVで配信されている僕が好きなコンテンツの「Aマッソのゲラニチョビ」がプロデューサーの逮捕により全削除されたりと(現在は一部復活)、YouTube上の動画もいつ見れなくなるかわからない状況です。

Clipboxとかの既存ダウンロードツールもありますが、一つ一つやるの億劫なので自動化しようと思い、そのメモです。

こちらを参考にさせていただきました。
PythonでYoutubeの動画をダウンロードするまで(2017/11/30現在)


私的利用のためのダウンロードであり、再配布やアップロードなどの法的悪用をするつもりはありませんのでご留意ください。

YouTubeチャンネルの動画リスト抽出

YouTube Data API (v3)のSearch APIを用いることでキーワード検索やチャンネル検索などいろいろできました。
公式ドキュメントにサンプルコードがあったので、そのまま流用。
https://developers.google.com/youtube/v3/code_samples/python#search_by_keyword

パラメータにChannelIDを指定するだけで、そのチャンネルから投稿された動画をlistで返してくれます。


ChannelIDは
https://www.youtube.com/channel/XXXXXXXXX
のXXX部分に該当します。


また、一回のリクエストで50件しか取得できない問題があるので、それ以上動画投稿してるチャンネルとかだと繰り返し処理する必要があります。
search APIを投げた際に指定した件数(maxResults)以上の件数動画が存在する場合はnextPageTokenというパラメータが帰って来るので、リクエストパラメータのpagetokenに指定して再帰的に処理することで全件取得しました。

この辺はこちらを参考にしました。500件以上だとできない問題あるらしいけど今回僕の対象はそこまで動画が多くないので今は無視。
YouTubeの特定のチャンネルに紐付く動画を取得する。(YouTube Data API (v3) Search)

from apiclient.discovery import build
from oauth2client.tools import argparser

DEVELOPER_KEY = "自分のキー"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
ChannelId = "UC7MyccaiE4MYa9ERzqZYY4Q" #チャンネルID
videos = [] #videoURLを格納する配列

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

  search_response = youtube.search().list(
    # q=options.q,
    part="snippet",
    channelId= ChannelId,
    maxResults=50,
    order="date", #日付順にソート
    pageToken=pagetoken #再帰的に指定
  ).execute()


  for search_result in search_response.get("items", []):
    if search_result["id"]["kind"] == "youtube#video":
      videos.append(search_result["id"]["videoId"])

  try:
      nextPagetoken =  search_response["nextPageToken"] #nextPageTokenが返ってくる限り処理を繰り返す
      youtube_search(nextPagetoken)
  except:
      return

YouTube動画の保存

Pythonにはpytubeというモジュールがあり、これがURLを引数に指定するだけで保存してくれる超優れものだったので使用しました。
ネット環境にも依存しますが、5分の動画を30秒くらいで保存してくれるのでまじ速い。もっと早く使えばよかったと思いました。

from pytube import YouTube

def save_video(search_list):
    for ID in search_list:
        query = 'https://www.youtube.com/watch?v=' + ID
        print(query+"を保存")
        yt = YouTube(query)
        yt.streams.filter(subtype='mp4').first().download("./videos")


これらを実行すると、

https://www.youtube.com/watch?v=PhYT-_l2lMkを保存
https://www.youtube.com/watch?v=Ty0J4YL9BwYを保存
https://www.youtube.com/watch?v=zBrA2nztpu8を保存
・
・
・

みたいに自動でチャンネル内全ての動画がローカルに保存されます。やったね。

最後に

僕は在宅時代、このシンガーソングライターさんのYouTube動画を見て、ライブ反省会と題した動画の中で自らのことをいちいちブスだの可愛いだのと一喜一憂する姿がとても人間味があって面白い人だなと感じ、彼女が所属するアイドル現場に足を運ぶきっかけとなりました。
このご時世YouTubeなどによる影響力はものすごいし、無料コンテンツながらもバタフライエフェクト的に経済を回していると思います。ですので、素晴らしい動画コンテンツが大人の事情で削除されるのは非常に勿体無いなぁと思いつつ、今回自動ダウンロードを試みました。
法律に詳しくないのでYouTubeなどの無料コンテンツは私的利用のためのダウンロードなら可能という認識ですが、間違っていたらご指摘ください。

Twitterのツイート情報から生活リズムを把握しよう

僕が応援するフィロソフィーのダンスというアイドルグループの十束おとはさん(以下、おとはす)が以前、こんなツイートをしていました。


このツイートを見てから、目には目を歯に歯を!的な精神でおとはすの生活リズムをツイート情報から定量的に把握できないかな、と思い生活リズムの把握を試みました。

※興味本位なものであり、決して悪用目的やプライバシーを侵害する事が目的ではありません。


ツイート投稿時間からの把握

まずはツイート投稿時間をプロットして、何時くらいのツイートが多いのか見てみようと思いました。
TwitterAPIでは一回のリクエストで200件のツイートが取得できるので、API制限上で取得可能な上限の3200件を取得するために16回同じ処理を回します。投稿時間はGETメソッドを用いてstatuses/user_timeline.jsonの"created_at"から取得可能です。

APIを使用するにあたりOAuth認証周りがめんどくさいのでRequests-OAuthlib というライブラリを使用しました。以下参考。
Python で Twitter API にアクセス

from requests_oauthlib import OAuth1Session
import json
import config

#対象者のTwitterID
screen_name = "ttk_philosophy"

def get_all_tweets():
        #config.py に取得したキーなどを登録しておく
	CK = config.CONSUMER_KEY
	CS = config.CONSUMER_SECRET
	AT = config.ACCESS_TOKEN
	ATS = config.ACCESS_TOKEN_SECRET

	# OAuth認証
	twitter = OAuth1Session(CK, CS, AT, ATS)
	#timelineのURL
	url = "https://api.twitter.com/1.1/statuses/user_timeline.json"
	
	max_id = None
	timeCounter = [0 for i in range(24)]
	for i in range(16):
		#パラメータ指定
		params = {"count" : 200 ,"screen_name": screen_name,"max_id":max_id}
		req = twitter.get(url, params = params)
		# レスポンスを確認
		if req.status_code == 200:
			tweets = json.loads(req.text)
			max_id = tweets[-1]["id"]-1
			for tweet in tweets:
		                # TimeZoneをAsia/Tokyoにする
				time = tweet["created_at"].split()
				hour = int(time[3][0:2])+9
				hour = hour -24 if hour >= 24 else hour
				timeCounter[hour]+=1
		else:
			print ("Error: %d" % req.status_code)
	return timeCounter


def plot_bar_chart(x,y):
	plt.title(screen_name+type) #タイトル
	plt.bar(x,y,color="gold",alpha=0.7) #カラー指定してbar plot
	plt.xticks(x)  #x軸目盛
	plt.ylim([0,600])  #y軸目盛
	plt.xlabel('Hour')
	plt.ylabel('Tweets')
	plt.savefig(screen_name+".png") #画像として保存


if __name__ == '__main__':
	x_axis = range(24)
	y_axis = get_all_tweet()
        plot_bar_chart(x_axis,y_axis)


max_idを指定することで直前の処理で取得した最後のツイートの一個前のツイートから取得することが可能です。
取得した3200件のツイートを1時間毎のツイート投稿時間を棒グラフにしてみます。可視化ライブラリはmatplotlibを使用しました。


予想

おとはすさんはアイドルの中でも割と早寝早起きタイプだと思われます。以前、他のメンバーから「おとちゃんはゲーマーなのに早寝早起きだからLINE送っても早朝に返信が来る」的なことを聞いたことがあったので、深夜のツイートは少ないと予測しました。

結果

f:id:shin_nandesu:20180612102131p:plain

な、なんと規則正しい生活...!
深夜3時以降のツイートはほとんどなく、8時台から朝のツイートをし始めている!
アイドルでゲーマーという、なんとも不規則そうな生活を送っているのに早寝早起きをしているおとはすはエライ!


ツイート内容からの把握

おとはすさんのツイート内容には、就寝時と起床時の一貫したルールが存在します。

・起床時


・就寝時



このように、朝一番のツイートには「おはす〜」、寝る前のツイートには「おやはす〜」という文字列が混在していることがわかります。
これらは、朝の挨拶の「おはよう」及び就寝時の挨拶の「おやすみ」に愛称である「おとはす」の"はす"の部分を結合したものでありマス!説明不要だと思いますが!



そのため、先ほど取得した3200件のツイートから「おはす」「おやはす」という文字列が混在するツイートをフィルタリングして、起床時間と就寝時間を見てみたいと思います。

ちなみに、午前中にツイートをせず昼を迎えた場合には「昼はす〜」という固有の挨拶も存在しますが、割愛しました。

結果

起床時と就寝時のツイートをプロットし、平均時間を算出しました。

・起床時のツイート時間
f:id:shin_nandesu:20180612230733p:plain

#起床ツイートの平均時間 10:31:47

・就寝時のツイート時間
f:id:shin_nandesu:20180612230737p:plain

#就寝ツイートの平均時間 0:54:17

以上の結果から、おとはすさんは、平均として10時31分47秒くらいに「おはす〜」を投稿し、0時54分17秒あたりに「おやはす〜」を投稿することが分かりました。
素晴らしく規則正しい生活、見習いたいです。

まとめ

興味本位で生活リズムを把握を試みましたが、予想通りと言えば予想通りの結果が現れました。
今回分析の対象とさせてくれた十束おとはさんには大変感謝しています。
ちなみにおとはすさんははてなブロガーなので、こちらもぜひ。
otohatotsuka.hatenablog.com

Webページ更新を自動チェックして通知させよう

以前、フィロソフィーのダンスというアイドルグループのクラウドファンディングに応募する際にモタモタ迷っていたら希望のリターンが枯れたという悲劇がありました。
しかし、コンビニ支払いを指定した人が支払いをしないことでキャンセル分が出現すると予測し、自動チェックプログラムを書くことにしました。

おそらく、この手のサービスやアプリは山ほどありそうだけど、なんとなく自分で作ってみたかったのでそのメモ。

自分が狙っていたリターンは40人限定で数分で完売したものでした。そこから、キャンセルするのは多くて2,3人…と踏んだため、誰よりも早くキャンセル出現に気づく必要があります。
この「キャンセル分早い者勝ち王決定戦」は絶対に負けられない戦いです。名前がダサい。

簡単な流れ

1.ページのtextデータを保存
2.毎分更新して比較
3.内容に変更があったら通知

URLのtextデータを保存

まず、クラウドファンディングのページに行ってhtmlタグを確認します。

f:id:shin_nandesu:20180526185245p:plain

(なんとなくリターンの内容は隠した)

これを観てみるとわかる通り、「OUT OF STOCK」の赤文字の部分が売り切れを示しています。

ちなみに、在庫があるリターンの表示はこちらで、選択ボタンが追加されます。

f:id:shin_nandesu:20180526185527j:plain

つまり、「OUT OF STOCK」という文字列が「残りn人まで」に変更したら通知を促せばよいのです。

そのため、この文字列が該当する部分のclass属性を抽出して、txtファイルに保存しました。


プログラムはPythonにて実装し、使用モジュールは以下の通りです。
・ requests
・ BeautifulSoup

パースの部分はこちらを参考にしました。
https://qiita.com/itkr/items/513318a9b5b92bd56185


import requests
import bs4

def get_website():
        url = 'hoge'
        file = 'hoge.txt'

	res = requests.get(url)
	res.raise_for_status()
	soup = bs4.BeautifulSoup(res.text,'html.parser')# Parser
	elems = soup.select('.limited.rfloat') # class要素の取得
	str_elems = str(elems) # stringに変換
	try:
		f = open(file)
		old_elems  = f.read()
	except:
		old_elems = ' '
	if(str_elems == old_elems):
		return False
	else:
		f = open(file, 'w') # 上書きする
		f.writelines(str_elems)
		f.close()
		return True

if __name__ == "__main__":
	if(get_website()):
		#slackに通知する

思ったよりめちゃくちゃ簡単に書けました。
txtファイルにいちいち書き出さないようにするとか、改善点はありますがとりあえず要件を満たしたものができたので満足。

2.毎分更新して比較

スクリプトを自前サーバに置いて、crontabにて毎分実行させました。

* * * * * python3 check.py


3.内容に変更があったら通知

保存したtxtファイルと、新しく取得したtxtファイルの内容が異なっていたらslack botにて通知を促すことにしました。

Slack通知は以下参考。
PythonでSlackに投稿する

完成

以上をもって、webページの指定部分が更新されたらSlackに通知されるプログラムができました。

f:id:shin_nandesu:20180526173808p:plain

こんな感じで通知が来るし、スマホにもプッシュ通知がきます。嬉しい。

使用感

わりと幅広くclassを指定してしまったせいか、頻繁に通知が来ました。結構うるさい。
と最初は思っていましたが、それも2、3日で慣れてそれよりかちゃんとシステムが動いていてwebページを監視しているのがなんか楽しかった。

結果

コンビニ支払い選択者の支払い期限が3/10 23:59:59だったので次の日あたりに通知が来るかな、と考えていたのですが見事3/11の13:50あたりに通知が来て2枠キャンセル分の余りが出ました。無事買えて、胸をなでおろす。

f:id:shin_nandesu:20180526221237j:plain

なんだろう、パトロンになりましたのパワーワード感。

終わりに

とりあえず要件を満たすものをエイヤと作ってみた結果、キャンセル分のリターンを獲得してパトロンになることができました。
今はこのプログラムを拡張してロシアW杯の準決勝のチケットをキャンセル待ちしています。同じことを考えている人が世界には山ほどいそうなので、毎秒スクリプト回すとかしてなんとかチケットを勝ち取りたいです。

いつ消えるか分からない推しのツイートをスクショしよう

僕が応援する清 竜人25という一夫多妻制アイドルが6/17のラスト♡コンサートを持って解散してしまうわけなんです。

そこで、解散後もTwitterを続けるかどうかが分からない夫人の皆さんのツイートたちを、アカウントが消される前に保存してしまおうという記事です。

アイドルを応援しているとグループが解散したり推しが突然辞めたりと予想しないことがたくさんあると思うので、そんなオタクに読んでほしいです。

ツイートの保存

ツイートを保存するといっても、いろんな形があるわけで。 そしてそれに伴う関連サービスも結構あるわけで。

例えば、コレ。 timg.azurewebsites.net

TwitterのIDを入力すると、そのアカウントの画像が一覧で見れて、しかもzipファイルで一括保存できるっていうめちゃ便利サービスです。

f:id:shin_nandesu:20170610231117p:plain こんな感じで、超いいじゃないっすか。

でも、画像だけじゃな〜〜〜やっぱツイートの内容もほしいな〜〜って思うわけですよ。

そこで、コレ。 TwimeMachine - Read and search your old tweets.

ユーザの今までのツイートをテキストベースで保存できるサービス。

f:id:shin_nandesu:20170610231401p:plain

なんだろう、コレジャナイ感。。。

そこで考えたんですけど、

f:id:shin_nandesu:20170610231608p:plain

ツイートって本文と、画像がセットで見れる感じ。やっぱこれなんですよ。僕らはTwitterアプリのUIに目が慣れきってしまったので、これこそがツイートだと思い込んじゃってるんですよ。

どうしたらこのまま保存できるか...と考え、思いました。




スクショすればいいんじゃね?






スクショしてみた

とりあえずスクショするにしても、一つ一つを手作業でやるおバカさんはいませんよね。

どうやってやろうかな〜と思って調べたらCasperJSで予想以上に簡単にできることが分かったので、チャチャッとやってみました。 CasperJSでスクショができるのは以下のURLを参考に。 qiita.com 技術的なことはあんま深く書かないしコードも公開はしないのでその辺は見逃してください。

TwitterAPIから指定したユーザのツイートのURLを取得し、そのページをひたすらスクショする。

そしてスクショしてみた画面はこちら。

f:id:shin_nandesu:20170610232955p:plain

ウェイ!いい感じ!!これこそがツイート!!

ユーザエージェントをiPadのせいにしたせいで、新しいTwitter for iPadをダウンロードみたいに出るけど、気にしない!!! ブラウザでツイート見るとこういうの出るでしょ?気にしない!!

他のツイートも見てみよう!!!

f:id:shin_nandesu:20170610233425p:plain

いやヲタクのリプめっちゃ見えるやん!!!!そしてリプだらけのせいで画像めっちゃ縦長やん!!!!!!

どうやら、ツイートのURLをブラウザで表示されると、ツイートとそれに対するリプライまで見えちゃう仕様みたいです。 そうよね、そりゃそうだよね。

いろいろ試行錯誤した結果、トリミングするのが一番手っ取り早い気がするのでヲタクのリプもそのまま保存しちゃおうってことにしました。

それも一つの思い出だしね。

あと、欲を言えば昔のツイートは当時のアイコンで保存したかったかな。欲を言えばだけど。

という感じで、自動でスクショを保存するシステムが完成しました。

思い出の残し方

このスクリプトを書こうと思った時、乃木坂ファンの研究室の先輩から「俺は橋本奈々未の卒業の時にブログを保存しようとしたけど、残さないからこそ良いものなんじゃないかって思ったから、何もしなかった」と言われました。 確かにいい意味で思い出補正ってのはあると思うし、記録じゃなくて記憶に残すからいいのかもしれません。 桜も散り際が美しいし、散った桜の花びらをジッパーに入れて冷凍保存とかしないもんね。



でも、僕は思い出を残すことにしました。それは、今の自分のためにじゃなくて、5年後10年後の自分のために。 清 竜人25は紛れもなく僕の青春であったし、こんなに好きになったグループはもう二度とないんじゃないかって思うし、ほぼデビュー当初から彼女たちのツイートを全て見てきたつもりなので、一つ一つの花びらに思い出があるんですよ。 そんな花びらたちを将来の自分が見て、あの時はこんなだったな〜って思えたらいいですね。

さいごに

今現在、全てのツイートをスクショするためにプログラム君が絶賛フルマラソン中です。

スクショ画像が欲しい方にはzipファイルとかであげようと思うので、後日個別に連絡してください〜。

追記

Twitterの仕様上、statuses/user_timelineというユーザのツイートを取得するAPIは3200件しかツイートを取得できないみたいです。 TwitterAPIを使ったのが3年くらい前だったので忘れてた...