2025, Oct 08 03:33

Vale डिविडेंड टेबल को Selenium से पेज-दर-पेज स्क्रैप करें

Vale के डिविडेंड पेज पर DataTables पेजिनेशन को Selenium से स्क्रैप करें: Next बटन से हर पेज पढ़ें, हेडर/रो जुटाएँ और Python pandas में DataFrame बनाएं.

Selenium के साथ पेजिनेटेड टेबल्स स्क्रैप करना अक्सर आसान लगता है, जब तक किसी विजेट का अंदरूनी व्यवहार सामने नहीं आ जाता। Vale के डिविडेंड पेज https://investidor10.com.br/acoes/vale3/ पर पेजिनेशन UI में संख्यात्मक बटन और Next/Previous शामिल हैं। संख्यात्मक इंडेक्स पर क्लिक करना शुरुआती कुछ पेजों तक ठीक चलता है, लेकिन idx="5" पर क्लिक करने की कोशिश सीधे idx="8" पर छलांग लगाती है और पेज 6 और 7 का डेटा छूट जाता है। इसके अलावा, कभी-कभी NoSuchElementError भी आ जाता है, जबकि लक्ष्य एलिमेंट DOM में दिख रहा होता है।

समस्या को पुन: उत्पन्न करना

नीचे दिया गया स्निपेट data-dt-idx एट्रिब्यूट के जरिए न्यूमेरिक पेजर एंकर पर सीधे क्लिक पर निर्भर करता है। यह स्क्रॉल करता है, चुने हुए पेज पर क्लिक करता है, टेबल आने का इंतज़ार करता है और फिर नियंत्रण स्क्रैपिंग रूटीन को दे देता है।

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep

def loop_pagers():
    tabs = browser.find_elements(By.CSS_SELECTOR, "a[data-dt-idx]")
    total = len(tabs)
    for k in range(total):
        hit_pager(str(k + 1))


def hit_pager(idx):
    try:
        locator = (By.CSS_SELECTOR, f'a[data-dt-idx="{idx}"]')
        pager = WebDriverWait(browser, 10).until(EC.presence_of_element_located(locator))
        browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        sleep(1)
        browser.execute_script("arguments[0].scrollIntoView({behavior:'instant', block:'center' });", pager)
        browser.execute_script("arguments[0].click();", pager)
        WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, "table-dividends-history")))
        harvest_grid()  # स्क्रैपिंग रूटीन
    except Exception as err:
        print(f"Failed to execute function: {err}")

असल में गड़बड़ी क्या है

जब पेजिनेशन कंट्रोल को न्यूमेरिक idx से निशाना बनाया जाता है, तो उसका व्यवहार सुसंगत नहीं रहता। देखा गया नतीजा यह है कि idx="5" के लिए किया गया क्लिक दृश्य को idx="8" पर ले जाता है, जिससे पेज 6 और 7 की पंक्तियाँ छूट जाती हैं। साथ ही, बटन पेज पर मौजूद होने के बावजूद कोड NoSuchElementError फेंक सकता है—जो डायनेमिक री-रेंडरिंग और अस्थायी क्लिकयोग्यता की समस्याओं से मेल खाता है। संक्षेप में, इस विजेट के लिए न्यूमेरिक-इंडेक्स आधारित रणनीति नाजुक है।

हर पेज जुटाने का व्यावहारिक तरीका

इस पेज पर अधिक भरोसेमंद पैटर्न यह है कि पहले पेज पर दिखाई दे रही सभी पंक्तियाँ ले ली जाएँ और फिर Next बटन के जरिए तब तक पेज-दर-पेज आगे बढ़ा जाए, जब तक वह मौजूद रहे। तरीका यह है: https://investidor10.com.br/acoes/vale3/ पर जाएँ, डिविडेंड सेक्शन (id="dividends-section") के दिखाई देने का इंतज़ार करें, डिविडेंड टेबल के रैपर को दृश्य में स्क्रॉल करें और dataTables_scroll क्षेत्र से हेडर पढ़ें।

इसके बाद कोड पहला पेज स्क्रैप करता है और एक लूप में चला जाता है। हर चक्र में वह सेलेक्टर #table-dividends-history_paginate > a.paginate_button.next से पहचाने गए Next कंट्रोल पर क्लिक करने की कोशिश करता है। क्लिक सफल होने पर तालिका को अपडेट होने का थोड़ा समय देता है और फिर नई दिखाई पंक्तियाँ पढ़ता है। यदि Next बटन नहीं मिलता, तो लूप समाप्त हो जाता है—मतलब सभी पेज प्रोसेस हो चुके हैं। अगर क्लिक इंटरसेप्ट हो जाए, तो अगली पुनरावृत्ति में चुपचाप फिर से प्रयास करता है। अंत में, मिली हुई हेडर पंक्तियों के साथ सभी रो को जोड़कर एक pandas DataFrame बना दिया जाता है।

