Compare commits

...

4 commits

Author SHA1 Message Date
2b16e52a53 Fix HOWOGE: iterate selectors to find visible submit button 2025-12-09 14:36:24 +01:00
f0a21bd5f4 Fix Gewobag: use Wohnungshelden iframe like Degewo
- Gewobag has Wohnungshelden iframe embedded on listing page
- Navigate directly to iframe URL
- Fill form fields: firstName, lastName, email, phone-number
- Submit button: 'Anfrage versenden'
2025-12-09 14:30:44 +01:00
180666c781 Fix Stadt und Land: embedded form with two-step submit
- Form is embedded directly on page (no iframe)
- Fields: name, surname, street, houseNo, postalCode, city, phone, email
- Checkboxes: privacy, provision
- Step 1: Click 'Eingaben prüfen' to validate
- Step 2: Click 'Absenden' to submit
2025-12-09 14:24:19 +01:00
9b322d72ca Fix HOWOGE: handle multi-step form with checkboxes and Weiter buttons
- HOWOGE form has 3-4 steps with checkboxes before form fields appear
- Each step: click visible checkbox, then click Weiter button
- Use JavaScript click for checkboxes (avoids viewport issues)
- Form fields: firstName, lastName, email
- Submit button: 'Anfrage senden'
2025-12-09 14:14:25 +01:00

View file

