fix: captura precisa de notas e total de comentarios reais por estabelecimento no Google Maps
This commit is contained in:
@@ -16,7 +16,7 @@ class ScraperService:
|
||||
def _extract_card_rating_and_reviews(card, detail_panel=None) -> tuple:
|
||||
"""
|
||||
Extrai a nota (rating) e a quantidade total de avaliações do container
|
||||
exclusivo do estabelecimento, com fallback para o painel de detalhes.
|
||||
exclusivo do estabelecimento (ex: "4,5 estrelas 56.778 comentários" ou "4,5(56.778)").
|
||||
"""
|
||||
rating = 0.0
|
||||
total_avaliacoes = 0
|
||||
@@ -38,59 +38,51 @@ class ScraperService:
|
||||
except Exception:
|
||||
container = card
|
||||
|
||||
# 2. Procurar aria-labels no container do lugar
|
||||
aria_elems = container.query_selector_all("[aria-label*='estrela'], [aria-label*='star'], [aria-label*='avaliaç']")
|
||||
# 2. Checar ARIA labels do container (ex: "4,5 estrelas 56.778 comentários")
|
||||
aria_elems = container.query_selector_all("[aria-label]")
|
||||
container_label = container.get_attribute("aria-label") or ""
|
||||
card_label = card.get_attribute("aria-label") or ""
|
||||
combined_aria = " ".join([e.get_attribute("aria-label") or "" for e in aria_elems] + [container_label, card_label])
|
||||
all_labels = [e.get_attribute("aria-label") or "" for e in aria_elems] + [container_label, card_label]
|
||||
|
||||
r_match = re.search(r'([1-5][.,]\d)\s*(?:estrelas?|stars?)', combined_aria, re.IGNORECASE)
|
||||
if r_match:
|
||||
try:
|
||||
rating = float(r_match.group(1).replace(',', '.'))
|
||||
except ValueError:
|
||||
pass
|
||||
for lbl in all_labels:
|
||||
m = re.search(r'([1-5][.,]\d)\s*(?:estrelas?|stars?)\s*([\d.,]+)', lbl, re.IGNORECASE)
|
||||
if m:
|
||||
try:
|
||||
r_val = float(m.group(1).replace(',', '.'))
|
||||
c_val = int(re.sub(r'\D', '', m.group(2)))
|
||||
if 1.0 <= r_val <= 5.0:
|
||||
rating = r_val
|
||||
total_avaliacoes = c_val
|
||||
return rating, total_avaliacoes
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
rev_match = re.search(r'([\d.,]+)\s*avaliaç', combined_aria, re.IGNORECASE)
|
||||
if rev_match:
|
||||
try:
|
||||
total_avaliacoes = int(re.sub(r'\D', '', rev_match.group(1)))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 3. Ler o texto bruto (inner_text) do container do lugar
|
||||
# 3. Ler linhas de texto do container (ex: "4,5(56.778)" ou "3,8(360)")
|
||||
container_text = container.inner_text() or card.inner_text() or ""
|
||||
for line in container_text.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
if rating == 0.0:
|
||||
# O rating no card do Maps vem como "4,8 (120)" ou "4.8 ★"
|
||||
m = re.search(r'([1-5][.,]\d)\s*(?:\(|\s*★|\s*estrelas?)', container_text, re.IGNORECASE)
|
||||
if not m:
|
||||
m = re.search(r'([1-5][.,]\d)', container_text)
|
||||
if m:
|
||||
# Testar formato oficial "4,5(56.778)"
|
||||
m_line = re.search(r'([1-5][.,]\d)\s*\(([\d.,]+)\)', line)
|
||||
if m_line:
|
||||
try:
|
||||
val = float(m.group(1).replace(',', '.'))
|
||||
if 1.0 <= val <= 5.0:
|
||||
rating = val
|
||||
rating = float(m_line.group(1).replace(',', '.'))
|
||||
total_avaliacoes = int(re.sub(r'\D', '', m_line.group(2)))
|
||||
return rating, total_avaliacoes
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if total_avaliacoes == 0:
|
||||
# O total de avaliações no card vem entre parênteses "(120)" ou "(1.250)"
|
||||
m = re.search(r'\(([\d.,]+)\)', container_text)
|
||||
if m:
|
||||
try:
|
||||
total_avaliacoes = int(re.sub(r'\D', '', m.group(1)))
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
m = re.search(r'([\d.,]+)\s*avaliaç', container_text, re.IGNORECASE)
|
||||
if m:
|
||||
if rating == 0.0:
|
||||
m_rat = re.search(r'^([1-5][.,]\d)', line)
|
||||
if m_rat:
|
||||
try:
|
||||
total_avaliacoes = int(re.sub(r'\D', '', m.group(1)))
|
||||
val = float(m_rat.group(1).replace(',', '.'))
|
||||
if 1.0 <= val <= 5.0:
|
||||
rating = val
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 4. Fallback para o detail_panel se o container do card não possuir a nota
|
||||
# 4. Fallback no detail_panel da empresa clicada caso o card na lista não exiba a contagem
|
||||
if (rating == 0.0 or total_avaliacoes == 0) and detail_panel:
|
||||
detail_text = detail_panel.inner_text() or ""
|
||||
|
||||
@@ -106,14 +98,14 @@ class ScraperService:
|
||||
pass
|
||||
|
||||
if total_avaliacoes == 0:
|
||||
rev_elem = detail_panel.query_selector("button[aria-label*='avaliaç'], span[aria-label*='avaliaç']")
|
||||
rev_elem = detail_panel.query_selector("button[aria-label*='avaliaç'], button[aria-label*='comentário'], span[aria-label*='comentário']")
|
||||
if rev_elem:
|
||||
rv_txt = rev_elem.get_attribute("aria-label") or rev_elem.inner_text()
|
||||
rv_m = re.search(r'([\d.,]+)\s*avaliaç', rv_txt, re.IGNORECASE)
|
||||
rv_m = re.search(r'([\d.,]+)\s*(?:comentários|avaliações)', rv_txt, re.IGNORECASE)
|
||||
if rv_m:
|
||||
try:
|
||||
cnt = int(re.sub(r'\D', '', rv_m.group(1)))
|
||||
if cnt < 20000:
|
||||
if cnt < 200000:
|
||||
total_avaliacoes = cnt
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user