import time
import pandas as pd
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException

pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

rows_agg = []


def harvest_rows(tbl):
    table_rows = tbl.find_elements(By.CSS_SELECTOR, "div.dataTables_scrollBody>table>tbody>tr")
    for row in table_rows:
        rows_agg.append([d.text for d in row.find_elements(By.TAG_NAME, 'td')])


chrome_cfg = ChromeOptions()
chrome_cfg.add_argument("--start-maximized")
chrome_cfg.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_cfg.add_experimental_option("useAutomationExtension", False)

browser = Chrome(options=chrome_cfg)
waiter = WebDriverWait(browser, 10)

target_url = "https://investidor10.com.br/acoes/vale3/"
browser.get(target_url)

waiter.until(EC.visibility_of_element_located((By.ID, "dividends-section")))

widget_wrap = browser.find_element(By.ID, "table-dividends-history_wrapper")
browser.execute_script("arguments[0].scrollIntoView(true);", widget_wrap)

grid_box = widget_wrap.find_element(By.CSS_SELECTOR, "div.dataTables_scroll")
headers = grid_box.find_element(By.CSS_SELECTOR, "div.dataTables_scrollHead").text.split('\n')
print(f"Table Header {headers}")
print("Extracting Page 1...")

harvest_rows(grid_box)
page_counter = 2
has_next = True

while has_next:
    try:
        next_btn = widget_wrap.find_element(By.CSS_SELECTOR, '#table-dividends-history_paginate>a[class="paginate_button next"]')
        try:
            next_btn.click()
            time.sleep(1)
            print(f"Extracting Page {page_counter}...")
            harvest_rows(grid_box)
            page_counter += 1
        except ElementClickInterceptedException:
            pass
    except NoSuchElementException:
        print("Reached End Page")
        has_next = False

# तालिका को संकलित करें और दिखाएँ
df = pd.DataFrame(rows_agg, columns=headers)
print(df)

एक नमूना रन हर पेज के निष्कर्षण को लॉग करता है और अंतिम पेज आने पर सूचित करता है। इसके बाद यह खोजी गई कॉलम हेडिंग्स के तहत सभी पेजों की सभी पंक्तियाँ समेटे एक ही DataFrame प्रिंट करता है।

तालिका हेडर ['TIPO', 'DATA COM', 'PAGAMENTO', 'VALOR']
पेज 1 निकाल रहा है...
पेज 2 निकाल रहा है...
पेज 3 निकाल रहा है...
पेज 4 निकाल रहा है...
पेज 5 निकाल रहा है...
पेज 6 निकाल रहा है...
पेज 7 निकाल रहा है...
पेज 8 निकाल रहा है...
अंतिम पेज पर पहुँचा

यह क्यों मायने रखता है

ऐसी पेजिनेशन UI जो डायनेमिक रूप से रिफ्लो, रीलैबल या री-रेंडर होती हैं, इंडेक्स-आधारित क्लिकिंग के लिए प्रतिकूल होती हैं। Next के जरिए क्रमवार बढ़ना गलत-संरेखित idx जंप से बचाता है और सुनिश्चित करता है कि हर पेज ठीक एक बार विज़िट हो। यह एक ही एलिमेंट कॉन्टेक्स्ट को दोबारा उपयोग करके रो संग्रहण को भी सरल बनाता है। ध्यान रहे, क्लिक के बाद तय एक-सेकंड का विराम सिर्फ best-effort देरी है और अविश्वसनीय हो सकता है; नेटवर्क या रेंडरिंग की परिस्थितियाँ बदलती हों तो यह बात खास तौर पर महत्त्वपूर्ण है।

निष्कर्ष

जब पेजिनेटर संख्यात्मक इंडेक्स क्लिक पर निर्धारणीय प्रतिक्रिया नहीं देता, तो एक निर्धारणीय traversal रणनीति अपनाएँ। जिस सेक्शन की ज़रूरत है उसे लोड करें, टेबल को दृश्य में लाएँ, पहले पेज से हेडर और रो इकट्ठा करें, और Next कंट्रोल के ज़रिए तब तक आगे बढ़ें जब तक वह गायब न हो जाए। जैसे-जैसे बढ़ें, परिणामों को जोड़ते रहें और अंत में अंतिम DataFrame बनाएँ। पेजिनेशन के दौरान स्थिर sleeps पर विशेष नज़र रखें; पेज-लोड टाइमिंग बदलने पर वे कमज़ोर कड़ी साबित हो सकते हैं।

यह लेख StackOverflow पर प्रश्न (लेखक: user30126350) और Ajeet Verma द्वारा दिए गए उत्तर पर आधारित है।