fix(tradein): yandex newbuilding rating drift — «4.3из 5» без пробела (#974) #1545
2 changed files with 33 additions and 11 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue