|
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