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