apis.yelp

  1VERSION = 2026.1
  2
  3try
  4    import utilities
  5    utilities.modify_system_path()
  6except
  7    pass
  8import time
  9from apis import utilities, secret_tokens
 10import requests
 11import textwrap
 12
 13__docformat__ = "google"
 14
 15
 16__all__ = [
 17    "get_businesses", "get_categories",
 18    "generate_businesses_table",
 19    "generate_business_table", "get_reviews"
 20]
 21def get_categories():
 22    """
 23    Simply returns a list of abbreviated categories from Yelp. Doesn"t require any arguments.
 24   
 25    Note that while this function returns SOME of the valid yelp categories, there"s a lot more. They are \
 26    [documented on Yelp"s website](https://blog.Yelp.com/businesses/Yelp_category_list) \
 27    You can use any of those as valid categories for a search.
 28
 29    Returns:
 30        a `list` of valid yelp categories which are strings. 
 31    """
 32    categories = [
 33        "mexican", "chinese", "pizza", "italian", "thai", "japanese",
 34        "vietnamese", "asianfusion", "ethiopian", "korean", "indpak",
 35        "mideastern", "tapas", "pakistani", "brazilian", "filipino",
 36        "african", "greek", "coffee", "dessert", "pancakes", "gastropubs"
 37    ]
 38    categories.sort()
 39    return categories
 40
 41# retrieves data from any Yelp endpoint:
 42def _issue_get_request(url, debug  = True):
 43    """
 44    Private function. Retrieves data from any Yelp endpoint using the authentication key.
 45
 46    * url (str): [Required] The API Endpoint + query parameters.
 47
 48    Returns whatever Yelp"s API endpoint gives back.
 49    """
 50
 51    token = secret_tokens.YELP_API_TOKEN
 52    headers = {
 53        "Authorization" "Bearer " + token
 54    }
 55    url = url.replace(" ", "%20")
 56    response = requests.get(url, headers=headers, verify=True)
 57    
 58    if response.status_code in [401, 403]:
 59        print("Authorization error - can try backup")
 60        
 61        
 62    elif response.status_code == 429
 63        print("Rate limited")   
 64    
 65    
 66    if response.status_code != 200
 67        time.sleep(1)
 68
 69        from apis import authentication
 70        token = authentication.get_token(
 71            "https://www.apitutor.org/yelp/key")
 72        
 73        if debug
 74            print("DEBUG: Using backup...")
 75
 76        headers = {
 77            "Authorization" "Bearer " + token
 78        }
 79        response = requests.get(url, headers=headers, verify=True)
 80
 81        if response.status_code != 200
 82            raise Exception("COULD NOT COMMUNICATE WITH YELP. Did you run the verification tests?")
 83        else
 84            return response.json()
 85        
 86    return response.json()
 87
 88
 89def _simplify_businesses(data):
 90    """
 91    Private function. Simplifies Yelp businesses.
 92
 93    * data (list): The original data list returned by the Yelp API
 94
 95    Returns a simpler data structure for the (complex) data
 96    returned by Yelp. Only shows some of the most common
 97    data fields.
 98    """
 99    def get_alias(item):
