|
1 | 1 | #!/usr/bin/python |
2 | 2 |
|
3 | | -from datetime import datetime, timedelta |
4 | | -import httplib2 |
| 3 | +### |
| 4 | +# |
| 5 | +# Retrieves YouTube Analytics report data. The script's default behavior |
| 6 | +# is to retrieve data for the authenticated user's channel. However, if you |
| 7 | +# set a value for the --content-owner command-line argument, then the script |
| 8 | +# retrieves data for that content owner. Note that the user running the script |
| 9 | +# must be authorized to retrieve data for that content owner. |
| 10 | +# |
| 11 | +# Note that when you retrieve Analytics data for a content owner, your API |
| 12 | +# request must set a value for the "filters" request parameter as explained |
| 13 | +# in the API documentation here: |
| 14 | +# https://developers.google.com/youtube/analytics/v1/content_owner_reports#Filters |
| 15 | +# |
| 16 | +# By default, if you set a value for the --content-owner argument, then the |
| 17 | +# "filters" parameter is set to "claimedStatus==claimed". (On the other hand, |
| 18 | +# this value is not set if you are retrieving data for your own channel.) |
| 19 | +# |
| 20 | +# You can use the --filters command-line argument to set the "filters" parameter |
| 21 | +# for any request. For example: |
| 22 | +# * --filters="channel==CHANNEL_ID" |
| 23 | +# * --filters="channel==CHANNEL_ID;country==COUNTRY_CODE" |
| 24 | +# * --filters="video==VIDEO_ID" |
| 25 | +# " --filters="claimedStatus==claimed;uploaderType==thirdParty" |
| 26 | +# and so forth. |
| 27 | +# |
| 28 | +### |
| 29 | + |
| 30 | +import argparse |
5 | 31 | import os |
6 | | -import sys |
7 | 32 |
|
8 | | -from apiclient.discovery import build |
9 | | -from apiclient.errors import HttpError |
10 | | -from oauth2client.client import flow_from_clientsecrets |
11 | | -from oauth2client.file import Storage |
12 | | -from oauth2client.tools import argparser, run_flow |
| 33 | +import google.oauth2.credentials |
| 34 | +import google_auth_oauthlib.flow |
| 35 | +from googleapiclient.discovery import build |
| 36 | +from googleapiclient.errors import HttpError |
| 37 | +from google_auth_oauthlib.flow import InstalledAppFlow |
| 38 | +from datetime import datetime, timedelta |
13 | 39 |
|
14 | 40 |
|
15 | 41 | # The CLIENT_SECRETS_FILE variable specifies the name of a file that contains |
16 | 42 | # the OAuth 2.0 information for this application, including its client_id and |
17 | 43 | # client_secret. You can acquire an OAuth 2.0 client ID and client secret from |
18 | 44 | # the {{ Google Cloud Console }} at |
19 | 45 | # {{ https://cloud.google.com/console }}. |
20 | | -# Please ensure that you have enabled the YouTube Data and YouTube Analytics |
21 | | -# APIs for your project. |
22 | | -# For more information about using OAuth2 to access the YouTube Data API, see: |
23 | | -# https://developers.google.com/youtube/v3/guides/authentication |
24 | | -# For more information about the client_secrets.json file format, see: |
| 46 | +# Please ensure that you have enabled the YouTube Analytics API for your project. |
| 47 | +# For more information about using OAuth2 to access the YouTube Analytics API, see: |
| 48 | +# https://developers.google.com/youtube/reporting/guides/authorization |
| 49 | +# For more information about the client_secret.json file format, see: |
25 | 50 | # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets |
26 | | -CLIENT_SECRETS_FILE = "client_secrets.json" |
| 51 | +CLIENT_SECRETS_FILE = 'client_secret.json' |
27 | 52 |
|
28 | 53 | # These OAuth 2.0 access scopes allow for read-only access to the authenticated |
29 | 54 | # user's account for both YouTube Data API resources and YouTube Analytics Data. |
30 | | -YOUTUBE_SCOPES = ["https://www.googleapis.com/auth/youtube.readonly", |
31 | | - "https://www.googleapis.com/auth/yt-analytics.readonly"] |
32 | | -YOUTUBE_API_SERVICE_NAME = "youtube" |
33 | | -YOUTUBE_API_VERSION = "v3" |
34 | | -YOUTUBE_ANALYTICS_API_SERVICE_NAME = "youtubeAnalytics" |
35 | | -YOUTUBE_ANALYTICS_API_VERSION = "v1" |
36 | | - |
37 | | -# This variable defines a message to display if the CLIENT_SECRETS_FILE is |
38 | | -# missing. |
39 | | -MISSING_CLIENT_SECRETS_MESSAGE = """ |
40 | | -WARNING: Please configure OAuth 2.0 |
41 | | -
|
42 | | -To make this sample run you will need to populate the client_secrets.json file |
43 | | -found at: |
44 | | -
|
45 | | - %s |
46 | | -
|
47 | | -with information from the {{ Cloud Console }} |
48 | | -{{ https://cloud.google.com/console }} |
49 | | -
|
50 | | -For more information about the client_secrets.json file format, please visit: |
51 | | -https://developers.google.com/api-client-library/python/guide/aaa_client_secrets |
52 | | -""" % os.path.abspath(os.path.join(os.path.dirname(__file__), |
53 | | - CLIENT_SECRETS_FILE)) |
54 | | - |
55 | | - |
56 | | -def get_authenticated_services(args): |
57 | | - flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, |
58 | | - scope=" ".join(YOUTUBE_SCOPES), |
59 | | - message=MISSING_CLIENT_SECRETS_MESSAGE) |
60 | | - |
61 | | - storage = Storage("%s-oauth2.json" % sys.argv[0]) |
62 | | - credentials = storage.get() |
63 | | - |
64 | | - if credentials is None or credentials.invalid: |
65 | | - credentials = run_flow(flow, storage, args) |
66 | | - |
67 | | - http = credentials.authorize(httplib2.Http()) |
68 | | - |
69 | | - youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, |
70 | | - http=http) |
71 | | - youtube_analytics = build(YOUTUBE_ANALYTICS_API_SERVICE_NAME, |
72 | | - YOUTUBE_ANALYTICS_API_VERSION, http=http) |
73 | | - |
74 | | - return (youtube, youtube_analytics) |
75 | | - |
76 | | -def get_channel_id(youtube): |
77 | | - channels_list_response = youtube.channels().list( |
78 | | - mine=True, |
79 | | - part="id" |
80 | | - ).execute() |
81 | | - |
82 | | - return channels_list_response["items"][0]["id"] |
83 | | - |
84 | | -def run_analytics_report(youtube_analytics, channel_id, options): |
85 | | - # Call the Analytics API to retrieve a report. For a list of available |
86 | | - # reports, see: |
| 55 | +SCOPES = ['https://www.googleapis.com/auth/yt-analytics.readonly'] |
| 56 | +API_SERVICE_NAME = 'youtubeAnalytics' |
| 57 | +API_VERSION = 'v1' |
| 58 | + |
| 59 | +# Authorize the request and store authorization credentials. |
| 60 | +def get_authenticated_service(): |
| 61 | + flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES) |
| 62 | + credentials = flow.run_console() |
| 63 | + |
| 64 | + api_service = build(API_SERVICE_NAME, API_VERSION, credentials=credentials) |
| 65 | + return api_service |
| 66 | + |
| 67 | +# Remove keyword arguments that are not set. |
| 68 | +def remove_empty_args(args): |
| 69 | + original_args = vars(args) |
| 70 | + good_args = {} |
| 71 | + if original_args is not None: |
| 72 | + for key, value in original_args.iteritems(): |
| 73 | + # The channel_id and content_owner arguments are provided as a means |
| 74 | + # of properly setting the "ids" parameter value. However, they should |
| 75 | + # not be included in the API request (since they're not parameters |
| 76 | + # supported by this method). |
| 77 | + if value and key != 'channel_id' and key != 'content_owner': |
| 78 | + good_args[key] = value |
| 79 | + return good_args |
| 80 | + |
| 81 | +# Set the "ids" request parameter for the YouTube Analytics API request. |
| 82 | +def set_ids_parameter(args): |
| 83 | + if args.content_owner: |
| 84 | + args.ids = 'contentOwner==' + args.content_owner |
| 85 | + if args.filters == '': |
| 86 | + args.filters = 'claimedStatus==claimed' |
| 87 | + elif args.channel_id: |
| 88 | + args.ids = 'channel==' + args.channel_id |
| 89 | + else: |
| 90 | + args.ids = 'channel==MINE' |
| 91 | + args = remove_empty_args(args) |
| 92 | + print args |
| 93 | + return args |
| 94 | + |
| 95 | +def run_analytics_report(youtube_analytics, args): |
| 96 | + # Call the Analytics API to retrieve a report. Pass args in as keyword |
| 97 | + # arguments to set values for the following parameters: |
| 98 | + # |
| 99 | + # * ids |
| 100 | + # * metrics |
| 101 | + # * dimensions |
| 102 | + # * filters |
| 103 | + # * start_date |
| 104 | + # * end_date |
| 105 | + # * sort |
| 106 | + # * max_results |
| 107 | + # |
| 108 | + # For a list of available reports, see: |
87 | 109 | # https://developers.google.com/youtube/analytics/v1/channel_reports |
| 110 | + # https://developers.google.com/youtube/analytics/v1/content_owner_reports |
88 | 111 | analytics_query_response = youtube_analytics.reports().query( |
89 | | - ids="channel==%s" % channel_id, |
90 | | - metrics=options.metrics, |
91 | | - dimensions=options.dimensions, |
92 | | - start_date=options.start_date, |
93 | | - end_date=options.end_date, |
94 | | - max_results=options.max_results, |
95 | | - sort=options.sort |
| 112 | + **args |
96 | 113 | ).execute() |
97 | 114 |
|
98 | | - print "Analytics Data for Channel %s" % channel_id |
| 115 | + #print 'Analytics Data for Channel %s' % channel_id |
99 | 116 |
|
100 | | - for column_header in analytics_query_response.get("columnHeaders", []): |
101 | | - print "%-20s" % column_header["name"], |
| 117 | + for column_header in analytics_query_response.get('columnHeaders', []): |
| 118 | + print '%-20s' % column_header['name'], |
102 | 119 | print |
103 | 120 |
|
104 | | - for row in analytics_query_response.get("rows", []): |
| 121 | + for row in analytics_query_response.get('rows', []): |
105 | 122 | for value in row: |
106 | | - print "%-20s" % value, |
| 123 | + print '%-20s' % value, |
107 | 124 | print |
108 | 125 |
|
109 | | -if __name__ == "__main__": |
| 126 | +if __name__ == '__main__': |
110 | 127 | now = datetime.now() |
111 | | - one_day_ago = (now - timedelta(days=1)).strftime("%Y-%m-%d") |
112 | | - one_week_ago = (now - timedelta(days=7)).strftime("%Y-%m-%d") |
113 | | - |
114 | | - argparser.add_argument("--metrics", help="Report metrics", |
115 | | - default="views,comments,favoritesAdded,favoritesRemoved,likes,dislikes,shares") |
116 | | - argparser.add_argument("--dimensions", help="Report dimensions", |
117 | | - default="video") |
118 | | - argparser.add_argument("--start-date", default=one_week_ago, |
119 | | - help="Start date, in YYYY-MM-DD format") |
120 | | - argparser.add_argument("--end-date", default=one_day_ago, |
121 | | - help="End date, in YYYY-MM-DD format") |
122 | | - argparser.add_argument("--max-results", help="Max results", default=10) |
123 | | - argparser.add_argument("--sort", help="Sort order", default="-views") |
124 | | - args = argparser.parse_args() |
125 | | - |
126 | | - (youtube, youtube_analytics) = get_authenticated_services(args) |
| 128 | + one_day_ago = (now - timedelta(days=1)).strftime('%Y-%m-%d') |
| 129 | + one_week_ago = (now - timedelta(days=7)).strftime('%Y-%m-%d') |
| 130 | + |
| 131 | + parser = argparse.ArgumentParser() |
| 132 | + |
| 133 | + # Set channel ID or content owner. Default is authenticated user's channel. |
| 134 | + parser.add_argument('--channel-id', default='', |
| 135 | + help='YouTube channel ID for which data should be retrieved. ' + |
| 136 | + 'Note that the default behavior is to retrieve data for ' + |
| 137 | + 'the authenticated user\'s channel.') |
| 138 | + parser.add_argument('--content-owner', default='', |
| 139 | + help='The name of the content owner for which data should be ' + |
| 140 | + 'retrieved. If you retrieve data for a content owner, then ' + |
| 141 | + 'your API request must also set a value for the "filters" ' + |
| 142 | + 'parameter. See the help for that parameter for more details.') |
| 143 | + |
| 144 | + # Metrics, dimensions, filters |
| 145 | + parser.add_argument('--metrics', help='Report metrics', |
| 146 | + default='views,estimatedMinutesWatched,averageViewDuration') |
| 147 | + parser.add_argument('--dimensions', help='Report dimensions', default='') |
| 148 | + parser.add_argument('--filters', default='', |
| 149 | + help='Filters for the report. Note that the filters request parameter ' + |
| 150 | + 'must be set in YouTube Analytics API requests for content owner ' + |
| 151 | + 'reports. The script sets "filters=claimedStatus==claimed" if a ' + |
| 152 | + 'content owner is specified and filters are not specified.') |
| 153 | + |
| 154 | + # Report dates. Defaults to start 7 days ago, end yesterday. |
| 155 | + parser.add_argument('--start-date', default=one_week_ago, |
| 156 | + help='Start date, in YYYY-MM-DD format') |
| 157 | + parser.add_argument('--end-date', default=one_day_ago, |
| 158 | + help='End date, in YYYY-MM-DD format') |
| 159 | + |
| 160 | + parser.add_argument('--max-results', help='Max results') |
| 161 | + parser.add_argument('--sort', help='Sort order', default='') |
| 162 | + |
| 163 | + args = parser.parse_args() |
| 164 | + args = set_ids_parameter(args) |
| 165 | + |
| 166 | + youtube_analytics = get_authenticated_service() |
127 | 167 | try: |
128 | | - channel_id = get_channel_id(youtube) |
129 | | - run_analytics_report(youtube_analytics, channel_id, args) |
| 168 | + run_analytics_report(youtube_analytics, args) |
130 | 169 | except HttpError, e: |
131 | | - print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content) |
| 170 | + print 'An HTTP error %d occurred:\n%s' % (e.resp.status, e.content) |
0 commit comments