Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI | |
| from playwright.async_api import async_playwright | |
| # Note: We removed 'playwright-stealth' because mobile emulation handles the user-agent naturally | |
| import urllib.parse | |
| import asyncio | |
| app = FastAPI() | |
| def home(): | |
| return {"status": "Mobile Worker is Running"} | |
| async def search_aliexpress(query: str): | |
| encoded_query = urllib.parse.quote(query) | |
| # 1. Use the Mobile URL (m.aliexpress.com) | |
| url = f"https://www.aliexpress.com/wholesale?SearchText={encoded_query}" | |
| results = [] | |
| error_snapshot = "" | |
| async with async_playwright() as p: | |
| # 2. Use a Preset Device Profile (Google Pixel 7) | |
| pixel_7 = p.devices['Pixel 7'] | |
| browser = await p.chromium.launch(headless=True, args=[ | |
| '--no-sandbox', | |
| '--disable-setuid-sandbox', | |
| '--disable-blink-features=AutomationControlled' | |
| ]) | |
| # 3. Create context with the Device Profile (Sets User-Agent, Screen Size, Touch, etc.) | |
| context = await browser.new_context( | |
| **pixel_7, | |
| locale='en-US', | |
| timezone_id='America/New_York' | |
| ) | |
| page = await context.new_page() | |
| try: | |
| await page.goto(url, timeout=60000) | |
| # Wait for items (Mobile layout is different, but links still contain /item/) | |
| try: | |
| await page.wait_for_selector('a[href*="/item/"]', timeout=15000) | |
| except: | |
| error_snapshot = await page.title() | |
| # If we see a captcha title, we throw an error | |
| if "captcha" in error_snapshot.lower() or "security" in error_snapshot.lower(): | |
| raise Exception(f"Blocked by Security. Title: {error_snapshot}") | |
| raise Exception(f"Products not found. Title: {error_snapshot}") | |
| # Extract Data (Logic adapted for Mobile/Responsive Layout) | |
| products = await page.eval_on_selector_all('a[href*="/item/"]', """ | |
| links => { | |
| return links.slice(0, 10).map(link => { | |
| // Find the container card | |
| let card = link.closest('div[class*="search-item"]') || link.parentElement.parentElement; | |
| // Default values | |
| let title = "No Title"; | |
| let price = "0.00"; | |
| let img = ""; | |
| if (card) { | |
| // Try to find image | |
| const imgEl = card.querySelector('img'); | |
| if (imgEl) img = imgEl.src; | |
| // Try to find text | |
| const text = card.innerText; | |
| const lines = text.split('\\n'); | |
| if (lines.length > 0) title = lines[0]; | |
| // Regex for price | |
| const priceMatch = text.match(/[\\$€£]\\s?(\\d+(\\.\\d+)?)/); | |
| if (priceMatch) price = priceMatch[0]; | |
| } | |
| return { | |
| title: title, | |
| price: price, | |
| image: img, | |
| link: link.href | |
| }; | |
| }); | |
| } | |
| """) | |
| # Simple Deduplication | |
| seen = set() | |
| for item in products: | |
| if item['link'] and item['link'] not in seen: | |
| results.append(item) | |
| seen.add(item['link']) | |
| except Exception as e: | |
| return {"error": str(e), "debug_title": error_snapshot} | |
| finally: | |
| await browser.close() | |
| return {"results": results} |