Cómo raspar Yelp en Python

Guía para raspado de datos de Yelp en Python. En este tutorial paso a paso, se explica cómo raspar los datos de Yelp, que proporciona una gran cantidad de información sobre las empresas locales.
16 min read
How to scrape Yelp data guide

Este tutorial cubrirá:

¿Por qué raspar Yelp?

Hay varias razones para raspar Yelp. Entre ellas

  • Obtener acceso a datos empresariales completos: proporciona una gran cantidad de información sobre las empresas locales, incluyendo comentarios, calificaciones, información de contacto, y mucho más.
  • Obtener información sobre las opiniones de los clientes: es conocida por las reseñas de sus usuarios, que proporcionan un tesoro de información sobre las opiniones y experiencias de los clientes.
  • Realizar análisis de la competencia y evaluaciones comparativas: ofrece información valiosa sobre el rendimiento de sus competidores, sus puntos fuertes y débiles y el sentimiento de los clientes.

Existen plataformas similares, pero Yelp es la preferida para el raspado de datos por su:

  • Amplia base de usuarios
  • Diversas categorías de negocios
  • Reputación consolidada

Los datos extraídos de Yelp pueden ser valiosos para la investigación de mercado, el análisis de la competencia, el análisis de opiniones y la toma de decisiones. Esta información también ayuda a identificar áreas de mejora, ajustar ofertas y mantenerse por delante de la competencia.

Bibliotecas y herramientas de raspado de Yelp

Python está ampliamente considerado como un lenguaje excelente para el raspado web debido a su facilidad de uso, su sintaxis sencilla y su amplia gama de bibliotecas. Por eso es el lenguaje de programación recomendado para el raspado de Yelp. Para obtener más información, consulte nuestra guía detallada sobre cómo hacer raspado web con Python.

El siguiente paso consiste en seleccionar las bibliotecas de raspado adecuadas entre la amplia gama de opciones disponibles. Para tomar una decisión informada, primero debe explorar la plataforma en un navegador web. Al inspeccionar las llamadas AJAX realizadas por las páginas web, descubrirá que la mayoría de los datos están incrustados dentro de los documentos HTML recuperados del servidor.

Esto implica que un simple cliente HTTP para hacer peticiones al servidor combinado con un analizador HTML, será suficiente para la tarea. Por esta razón debe optar por:

  • Requests: la librería cliente HTTP más popular para Python. Agiliza el proceso de envío de peticiones HTTP y la gestión de sus correspondientes respuestas.
  • Beautiful Soup: una completa biblioteca de análisis de HTML y XML muy utilizada para el raspado web. Proporciona métodos robustos para navegar y extraer datos del DOM.

Gracias a Requests y Beautiful Soup, es posible raspar Yelp con Python. Veamos en detalle cómo llevar a cabo esta tarea.

Raspado de datos de negocios de Yelp con Beautiful Soup

Siga este tutorial paso a paso y aprenda a crear un raspador de Yelp.

Paso 1: Configuración del proyecto Python

Antes de empezar, es necesario asegurarse de que tiene:

Primero, cree una carpeta yelp-scraper e inicialícelo como un proyecto Python con un entorno virtual con:

mkdir yelp-scraper

cd yelp-scraper

python -m venv env

En Windows, ejecute el siguiente comando para activar el entorno:

env\Scripts\activate.ps1

While on Linux or macOS:

env/bin/activate

A continuación, añada un archivo scraper.py que contenga la siguiente línea en la carpeta del proyecto:

print('Hello, World!')

Este es el script Python más sencillo. Por ahora, sólo imprime “¡Hola, mundo!” pero pronto contendrá la lógica para raspar Yelp.

Se puede ejecutar el raspador con:

python scraper.py

Debería imprimir en la terminal:

Hello, World!

Exactamente lo que se esperaba. Ahora que sabemos que todo funciona, abra la carpeta del proyecto en su IDE de Python.

¡Genial, prepárese para escribir algo de código Python!

Paso 2: Instalar las librerías de raspado

