-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfb_ads_library_api.py
More file actions
108 lines (89 loc) · 3.5 KB
/
Copy pathfb_ads_library_api.py
File metadata and controls
108 lines (89 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
import json
import math
import re
import time
import urllib
from datetime import datetime
from random import randrange
import requests
def get_ad_archive_id(data):
"""
Extract ad_archive_id from ad_snapshot_url
"""
return re.search(r"/\?id=([0-9]+)", data["ad_snapshot_url"]).group(1)
class FbAdsLibraryTraversal:
def __init__(
self,
access_token,
fields,
search_parameters,
page_limit=2000,
retry_limit=3,
rate_limit=200,
):
self.page_count = 0
self.access_token = access_token
self.fields = fields
self.parameters = search_parameters
self.page_limit = page_limit
self.retry_limit = retry_limit
self.rate_limit = rate_limit
def generate_ad_archives(self):
default_api_version = "v12.0"
next_page_url = f"https://graph.facebook.com/{default_api_version}/ads_archive?access_token={self.access_token}&fields={self.fields}&limit={self.page_limit}"
for param_name in self.parameters:
param_value = self.parameters[param_name]
next_page_url += f"&{param_name}={param_value}"
return self._get_ad_archives_from_url(
next_page_url=next_page_url, retry_limit=self.retry_limit, rate_limit=self.rate_limit
)
def _get_ad_archives_from_url(self,
next_page_url, retry_limit=3, rate_limit=200
):
last_error_url = None
last_retry_count = 0
sleep_time = math.ceil((60*60) / rate_limit)
while next_page_url is not None:
response = requests.get(next_page_url)
response_data = json.loads(response.text)
if "error" in response_data:
if response_data["error"]["type"] == "OAuthException" and response_data["error"]["code"] == 190:
new_access_token = input("Token has expired, generate a new one and copy it in here:")
next_page_url = next_page_url.replace(self.access_token, new_access_token)
self.access_token = new_access_token
if next_page_url == last_error_url:
# failed again
if last_retry_count >= retry_limit:
raise Exception(
"Error message: [{}], failed on URL: [{}]".format(
json.dumps(response_data["error"]), next_page_url
)
)
else:
last_error_url = next_page_url
last_retry_count = 0
last_retry_count += 1
time.sleep(sleep_time)
continue
data = list(response_data["data"])
if len(data) == 0:
# there is no data
next_page_url = None
break
yield data
time.sleep(sleep_time)
if "paging" in response_data:
next_page_url = response_data["paging"]["next"]
else:
next_page_url = None
def generate_ad_archives_from_url(self, failure_url):
"""
if we failed from error, later we can just continue from the last failure url
"""
return self._get_ad_archives_from_url(failure_url)