fix(tradein): yandex newbuilding rating drift — «4.3из 5» без пробела (#974) #1545

Merged
lekss361 merged 1 commit from fix/yandex-nb-rating-drift into main 2026-06-15 18:31:12 +00:00
2 changed files with 33 additions and 11 deletions

View file

@ -93,7 +93,10 @@ RE_AREA_HA = re.compile(r"участке\s+([\d,]+)\s*га", re.IGNORECASE)
RE_COMMISSION = re.compile(
r"(?:введ[её]н|сдан)\s+в\s+эксплуатацию\s+в\s+(\w+)\s+(\d{4})", re.IGNORECASE
)
RE_RATING = re.compile(r"(\d[.,]\d)\s+из\s+5")
# Yandex drift (#974 follow-up): рейтинг рендерится без пробела перед «из»
# ("4.3из 5" / "4,5из 5", nbsp внутри) → \s* (а не \s+) перед «из», иначе rating=NULL
# при заполненных ratings_count/text_reviews (verified live 2026-06-15, ЖК Татлин/Рио).
RE_RATING = re.compile(r"(\d[.,]\d)\s*из\s+5")
RE_RATINGS_COUNT = re.compile(r"(\d+)\s+оценок", re.IGNORECASE)
RE_TEXT_REVIEWS = re.compile(r"Смотреть\s+все\s+(\d+)\s+отзыв", re.IGNORECASE)
RE_CORPUS_COUNT_WORD = re.compile(

View file

@ -246,6 +246,32 @@ def test_parse_rating_comma_separator():
assert info.ratings_count == 200
def test_parse_rating_no_space_before_iz():
"""Yandex drift (#974): '4.3из 5' / '4,5из\xa05' — без пробела перед «из», nbsp внутри.
Регрессия 2026-06-15: live-страницы рендерили рейтинг как «4.3из 5» (без
разделителя), из-за чего \\s+ перед «из» не матчил rating=NULL при
заполненных ratings_count. \\s* должен извлечь рейтинг в обеих формах.
"""
scraper = YandexNewbuildingScraper()
info = scraper.parse(
"<html><body><div>Отзывы4.3из 5Всего 1508 оценок</div></body></html>",
jk_slug="x",
jk_id="1",
source_url="http://x",
)
assert info.rating == pytest.approx(4.3, abs=0.01)
assert info.ratings_count == 1508
# nbsp-вариант (как в реальном body_text)
info2 = scraper.parse(
"<html><body><div>4,5из\xa05Всего 850 оценок</div></body></html>",
jk_slug="y",
jk_id="2",
source_url="http://y",
)
assert info2.rating == pytest.approx(4.5, abs=0.01)
# ── Developer link test ───────────────────────────────────────────────────────
@ -284,9 +310,7 @@ def test_metro_stations_parsed():
source_url="https://realty.yandex.ru/ekaterinburg/kupit/novostrojka/tatlin-1592987/",
)
assert len(info.metro_stations) >= 1
uralskaya = next(
(s for s in info.metro_stations if "Уральская" in s.name), None
)
uralskaya = next((s for s in info.metro_stations if "Уральская" in s.name), None)
assert uralskaya is not None
assert uralskaya.walk_min == 11
@ -302,10 +326,7 @@ def test_parse_metro_direct():
def test_parse_metro_caps_at_five():
text = (
"Альфа 5 мин. Бета 6 мин. Гамма 7 мин. "
"Дельта 8 мин. Эпсилон 9 мин. Зета 10 мин."
)
text = "Альфа 5 мин. Бета 6 мин. Гамма 7 мин. " "Дельта 8 мин. Эпсилон 9 мин. Зета 10 мин."
stations = _parse_metro(text)
assert len(stations) == 5
@ -387,9 +408,7 @@ def test_developer_other_jk_dedup():
def test_developer_other_jk_capped_at_10():
"""Only first 10 other JK entries are kept."""
links = "\n".join(
f'<a href="/{i}">ЖК {i}</a>' for i in range(15)
)
links = "\n".join(f'<a href="/{i}">ЖК {i}</a>' for i in range(15))
html = f"""<html><body>
<div data-test="CardDevSites">{links}</div>
</body></html>"""