gendesign/tradein-mvp/scripts/probe-db-leak-full.py
lekss361 d2a0257639
All checks were successful
Deploy Trade-In / changes (push) Successful in 6s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 32s
feat(tradein-scripts): scrapers + probes + Phase C improvements (#551)
2026-05-24 19:58:38 +00:00

129 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Полная проверка региональной утечки в БД Avito listings.
Угол 1: source_url slug (canonical, надёжный)
Угол 2: address с маркером области (квартиры в ЕКБ не содержат "обл.")
Угол 3: address contains имя пригорода/соседнего города
Угол 4: top самых частых первых слов address (sanity check)
"""
from __future__ import annotations
import os
from collections import Counter
import psycopg
dsn = os.environ["DATABASE_URL"].replace("postgresql+psycopg://", "postgresql://")
conn = psycopg.connect(dsn)
cur = conn.cursor()
cur.execute("SELECT count(*) FROM listings WHERE source='avito'")
total = cur.fetchone()[0]
print(f"=== Avito total: {total} ===\n")
# Угол 1: URL slug (canonical)
print("[1] URL slug check:")
cur.execute(
"""
SELECT
count(*) FILTER (WHERE source_url ~ 'avito\\.ru/ekaterinburg/') AS ekb,
count(*) FILTER (WHERE source_url IS NOT NULL AND source_url !~ 'avito\\.ru/ekaterinburg/') AS not_ekb,
count(*) FILTER (WHERE source_url IS NULL) AS no_url
FROM listings WHERE source='avito'
"""
)
ekb, not_ekb, no_url = cur.fetchone()
print(f" ekaterinburg URL: {ekb}")
print(f" NOT ekaterinburg: {not_ekb} {'← LEAK' if not_ekb else '✓ clean'}")
print(f" NULL url: {no_url}")
# Угол 2: address с "область" / "обл."
print("\n[2] Address contains region marker (область/обл.):")
cur.execute(
"""
SELECT count(*) FROM listings
WHERE source='avito'
AND address IS NOT NULL
AND (address ILIKE '%область%' OR address ~* '\\bобл\\b' OR address ~* '\\bобл\\.')
"""
)
n = cur.fetchone()[0]
print(f" count: {n} {'← suspicious' if n > 5 else '✓ ok'}")
if n > 0:
cur.execute(
"""
SELECT source_url, address FROM listings
WHERE source='avito' AND address IS NOT NULL
AND (address ILIKE '%область%' OR address ~* '\\bобл\\b' OR address ~* '\\bобл\\.')
LIMIT 10
"""
)
for url, addr in cur.fetchall():
slug = url.split('avito.ru/')[1].split('/')[0] if url and 'avito.ru/' in url else '?'
print(f" [{slug}] {addr[:90]!r}")
# Угол 3: address contains имя другого города/пригорода
print("\n[3] Address contains satellite-city marker:")
satellites = [
"Верхняя Пышма", "Арамиль", "Сысерть", "Дегтярск", "Первоуральск",
"Заречный", "Невьянск", "Нижний Тагил", "Кыштым", "Челябинск",
"Алапаевск", "Краснофимск", "Озёрск", "Озерск", "Снежинск", "Касли",
"Рефтинский", "Свободный", "Большой Исток", "Пышма", "Покровское",
"Реж", "Лесной", "Полевской", "Берёзовск", "Березовск", "Михайловск",
"Туринск", "Североуральск", "Тавда", "Богданович", "Артёмовский",
"Артемовский", "Качканар", "Ивдель", "Серов", "Ирбит", "Новоуральск",
"Камышлов", "Сухой Лог",
]
for city in satellites:
cur.execute(
"SELECT count(*) FROM listings WHERE source='avito' AND address ILIKE %s",
(f"%{city}%",),
)
n = cur.fetchone()[0]
if n > 0:
print(f" {city}: {n}")
cur.execute(
"""
SELECT count(*) FROM listings WHERE source='avito' AND (
address ILIKE '%Верхняя Пышма%' OR address ILIKE '%Арамиль%' OR
address ILIKE '%Сысерть%' OR address ILIKE '%Дегтярск%' OR
address ILIKE '%Первоуральск%' OR address ILIKE '%Заречный%' OR
address ILIKE '%Невьянск%' OR address ILIKE '%Нижний Тагил%' OR
address ILIKE '%Кыштым%' OR address ILIKE '%Челябинск%' OR
address ILIKE '%Алапаевск%' OR address ILIKE '%Краснофимск%' OR
address ILIKE '%Озёрск%' OR address ILIKE '%Озерск%' OR
address ILIKE '%Снежинск%' OR address ILIKE '%Касли%' OR
address ILIKE '%Рефтинский%' OR address ILIKE '%Большой Исток%' OR
address ILIKE '%Реж%' OR address ILIKE '%Лесной%' OR address ILIKE '%Полевской%' OR
address ILIKE '%Берёзовск%' OR address ILIKE '%Березовск%'
)
"""
)
sat_total = cur.fetchone()[0]
print(f" TOTAL satellite-city leaks: {sat_total}")
# Угол 4: top first-words of address
print("\n[4] Top 20 first-words in address (sanity check):")
cur.execute(
"""
SELECT split_part(trim(address), ' ', 1) AS first_word, count(*) AS n
FROM listings WHERE source='avito' AND COALESCE(address, '') != ''
GROUP BY 1 ORDER BY 2 DESC LIMIT 20
"""
)
for word, n in cur.fetchall():
print(f" {n:>5} {word!r}")
# Угол 5: address NULL — сколько неopределённых
print("\n[5] Coverage:")
cur.execute(
"""
SELECT
count(*) FILTER (WHERE COALESCE(address, '') = '') AS no_addr,
count(*) FILTER (WHERE COALESCE(address, '') != '') AS has_addr
FROM listings WHERE source='avito'
"""
)
no_a, has_a = cur.fetchone()
print(f" has address: {has_a}")
print(f" no address: {no_a}")