Como raspar Yelp em Python

Guia de como raspar Yelp em Python. Neste tutorial passo a passo, aprenda a raspar dados de Yelp, que fornecem uma grande quantidade de informações sobre empresas locais.
16 min read
How to scrape Yelp data guide

Este tutorial abordará:

Porquê raspar Yelp?

Existem várias razões pelas quais as empresas raspam Yelp. Estas incluem:

  • Obter acesso a dados comerciais abrangentes: Fornece uma grande quantidade de informações sobre empresas locais, incluindo avaliações, classificações, informações de contato e muito mais.
  • Obter informações sobre as opiniões dos clientes: É conhecido pelos seus comentários de usuários, proporcionando um tesouro de informações sobre as opiniões e experiências dos clientes.
  • Efetuar análises competitivas e benchmarking: Oferece informações valiosas sobre o desempenho, os pontos fortes, os pontos fracos e o sentimento dos clientes dos seus concorrentes.

Existem plataformas semelhantes, mas Yelp é a escolha preferida para a raspagem de dados devido a:

  • Sua vasta base de usuários
  • Diversas categorias de empresas
  • Reputação bem estabelecida

Os dados extraídos de Yelp podem ser valiosos para estudos de mercado, análise da concorrência, análise de sentimentos e tomada de decisões. Essas informações também o ajudam a identificar áreas a melhorar, a afinar as suas ofertas e a manter-se à frente da concorrência.

Bibliotecas e ferramentas de raspagem de Yelp

Python é amplamente considerado como uma excelente linguagem para a raspagem da web devido à sua natureza fácil de utilizar, à sintaxe simples e à vasta gama de bibliotecas. É por isso que é a linguagem de programação recomendada para a raspagem de Yelp. Para saber mais sobre isso, reveja nosso guia detalhado sobre como fazer raspagem da web com Python.

O passo seguinte consiste em selecionar as bibliotecas de raspagem adequadas a partir da vasta gama de opções disponíveis. Para tomar uma decisão informada, deve começar por explorar a plataforma num navegador web. Ao inspecionar as chamadas AJAX feitas pelas páginas web, descobrirá que a maioria dos dados está incorporada nos documentos HTML recuperados do servidor.

Isto implica que um simples cliente HTTP para efetuar pedidos ao servidor, combinado com um analisador HTML, será suficiente para a tarefa. Eis por que deve optar por:

  • Requests: A biblioteca de cliente HTTP mais popular para Python. Simplifica o processo de envio de pedidos HTTP e o tratamento das respostas correspondentes.
  • Beautiful Soup: Uma biblioteca abrangente de análise de HTML e XML amplamente utilizada para raspagem da web. Fornece métodos robustos para navegar e extrair dados do DOM.

Graças a Requests e a Beautiful Soup, é possível raspar Yelp usando Python. Vamos entrar nos pormenores de como realizar esta tarefa!

Raspagem de dados comerciais de Yelp com Beautiful Soup

Siga este tutorial passo a passo e saiba como criar um raspador de Yelp.

Passo 1: Configuração do projeto em Python

Antes de começar, é necessário certificar-se de que possui:

Primeiro, crie uma pasta yelp-scraper e inicialize-a como um projeto em Python com um ambiente virtual com:

mkdir yelp-scraper

cd yelp-scraper

python -m venv env

No Windows, execute o comando abaixo para ativar o ambiente:

env\Scripts\activate.ps1

While on Linux or macOS:

env/bin/activate

Em seguida, adicione um ficheiro scraper.py contendo a linha abaixo na pasta do projeto:

print('Hello, World!')

Este é o script de Python mais fácil. Neste momento, apenas imprime “Hello, World!” (Olá, mundo!), mas em breve irá conter a lógica para raspar Yelp.

Pode iniciar o raspador com:

python scraper.py

Deve imprimir no terminal:

Hello, World!

Exatamente o que se esperava. Agora que sabe que tudo funciona, abra a pasta do projeto no seu IDE de Python.

Ótimo, prepare-se para escrever algum código de Python!

Passo 2: Instalar as bibliotecas de raspagem

Agora tem de adicionar as bibliotecas necessárias para efetuar a raspagem da web às dependências do projeto. No ambiente virtual ativado, execute o seguinte comando para instalar Beautiful Soup e Requests:

