"""Полная проверка региональной утечки в БД 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}")