2025, Oct 07 09:33
Selenium में header-icon के लिए सही XPath और वेट
XPath की गलती से Selenium का wait_for_element_present फेल क्यों होता है, और @src-आधारित contains() से मज़बूत लोकेटर कैसे लिखें. एब्सोल्यूट पाथ व iframe टिप्स।
टेबल में हेडर‑आइकन के दिखाई देने का इंतज़ार करना UI परीक्षण का सामान्य परिदृश्य है। फिर भी कई बार टेस्ट "इंतज़ार नहीं करते" — वजह समय नहीं, बल्कि लोकेटर होता है। यहां एक वास्तविक उदाहरण है जहाँ एक XPath ने Selenium को सही ढंग से इंतज़ार करने से रोका, और इसे साफ तरीके से कैसे सुधारा जाए।
समस्या
पेज एक टेबल हेडर सेल के अंदर इमेज‑आधारित इनपुट के साथ हेडर रेंडर करता है:
<th><input type="image" src="../../..//images/icons/cell_state_header_icon.png" onclick="javascript:__doPostBack('ctl00$left_pane$gvSelectedFeatures','Sort$Status')" style="border-width:0px;"></th>टेस्ट इस एलिमेंट के मौजूद होने का इंतज़ार करने की कोशिश करता है:
runner.wait_for_element_present("//*[/html/body/form/div[3]/div/div[3]/div/div[5]/div[2]/div/div[2]/table/tbody/tr[1]/th[1]/input=cell_state_header_icon.png]", timeout=None)पेज पर एलिमेंट मौजूद होने के बावजूद वेट उम्मीद के मुताबिक काम नहीं करता। सवाल यह है कि क्या सेलेक्टर सही है।
वेट फेल क्यों होता है
यह XPath गलत बना हुआ है। यह किसी एट्रिब्यूट को परखे बिना input=cell_state_header_icon.png के जरिए एलिमेंट की सीधे ही फ़ाइल‑नाम से तुलना करने की कोशिश करता है। इमेज फ़ाइल‑नाम के आधार पर input मिलाने के लिए तुलना src एट्रिब्यूट पर होनी चाहिए, जैसे @src के साथ contains()।
दूसरी संरचनात्मक दिक्कत यह है कि पाथ एब्सोल्यूट है और पेज के लेआउट से बुरी तरह जुड़ा हुआ है। div और टेबल के अंदरूनी हिस्सों की इतनी लंबी चेन नाज़ुक होती है। DOM बदलते ही ये अक्सर टूट जाती हैं, और व्यवहार में tbody रनटाइम पर अनुपस्थित हो सकता है, भले DevTools HTML के नीचे उसे दिखाए। मज़बूत XPath को ऐसी आकस्मिक संरचना पर निर्भरता से बचना चाहिए।
यदि कोड को व्यापक try/except में लपेट दिया गया है, तो XPath से जुड़ी त्रुटियाँ चुपचाप निगल ली जा सकती हैं। उस रैपर के बिना वेट चलाने से सार्थक एरर मैसेज सामने आते हैं। वास्तविक URL के बिना अन्य पेज‑विशिष्ट कारणों को नकारा नहीं जा सकता; जैसे कि किसी iframe के अंदर मौजूद एलिमेंट पर वेट लगाने से पहले संदर्भ बदलना पड़ता है।
लोकेटर को ठीक करना
इमेज फ़ाइल‑नाम के आधार पर एलिमेंट चुनने के लिए src एट्रिब्यूट के साथ तुलना करें। लक्ष्य नोड से मेल खाने वाली सबसे सरल क्वेरी से शुरू करें। खुद input के लिए संक्षिप्त रूप कुछ ऐसा होगा:
//input[contains(@src, "cell_state_header_icon.png")]यदि लक्ष्य वह टेबल हेडर सेल है जिसमें यह input मौजूद है, तो प्रेडिकेट के साथ उसके पेरेंट th को चुनें:
//th[input[contains(@src, "cell_state_header_icon.png")]]सिर्फ पहले मिलते हेडर सेल को निशाना बनाने के लिए किसी भी प्रेडिकेट क्रम का उपयोग कर सकते हैं:
//th[1][input[contains(@src, "cell_state_header_icon.png")]]
//th[input[contains(@src, "cell_state_header_icon.png")]][1]कोष्ठक‑आधारित ग्रुपिंग वाले विकल्प भी संभव हैं:
(//th[1])[input[contains(@src, "cell_state_header_icon.png")]]
(//th[input[contains(@src, "cell_state_header_icon.png")]])[1]डिबग करते समय त्वरित जाँच के लिए पहले //input से देखें कि ड्राइवर किसी भी input को ढूँढ पा रहा है या नहीं। फिर एब्सोल्यूट पाथ से शुरू करने के बजाय XPath को धीरे‑धीरे परिष्कृत करें।
कार्यशील उदाहरण
नीचे दिया गया स्निपेट उस पहले th के लिए इंतज़ार दिखाता है जिसमें दिए गए फ़ाइल‑नाम वाला image input हो। यह ends-with() के विकल्प के रूप में substring अभिव्यक्ति का उपयोग भी दिखाता है।
from seleniumbase import SB
import seleniumbase as sb_mod
print("SeleniumBase:", sb_mod.__version__)
markup = """
<table>
<tr>
<th><input type="image" src="../../..//images/icons/cell_state_header_icon.png" onclick="javascript:__doPostBack('ctl00$left_pane$gvSelectedFeatures','Sort$Status')" style="border-width:0px;">Header 1</th>
<th><input type="image" src="../../..//images/icons/cell_state_header_icon.png" onclick="javascript:__doPostBack('ctl00$left_pane$gvSelectedFeatures','Sort$Status')" style="border-width:0px;">Header 2</th>
<th><input type="image" src="../../..//images/icons/cell_state_header_icon.png" onclick="javascript:__doPostBack('ctl00$left_pane$gvSelectedFeatures','Sort$Status')" style="border-width:0px;">Header 3</th>
</tr>
<table>
"""
with SB(uc=True) as browser:
    browser.load_html_string(markup)
    # contains() का उपयोग करने का उदाहरण
    #node = browser.wait_for_element_present('//th[1][input[contains(@src, "icon.png")]])
    # ends-with() के विकल्प के रूप में: substring तुलना का उपयोग करें
    node = browser.wait_for_element_present(
        '//th[1][input[substring(@src, string-length(@src) - string-length("icon.png") + 1) = "icon.png"]]'
    )
    print('text:', node.text)यदि कोई व्यापक कारण एलिमेंट तक पहुँचने से रोक रहा है, जैसे कि वह iframe के अंदर अलग-थलग हो, तो XPath से असंबंधित वजहों से वेट फिर भी सफल नहीं होगा। ऐसे में पहले पहुँच‑योग्यता की समस्या सुलझाएँ, उसके बाद सरल लोकेटर वाला तरीका दोबारा लगाएँ।
यह क्यों मायने रखता है
सेलेक्टर्स तय करते हैं कि आपके UI टेस्ट कितने लचीले हैं। अत्यधिक एब्सोल्यूट XPath फलेक़ीनेस बढ़ाते हैं और असली समस्याओं को टाइमिंग के शोर के पीछे छिपा देते हैं। एट्रिब्यूट‑आधारित लोकेटर, खासकर @src जैसे स्थिर संकेतों पर आधारित, लेआउट में छोटे बदलाव या मार्कअप के फेरबदल के बावजूद टिकाऊ रहते हैं। शुरुआती, सरल जाँच समय बचाती है: अगर //input कुछ भी मैच नहीं करता, तो कोई भी जटिल XPath मूल असंगति को नहीं सुधारेगा।
निष्कर्ष
जब वेट "वेट" नहीं करता, तो सबसे पहले टाइमआउट न बढ़ाएँ। XPath की जाँच करें। एट्रिब्यूट्स को स्पष्ट रूप से निशाना बनाएँ, एब्सोल्यूट पाथ और अस्थायी कंटेनरों से बचें, और न्यूनतम क्वेरी से बुनियादी बातें परखें। अस्पष्ट एरर‑हैंडलिंग हटाएँ ताकि वास्तविक पार्सर या लुकअप त्रुटियाँ दिख सकें। यदि पेज संरचना अतिरिक्त प्रतिबंध लगाती है, जैसे iframe के भीतर सामग्री, तो किसी भी लोकेटर से उम्मीद करने से पहले संदर्भ सँभालें। साफ, एट्रिब्यूट‑केंद्रित XPath आपके वेट को निर्धार्य बनाता है और सूट को टिकाऊ रखता है।