pip install beautifulsoup4 requests

Limpe o ficheiro scraper.py e, em seguida, adicione estas linhas para importar os pacotes:

import requests

from bs4 import BeautifulSoup

# scraping logic...

Certifique-se de que o seu IDE de Python não reporta erros. Poderá receber alguns avisos devido a importações não utilizadas, mas pode ignorá-los. Está prestes a utilizar essas bibliotecas de raspagem para extrair dados de Yelp.

Passo 3: Identificar e descarregar a página de destino

Navegue no Yelp e identifique a página que pretende raspar. Neste guia, verá como obter dados da lista dos restaurantes italianos mais bem classificados de Nova Iorque:

Atribua o URL da página de destino a uma variável:

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)

A página variável conterá agora a resposta produzida pelo servidor.

Especificamente, page.text armazena o documento HTML associado à página web de destino. Pode verificar isso registando-o:

print(page.text)

Isto deve ser impresso:

<!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... -->

Perfeito! Vamos aprender a analisá-lo para obter dados a partir dele.

Passo 4: Analisar o conteúdo HTML

Alimentar o conteúdo HTML recuperado pelo servidor para o construtor BeautifulSoup() para o analisar:

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

A função recebe dois argumentos:

  1. A cadeia que contém o HTML.
  2. O analisador que a Beautiful Soup utilizará para analisar o conteúdo.

html.parser” é o nome do analisador HTML incorporado no Python.

BeautifulSoup() devolverá o conteúdo analisado como uma estrutura de árvore explorável. Em particular, a variável soup expõe métodos úteis para selecionar elementos da árvore DOM. Os mais populares são:

  • find(): Retorna o primeiro elemento HTML que corresponde à estratégia do seletor passada como parâmetro.
  • find_all(): Devolve a lista de elementos HTML que correspondem à estratégia do seletor de entrada.
  • select_one(): Retorna o primeiro elemento HTML que corresponde ao seletor CSS passado como parâmetro.
  • select(): Retorna a lista de elementos HTML que correspondem ao seletor CSS de entrada.

Fantástico! Em breve, os utilizará para extrair os dados desejados de Yelp.

Passo 5: Familiarizar-se com a página

Para conceber uma estratégia de seleção eficaz, é necessário, em primeiro lugar, familiarizar-se com a estrutura da página web de destino. Abra-a no seu navegador e comece a explorá-la.

Clique com o botão direito do rato num elemento HTML da página e selecione “Inspecionar” para abrir as ferramentas de desenvolvimento:

Verificará imediatamente que o sítio se baseia em classes CSS que parecem ser geradas aleatoriamente no momento da criação. Como podem mudar a cada implementação, não deve basear os seus seletores CSS nelas. Esta é uma informação essencial para criar um raspador eficaz.

Se analisa o DOM, verá também que os elementos mais importantes têm atributos HTML distintos. Assim, a sua estratégia de seleção deve basear-se neles.

Continue a inspecionar a página nas ferramentas de desenvolvimento até se sentir preparado para a raspar com Python!

Passo 6: Extrair os dados comerciais

O objetivo aqui é extrair informações comerciais de cada cartão na página. Para manter o registo destes dados, é necessária uma estrutura de dados para os armazenar:

items = []

Primeiro, inspecione um elemento HTML do cartão:

Note que pode selecioná-los a todos com:

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

Itere sobre eles e prepare o seu script para:

  1. Extrair dados de cada um deles.
  2. Guardá-lo num item do dicionário de Python.
  3. Adicioná-lo aos itens.
for html_item_card in html_item_cards:

    item = {}

    # scraping logic...

    items.append(item)

É momento de implementar a lógica de raspagem!

Inspecione o elemento de imagem:

Recupere o URL da imagem da empresa com:

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

Depois de obter um elemento com select_one(), pode aceder ao seu atributo HTML através do membro attrs.

Outras informações úteis a recuperar são o título e o URL da página de pormenor da empresa:

Como pode ver, pode obter ambos os campos de dados a partir do nó 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']

O atributo text devolve o conteúdo de texto no elemento atual e em todos os seus secundários. Como algumas ligações são relativas, pode ser necessário adicionar o URL de base para as completar.