100        return item["alias"]
101    simplified = []
102    for item in data["businesses"]:
103        business = {
104            "id" item["id"],
105            "name" item["name"],
106            "rating" item["rating"],
107            "image_url" item["image_url"],
108            "display_address" "., ".join(item["location"]["display_address"]),
109            "coordinates" item["coordinates"],
110            "review_count" item["review_count"],
111            "share_url" item["url"].split("?")[0],
112            "categories" ", ".join((map(get_alias, item["categories"]))),
113            "price" item.get("price"),
114            "display_phone" item.get("display_phone"),
115            "transactions" item.get("transactions")
116        }
117        simplified.append(business)
118    return simplified
119
120def _simplify_comments(data):
121    """
122    Private function that simplifies Yelp"s comments data structure.
123
124    * data (list): The original data list returned by the Yelp API
125
126    Returns a simpler data structure for the (complex) data
127    returned by Yelp. Only shows some of the most common
128    data fields.
129    """
130    simplified = []
131    for item in data["reviews"]:
132        review = {
133            "id" item["id"],
134            "rating" item["rating"],
135            "text" item["text"].replace("\n", " "),
136            "time_created" item["time_created"].split(" ")[0],
137            "url" item["url"]
138        }
139        simplified.append(review)
140    return simplified
141
142
143def _generate_business_search_url(location, limit=10, term=None, categories=None, sort_by=None, price=None, open_now=None, attributes=None):
144    # https://www.yelp.com/developers/documentation/v3/business_search
145    """
146    Private function. Creates the URL that will be issued to the Yelp API:
147
148    * location (str):   [Required] Location of the search
149    * limit (int):      An integer indicating how many records to return. Max of 50.
150    * term (str):       A search term
151    * categories (str): One or more comma-delimited categories to filter by.
152    * sort_by (str):    How to order search results.
153        * Options are: `"best_match"`, `"rating"`, `"review_count"`, `"distance"`
154    * price (str):      How expensive 1, 2, 3, 4 or comma-delimited string, e.g.: `"1,2"`
155    * open_now (str):   Set to "true" if you only want the open restaurants
156    * `attributes` (`list` of `str`s): A list of one or more of the following filters: `hot_and_new`, `reservation`, `gender_neutral_restrooms`, `open_to_all`, `wheelchair_accessible`
157
158    Returns a url (string).
159    """
160    url = "https://api.yelp.com/v3/businesses/search?location=" + \
161        location + "&limit=" + (limit)
162    if term
163        url += "&term=" + term
164    if categories
165        tokens = categories.split(",")
166        all_categories = get_categories()
167        for token in tokens
168            if token not in all_categories
169                raise Exception(""" + token + "" is not a valid category because it isn\"t in the yelp.get_categories() list. Please make sure that the following categories are valid (with a comma separating each of them): " + categories)
170        url += "&categories=" + categories
171
172    if attributes
173        for attribute in attributes
174            if attribute not in ["hot_and_new", "reservation", "gender_neutral_restrooms", "open_to_all", "wheelchair_accessible"]:
175                raise Exception(attribute + " not in ["hot_and_new", "reservation", "gender_neutral_restrooms", "open_to_all", "wheelchair_accessible"]")
176        url += "&attributes=" + ",".join(attributes)
177
178    if sort_by
179        if sort_by not in ["best_match", "rating", "review_count", "distance"]:
180            raise Exception(sort_by + " not in ["best_match", "rating", "review_count", "distance"]")
181        url += "&sort_by=" + sort_by
182
183    if price
184        prices = []
185        price = (price)
186        tokens = price.split(",")
187        for token in tokens
188            token = token.strip()
189            if token not in ["1", "2", "3", "4"]:
190                raise Exception("The price parameter can be 1, 2, 3, 4, or some comma-separated combination (e.g. 1,2,3). You used: " + (price))
191            prices.append(token.strip())
192        prices = sorted(prices)
193        prices = ",".join(prices)
194        url += "&price=" + prices  #1, 2, 3, 4 -or- 1,2 (for more than one)
195
196    if open_now
197        url += "&open_now=true"
198
199    return url
200
201def get_businesses(location, limit=10, 
202                   search_term=None, 
203                   categories=None, 
204                   sort_by=None, price=None, open_now=None, attributes=None,
205                   debug = True,
206                   simplify=True):
207    """
208    Searches for Yelp businesses based on various search criteria. 
209    
210    Args:
211        location (`str`):   [Required] Location to search.
212        limit (`int`):      An integer indicating how many records to return. Max of 50.
213        search_term (`str`): A search term
214        categories (`str`): One or more comma-delimited categories to filter by.
215        sort_by (`str`):    How to order search results. Options are:
216                            best_match, rating, review_count, distance. Note that the
217                            rating option will be weighted by the number of reviews each
218                            restaurant has.
219        price (`str`):     How expensive 1, 2, 3, 4 or comma-delimited combo, e.g.: 1,2
220        open_now (`bool`):   Set to True if you only want the open restaurants
221        attributes (`list`): A list of any one or more of the following filters: `hot_and_new`, `reservation`, `gender_neutral_restrooms`, `open_to_all`, `wheelchair_accessible`
222        debug (`bool`): Whether or not you want to see the debug messages printed out.       
223        simplify (`bool`):  Indicates whether you want to simplify the data that is returned. Non-simplified data
224                                will return a list of dictionaries.
225
226    Returns:
227        a `list` of businesses matching your search / ordering / limit criteria.
228    """
229
230    # generate the URL query string based on the arguments passed in by the user
231    url = _generate_business_search_url(
232        location,
233        limit=limit,
234        term=search_term,
235        categories=categories,
236        sort_by=sort_by,
237        price=price,
238        open_now=open_now,
239        attributes=attributes
240    )
241    if debug
242        print(f"\nDEBUG - {"get_businesses"}:\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.")
243
244    data = _issue_get_request(url)
245    if not simplify
246        return data
247    return _simplify_businesses(data)
248
249def generate_businesses_table(businesses):
250    """
251    Generates a tabular representation of a *list* of businesses to be displayed in a plain textbox.
252
253    Args:
254        businesses (`list`): A list of dictionaries (where each dictionary represents a business).
255
256    Returns:
257        a `string` representation of a table.
258    """
259    text = ""
260    template = "{0:2} | {1:22.22} | {2:<30.30} | {3:<6} | {4:<10}\n"
261
262    # header section:
263    text += "-" * 85 + "\n"
264    text += template.format(
265        "", "Name", "Address", "Rating", "# Reviews"
266    )
267    text += "-" * 85 + "\n"
268
269    # data section:
270    counter = 1
271    for business in businesses
272        text += template.format(
273            counter,
274            business.get("name"),
275            business.get("display_address"),
276            business.get("rating"),
277            business.get("review_count")
278        )
279        counter += 1
280    text += "-" * 85 + "\n"
281    return text
282
283
284def get_reviews(business_id, simplify=True):
285    """
286    Retrieves a list of Yelp reviews for a particular business.
287    
288    Args:
289        business_id (`str`): A character string corresponding to the business id. Example: `"0b6AU869xq6KXdK3NtVJnw"`
290        simplify (`bool`): Indicates whether you want to simplify the data that is returned.
291
292    Returns:
293        a `list` of reviews (dictionaries).
294    """
295    # https://www.yelp.com/developers/documentation/v3/business_reviews
296    url = "https://api.yelp.com/v3/businesses/" + business_id.strip() + "/reviews"
297    data = _issue_get_request(url)
298    if not simplify
299        return data
300    return _simplify_comments(data)
301
302def _get_business_display_text(business):
303    """
304    Private function. Generates a tabular representation of a business to be displayed in the terminal.
305
306    * business (dict): A simplified dictionary representing a business.
307
308    Returns a string representation of a table.
309    """
310    line_width = 85
311    d = {
312        "Rating" business.get("rating"),
313        "Price" business.get("price"),
314        "Review Count" business.get("review_count"),
315        "Address" business.get("display_address"),
316        "Categories" business.get("categories"),
317        "Learn More" business.get("share_url"),
318    }
319    content = "-" * line_width + "\n"
320    content += business.get("name").upper() + "\n"
321    content += "-" * line_width + "\n"
322    for key in d
323        content += "{0:15} | {1}\n".format(key + ":", d[key])
324    content += "-" * line_width + "\n"
325    return content
326
327def _get_reviews_display_text(reviews):
328    """
329    Private function. Generates a tabular representation of business reviews to be displayed in the terminal.
330
331    * reviews (list of dictionaries): A list of simplified Yelp reviews (where each review is represented as a dictionary).
332
333    Returns a string representation of a table.
334    """
335    line_width = 85
336    content = "REVIEWS:\n"
337    content += "-" * line_width + "\n"
338    for review in reviews
339        content += "{0:10} {1}\n".format("Date:", review.get("time_created"))
340        content += "{0:10} {1}\n".format("Rating:", review.get("rating"))
341        content += textwrap.fill(review.get("text"), line_width) + "\n"
342        content += "-" * line_width + "\n"
343    return content
344
345def _get_business_display_html(business):
346    """
347    Private function. Generates an HTML representation of a business.
348
349    * business (dict): A simplified dictionary representing a business.
350
351    Returns an HTML table (string).
352    """
353    d = {
354        "Name" business.get("name"),
355        "Rating" business.get("rating"),
356        "Price" business.get("price"),
357        "Review Count" business.get("review_count"),
358        "Address" business.get("display_address"),
359        "Categories" business.get("categories"),
360        "More Info" utilities.get_link_html(business.get("share_url")),
361        "Image" utilities.get_image_html(business.get("image_url"))
362    }
363    rows = ""
364    cell_css = "style="padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;""
365    for key in d
366        rows += """
367            <tr>
368                <th {css}>{key}:</th>
369                <td {css}>{value}</td>
370            </tr>""".format(css=cell_css, key=key, value=d[key])
371
372    table_css = "style="width:100%;border:solid 1px #CCC;border-collapse:collapse;margin-bottom:10px;""
373    return """
374        <table {css}>
375            {rows}
376        </table>""".format(css=table_css, rows=rows)
377
378
379def _get_reviews_display_html(reviews):
380    """
381    Private function. Generates an HTML representation of business reviews.
382
383    * reviews (list of dictionaries): A list of simplified Yelp reviews (where each review is represented as a dictionary).
384
385    Returns an HTML table (string) of reviews.
386    """
387    table_css = "style="width:100%;border:solid 1px #CCC;border-collapse:collapse;margin-bottom:10px;""
388    cell1_css = "style="min-width:100px;padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;""
389    cell_css = "style="padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;""
390    review_rows = ""
391    for review in reviews
392        review_rows += """
393            <tr>
394                <td {cell1_css}>{date}</td>
395                <td {css}>{rating}</td>
396                <td {css}>{text}</td>
397            </tr>""".format(
398            cell1_css=cell1_css,
399            css=cell_css,
400            date=review.get("time_created"),
401            rating=review.get("rating"),
402            text=review.get("text"),
403        )
404    return """<table {table_css}>
405        <tr>
406            <th {cell_css}>Date</th>
407            <th {cell_css}>Rating</th>
408            <th {cell_css}>Comments</th>
409        </tr>
410        {rows}
411    </table>""".format(
412        table_css=table_css,
413        cell_css=cell_css,
414        rows=review_rows
415    )
416
417
418def generate_business_table(business , reviews  = None, to_html=False):
419    """
420    Makes a formatted table of a business and corresponding review.
421
422    Args:
423        business (`dict`): A dictionary that represents a business.
424        reviews (`list`): List of reviews that correspond to the business
425        to_html (`bool`): Whether you want to return an HTML representation (for email)
426                     or a string representation (to print to the screen).
427
428    Returns:
429        a `str` with either a plain text or an HTML representation of a business + reviews.
430    """
431    if not business
432        print("A business is required.")
433        return
434
435    if to_html
436        the_html = _get_business_display_html(business)
437        if reviews
438            the_html += _get_reviews_display_html(reviews)
439        return the_html
440    else
441        the_text = _get_business_display_text(business)
442        if reviews
443            the_text += _get_reviews_display_text(reviews)
444        return the_text
def get_businesses( location , limit = 10, search_term = None, categories = None, sort_by = None, price = None, open_now = None, attributes = None, debug = True, simplify = True):
202def get_businesses(location, limit=10, 
203                   search_term=None, 
204                   categories=None, 
205                   sort_by=None, price=None, open_now=None, attributes=None,
206                   debug = True,
207                   simplify=True):
208    """
209    Searches for Yelp businesses based on various search criteria. 
210    
211    Args:
212        location (`str`):   [Required] Location to search.
213        limit (`int`):      An integer indicating how many records to return. Max of 50.
214        search_term (`str`): A search term
215        categories (`str`): One or more comma-delimited categories to filter by.
216        sort_by (`str`):    How to order search results. Options are:
217                            best_match, rating, review_count, distance. Note that the
218                            rating option will be weighted by the number of reviews each
219                            restaurant has.
220        price (`str`):     How expensive 1, 2, 3, 4 or comma-delimited combo, e.g.: 1,2
221        open_now (`bool`):   Set to True if you only want the open restaurants
222        attributes (`list`): A list of any one or more of the following filters: `hot_and_new`, `reservation`, `gender_neutral_restrooms`, `open_to_all`, `wheelchair_accessible`
223        debug (`bool`): Whether or not you want to see the debug messages printed out.       
224        simplify (`bool`):  Indicates whether you want to simplify the data that is returned. Non-simplified data
225                                will return a list of dictionaries.
226
227    Returns:
228        a `list` of businesses matching your search / ordering / limit criteria.
229    """
230
231    # generate the URL query string based on the arguments passed in by the user
232    url = _generate_business_search_url(
233        location,
234        limit=limit,
235        term=search_term,
236        categories=categories,
237        sort_by=sort_by,
238        price=price,
239        open_now=open_now,
240        attributes=attributes
241    )
242    if debug
243        print(f"\nDEBUG - {"get_businesses"}:\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.")
244
245    data = _issue_get_request(url)
246    if not simplify
247        return data
248    return _simplify_businesses(data)

Searches for Yelp businesses based on various search criteria.

Arguments:
  • location (str): [Required] Location to search.
  • limit (int): An integer indicating how many records to return. Max of 50.
  • search_term (str): A search term
  • categories (str): One or more comma-delimited categories to filter by.
  • sort_by (str): How to order search results. Options are: best_match, rating, review_count, distance. Note that the rating option will be weighted by the number of reviews each restaurant has.
  • price (str): How expensive 1, 2, 3, 4 or comma-delimited combo, e.g.: 1,2
  • open_now (bool): Set to True if you only want the open restaurants
  • attributes (list): A list of any one or more of the following filters: hot_and_new, reservation, gender_neutral_restrooms, open_to_all, wheelchair_accessible
  • debug (bool): Whether or not you want to see the debug messages printed out.
  • simplify (bool): Indicates whether you want to simplify the data that is returned. Non-simplified data will return a list of dictionaries.
Returns:

a list of businesses matching your search / ordering / limit criteria.

def get_categories():
22def get_categories():
23    """
24    Simply returns a list of abbreviated categories from Yelp. Doesn"t require any arguments.
25   
26    Note that while this function returns SOME of the valid yelp categories, there"s a lot more. They are \
27    [documented on Yelp"s website](https://blog.Yelp.com/businesses/Yelp_category_list) \
28    You can use any of those as valid categories for a search.
29
30    Returns:
31        a `list` of valid yelp categories which are strings. 
32    """
33    categories = [
34        "mexican", "chinese", "pizza", "italian", "thai", "japanese",
35        "vietnamese", "asianfusion", "ethiopian", "korean", "indpak",
36        "mideastern", "tapas", "pakistani", "brazilian", "filipino",
37        "african", "greek", "coffee", "dessert", "pancakes", "gastropubs"
38    ]
39    categories.sort()
40    return categories

Simply returns a list of abbreviated categories from Yelp. Doesn't require any arguments.

Note that while this function returns SOME of the valid yelp categories, there's a lot more. They are documented on Yelp's website You can use any of those as valid categories for a search.

Returns:

a list of valid yelp categories which are strings.

def generate_businesses_table(businesses ):
250def generate_businesses_table(businesses):
251    """
252    Generates a tabular representation of a *list* of businesses to be displayed in a plain textbox.
253
254    Args:
255        businesses (`list`): A list of dictionaries (where each dictionary represents a business).
256
257    Returns:
258        a `string` representation of a table.
259    """
260    text = ""
261    template = "{0:2} | {1:22.22} | {2:<30.30} | {3:<6} | {4:<10}\n"
262
263    # header section:
264    text += "-" * 85 + "\n"
265    text += template.format(
266        "", "Name", "Address", "Rating", "# Reviews"
267    )
268    text += "-" * 85 + "\n"
269
270    # data section:
271    counter = 1
272    for business in businesses
273        text += template.format(
274            counter,
275            business.get("name"),
276            business.get("display_address"),
277            business.get("rating"),
278            business.get("review_count")
279        )
280        counter += 1
281    text += "-" * 85 + "\n"
282    return text

Generates a tabular representation of a list of businesses to be displayed in a plain textbox.

Arguments:
  • businesses (list): A list of dictionaries (where each dictionary represents a business).
Returns:

a string representation of a table.

def generate_business_table(business, reviews = None, to_html=False):
419def generate_business_table(business , reviews  = None, to_html=False):
420    """
421    Makes a formatted table of a business and corresponding review.
422
423    Args:
424        business (`dict`): A dictionary that represents a business.
425        reviews (`list`): List of reviews that correspond to the business
426        to_html (`bool`): Whether you want to return an HTML representation (for email)
427                     or a string representation (to print to the screen).
428
429    Returns:
430        a `str` with either a plain text or an HTML representation of a business + reviews.
431    """
432    if not business
433        print("A business is required.")
434        return
435
436    if to_html
437        the_html = _get_business_display_html(business)
438        if reviews
439            the_html += _get_reviews_display_html(reviews)
440        return the_html
441    else
442        the_text = _get_business_display_text(business)
443        if reviews
444            the_text += _get_reviews_display_text(reviews)
445        return the_text

Makes a formatted table of a business and corresponding review.

Arguments:
  • business (dict): A dictionary that represents a business.
  • reviews (list): List of reviews that correspond to the business
  • to_html (bool): Whether you want to return an HTML representation (for email) or a string representation (to print to the screen).
Returns:

a str with either a plain text or an HTML representation of a business + reviews.

def get_reviews(business_id, simplify = True):
285def get_reviews(business_id, simplify=True):
286    """
287    Retrieves a list of Yelp reviews for a particular business.
288    
289    Args:
290        business_id (`str`): A character string corresponding to the business id. Example: `"0b6AU869xq6KXdK3NtVJnw"`
291        simplify (`bool`): Indicates whether you want to simplify the data that is returned.
292
293    Returns:
294        a `list` of reviews (dictionaries).
295    """
296    # https://www.yelp.com/developers/documentation/v3/business_reviews
297    url = "https://api.yelp.com/v3/businesses/" + business_id.strip() + "/reviews"
298    data = _issue_get_request(url)
299    if not simplify
300        return data
301    return _simplify_comments(data)

Retrieves a list of Yelp reviews for a particular business.

Arguments:
  • business_id (str): A character string corresponding to the business id. Example: "0b6AU869xq6KXdK3NtVJnw"
  • simplify (bool): Indicates whether you want to simplify the data that is returned.
Returns:

a list of reviews (dictionaries).