Ahora es necesario añadir las librerías necesarias para realizar el raspado web a las dependencias del proyecto. En el entorno virtual activado, ejecute el siguiente comando para instalar Beautiful Soup y Requests:

pip install beautifulsoup4 requests

Elimine el archivo scraper.py y luego añada estas líneas para importar los paquetes:

import requests

from bs4 import BeautifulSoup

# scraping logic...

Asegúrese de que su IDE de Python no informa de ningún error. Es posible que reciba algunas advertencias debido a importaciones no utilizadas, pero puede ignorarlas. Está a punto de utilizar estas bibliotecas de raspado para extraer datos de Yelp.

Paso 3: Identificar y descargar la página de destino

Navegue por Yelp e identifique la página que desea raspar. En esta guía, verá cómo recuperar datos de la lista de los restaurantes italianos mejor valorados de Nueva York:

Asigne la URL de la página de destino a una variable:

url = 'https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY'

Next, use requests.get() to make an HTTP GET request to that URL:

page = requests.get(url)

La página variable contendrá ahora la respuesta producida por el servidor.

En concreto, page.text almacena el documento HTML asociado a la página web de destino. Puede comprobarlo registrándolo:

print(page.text)

Esto debería imprimirse:

<!DOCTYPE html><html lang="en-US" prefix="og: http://ogp.me/ns#" style="margin: 0;padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;"><head><script>document.documentElement.className=document.documentElement.className.replace(no-j/,"js");</script><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><meta http-equiv="Content-Language" content="en-US" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="mask-icon" sizes="any" href="https://s3-media0.fl.yelpcdn.com/assets/srv0/yelp_large_assets/b2bb2fb0ec9c/assets/img/logos/yelp_burst.svg" content="#FF1A1A"><link rel="shortcut icon" href="https://s3-media0.fl.yelpcdn.com/assets/srv0/yelp_large_assets/dcfe403147fc/assets/img/logos/favicon.ico"><script> window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;window.ygaPageStartTime=new Date().getTime();</script><script>

<!-- Omitted for brevity... -->

Perfecto. Aprendamos a analizarlo para recuperar datos de él.

Paso 4: Analizar el contenido HTML

Introduzca el contenido HTML recuperado por el servidor en el constructor BeautifulSoup() para analizarlo:

soup = BeautifulSoup(page.text, 'html.parser')

La función toma dos argumentos:

  1. La cadena que contiene el HTML.
  2. El analizador que Beautiful Soup utilizará para analizar el contenido.

html.parser” es el nombre del analizador HTML incorporado en Python.

BeautifulSoup() devolverá el contenido analizado como una estructura de árbol explorable. En particular, la variable soup expone métodos útiles para seleccionar elementos del árbol DOM. Los más populares son:

  • find(): devuelve el primer elemento HTML que coincida con la estrategia de selección pasada como parámetro.
  • find_all(): devuelve la lista de elementos HTML que coinciden con la estrategia de selector introducida.
  • select_one(): devuelve el primer elemento HTML que coincida con el selector CSS pasado como parámetro.
  • select(): devuelve la lista de elementos HTML que coinciden con el selector CSS introducido.

¡Fantástico! Pronto los utilizará para extraer los datos deseados de Yelp.

Paso 5: Familiarización con la página

Para idear una estrategia de selección eficaz, primero es necesario familiarizarse con la estructura de la página web de destino. Abra la página en su navegador y comience a explorarla.

Haga clic con el botón derecho del ratón en un elemento HTML de la página y seleccione “Inspeccionar” para abrir las DevTools:

Inmediatamente observará que el sitio se basa en clases CSS que parecen generarse aleatoriamente en el momento de la compilación. Como pueden cambiar en cada despliegue, no debe basar sus selectores CSS en ellos. Esta información es esencial para construir un raspador eficaz.

Si profundiza en el DOM, también verá que los elementos más importantes tienen atributos HTML distintivos. Por lo tanto, su estrategia de selección debe basarse en ellos.

Siga inspeccionando la página en DevTools hasta que se sienta preparado para rasparla con Python.

Paso 6: Extracción de los datos de negocio

