Files
BSN-Chatsystem/scrape_mazda.py
T

144 lines
5.6 KiB
Python

#!/usr/bin/env python3
"""Scrape mobile.de for Mazda 2 listings near Mönchengladbach using Playwright + proxy"""
import asyncio, json, re, sys
from playwright.async_api import async_playwright
MG_PLZ = "41061"
BUDGET = 10000
MAX_KM = 120000
MIN_YEAR = 2014
# mobile.de URL with filters
URL = (
f"https://suchen.mobile.de/fahrzeuge/search.html"
f"?dam=0&isSearchRequest=true&ms=1200;3;;&pMax={BUDGET}"
f"&mlMin={MIN_YEAR}&mlMax:{MAX_KM}&sf={MG_PLZ}&vc=Car&od=down&sb=rel"
)
PROXY = {
"server": "http://geo.iproyal.com:12321",
"username": "8B7M8nJOfXTNHPUw",
"password": "lR1VidJhN5RrRbeK_region-europe"
}
async def main():
listings = []
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=['--no-sandbox', '--disable-setuid-sandbox']
)
context = await browser.new_context(
proxy=PROXY,
user_agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
locale='de-DE',
timezone_id='Europe/Berlin'
)
page = await context.new_page()
try:
print(f"Loading {URL}")
await page.goto(URL, wait_until='networkidle', timeout=30000)
content = await page.content()
print(f"Page loaded: {len(content)} bytes")
if 'Zugriff verweigert' in content or 'Access denied' in content:
print("BLOCKED - access denied with proxy too")
await browser.close()
return []
# Wait for results to load
try:
await page.wait_for_selector('[data-testid="result-list"], .result-list, .cfyXj, article', timeout=10000)
except:
print("No results selector found, trying to extract anyway...")
# Extract listings from page
content = await page.content()
# Try to find structured data via JSON-LD or embedded data
# mobile.de embeds listing data in <script> tags or in the HTML
# Method 1: Look for data attributes with listing info
listing_blocks = re.findall(
r'<article[^>]*>(.*?)</article>',
content, re.DOTALL
)
print(f"Found {len(listing_blocks)} article blocks")
if not listing_blocks:
# Try alternative selectors
listing_blocks = re.findall(
r'<(?:div|article)[^>]*class="[^"]*?(?:result-item|listing-item|cBoxBody)[^"]*"[^>]*>(.*?)</(?:div|article)>',
content, re.DOTALL
)
print(f"Found {len(listing_blocks)} result-item blocks")
for i, block in enumerate(listing_blocks[:25]):
# Extract title
title_m = re.search(r'<h[23][^>]*>(.*?)</h[23]>', block, re.DOTALL)
title = re.sub(r'<[^>]+>', '', title_m.group(1)).strip() if title_m else '?'
# Extract price
price_m = re.search(r'(\d{1,2}[\.\d]*\s*€)', block)
price = price_m.group(1) if price_m else '?'
# Extract mileage
km_m = re.search(r'(\d{1,3}[\.\d]*\s*km)', block, re.IGNORECASE)
km = km_m.group(1) if km_m else '?'
# Extract year
year_m = re.search(r'(EZ\s*\d{1,2}/\d{4}|(?:20[12]\d{1}))', block)
year = year_m.group(1) if year_m else '?'
# Extract location
loc_m = re.search(r'(?:4[0-9]{4}\s[A-Za-zäöüß\s-]+)', block)
loc = loc_m.group(0) if loc_m else '?'
# Extract link
link_m = re.search(r'href="(/fahrzeuge/detail/[^"]+)"', block)
link = f"https://suchen.mobile.de{link_m.group(1)}" if link_m else '?'
listings.append({
'title': title,
'price': price,
'km': km,
'year': year,
'location': loc,
'link': link
})
# If no blocks found, try extracting from full page text
if not listings:
# Try to find price/km/year patterns in the whole page
price_kms = re.findall(r'(\d{1,2}[\.\d]*\s*€)\s*.*?(\d{1,3}[\.\d]*\s*km)', content, re.DOTALL)
print(f"Found {len(price_kms)} price-km pairs in full text")
for pk in price_kms[:15]:
print(f" {pk[0]} | {pk[1]}")
# Print results
print(f"\n=== Extracted {len(listings)} listings ===")
for i, l in enumerate(listings):
print(f"{i+1}. {l['title']}")
print(f" {l['price']} | {l['km']} | {l['year']} | {l['location']}")
print(f" {l['link']}")
print()
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
finally:
await browser.close()
return listings
if __name__ == "__main__":
listings = asyncio.run(main())
# Write JSON output
with open("/tmp/mazda2_listings.json", "w") as f:
json.dump(listings, f, indent=2, ensure_ascii=False)
print(f"\nSaved {len(listings)} listings to /tmp/mazda2_listings.json")