| from fastapi import FastAPI |
| from pydantic import BaseModel |
| from playwright.sync_api import sync_playwright |
| import random |
| import time |
| import re |
|
|
| app = FastAPI() |
|
|
| MIN_DELAY = 2 |
| MAX_DELAY = 9 |
|
|
|
|
| def human_delay(): |
| """Add a random delay to mimic human behavior""" |
| time.sleep(random.uniform(MIN_DELAY, MAX_DELAY)) |
|
|
|
|
| def clean_amazon_image(url): |
| """Remove Amazon image size parameters to get higher quality image""" |
| if url: |
| |
| return re.sub(r'\._AC_[^.]*\.', '.', url) |
| return url |
|
|
|
|
| class SearchInput(BaseModel): |
| upc: str |
|
|
|
|
| @app.get("/") |
| def home(): |
| """Health check endpoint""" |
| return {"status": "Amazon scraper API running", "version": "1.0"} |
|
|
|
|
| @app.post("/search") |
| def search_product(data: SearchInput): |
| """ |
| Search for a product on Amazon by UPC |
| Returns product details including ASIN, title, image, and URL |
| """ |
| upc = data.upc.strip() |
| |
| if not upc: |
| return {"error": "UPC cannot be empty", "SKU": upc} |
|
|
| browser = None |
| |
| try: |
| with sync_playwright() as p: |
| |
| browser = p.chromium.launch( |
| headless=True, |
| args=['--disable-blink-features=AutomationControlled'] |
| ) |
| |
| |
| context = browser.new_context( |
| locale="en-US", |
| user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" |
| ) |
| |
| page = context.new_page() |
|
|
| |
| print(f"Searching for UPC: {upc}") |
| page.goto( |
| f"https://www.amazon.com/s?k={upc}", |
| wait_until="domcontentloaded", |
| timeout=60000 |
| ) |
|
|
| |
| try: |
| page.wait_for_selector( |
| "div[data-component-type='s-search-result']", |
| timeout=10000 |
| ) |
| except Exception as wait_error: |
| print(f"Wait error: {wait_error}") |
| |
| page.screenshot(path="/home/claude/debug_screenshot.png") |
| browser.close() |
| return { |
| "error": "No search results found or page took too long to load", |
| "SKU": upc |
| } |
|
|
| human_delay() |
|
|
| |
| item = page.query_selector( |
| "div[data-component-type='s-search-result']" |
| ) |
|
|
| if not item: |
| browser.close() |
| return {"error": "Product not found", "SKU": upc} |
|
|
| |
| asin = item.get_attribute("data-asin") |
| if not asin: |
| asin = "" |
|
|
| |
| title_el = item.query_selector("h2 span") |
| title = title_el.inner_text().strip() if title_el else "" |
|
|
| |
| img_el = item.query_selector("img.s-image") |
| image = "" |
| if img_el: |
| |
| image = img_el.get_attribute("src") |
| if not image or "data:image" in image: |
| image = img_el.get_attribute("data-image-latency-src") or "" |
| image = clean_amazon_image(image) |
|
|
| |
| link_el = item.query_selector("h2 a") |
| link = "" |
| if link_el: |
| href = link_el.get_attribute("href") |
| if href: |
| |
| if href.startswith("http"): |
| link = href |
| else: |
| link = "https://www.amazon.com" + href |
|
|
| browser.close() |
|
|
| return { |
| "SKU": upc, |
| "ASIN": asin, |
| "Title": title, |
| "Image": image, |
| "AmazonURL": link |
| } |
|
|
| except Exception as e: |
| if browser: |
| try: |
| browser.close() |
| except: |
| pass |
| |
| print(f"Error occurred: {str(e)}") |
| return { |
| "error": f"An error occurred: {str(e)}", |
| "SKU": upc |
| } |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=8000) |