El objetivo aquí es extraer la información de negocio de cada tarjeta de la página. Para seguir la pista de estos datos, es necesario una estructura de datos donde almacenarlos:

items = []

Primero, inspeccione un elemento HTML de la tarjeta:

Tenga en cuenta que puede seleccionarlos todos con:

html_item_cards = soup.select('[data-testid="serp-ia-card"]')

Itere sobre ellas y prepare su script para:

  1. Extraer datos de cada uno de ellos.
  2. Guardarlo en un elemento del diccionario Python.
  3. Añadirlos a los elementos.
for html_item_card in html_item_cards:

    item = {}

    # scraping logic...

    items.append(item)

¡Es hora de implementar la lógica de raspado!

Inspeccionar el elemento imagen:

Recuperar la URL de la imagen del negocio con:

image = html_item_card.select_one('[data-lcp-target-id="SCROLLABLE_PHOTO_BOX"] img').attrs['src']

Después de recuperar un elemento con select_one(), se puede acceder a su atributo HTML a través del miembro attrs.

Otra información útil a recuperar es el título y la URL de la página de detalles del negocio:

Como puede ver, puede obtener ambos campos de datos del nodo h3 a:

name = html_item_card.select_one('h3 a').text

url = 'https://www.yelp.com' + html_item_card.select_one('h3 a').attrs['href']

El atributo text devuelve el contenido de texto dentro del elemento actual y todos sus hijos. Como algunos enlaces son relativos, puede que sea necesario añadir la URL base para completarlos.

Uno de los datos más importantes en Yelp es la tasa de reseñas de los usuarios:

En este caso, no hay una forma fácil de obtenerlo, pero aun así se puede lograr el objetivo con:

html_stars_element = html_item_card.select_one('[class^="five-stars"]')

stars = html_stars_element.attrs['aria-label'].replace(' star rating', '')

reviews = html_stars_element.parent.parent.next_sibling.text

Observe el uso de la función replace() de Python para limpiar la cadena y obtener sólo los datos relevantes.

Inspeccione también las etiquetas y los elementos del rango de precios:

Para recoger todas las cadenas de etiquetas, es necesario seleccionarlas todas e iterar sobre ellas:

tags = []

html_tag_elements = html_item_card.select('[class^="priceCategory"] button')

for html_tag_element in html_tag_elements:

    tag = html_tag_element.text

    tags.append(tag)

En cambio, recuperar la indicación opcional de rango de precios es mucho más sencillo:

price_range_html = html_item_card.select_one('[class^="priceRange"]')

# since the price range info is optional

if price_range_html is not None:

    price_range = price_range_html.text

Por último, también hay que raspar los servicios ofrecidos por el restaurante:

De nuevo, es necesario iterar sobre cada nodo:

services = []

html_service_elements = html_item_card.select('[data-testid="services-actions-component"] p[class^="tagText"]')

for html_service_element in html_service_elements:

    service = html_service_element.text

    services.append(service)

¡Bien hecho! Ha implementado la lógica de raspado.

Añada las variables de los datos raspados al diccionario:

item['name'] = name

item['image'] = image

item['url'] = url

item['stars'] = stars

item['reviews'] = reviews

item['tags'] = tags

item['price_range'] = price_range

item['services'] = services

Con print(item) compruebe que el proceso de extracción de datos funciona como desea. En la primera tarjeta, obtendrá:

{'name': 'Olio e Più', 'image': 'https://s3-media0.fl.yelpcdn.com/bphoto/CUpPgz_Q4QBHxxxxDJJTTA/348s.jpg', 'url': 'https://www.yelp.com/biz/olio-e-pi%C3%B9-new-york-7?osq=Italian', 'stars': '4.5', 'reviews': '4588', 'tags': ['Pizza', 'Italian', 'Cocktail Bars'], 'price_range': '$$', 'services': ['Outdoor seating', 'Delivery', 'Takeout']}

¡Genial! ¡Se acerca más a su objetivo!

Paso 7: Implementar la lógica de recopilación

