feat(amazon): enhance product fetching and parsing logic for better offer retrieval

- Updated the fetch function to correctly construct the URL for the all-offers panel.
- Improved the product parser to include additional selectors for price and seller information.
- Added a fallback mechanism to retrieve seller information from the buy-box when not available in the standard link.
This commit is contained in:
Anish Sarkar 2026-07-15 11:04:46 +05:30
parent 8331440f4b
commit 603c94c80b
3 changed files with 52 additions and 8 deletions

View file

@ -195,8 +195,13 @@ async def fetch_aod_html(
cookies: dict[str, str] | None = None,
proxy: str | None = None,
) -> str | None:
"""Fetch the public all-offers panel for one product."""
url = f"{_origin(domain)}/gp/product/ajax?asin={asin}&experienceId=aodAjaxMain"
"""Fetch the public all-offers panel for one product.
``aodAjaxMain`` is a path segment, not a query param. Amazon also serves this
panel as a JS-only modal for many (especially US) ASINs, so a 404 here is
expected and the caller falls back to the PDP buy-box winner.
"""
url = f"{_origin(domain)}/gp/product/ajax/aodAjaxMain/?asin={asin}"
return await fetch_html(url, cookies=cookies, proxy=proxy)

View file

@ -258,14 +258,20 @@ def parse_product(
price_text = _first_text(
doc,
"#corePrice_feature_div .a-offscreen",
"#corePriceDisplay_desktop_feature_div .a-offscreen",
"#apex_desktop .a-price .a-offscreen",
"#priceblock_ourprice",
"#priceblock_dealprice",
".priceToPay .a-offscreen",
"#price_inside_buybox",
"#centerCol .a-price .a-offscreen",
)
list_price_text = _first_text(
doc,
"#corePrice_feature_div .basisPrice .a-offscreen",
"#corePriceDisplay_desktop_feature_div .basisPrice .a-offscreen",
".priceBlockStrikePriceString",
"#centerCol .a-price.a-text-price .a-offscreen",
)
availability = _first_text(doc, "#availability", "#outOfStock")
availability_lower = (availability or "").lower()
@ -330,6 +336,13 @@ def parse_product(
)
seller_href = _attr(seller_link, "href")
seller_id = (parse_qs(urlparse(seller_href or "").query).get("seller") or [None])[0]
# Fall back to the buy-box "Sold by" name when there is no seller profile link
# (Amazon-direct items and some link-less third-party merchants).
seller_name = _text(seller_link) or _first_text(
doc,
"#tabular-buybox [tabular-attribute-name='Sold by'] .tabular-buybox-text",
"#merchantInfoFeature_feature_div .offer-display-feature-text-message",
)
return {
"title": title,
"url": url,
@ -425,10 +438,10 @@ def parse_product(
"seller": (
{
"id": seller_id,
"name": _text(seller_link),
"name": seller_name,
"url": _absolute(domain, seller_href),
}
if seller_link is not None
if (seller_link is not None or seller_name)
else None
),
"bestsellerRanks": ranks,

View file

@ -49,6 +49,28 @@ def _error(
).model_dump()
def _buybox_offer(fields: dict[str, Any]) -> dict[str, Any] | None:
"""Synthesize a single offer from the PDP buy box.
Amazon serves the All-Offers-Display panel as a JS-only modal for many
(especially US) ASINs, so the AOD ajax endpoint 404s. Fall back to the
featured buy-box winner already parsed from the product page.
"""
price = fields.get("price")
seller = fields.get("seller")
seller = seller if isinstance(seller, dict) else None
if not price and not (seller and seller.get("name")):
return None
return {
"position": 1,
"price": price,
"condition": fields.get("condition") or "New",
"delivery": fields.get("delivery"),
"seller": seller,
"isPinnedOffer": True,
}
def _page_url(url: str, page: int) -> str:
parsed = urlparse(url)
query = dict(parse_qsl(parsed.query, keep_blank_values=True))
@ -154,10 +176,14 @@ async def _product_flow(
cookies=offer_cookies or cookies,
proxy=offer_proxy or proxy,
)
if offers_html:
fields["offers"] = parse_aod_offers(offers_html, domain=resolved.domain)[
: input_model.maxOffers
]
offers = (
parse_aod_offers(offers_html, domain=resolved.domain) if offers_html else []
)
if not offers:
buybox = _buybox_offer(fields)
offers = [buybox] if buybox else []
if offers:
fields["offers"] = offers[: input_model.maxOffers]
if input_model.scrapeSellers:
seller_ids: list[str] = []