Plataforma web que, a partir da sua localização atual, faz scraping do Google Maps e lista os negócios locais dentro de um raio configurável. Os resultados são salvos em um banco SQLite com deduplicação automática.
| Camada | Tecnologia |
|---|---|
| Backend | Python + FastAPI |
| Scraping | Playwright (Chromium headless) |
| Banco de dados | SQLite via aiosqlite |
| Frontend | HTML + CSS + JS (sem framework) |
- uv instalado
- Python 3.11+
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv pip install -r requirements.txtplaywright install chromiumO Playwright baixa um binário do Chromium isolado (~170 MB). Esse passo é necessário apenas uma vez.
uvicorn main:app --reloadAcesse em: http://localhost:8000
- Abra
http://localhost:8000no navegador - Informe o raio de busca em km (1–100)
- Clique em Buscar Negócios
- O navegador solicitará permissão de geolocalização — autorize
- O scraping é iniciado; aguarde (pode levar alguns minutos dependendo do raio)
- Os resultados aparecem em tabela com: nome, categoria, endereço, telefone, avaliação e site
- Ao reabrir a página, os negócios já salvos são carregados automaticamente
essena_schrute/
├── main.py # App FastAPI: rotas e inicialização
├── scraper.py # Scraping do Google Maps com Playwright
├── database.py # Schema SQLite e operações de leitura/escrita
├── requirements.txt # Dependências Python
├── businesses.db # Banco de dados (criado automaticamente na 1ª execução)
└── static/
└── index.html # Frontend completo
| Método | Rota | Descrição |
|---|---|---|
GET |
/ |
Serve o frontend |
POST |
/scrape |
Inicia o scraping e retorna a lista atualizada |
GET |
/businesses |
Retorna todos os negócios salvos no banco |
curl -X POST http://localhost:8000/scrape \
-H "Content-Type: application/json" \
-d '{"lat": -23.5505, "lon": -46.6333, "radius_km": 5}'{
"scraped": 42,
"new": 8,
"businesses": [
{
"id": 1,
"place_id": "Pizzaria+Roma_Rua+das+Flores",
"name": "Pizzaria Roma",
"category": "Pizzaria",
"address": "Rua das Flores, 123",
"phone": "(11) 99999-0000",
"rating": 4.5,
"review_count": 312,
"website": "https://pizzariaroma.com.br",
"lat": -23.5505,
"lon": -46.6333,
"first_seen": "2026-03-30T20:00:00+00:00",
"last_seen": "2026-03-30T20:00:00+00:00"
}
]
}O arquivo businesses.db é criado automaticamente na raiz do projeto na primeira execução.
| Coluna | Tipo | Descrição |
|---|---|---|
id |
INTEGER PK | Auto-incremento |
place_id |
TEXT UNIQUE | Identificador único (nome + endereço ou ID do Google) |
name |
TEXT | Nome do negócio |
category |
TEXT | Categoria (ex: Restaurante, Farmácia) |
address |
TEXT | Endereço completo |
phone |
TEXT | Telefone |
rating |
REAL | Nota média (0–5) |
review_count |
INTEGER | Número de avaliações |
website |
TEXT | URL do site |
lat / lon |
REAL | Coordenadas da busca que originou o registro |
first_seen |
TEXT | ISO 8601 — primeira vez mapeado |
last_seen |
TEXT | ISO 8601 — última vez encontrado no scraping |
Negócios já existentes no banco não são duplicados. A inserção usa INSERT OR IGNORE com base no place_id. Em buscas subsequentes, apenas last_seen, rating e review_count são atualizados.
- O Google Maps pode demorar para carregar os resultados dependendo da velocidade da conexão e do tamanho do raio
- Raios menores (2–10 km) tendem a retornar resultados mais precisos e rápidos
- Os seletores CSS do Google Maps podem mudar sem aviso — se o scraping parar de extrair dados corretamente, verifique e atualize os seletores em
scraper.py - O browser roda em modo headless (sem interface gráfica). Para depurar o scraping visualmente, altere em
scraper.py:browser = await p.chromium.launch(headless=False)
- O scraping simula um usuário real com locale
pt-BRe geolocalização injetada
Não há arquivo .env — as configurações relevantes ficam diretamente nos arquivos:
| Configuração | Arquivo | Padrão |
|---|---|---|
| Caminho do banco SQLite | database.py → DB_PATH |
businesses.db |
| Máximo de scrolls no feed | scraper.py → range(15) |
15 tentativas |
| Zoom mínimo/máximo | scraper.py → _zoom_from_radius |
10–16 |
| Porta do servidor | comando uvicorn |
8000 |