-
Notifications
You must be signed in to change notification settings - Fork 1
/
search_facebook.py
153 lines (117 loc) · 6.19 KB
/
search_facebook.py
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import pandas as pd
import os
import requests
from datetime import datetime
import facebook
# TODO: Facebook API Python wrapper/package is outdated, only supports Facebook API version 3.1 (Facebook API version up to 7.0 now)
# Facebook API Python wrapper/package latest release: 11/6/2018
# https://facebook-sdk.readthedocs.io/en/latest/changes.html
# https://developers.facebook.com/docs/graph-api/changelog#versions
def get_long_lived_user_access_token(creds, fb_graph_api_version='v7.0'):
"""
https://developers.facebook.com/docs/facebook-login/access-tokens/refreshing
:param creds:
:param fb_graph_api_version:
:return:
"""
params = (
('grant_type', 'fb_exchange_token'),
('client_id', creds['fb-app_id']),
('client_secret', creds['fb-app-secret']),
('fb_exchange_token', creds['fb-user-access-token']),
)
response = requests.get(f'https://graph.facebook.com/{fb_graph_api_version}/oauth/access_token', params=params)
return response
def init_fb_graph_object(fb_access_token, api_version='3.1'):
try:
graph = facebook.GraphAPI(access_token=fb_access_token, version=api_version)
except: # TODO: Better error handling (which exceptions to anticipate?)
graph = None
print('Error: Unable to initialize Facebook Graph API access.')
return graph
def business_search_facebook(fb_graph, location_query=None, location_center=None, location_distance=None):
"""
EITHER (location_center, location_distance) OR q are required as arguments.
:param fb_graph: (obj) Facebook graph object initialized in init_fb_graph_object().
:param location_query: (str) Query to search for location (ex: "san francisco, ca").
:param location_center: (str) Coordinates in the form "latitude,longitude".
:param location_distance: (int) Distance from coordinates provided, in meters (max value = ?). # TODO: Assume there's a max value?
:return: places: (list) FB Graph search results -- each record is a dict containing the fields ('name', 'id',
'location' by default) as keys, and search results as values
"""
if location_center and location_distance:
try:
search_results = fb_graph.search(type='place',
center=location_center,
distance=location_distance,
fields='name,id,location' # using these basic fields as default;
# grab more details in business_details_facebook()
)
except: # TODO: Better error handling (which exceptions to anticipate?) Also, logging?
search_results = None
print('Error: Facebook Graph API search invalid (coordinates and distance provided).')
elif location_query:
try:
search_results = fb_graph.search(type='place',
fields='name,id,location',
q=location_query
)
except: # TODO: Better error handling (which exceptions to anticipate?) Also, logging?
search_results = None
print('Error: Facebook Graph API search invalid (location query provided).')
else:
search_results = None
print('Error: Neither (coordinates, distance) or location query were provided.')
places = search_results['data']
return places
def business_details_facebook(fb_graph, facebook_id, detail_fields):
"""
:param fb_graph: (obj) Facebook graph object initialized in init_fb_graph_object().
:param facebook_id: (str) Unique ID identifying a business found via FB Graph search.
:param detail_fields: (str) Which of the available fields to include in results.
Must be formatted exactly like: 'field1,field2,field3,...'
:return: info: (dict) Dict containing details for the business associated with the business ID passed.
"""
try:
place_details = fb_graph.get_object(id=facebook_id, fields=detail_fields)
except: # TODO: Better error handling (which exceptions to anticipate?) Also, logging?
place_details = None
print("Error: Invalid 'get_object' API call.")
full_keys = detail_fields.split(',')
# If a record has no data for a particular field, the API call will simply return the record with neither
# the value NOR the key. So, populate missing keys as None/null.
missing_fields = full_keys - place_details.keys()
for m in missing_fields:
place_details[m] = None # TODO: Or null/NA ?
try:
zip_code = place_details['location']['zip']
except:
zip_code = 'No zip code found'
place_details['zip_code'] = zip_code
return place_details
def all_business_details_facebook(fb_graph, places, detail_fields):
details_list = []
for p in places:
details = business_details_facebook(fb_graph=fb_graph, facebook_id=p['id'], detail_fields=detail_fields)
details_list.append(details)
all_business_details_df = pd.DataFrame(details_list)
all_business_details_df['api_call_datetime'] = datetime.now()
return all_business_details_df
def main():
fb_token = os.environ['FB_ACCESS_TOKEN'] # this access token will expire on June 29, 2020
print('Initializing FB Graph object...')
graph_obj = init_fb_graph_object(fb_token)
print('Searching FB API...')
palm_springs_places = business_search_facebook(graph_obj, location_query='palm springs, ca')
# TODO: Add location_query as field
detail_fields_1 = "id,name,description,category_list,checkins,location,temporary_status," \
"differently_open_offerings,hours," \
"is_always_open,is_permanently_closed," \
"is_verified,parking,payment_options," \
"restaurant_services"
print('Getting details on each business...')
palm_springs_details = all_business_details_facebook(graph_obj, palm_springs_places, detail_fields_1)
# print(palm_springs_details[['name', 'is_always_open']])
return palm_springs_details
if __name__ == '__main__':
palm_springs_details = main()