Um dos dados mais importantes de Yelp é a taxa de avaliação dos usuários:

Neste caso, não existe uma forma fácil de o conseguir, mas é possível atingir o objetivo:

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

Repare na utilização da função Python replace() para limpar a cadeia de caracteres e obter apenas os dados relevantes.

Inspecione também as etiquetas e os elementos da gama de preços:

Para coletar todas as cadeias de etiquetas, é necessário selecioná-las todas e iterar sobre elas:

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)

Em vez disso, é muito mais fácil obter a indicação opcional do intervalo de preços:

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, deve também raspar os serviços oferecidos pelo restaurante:

Mais uma vez, é necessário iterar sobre cada um dos nós:

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)

Muito bem! Acabou de implementar a lógica de raspagem.

Adicione as variáveis de dados extraídos ao dicionário:

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

Utilize print(item) para se certificar de que o processo de extração de dados funciona como pretendido. No primeiro cartão, obterá:

{'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']}

Espetacular! Está mais perto do seu objetivo!

Passo 7: Implementar a lógica de rastejamento

Não se esqueça de que as empresas são apresentadas aos usuários numa lista paginada. Acabou de ver como extrair uma única página, mas e se quisesse obter todos os dados? Para o fazer, terá de integrar o rastejamento da web no raspador de dados de Yelp.

Primeiro, defina algumas estruturas de dados de suporte no topo do seu 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 iteração irá remover uma página da lista, raspá-la, descobrir novas páginas e adicioná-las à fila. O limite simplesmente impede que o raspador funcione para sempre.

Resta apenas implementar a lógica de rastejamento. Inspecione o elemento de paginação HTML:

É composto por vários links. Recolha-os todos e adicione os recém-descobertos a pages_to_visit com:

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)

Maravilhoso! Agora, o seu raspador passará automaticamente por todas as páginas de paginação.

Passo 8: Exportar os dados raspados para CSV

A etapa final consiste em tornar os dados recolhidos mais fáceis de partilhar e ler. A melhor maneira de o fazer é exportá-los para um formato legível por humanos, como o 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)

Crie um ficheiro restaurants.csv com open(). Em seguida, utilize DictWriter e alguma lógica personalizada para o preencher. Uma vez que o pacote csv vem da Biblioteca Padrão de Python, não é necessário instalar dependências adicionais.

Ótimo! Começou com dados em bruto contidos numa página web e agora tem dados CSV semiestruturados. É momento de analisar todo o raspador de Yelp com Python.

Passo 9: Juntar tudo

Aqui está o aspeto do 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)    

Em cerca de 100 linhas de código, é possível construir um rastreador da rede para extrair dados de empresas de Yelp.

Utilizar o raspador com:

python scraper.py

Aguarde que a execução seja concluída e encontrará o ficheiro restaurants.csv abaixo na pasta raiz do seu projeto:

restaurants.csv

Parabéns! Acabou de aprender a fazer raspagem de Yelp em Python!

Conclusão

Neste guia passo-a-passo, percebeu por que razão Yelp é um dos melhores alvos de raspagem para obter dados de usuários sobre empresas locais. Em pormenor, aprendeu a construir um raspador com Python que pode obter dados de Yelp. Como mostrado aqui, são necessárias apenas algumas linhas de código.

Ao mesmo tempo, os sítios continuam a evoluir e a adaptar a sua IU e estrutura às expectativas em constante mudança dos usuários. O raspador criado aqui funciona hoje, mas pode deixar de ser eficaz amanhã. Evite gastar tempo e dinheiro em manutenção, experimente o nosso raspador de Yelp!

Além disso, não se esqueça de que a maioria dos sítios depende fortemente de JavaScript. Nestes cenários, uma abordagem tradicional baseada num analisador HTML não funcionará. Em vez disso, terá de utilizar uma ferramenta que possa processar JavaScript e lidar com as impressões digitais, CAPTCHAs e tentativas automáticas por você. É exatamente este o objetivo da nossa nova solução, o Navegador de Raspagem!

Não quer lidar com a raspagem de Yelp na web e só quer dados? Compre conjuntos de dados de Yelp