No olvide que las empresas se presentan a los usuarios en una lista paginada. Acabamos de ver cómo raspar una sola página, pero ¿y si quisiéramos obtener todos los datos? Para ello, hay que integrar la recopilación web en el raspador de datos de Yelp.

En primer lugar, hay que definir algunas estructuras de datos de apoyo en la parte superior del script:

visited_pages = []

pages_to_scrape = ['https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY']
visited_pages will contain the URLs of the pages scraped, while pages_to_scrape the next ones to visit.

Create a while loop that terminates when there are no longer pages to scrape or after a specific number of iterations:


limit = 5 # in production, you can remove it

i = 0

while len(pages_to_scrape) != 0 and i < limit:

    # extract the first page from the array

    url = pages_to_scrape.pop(0)

    # mark it as "visited"

    visited_pages.append(url)

    # download and parse the page

    page = requests.get(url)

    soup = BeautifulSoup(page.text, 'html.parser')

    # scraping logic...

    # crawling logic...

    # increment the page counter

    i += 1

Cada iteración se encargará de eliminar una página de la lista, rasparla, descubrir nuevas páginas y añadirlas a queue. limit simplemente evita que el raspador se ejecute eternamente.

Sólo queda implementar la lógica de recopilación. Inspeccione el elemento HTML de paginación:

Consta de varios enlaces. Recopílelos todos y añada los recién descubiertos a pages_to_visit con:

pagination_link_elements = soup.select('[class^="pagination-links"] a')

for pagination_link_element in pagination_link_elements:

    pagination_url = pagination_link_element.attrs['href']

    # if the discovered URL is new

    if pagination_url not in visited_pages and pagination_url not in pages_to_scrape:

        pages_to_scrape.append(pagination_url)

¡Maravilloso! Ahora su raspador recorrerá automáticamente todas las páginas de paginación.

Paso 8: Exportar los datos raspados a CSV

El último paso es hacer que los datos recopilados sean más fáciles de compartir y leer. La mejor manera de hacerlo es exportarlos a un formato legible por humanos, como CSV:

import csv

# ...

# initialize the .csv output file

with open('restaurants.csv', 'w', newline='', encoding='utf-8') as csv_file:

    writer = csv.DictWriter(csv_file, fieldnames=headers, quoting=csv.QUOTE_ALL)

    writer.writeheader()

    # populate the CSV file

    for item in items:

        # transform array fields from "['element1', 'element2', ...]"

        # to "element1; element2; ..."

        csv_item = {}

        for key, value in item.items():

            if isinstance(value, list):

                csv_item[key] = '; '.join(str(e) for e in value)

            else:

                csv_item[key] = value

        # add a new record

        writer.writerow(csv_item)

Cree un archivo restaurants.csv con open(). A continuación, utilice DictWriter y alguna lógica personalizada para rellenarlo. Como el paquete csv viene de la Python Standard Library, no es necesario instalar dependencias adicionales.

¡Genial! Ha empezado a partir de datos sin procesar contenidos en una página web y ahora tiene datos CSV semiestructurados. Es hora de echar un vistazo a todo el raspador Yelp Python.

Paso 9: Conjuntando todo

Este es el aspecto del script scraper.py completo:

import requests

from bs4 import BeautifulSoup

import csv

# support data structures to implement the

# crawling logic

visited_pages = []

pages_to_scrape = ['https://www.yelp.com/search?find_desc=Italian&find_loc=New+York%2C+NY']

# to store the scraped data

items = []

# to avoid overwhelming Yelp's servers with requests

limit = 5

i = 0

# until all pagination pages have been visited

# or the page limit is hit

