mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-20 23:21:06 +02:00
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:
parent
8331440f4b
commit
603c94c80b
3 changed files with 52 additions and 8 deletions
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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] = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue