2025, Sep 23 11:46
FlixBus पर फॉर्म रीसेट से बचें: Selenium/Python में shadow DOM और ऑटोकम्प्लीट का सही उपयोग
FlixBus.ca पर Selenium/Python से From/To बदलते ही रीसेट की दिक्कत? shadow DOM कुकी बैनर को ठीक से हैंडल करें, ऑटोकम्प्लीट चुनें और स्थिर ऑटोमेशन पाएं.
आधुनिक वेबसाइटों पर फॉर्म भरने का ऑटोमेशन भ्रामक हो सकता है: आप एक फ़ील्ड में टाइप करते हैं, दूसरे पर जाते हैं, और पहला चुपचाप रीसेट हो जाता है। flixbus.ca पर Selenium/Python से डिफ़ॉल्ट रूट बदलने की कोशिश करते समय यही होता है। Origin टोरंटो से Ottawa में ठीक से बदल जाता है, लेकिन जैसे ही आप Destination को Sudbury करते हैं या तारीख छूते हैं, Origin फिर से पलट जाता है। असली वजह आपके send_keys कॉल्स में नहीं है।
समस्या को दोहराना
नीचे दिया गया स्क्रिप्ट flixbus.ca खोलता है, कुकीज़ स्वीकार करने की कोशिश करता है और फिर From, To और तारीख वाले फ़ील्ड्स को ओवरराइट करता है। जैसे ही Destination या Date से इंटरैक्ट होता है, Origin इनपुट वापस डिफ़ॉल्ट मान पर लौट आता है।
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
import time
flix_url = 'https://www.flixbus.ca/'
from_value = 'Ottawa (VIA Rail)'
to_value = 'Sudbury, ON'
when_value = 'Sat, Sep 20'
browser = webdriver.Chrome()
browser.maximize_window()
browser.get(flix_url)
# कुकीज़ स्वीकार करने का प्रयास
action_cookie = browser.find_element(By.XPATH, '//*[@id="uc-center     container"]/div[2]/div/div/div/button[2]')
action_cookie.click()
# Origin सेट करें
from_input = browser.find_element(By.ID, 'searchInput-from')
from_input.clear()
time.sleep(1)
from_input.send_keys(from_value)
time.sleep(1)
# Destination सेट करें
to_input = browser.find_element(By.ID, 'searchInput-to')
to_input.clear()
time.sleep(1)
to_input.send_keys(to_value)
time.sleep(1)
# तारीख सेट करें
date_input = browser.find_element(By.ID, 'dateInput-from')
date_input.click()
time.sleep(1)
date_input.send_keys(when_value)
# खोज सबमिट करें
submit_btn = browser.find_element(By.XPATH, '//*[@id="search-mask-    component"]/div/div/div/div/div[5]/div/button')
submit_btn.click()
असल में हो क्या रहा है
इस साइट पर कुकी बैनर एक shadow DOM के अंदर रेंडर होता है। Accept All कंट्रोल सामान्य DOM ट्री का हिस्सा नहीं है, इसलिए By.XPATH से सीधे खोजकर क्लिक करना भरोसेमंद नहीं है। वह क्लिक अस्थिर रहता है, और नतीजे में From/Origin और To/Destination विजेट्स को नियंत्रित करने वाला बाद का JavaScript/AJAX स्थिर नहीं होता। आप जिन इनपुट्स में टाइप करते हैं, पेज उन्हें बाद में दोबारा रेंडर कर देता है और आपकी बदलावट मिट जाती है।
कारगर तरीका
समाधान यह है कि कुकी बैनर से उसके shadow root के ज़रिए इंटरैक्ट करें और उसके बाद ही फॉर्म पर आगे बढ़ें। Accept All बटन पाने के लिए shadowRoot.querySelector का उपयोग करें, उसे क्लिक करें, और इनपुट्स के सचमुच क्लिक करने योग्य होने तक प्रतीक्षा करें। टाइपिंग के बाद, ऑटोकम्प्लीट सूची से इच्छित विकल्प चुनें ताकि पेज आपकी पसंद को मान्यता दे।
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
browser.maximize_window()
browser.get("https://www.flixbus.ca/")
# shadow DOM के ज़रिए कुकीज़ स्वीकार करें
time.sleep(10)
accept_btn = browser.execute_script(
    """return document.querySelector('#usercentrics-root').shadowRoot
    .querySelector("button[data-testid='uc-accept-all-button']")"""
)
accept_btn.click()
# From भरें और ऑटोकम्प्लीट से पुष्टि करें
from_box = WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "input#searchInput-from"))
)
from_box.click()
from_box.clear()
from_box.send_keys("Ottawa (VIA Rail)")
WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@data-e2e='autocomplete-options-from']//span[contains(., 'Ottawa (VIA Rail']"))
).click()
# To भरें और ऑटोकम्प्लीट से पुष्टि करें
to_box = WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "input#searchInput-to"))
)
to_box.click()
to_box.clear()
to_box.send_keys("Sudbury, ON")
WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@data-e2e='autocomplete-options-to']//span[contains(., 'Sudbury, ON')]"))
).click()
यह क्यों मायने रखता है
कुकी सहमति लेयर्स अब अक्सर shadow DOM में होती हैं। इसे नजरअंदाज करके सामान्य DOM के जरिए जबरन क्लिक करने की कोशिश करेंगे, तो आपका टेस्ट असंगत बर्ताव करेगा। डेटा एक्सट्रैक्शन और E2E फ्लोज़ में ऐसी अस्थिरता से phantom regressions, heisenbugs जैसे मुद्दे पैदा होते हैं और गैर-नियत अवस्थाओं के पीछे घंटों बर्बाद होते हैं। सहमति बैनर को सही तरीके से हैंडल करने से स्थिरता लौटती है और देर से होने वाले रेंडरिंग के कारण फॉर्म इनपुट्स के रीसेट होने से बचाव होता है।
मुख्य निष्कर्ष
महत्वपूर्ण इनपुट्स से पहले, shadowRoot.querySelector का उपयोग करके shadow DOM पर बने ओवरले को ठीक से बंद करें। इसके साथ क्लिक करने योग्य होने के लिए स्पष्ट प्रतीक्षा जोड़ें और ऑटोकम्प्लीट सुझावों में से ठोस प्रविष्टि चुनें। flixbus.ca जैसी पेजों पर यह क्रम Origin और Destination फ़ील्ड्स को पलटने से रोकता है और आपके Selenium ऑटोमेशन को भरोसेमंद बनाता है।
यह लेख StackOverflow पर प्रश्न (लेखक: brooklin7) और undetected Selenium के उत्तर पर आधारित है।