Este tutorial cubrirá:
- ¿Por qué raspar Yelp?
- Bibliotecas y herramientas de raspado de Yelp
- Raspado de datos de negocios de Yelp con Beautiful Soup
¿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:
- Python 3+ instalado en su ordenador:
Descargue el instalador, ejecútelo y siga las instrucciones. - Un IDE de Python de su elección: Visual Studio Code con la extensión Python o PyCharm Community Edition.
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:
- La cadena que contiene el HTML.
- 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:
- Extraer datos de cada uno de ellos.
- Guardarlo en un elemento del diccionario Python.
- 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:
¡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.