while len(pages_to_scrape) != 0 and i < limit:

    # extract the first page from the array

    url = pages_to_scrape.pop(0)

    # mark it as "visited"

    visited_pages.append(url)

    # download and parse the page

    page = requests.get(url)

    soup = BeautifulSoup(page.text, 'html.parser')

    # select all item card

    html_item_cards = soup.select('[data-testid="serp-ia-card"]')

    for html_item_card in html_item_cards:

        # scraping logic

        item = {}

        image = html_item_card.select_one('[data-lcp-target-id="SCROLLABLE_PHOTO_BOX"] img').attrs['src']

        name = html_item_card.select_one('h3 a').text

        url = 'https://www.yelp.com' + html_item_card.select_one('h3 a').attrs['href']

        html_stars_element = html_item_card.select_one('[class^="five-stars"]')

        stars = html_stars_element.attrs['aria-label'].replace(' star rating', '')

        reviews = html_stars_element.parent.parent.next_sibling.text

        tags = []

        html_tag_elements = html_item_card.select('[class^="priceCategory"] button')

        for html_tag_element in html_tag_elements:

            tag = html_tag_element.text

            tags.append(tag)

        price_range_html = html_item_card.select_one('[class^="priceRange"]')

        # this HTML element is optional

        if price_range_html is not None:

            price_range = price_range_html.text

        services = []

        html_service_elements = html_item_card.select('[data-testid="services-actions-component"] p[class^="tagText"]')

        for html_service_element in html_service_elements:

            service = html_service_element.text

            services.append(service)

        # add the scraped data to the object

        # and then the object to the array

        item['name'] = name

        item['image'] = image

        item['url'] = url

        item['stars'] = stars

        item['reviews'] = reviews

        item['tags'] = tags

        item['price_range'] = price_range

        item['services'] = services

        items.append(item)

    # discover new pagination pages and add them to the queue

    pagination_link_elements = soup.select('[class^="pagination-links"] a')

    for pagination_link_element in pagination_link_elements:

        pagination_url = pagination_link_element.attrs['href']

        # if the discovered URL is new

        if pagination_url not in visited_pages and pagination_url not in pages_to_scrape:

            pages_to_scrape.append(pagination_url)

    # increment the page counter

    i += 1

# extract the keys from the first object in the array

# to use them as headers of the CSV

headers = items[0].keys()

# initialize the .csv output file

with open('restaurants.csv', 'w', newline='', encoding='utf-8') as csv_file:

    writer = csv.DictWriter(csv_file, fieldnames=headers, quoting=csv.QUOTE_ALL)

    writer.writeheader()

    # populate the CSV file

    for item in items:
# transform array fields from "['element1', 'element2', ...]"

        # to "element1; element2; ..."

        csv_item = {}

        for key, value in item.items():

            if isinstance(value, list):

                csv_item[key] = '; '.join(str(e) for e in value)

            else:

                csv_item[key] = value

        # add a new record

        writer.writerow(csv_item)    

En unas 100 líneas de código, se puede construir un web spider para extraer datos de negocios de Yelp.

Ejecute el raspador con:

python scraper.py

Espere a que se complete la ejecución, y encontrará el archivo restaurants.csv a continuación en la carpeta raíz de su proyecto:

restaurants.csv

¡Enhorabuena! Así es como se raspa Yelp en Python.

Conclusión

En esta guía paso a paso, hemos explicado por qué Yelp es uno de los mejores objetivos de raspado para obtener datos de usuarios sobre negocios locales. En detalle, aprendió a construir un raspador Python que puede recuperar datos de Yelp. Como se muestra aquí, sólo se necesitan unas pocas líneas de código.

Al mismo tiempo, los sitios siguen evolucionando y adaptando su interfaz de usuario y su estructura a las expectativas siempre cambiantes de los usuarios. El raspador construido aquí funciona hoy, pero puede dejar de ser eficaz mañana. Evite gastar tiempo y dinero en mantenimiento, ¡pruebe nuestro raspador de Yelp!

Además, siga teniendo en cuenta que la mayoría de los sitios dependen en gran medida de JavaScript. En estos casos, un enfoque tradicional basado en un analizador HTML no funcionará. En su lugar, tendrá que utilizar una herramienta que pueda procesar JavaScript y gestionar las huellas dactilares, los CAPTCHA y los reintentos automáticos. En eso consiste exactamente nuestra nueva solución Scraping Browser.

¿No quiere ocuparse del raspado web de Yelp y sólo quiere datos? Adquirir conjuntos de datos de Yelp.