@ -450,90 +450,99 @@ class ApplicationHandler:
await apply_btn.click()
await asyncio.sleep(3)
await page.wait_for_load_state("networkidle")
logger.info("[HOWOGE] Clicked button, waiting for form...")
logger.info("[HOWOGE] Clicked button, starting multi-step form process...")
# Scroll down the page to reveal checkboxes
await page.evaluate("window.scrollBy(0, 500)")
await asyncio.sleep(1)
logger.info("[HOWOGE] Scrolled down to reveal checkboxes")
# HOWOGE has a multi-step form (typically 3-4 steps):
# Each step has a checkbox that must be clicked, then "Weiter" button
# Final step has the actual contact form
# Screenshot after clicking (before checkboxes)
screenshot_path = DATA_DIR / f"howoge_form_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[HOWOGE] Saved form screenshot to {screenshot_path}")
max_steps = 6 # safety limit
for step in range(1, max_steps + 1):
logger.info(f"[HOWOGE] Processing step {step}")
# HOWOGE requires clicking checkboxes before the form fields become visible
# Look for and click all required checkboxes
checkboxes_clicked = 0
checkbox_selectors = [
'input[type="checkbox"]',
'.checkbox input',
'input[name*="datenschutz" i]',
'input[name*="privacy" i]',
'input[name*="einwillig" i]',
'input[name*="zustimmung" i]',
'input[name*="accept" i]',
]
# Scroll down to reveal checkboxes
await page.evaluate("window.scrollBy(0, 300)")
await asyncio.sleep(0.5)
for selector in checkbox_selectors:
checkboxes = await page.query_selector_all(selector)
# Check if we've reached the form (email field is visible)
email_field = await page.query_selector('input[name*="email" i]')
if email_field and await email_field.is_visible():
logger.info("[HOWOGE] Email field is visible - form is ready!")
break
# Find and click any visible unchecked checkboxes
checkboxes = await page.query_selector_all('input[type="checkbox"]')
clicked_checkbox = False
for checkbox in checkboxes:
try:
if await checkbox.is_visible() and not await checkbox.is_checked():
await checkbox.scroll_into_view_if_needed()
await checkbox.click()
checkboxes_clicked += 1
logger.info(f"[HOWOGE] Clicked checkbox")
await asyncio.sleep(0.3)
# Use JavaScript click to avoid viewport issues
await checkbox.evaluate("el => el.click()")
clicked_checkbox = True
logger.info(f"[HOWOGE] Clicked checkbox in step {step}")
await asyncio.sleep(0.5)
except Exception as e:
logger.debug(f"[HOWOGE] Checkbox click failed: {e}")
if checkboxes_clicked > 0:
logger.info(f"[HOWOGE] Clicked {checkboxes_clicked} checkboxes")
await asyncio.sleep(1)
if clicked_checkbox:
await asyncio.sleep(1) # Wait for page to update after checkbox
# Screenshot after clicking checkboxes
screenshot_path = DATA_DIR / f"howoge_after_checkboxes_{listing['id']}.png"
# Screenshot this step
screenshot_path = DATA_DIR / f"howoge_step{step}_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[HOWOGE] Saved after-checkboxes screenshot")
# Scroll back up to find the form fields
await page.evaluate("window.scrollTo(0, 0)")
await asyncio.sleep(0.5)
# Look for visible "Weiter" button and click it
weiter_btns = await page.query_selector_all('button:has-text("Weiter")')
weiter_clicked = False
for btn in weiter_btns:
try:
if await btn.is_visible():
await btn.click()
weiter_clicked = True
logger.info(f"[HOWOGE] Clicked 'Weiter' button in step {step}")
await asyncio.sleep(2)
await page.wait_for_load_state("networkidle")
break
except Exception as e:
logger.debug(f"[HOWOGE] Weiter click failed: {e}")
# Fill in the contact form
# Look for name fields (Vorname, Nachname)
vorname_field = await page.query_selector('input[name*="vorname" i], input[name*="firstname" i], input[placeholder*="Vorname" i], input#vorname')
nachname_field = await page.query_selector('input[name*="nachname" i], input[name*="lastname" i], input[name*="surname" i], input[placeholder*="Nachname" i], input#nachname')
email_field = await page.query_selector('input[type="email"], input[name*="email" i], input[name*="mail" i]')
if not weiter_clicked and not clicked_checkbox:
logger.warning(f"[HOWOGE] No action possible in step {step}, breaking")
break
# Now try to fill the form
logger.info("[HOWOGE] Attempting to fill form fields...")
# Look for name fields - HOWOGE uses firstName/lastName
vorname_field = await page.query_selector('input[name*="firstName" i], input[name*="vorname" i]')
nachname_field = await page.query_selector('input[name*="lastName" i], input[name*="nachname" i]')
email_field = await page.query_selector('input[type="email"], input[name*="email" i]')
form_filled = False
if vorname_field:
await vorname_field.scroll_into_view_if_needed()
await asyncio.sleep(0.2)
if vorname_field and await vorname_field.is_visible():
await vorname_field.fill(FORM_VORNAME)
logger.info(f"[HOWOGE] Filled Vorname: {FORM_VORNAME}")
form_filled = True
else:
logger.warning("[HOWOGE] Vorname field not found or not visible")
if nachname_field:
await nachname_field.scroll_into_view_if_needed()
await asyncio.sleep(0.2)
if nachname_field and await nachname_field.is_visible():
await nachname_field.fill(FORM_NACHNAME)
logger.info(f"[HOWOGE] Filled Nachname: {FORM_NACHNAME}")
form_filled = True
else:
logger.warning("[HOWOGE] Nachname field not found or not visible")
if email_field:
await email_field.scroll_into_view_if_needed()
await asyncio.sleep(0.2)
if email_field and await email_field.is_visible():
await email_field.fill(FORM_EMAIL)
logger.info(f"[HOWOGE] Filled Email: {FORM_EMAIL}")
form_filled = True
else:
logger.warning("[HOWOGE] Email field not found or not visible")
# Also look for phone field
phone_field = await page.query_selector('input[type="tel"], input[name*="telefon" i], input[name*="phone" i]')
if phone_field:
await phone_field.scroll_into_view_if_needed()
await asyncio.sleep(0.2)
if phone_field and await phone_field.is_visible():
await phone_field.fill(FORM_PHONE)
logger.info(f"[HOWOGE] Filled Phone: {FORM_PHONE}")
@ -543,9 +552,16 @@ class ApplicationHandler:
logger.info(f"[HOWOGE] Saved filled form screenshot to {screenshot_path2}")
if form_filled:
# Look for submit button
submit_btn = await page.query_selector('button[type="submit"], input[type="submit"], button:has-text("Absenden"), button:has-text("Senden"), button:has-text("Anfrage")')
if submit_btn and await submit_btn.is_visible():
# Look for submit button - HOWOGE uses "Anfrage senden"
# Try specific selectors first, then fall back
submit_btn = None
for selector in ['button:has-text("Anfrage senden")', 'button:has-text("Absenden")', 'button:has-text("Senden")']:
btn = await page.query_selector(selector)
if btn and await btn.is_visible():
submit_btn = btn
logger.info(f"[HOWOGE] Found submit button with selector: {selector}")
break
if submit_btn:
logger.info("[HOWOGE] Found submit button, clicking...")
await submit_btn.click()
await asyncio.sleep(3)
@ -557,7 +573,7 @@ class ApplicationHandler:
logger.info(f"[HOWOGE] Saved post-submit screenshot to {screenshot_path3}")
content = await page.content()
if "erfolgreich" in content.lower() or "gesendet" in content.lower() or "danke" in content.lower():
if "erfolgreich" in content.lower() or "gesendet" in content.lower() or "danke" in content.lower() or "bestätigung" in content.lower():
result["success"] = True
result["message"] = "Application submitted successfully"
logger.info("[HOWOGE] Success! Confirmation message detected")
@ -571,8 +587,8 @@ class ApplicationHandler:
logger.warning("[HOWOGE] Could not find submit button")
else:
result["success"] = False
result["message"] = "Could not find form fields to fill"
logger.warning("[HOWOGE] No form fields found")
result["message"] = "Could not find form fields to fill after navigating steps"
logger.warning("[HOWOGE] No form fields found after multi-step navigation")
else:
result["message"] = "No application button found"
logger.warning("[HOWOGE] Could not find 'Besichtigung vereinbaren' button")
@ -597,6 +613,11 @@ class ApplicationHandler:
return result
async def _apply_gewobag(self, listing: dict, result: dict) -> dict:
"""
Gewobag uses Wohnungshelden (app.wohnungshelden.de) for their application system.
The application form is embedded in an iframe on the listing page.
We navigate directly to the iframe URL to fill the form.
"""
page = await self.context.new_page()
try:
logger.info(f"[GEWOBAG] Opening page: {listing['link']}")
@ -612,25 +633,153 @@ class ApplicationHandler:
await asyncio.sleep(1)
except: pass
logger.info("[GEWOBAG] Looking for application button...")
apply_btn = await page.query_selector('a:has-text("Kontakt"), button:has-text("Anfrage"), a.btn:has-text("Anfragen")')
if apply_btn and await apply_btn.is_visible():
logger.info("[GEWOBAG] Found application button, clicking...")
await apply_btn.click()
# Gewobag has Wohnungshelden iframe directly on the page
logger.info("[GEWOBAG] Looking for Wohnungshelden iframe...")
iframe_element = await page.query_selector('iframe[src*="wohnungshelden.de"]')
if iframe_element:
iframe_url = await iframe_element.get_attribute('src')
logger.info(f"[GEWOBAG] Found Wohnungshelden iframe: {iframe_url}")
# Navigate to the iframe URL directly in a new page
iframe_page = await self.context.new_page()
try:
await iframe_page.goto(iframe_url, wait_until="networkidle")
await asyncio.sleep(2)
logger.info("[GEWOBAG] Loaded Wohnungshelden application page")
screenshot_path = DATA_DIR / f"gewobag_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path))
logger.info(f"[GEWOBAG] Saved screenshot to {screenshot_path}")
# Take screenshot
screenshot_path = DATA_DIR / f"gewobag_wohnungshelden_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[GEWOBAG] Saved Wohnungshelden screenshot")
result["success"] = False
result["message"] = "Application page opened but not submitted (not implemented)"
# Fill out Wohnungshelden form (same fields as Degewo)
form_filled = False
# Anrede (Salutation) - ng-select dropdown
try:
salutation_dropdown = await iframe_page.query_selector('#salutation-dropdown, ng-select[id*="salutation"]')
if salutation_dropdown:
await salutation_dropdown.click()
await asyncio.sleep(0.5)
anrede_option = await iframe_page.query_selector(f'.ng-option:has-text("{FORM_ANREDE}")')
if anrede_option:
await anrede_option.click()
logger.info(f"[GEWOBAG] Selected Anrede: {FORM_ANREDE}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not set Anrede: {e}")
# Vorname (First name)
try:
vorname_field = await iframe_page.query_selector('#firstName')
if vorname_field:
await vorname_field.fill(FORM_VORNAME)
logger.info(f"[GEWOBAG] Filled Vorname: {FORM_VORNAME}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not fill Vorname: {e}")
# Nachname (Last name)
try:
nachname_field = await iframe_page.query_selector('#lastName')
if nachname_field:
await nachname_field.fill(FORM_NACHNAME)
logger.info(f"[GEWOBAG] Filled Nachname: {FORM_NACHNAME}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not fill Nachname: {e}")
# E-Mail
try:
email_field = await iframe_page.query_selector('#email')
if email_field:
await email_field.fill(FORM_EMAIL)
logger.info(f"[GEWOBAG] Filled E-Mail: {FORM_EMAIL}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not fill E-Mail: {e}")
# Telefonnummer - Gewobag uses #phone-number
try:
tel_field = await iframe_page.query_selector('#phone-number, input[id*="telefonnummer"], input[id*="phone"]')
if tel_field:
await tel_field.fill(FORM_PHONE)
logger.info(f"[GEWOBAG] Filled Telefon: {FORM_PHONE}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not fill Telefon: {e}")
# Anzahl einziehende Personen - Gewobag uses formly_*_gesamtzahl
try:
personen_field = await iframe_page.query_selector('input[id*="gesamtzahl"], input[id*="numberPersonsTotal"]')
if personen_field:
await personen_field.fill(FORM_PERSONS)
logger.info(f"[GEWOBAG] Filled Anzahl Personen: {FORM_PERSONS}")
form_filled = True
except Exception as e:
logger.warning(f"[GEWOBAG] Could not fill Anzahl Personen: {e}")
await asyncio.sleep(1)
# Screenshot after filling
screenshot_path = DATA_DIR / f"gewobag_filled_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[GEWOBAG] Saved filled form screenshot")
# Try to submit
if form_filled:
try:
submit_selectors = [
'button[type="submit"]',
'button:has-text("Absenden")',
'button:has-text("Senden")',
'button:has-text("Anfrage")',
'.btn-primary',
]
submit_btn = None
for selector in submit_selectors:
submit_btn = await iframe_page.query_selector(selector)
if submit_btn and await submit_btn.is_visible():
logger.info(f"[GEWOBAG] Found submit button: {selector}")
break
submit_btn = None
if submit_btn:
await submit_btn.click()
logger.info("[GEWOBAG] Clicked submit button")
await asyncio.sleep(3)
# Screenshot after submission
screenshot_path = DATA_DIR / f"gewobag_submitted_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[GEWOBAG] Saved submission screenshot")
result["success"] = True
result["message"] = "Application submitted via Wohnungshelden"
else:
result["message"] = "No application button found"
logger.warning("[GEWOBAG] Could not find application button")
result["success"] = False
result["message"] = "Form filled but submit button not found"
logger.warning("[GEWOBAG] Submit button not found")
except Exception as e:
result["success"] = False
result["message"] = f"Submit error: {str(e)}"
logger.warning(f"[GEWOBAG] Submit error: {e}")
else:
result["success"] = False
result["message"] = "No form fields found in Wohnungshelden"
logger.warning("[GEWOBAG] Could not find form fields")
finally:
await iframe_page.close()
else:
result["success"] = False
result["message"] = "No Wohnungshelden iframe found"
logger.warning("[GEWOBAG] No Wohnungshelden iframe found")
screenshot_path = DATA_DIR / f"gewobag_nobtn_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path))
except Exception as e:
result["success"] = False
result["message"] = f"Error: {str(e)}"
logger.error(f"[GEWOBAG] Exception: {str(e)}")
finally:
@ -903,9 +1052,11 @@ class ApplicationHandler:
async def _apply_stadtundland(self, listing: dict, result: dict) -> dict:
"""
Stadt und Land uses Wohnungshelden (app.wohnungshelden.de) for their application system.
The application form is loaded in an iframe from a different domain.
We need to navigate directly to the iframe URL.
Stadt und Land has an embedded contact form directly on their listing page.
No iframe - the form fields are directly accessible.
Fields: name, surname, street, houseNo, postalCode, city, phone, email
Checkboxes: privacy, provision
Submit: "Eingaben prüfen"
"""
page = await self.context.new_page()
try:
@ -923,180 +1074,196 @@ class ApplicationHandler:
await asyncio.sleep(1)
except: pass
# Look for "Kontakt" or "Anfragen" button to open the form
logger.info("[STADTUNDLAND] Looking for contact button...")
apply_btn = await page.query_selector('a:has-text("Kontakt"), button:has-text("Kontakt"), a:has-text("Anfragen"), button:has-text("Anfragen"), a:has-text("kontaktieren"), button:has-text("kontaktieren")')
if apply_btn and await apply_btn.is_visible():
logger.info("[STADTUNDLAND] Found contact button, clicking...")
await apply_btn.click()
await asyncio.sleep(3)
# Scroll down to the contact form
await page.evaluate("window.scrollBy(0, 500)")
await asyncio.sleep(0.5)
# Stadt und Land uses Wohnungshelden iframe for the application form
iframe_element = await page.query_selector('iframe[src*="wohnungshelden.de"]')
if iframe_element:
iframe_url = await iframe_element.get_attribute('src')
logger.info(f"[STADTUNDLAND] Found Wohnungshelden iframe: {iframe_url}")
# Take initial screenshot
screenshot_path = DATA_DIR / f"stadtundland_page_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved page screenshot to {screenshot_path}")
# Navigate to the iframe URL directly in a new page for full access
iframe_page = await self.context.new_page()
try:
await iframe_page.goto(iframe_url, wait_until="networkidle")
await asyncio.sleep(2)
logger.info("[STADTUNDLAND] Loaded Wohnungshelden application page")
# Take screenshot of the Wohnungshelden form
screenshot_path = DATA_DIR / f"stadtundland_wohnungshelden_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved Wohnungshelden screenshot to {screenshot_path}")
# Save HTML for debugging
html_content = await iframe_page.content()
html_path = DATA_DIR / f"stadtundland_wohnungshelden_{listing['id']}.html"
with open(html_path, 'w', encoding='utf-8') as f:
f.write(html_content)
logger.info(f"[STADTUNDLAND] Saved HTML to {html_path}")
# Fill out Wohnungshelden form (same as Degewo)
# Fill out the embedded form directly
form_filled = False
# Anrede (Salutation) - ng-select dropdown
# Vorname (name field)
try:
salutation_dropdown = await iframe_page.query_selector('#salutation-dropdown, ng-select[id*="salutation"]')
if salutation_dropdown:
await salutation_dropdown.click()
await asyncio.sleep(0.5)
anrede_option = await iframe_page.query_selector(f'.ng-option:has-text("{FORM_ANREDE}")')
if anrede_option:
await anrede_option.click()
logger.info(f"[STADTUNDLAND] Selected Anrede: {FORM_ANREDE}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not set Anrede: {e}")
# Vorname (First name)
try:
vorname_field = await iframe_page.query_selector('#firstName')
if vorname_field:
vorname_field = await page.query_selector('input[name="name"]')
if vorname_field and await vorname_field.is_visible():
await vorname_field.fill(FORM_VORNAME)
logger.info(f"[STADTUNDLAND] Filled Vorname: {FORM_VORNAME}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Vorname: {e}")
# Nachname (Last name)
# Nachname (surname field)
try:
nachname_field = await iframe_page.query_selector('#lastName')
if nachname_field:
nachname_field = await page.query_selector('input[name="surname"]')
if nachname_field and await nachname_field.is_visible():
await nachname_field.fill(FORM_NACHNAME)
logger.info(f"[STADTUNDLAND] Filled Nachname: {FORM_NACHNAME}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Nachname: {e}")
# E-Mail
# Straße (street field)
try:
email_field = await iframe_page.query_selector('#email')
if email_field:
street_field = await page.query_selector('input[name="street"]')
if street_field and await street_field.is_visible():
await street_field.fill(FORM_STRASSE)
logger.info(f"[STADTUNDLAND] Filled Straße: {FORM_STRASSE}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Straße: {e}")
# Hausnummer (houseNo field)
try:
house_field = await page.query_selector('input[name="houseNo"]')
if house_field and await house_field.is_visible():
await house_field.fill(FORM_HAUSNUMMER)
logger.info(f"[STADTUNDLAND] Filled Hausnummer: {FORM_HAUSNUMMER}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Hausnummer: {e}")
# PLZ (postalCode field)
try:
plz_field = await page.query_selector('input[name="postalCode"]')
if plz_field and await plz_field.is_visible():
await plz_field.fill(FORM_PLZ)
logger.info(f"[STADTUNDLAND] Filled PLZ: {FORM_PLZ}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill PLZ: {e}")
# Ort (city field)
try:
city_field = await page.query_selector('input[name="city"]')
if city_field and await city_field.is_visible():
await city_field.fill(FORM_ORT)
logger.info(f"[STADTUNDLAND] Filled Ort: {FORM_ORT}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Ort: {e}")
# Telefon (phone field)
try:
phone_field = await page.query_selector('input[name="phone"]')
if phone_field and await phone_field.is_visible():
await phone_field.fill(FORM_PHONE)
logger.info(f"[STADTUNDLAND] Filled Telefon: {FORM_PHONE}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Telefon: {e}")
# E-Mail (email field)
try:
email_field = await page.query_selector('input[name="email"]')
if email_field and await email_field.is_visible():
await email_field.fill(FORM_EMAIL)
logger.info(f"[STADTUNDLAND] Filled E-Mail: {FORM_EMAIL}")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill E-Mail: {e}")
# Telefonnummer
# Click privacy checkbox
try:
tel_field = await iframe_page.query_selector('input[id*="telefonnummer"]')
if tel_field:
await tel_field.fill(FORM_PHONE)
logger.info(f"[STADTUNDLAND] Filled Telefon: {FORM_PHONE}")
form_filled = True
privacy_checkbox = await page.query_selector('input[name="privacy"]')
if privacy_checkbox and await privacy_checkbox.is_visible():
if not await privacy_checkbox.is_checked():
await privacy_checkbox.click()
logger.info("[STADTUNDLAND] Clicked privacy checkbox")
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Telefon: {e}")
logger.warning(f"[STADTUNDLAND] Could not click privacy checkbox: {e}")
# Anzahl einziehende Personen
# Click provision checkbox (optional)
try:
personen_field = await iframe_page.query_selector('input[id*="numberPersonsTotal"]')
if personen_field:
await personen_field.fill(FORM_PERSONS)
logger.info(f"[STADTUNDLAND] Filled Anzahl Personen: {FORM_PERSONS}")
form_filled = True
provision_checkbox = await page.query_selector('input[name="provision"]')
if provision_checkbox and await provision_checkbox.is_visible():
if not await provision_checkbox.is_checked():
await provision_checkbox.click()
logger.info("[STADTUNDLAND] Clicked provision checkbox")
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not fill Anzahl Personen: {e}")
# "Für sich selbst" dropdown
try:
selbst_dropdown = await iframe_page.query_selector('ng-select[id*="fuer_wen"]')
if selbst_dropdown:
await selbst_dropdown.click()
await asyncio.sleep(0.5)
selbst_option = await iframe_page.query_selector('.ng-option:has-text("Für mich selbst"), .ng-option:has-text("selbst")')
if selbst_option:
await selbst_option.click()
logger.info("[STADTUNDLAND] Selected: Für mich selbst")
form_filled = True
except Exception as e:
logger.warning(f"[STADTUNDLAND] Could not set 'Für sich selbst': {e}")
logger.warning(f"[STADTUNDLAND] Could not click provision checkbox: {e}")
await asyncio.sleep(1)
# Take screenshot after filling form
screenshot_path = DATA_DIR / f"stadtundland_form_filled_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
screenshot_path = DATA_DIR / f"stadtundland_filled_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved filled form screenshot to {screenshot_path}")
# Try to submit
# Submit form
if form_filled:
try:
submit_selectors = [
'button[type="submit"]',
'input[type="submit"]',
# Stadt und Land uses "Eingaben prüfen" button
# Step 1: Click "Eingaben prüfen" button
pruefen_btn = await page.query_selector('button:has-text("Eingaben prüfen")')
if pruefen_btn and await pruefen_btn.is_visible():
await pruefen_btn.click()
logger.info("[STADTUNDLAND] Clicked 'Eingaben prüfen' button")
await asyncio.sleep(2)
await page.wait_for_load_state("networkidle")
# Take screenshot after validation
screenshot_path = DATA_DIR / f"stadtundland_validated_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved validation screenshot")
# Step 2: Click the final submit button
final_submit_selectors = [
'button:has-text("Absenden")',
'button:has-text("Senden")',
'button:has-text("Anfrage")',
'.btn-primary',
'button:has-text("Anfrage senden")',
'button:has-text("Bestätigen")',
'button[type="submit"]',
]
submit_btn = None
for selector in submit_selectors:
submit_btn = await iframe_page.query_selector(selector)
if submit_btn and await submit_btn.is_visible():
logger.info(f"[STADTUNDLAND] Found submit button with selector: {selector}")
final_btn = None
for selector in final_submit_selectors:
final_btn = await page.query_selector(selector)
if final_btn and await final_btn.is_visible():
logger.info(f"[STADTUNDLAND] Found final submit button: {selector}")
break
submit_btn = None
final_btn = None
if submit_btn:
await submit_btn.click()
logger.info("[STADTUNDLAND] Clicked submit button")
if final_btn:
await final_btn.click()
logger.info("[STADTUNDLAND] Clicked final submit button")
await asyncio.sleep(3)
await page.wait_for_load_state("networkidle")
# Take screenshot after submission
# Take screenshot after final submission
screenshot_path = DATA_DIR / f"stadtundland_submitted_{listing['id']}.png"
await iframe_page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved submission screenshot to {screenshot_path}")
await page.screenshot(path=str(screenshot_path), full_page=True)
logger.info(f"[STADTUNDLAND] Saved submission screenshot")
# Check for confirmation message
content = await page.content()
if "erfolgreich" in content.lower() or "gesendet" in content.lower() or "danke" in content.lower() or "bestätigung" in content.lower():
result["success"] = True
result["message"] = "Application submitted via Wohnungshelden"
result["message"] = "Application submitted successfully"
logger.info("[STADTUNDLAND] Success! Confirmation message detected")
else:
result["success"] = True
result["message"] = "Form submitted"
logger.info("[STADTUNDLAND] Form submitted")
else:
result["success"] = False
result["message"] = "Form filled, submit button not found"
logger.warning("[STADTUNDLAND] Submit button not found")
result["message"] = "Validated but final submit button not found"
logger.warning("[STADTUNDLAND] Final submit button not found")
else:
result["success"] = False
result["message"] = "Form filled but 'Eingaben prüfen' button not found"
logger.warning("[STADTUNDLAND] 'Eingaben prüfen' button not found")
except Exception as e:
result["success"] = False
result["message"] = f"Form filled, submit error: {str(e)}"
result["message"] = f"Submit error: {str(e)}"
logger.warning(f"[STADTUNDLAND] Submit error: {e}")
else:
result["success"] = False
result["message"] = "No form fields found in Wohnungshelden"
logger.warning("[STADTUNDLAND] Could not find form fields in Wohnungshelden")
finally:
await iframe_page.close()
else:
# No Wohnungshelden iframe found
result["success"] = False
result["message"] = "No Wohnungshelden iframe found"
logger.warning("[STADTUNDLAND] No Wohnungshelden iframe found")
screenshot_path = DATA_DIR / f"stadtundland_nobtn_{listing['id']}.png"
await page.screenshot(path=str(screenshot_path), full_page=True)
result["message"] = "No form fields found on page"
logger.warning("[STADTUNDLAND] Could not find form fields")
except Exception as e:
result["success"] = False