From 1d788b27d010e66f91d0c3dc47424dc18e471b71 Mon Sep 17 00:00:00 2001 From: Light1YT Date: Fri, 22 May 2026 13:49:09 +0500 Subject: [PATCH] =?UTF-8?q?fix(tradein):=20Avito-=D0=BF=D0=B0=D1=80=D1=81?= =?UTF-8?q?=D0=B5=D1=80=20=D1=82=D0=B0=D1=89=D0=B8=D1=82=20=D1=84=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=20=D0=B8=20=D1=80=D0=B5=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=20=D0=B8=D0=B7=20?= =?UTF-8?q?DOM-=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DOM-путь (_dom_card_to_lot) — основной для Avito: mobile API отдаёт бан, формат __initialState__ устарел, так что почти все объявления идут через него. Но он не извлекал фото вообще, а адрес брал мёртвым селектором [class*="geo-address"] — поэтому объявления Avito оставались без фото (серый блок в Сводке) и с заглушкой «Екатеринбург (Avito)». Теперь: - фото — из карточки-слайдера (до 5 шт); - адрес — первый

внутри data-marker="item-location" (улица + дом). Селекторы проверены на живой выдаче Avito. --- .../backend/app/services/scrapers/avito.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/tradein-mvp/backend/app/services/scrapers/avito.py b/tradein-mvp/backend/app/services/scrapers/avito.py index 78a697d8..2d724764 100644 --- a/tradein-mvp/backend/app/services/scrapers/avito.py +++ b/tradein-mvp/backend/app/services/scrapers/avito.py @@ -295,7 +295,8 @@ class AvitoScraper(BaseScraper): return None def _dom_card_to_lot(self, card: Any, source_url_base: str) -> ScrapedLot | None: - """Парсинг через DOM (fallback). Avito state часто пустой — items падают сюда.""" + """Парсинг через DOM. Avito state давно пустой (см. _parse_html) — почти все + объявления приходят сюда, поэтому путь обязан тащить и адрес, и фото.""" try: link_el = card.css_first('a[data-marker="item-title"]') if link_el is None: @@ -313,8 +314,24 @@ class AvitoScraper(BaseScraper): area = _extract_area_from_title(title) floor, total = _extract_floor_from_title(title) - addr_el = card.css_first('[class*="geo-address"]') - address = addr_el.text(strip=True) if addr_el is not None else None + # Адрес: первый

внутри data-marker="item-location" — это улица + дом. + # Второй

— метро/район, его отбрасываем. Старый селектор + # [class*="geo-address"] мёртв: Avito перешёл на CSS-модули с хешами. + address: str | None = None + loc_el = card.css_first('[data-marker="item-location"]') + if loc_el is not None: + street_el = loc_el.css_first("p") + if street_el is not None: + address = street_el.text(strip=True) or None + + # Фото: карточки-слайдера, реальный URL прямо в src. + photo_urls: list[str] = [] + for img in card.css('img[itemprop="image"]'): + src = img.attributes.get("src") or "" + if src.startswith("http") and src not in photo_urls: + photo_urls.append(src) + if len(photo_urls) >= 5: + break # Anchor jitter — Avito DOM не отдаёт точные coords, используем центр scrape ±jitter lat = lon = None @@ -335,9 +352,10 @@ class AvitoScraper(BaseScraper): floor=floor, total_floors=total, price_rub=price, - raw_payload={"title": title}, + photo_urls=photo_urls, + raw_payload={"title": title, "address": address}, ) - except Exception: # noqa: BLE001 + except Exception: return None -- 2.45.3