【Python】YoutubeのチャンネルIDから再生数等の情報を取り出してExcelに書き出す

## これは何
前回の修正版です。
前回→ https://qiita.com/shobota/items/eef7143b574397c8d295
再生リストからだと使い勝手が悪かったので、対象チャンネルの動画データを全部取ってくる方向にしました。

## 概要
実行すると標準入力でチャンネルIDを聞かれるので、入力してEnterキーを押すとファイルに出力されます。
動画の概要欄をそのままExcelに書き出すと、XML Parse Errorが出ることがあったので当該文字を除去するなどで対応しています。

「自分で書くの面倒！」という人はAPIキーを取得したら下記を実行するだけで完了です。
社内のレポート用とかに使うと便利じゃないでしょうか。

```youtube_bychannel.py
###############################################################################
# チャンネルIDから再生数、コメント数、高評価、低評価を入力する
# ファイルは時刻をくっつけて保存される
###############################################################################

from googleapiclient.discovery import build
import json
import openpyxl
import re
import datetime
from datetime import date
from openpyxl.styles import Font
import sys
import traceback
import os

# YoutubeAPI用キー
# 各自デベロッパーコンソールから取得しておくこと
API_KEY = ''



#================================================================================
# 初期化
#================================================================================
def initYoutube(API_KEY):
    API_SERVICE_NAME = "youtube"
    API_VERSION = "v3"
    return build(API_SERVICE_NAME, API_VERSION, developerKey=API_KEY)



#================================================================================
# プレイリストIDを渡して動画IDリストを得る
#================================================================================
def getIdListFromPlaylist(id_,youtube):

    nextPageToken = 'start'
    response = []

    while(nextPageToken is not None):

        if(nextPageToken == 'start'):
            search_response = youtube.playlistItems().list(
            part= 'snippet',
            playlistId=id_,
            maxResults = 50,
            ).execute()
        else:
            search_response = youtube.playlistItems().list(
            part= 'snippet',
            playlistId=id_,
            maxResults = 50,
            pageToken = nextPageToken
            ).execute()

        if('nextPageToken' in search_response):
            nextPageToken = search_response['nextPageToken']
        else:
            nextPageToken = None
        
        for item in search_response['items']:
            response.append(item['snippet']['resourceId']['videoId'])

    print('動画数:{:,}'.format(len(response)))
    response.reverse()   
    return response



#================================================================================
# YoutubeのAPIを叩いて統計情報を取得する
#================================================================================
def getCountDetails(id_, youtube):

    #YoutubeDataAPIは1リクエストあたり50件しか処理できないので、50件ずつに分割
    idLists = split_list(id_,50)
    response = []

    for idList in idLists:
        search_response = youtube.videos().list(
        part= 'statistics,snippet',
        id=idList,
        ).execute()

        #配列に入れ込んでいく
        response.extend(search_response['items'])

    return response



#================================================================================
# 指定ワークシートからIDリストを取得して数値を更新する
#================================================================================
def setCountDetail(ws,idList,youtube):

    result = getCountDetails(idList,youtube)


    row = 1
    ws.cell(row,1).value = '公開日'
    ws.cell(row,2).value = 'タイトル'
    ws.cell(row,3).value = 'URL'     
    ws.cell(row,4).value = '再生数'
    ws.cell(row,5).value = 'コメント数'
    ws.cell(row,6).value = '高評価'
    ws.cell(row,7).value = '低評価'
    ws.cell(row,8).value = '概要'

    row += 1

    for item in result:

        published = datetime.datetime.fromisoformat(item['snippet']['publishedAt'].replace('Z', '+00:00')).strftime('%Y/%m/%d')
        ws.cell(row,1).value = published

        #先頭が=だと数式と見なされるのでその場合は半角スペースを入れる
        if(item['snippet']['title'].startswith('=')):
            ws.cell(row,2).value = ' ' + item['snippet']['title']
        else:
            ws.cell(row,2).value = item['snippet']['title']

        #Excelで保存するときパースエラーになる文字列を除外する(EFBFB0-EFBFBF)
        #ここはもうちょっとやり方あるかも
        item['snippet']['description'] = re.sub(b'\xef\xbf[\xb0-\xbf]',b'',item['snippet']['description'].encode('UTF-8')).decode('UTF-8')

        #先頭が=だと数式と見なされるのでその場合は半角スペースを入れる
        if(item['snippet']['description'].startswith('=')):
            ws.cell(row,8).value = ' ' + item['snippet']['description']
        else:
            ws.cell(row,8).value = item['snippet']['description']

        #IDはURLに
        ws.cell(row,3).value = 'https://www.youtube.com/watch?v='+item['id']

        #数値を入れていく
        ws.cell(row,4).value = int(item['statistics']['viewCount']) if 'viewCount' in item['statistics'] else -1
        ws.cell(row,5).value = int(item['statistics']['commentCount'])  if 'commentCount' in item['statistics'] else -1
        ws.cell(row,6).value = int(item['statistics']['likeCount'])  if 'likeCount' in item['statistics'] else -1
        ws.cell(row,7).value = int(item['statistics']['dislikeCount']) if 'dislikeCount' in item['statistics'] else -1


        #ws.cell(row,3).hyperlink = ws.cell(row,3).value
        ws.cell(row,4).number_format = '#,##0'
        ws.cell(row,5).number_format = '#,##0'
        ws.cell(row,6).number_format = '#,##0'
        ws.cell(row,7).number_format = '#,##0'


        row += 1

    

#================================================================================
# ワークシート内のフォントを設定する
#================================================================================
def setFontOfWorkSheet(ws,fontName):

    font = Font(name=fontName)

    for row in ws:
        for cell in row:
            ws[cell.coordinate].font = font



#================================================================================
# 指定チャンネルの動画リストを取得
#================================================================================
def getPlaylistFromChannel(id_,youtube):
    search_response = youtube.channels().list(
    part= 'snippet,contentDetails',
    id=id_,
    ).execute()

    print('チャンネル名:'+search_response['items'][0]['snippet']['title'])

    response = {
    'title':search_response['items'][0]['snippet']['title'],
    'id':search_response['items'][0]['contentDetails']['relatedPlaylists']['uploads']
    }

    return response



#================================================================================
# 配列を指定した個数ごとに分割
#================================================================================
def split_list(l, n):
    for idx in range(0, len(l), n):
        yield l[idx:idx + n]



#================================================================================
# メイン処理
#================================================================================
def main():

    youtube = initYoutube(API_KEY)

    #取得したいYoutubeチャンネルのIDを標準入力からとってくる
    channelID = input('取得したいYoutubeチャンネルのIDを入力してください:')

    try:
        channelPlayList = getPlaylistFromChannel(channelID,youtube) 
    except:
        #ID間違えたりしてた場合は終了
        print('指定したチャンネルのリスト取得に失敗しました。')
        exit()

    try:
        # Excelファイル新規作成
        wb = openpyxl.Workbook()

        #データを取得してワークシートに書き込む
        wb.create_sheet(channelPlayList['id'],0)
        setCountDetail(wb[channelPlayList['id']],getIdListFromPlaylist(channelPlayList['id'],youtube),youtube)

        #フォントをMeiryo UIに
        setFontOfWorkSheet(wb[channelPlayList['id']],'Meiryo UI')

        #タイムスタンプ付けて保存
        dt_now = datetime.datetime.now()
        filename = 'Youtube_'+channelPlayList['id']+'_'+dt_now.strftime('_%Y%m%d_%H%M')+'.xlsx'
        wb.save(filename)
        print(os.getcwd() + '\\' + filename+'に保存しました。')    

    except:
        print('データ取得時にエラーが発生しました。ファイルを保存することができませんでした。')
        traceback.print_exc()



main()





```