You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Selenium ChromeDriver报错:No node with given id found 求助

问题描述

使用Python 3.10.5 + Selenium + ChromeDriver 113.0.5672.63在Windows 10环境下编写爬虫脚本,通过多级页面导航(省→市→区→社区)点击按钮获取数据,固定迭代次数后触发报错(如provinsi[0]在689次迭代后报错,provinsi[1]在35次后报错)。已尝试添加time.sleep(1)、检查元素可见性与可点击性,问题仍存在。

报错信息

WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32000,"message":"No node with given id found"}

脚本代码

url = '.../'
path = Service(r'...\chromedriver_win32')

options = Options()
options.add_experimental_option("debuggerAddress", "localhost:9222")
driver = webdriver.Chrome(service=path, options=options)
driver.get(url)
wait = WebDriverWait(driver, 10)

def scrape_left_table(prob, kab, kec):
    data = [] 
    rows = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(1) > table > tbody > tr')
    for row in rows:
        wilayah = row.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > button').text
        persentasi = row.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > span').text
        class_1= row.find_element(By.CSS_SELECTOR, 'td:nth-child(2)').text
        class_2= row.find_element(By.CSS_SELECTOR, 'td:nth-child(3)').text

        data.append([prob, kab, kec, wilayah, persentasi, class_1, class_2])
    
    return data

def scrape_right_table(prob, kab, kec):
    data = [] 
    rows = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(2) > table > tbody > tr')
    for row in rows:
        wilayah = row.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > button').text
        persentasi = row.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > span').text
        class_1= row.find_element(By.CSS_SELECTOR, 'td:nth-child(2)').text
        class_2= row.find_element(By.CSS_SELECTOR, 'td:nth-child(3)').text

        data.append([prob, kab, kec, wilayah, persentasi, class_1, class_2])
    
    return data

data = [] 
 
provinsi = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(1) > table > tbody > tr')
button = provinsi[1].find_element(By.TAG_NAME, 'button')
pro = button.text
wait.until(EC.element_to_be_clickable(button)).click()

wait.until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, 'div:nth-child(1) > table > tbody > tr')))
for i in [1,2]:
    time.sleep(1)
    kabupaten = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(' + str(i) + ') > table > tbody > tr')
    for kab in kabupaten:
        time.sleep(1)
        wait.until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, 'div:nth-child(' + str(i) + ') > table > tbody > tr')))
        kab_button = kab.find_element(By.TAG_NAME, 'button')
        kab_name = kab_button.text
        driver.execute_script("arguments[0].scrollIntoView();", kab_button)
        driver.execute_script("arguments[0].click();", kab_button)

        for i in [1,2]:
            time.sleep(1)
            kecamatan = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(' + str(i) + ') > table > tbody > tr')
            for kec in kecamatan:
                wait.until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, 'div:nth-child(' + str(i) + ') > table > tbody > tr')))

                kec_button = kec.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > button')
                kec_name = kec_button.text
                driver.execute_script("arguments[0].scrollIntoView();", kec_button)
                driver.execute_script("arguments[0].click();", kec_button)

                kelurahan = driver.find_elements(By.CSS_SELECTOR, 'div:nth-child(1) > table > tbody > tr')
                time.sleep(1)
                left_table = scrape_left_table(pro, kab_name, kec_name)
                right_table = scrape_right_table(pro, kab_name, kec_name)
                data += left_table + right_table

                back = driver.find_element(By.CSS_SELECTOR, '#app > div.sticky-top.bg-white > div > div:nth-child(2) > div > div > div > div:nth-child(5) > div > div > div.vs__actions > button')
                driver.execute_script("arguments[0].scrollIntoView();", back)
                driver.execute_script("arguments[0].click();", back)
                
        back = driver.find_element(By.CSS_SELECTOR, '#app > div.sticky-top.bg-white > div > div:nth-child(2) > div > div > div > div:nth-child(4) > div > div > div.vs__actions > button')
        driver.execute_script("arguments[0].scrollIntoView();", back)
        driver.execute_script("arguments[0].click();", back)

报错栈信息

WebDriverException                        Traceback (most recent call last)
c:\...\web_scraping.ipynb Cell 4 in ()
     23 for kec in kecamatan:
     24     wait.until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, 'div:nth-child(' + str(i) + ') > table > tbody > tr')))
---> 26     kec_button = kec.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > button')
     27     kec_name = kec_button.text
     28     driver.execute_script("arguments[0].scrollIntoView();", kec_button)

WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32000,"message":"No node with given id found"}

解决方案

核心原因

报错本质是元素引用失效(Stale Element Reference):提前获取的kecamatan元素列表,在点击进入下级页面再返回后,页面DOM已刷新,旧的kec元素引用无法对应新DOM中的节点,此时调用kec.find_element就会触发"No node with given id found"。另外代码存在变量名冲突、固定sleep不可靠等问题,加剧了报错概率。

具体修复步骤

  1. 放弃复用旧元素引用,每次循环重新定位
    不要一次性获取所有行再循环,改为先获取行数量,再通过索引逐行重新定位元素,确保每次拿到的都是当前DOM中的有效元素:
# 替换原kecamatan循环逻辑
for j in [1,2]:
    wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr')))
    # 获取当前页面的行总数
    kec_row_count = len(driver.find_elements(By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr'))
    # 通过索引循环,每次重新定位元素
    for kec_idx in range(kec_row_count):
        kec = driver.find_element(By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr:nth-child({kec_idx+1})')
        # 后续操作不变
  1. 修复变量名冲突
    嵌套循环中重复使用i变量,会导致外层循环的i被内层覆盖,导致CSS选择器定位错误。把内层循环的i改为j:
# 原内层循环
# for i in [1,2]:
# 修改为
for j in [1,2]:
  1. 替换固定sleep为显式等待
    删掉所有time.sleep(1),用WebDriverWait等待元素就绪,既保证元素加载完成,又避免不必要的等待:
# 替换time.sleep(1)
wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr')))
  1. 优化元素定位稳定性
    避免使用div:nth-child(1)这种依赖DOM顺序的选择器,尽量用元素的专属class或属性。比如如果表格有region-list-table类,就用div.region-list-table > table,减少页面结构变化带来的定位失败。

  2. 简化返回按钮定位
    原返回按钮的CSS选择器过长且依赖DOM结构,可简化为:

# 用文本定位(如果按钮有返回文本)
back = wait.until(EC.element_to_be_clickable((By.XPATH, '//button[contains(text(), "返回")]')))
back.click()
# 或用按钮的图标class定位
back = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button.vs__clear')))
back.click()

修复后的关键代码片段

# 外层kabupaten循环
for i in [1,2]:
    wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({i}) > table > tbody > tr')))
    kab_row_count = len(driver.find_elements(By.CSS_SELECTOR, f'div:nth-child({i}) > table > tbody > tr'))
    for kab_idx in range(kab_row_count):
        kab = driver.find_element(By.CSS_SELECTOR, f'div:nth-child({i}) > table > tbody > tr:nth-child({kab_idx+1})')
        kab_button = kab.find_element(By.TAG_NAME, 'button')
        kab_name = kab_button.text
        wait.until(EC.element_to_be_clickable(kab_button)).click()

        # 内层kecamatan循环,变量名改为j
        for j in [1,2]:
            wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr')))
            kec_row_count = len(driver.find_elements(By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr'))
            for kec_idx in range(kec_row_count):
                # 每次重新定位kec元素
                kec = driver.find_element(By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr:nth-child({kec_idx+1})')
                kec_button = kec.find_element(By.CSS_SELECTOR, 'td.text-xs-left.wilayah-name > button')
                kec_name = kec_button.text
                wait.until(EC.element_to_be_clickable(kec_button)).click()

                # 等待表格加载完成后抓取数据
                wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'div:nth-child(1) > table > tbody > tr')))
                left_table = scrape_left_table(pro, kab_name, kec_name)
                right_table = scrape_right_table(pro, kab_name, kec_name)
                data += left_table + right_table

                # 返回kecamatan列表,等待页面加载完成
                back = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#app > div.sticky-top.bg-white > div > div:nth-child(2) > div > div > div > div:nth-child(5) > div > div > div.vs__actions > button')))
                back.click()
                wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({j}) > table > tbody > tr')))

        # 返回kabupaten列表,等待页面加载完成
        back = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#app > div.sticky-top.bg-white > div > div:nth-child(2) > div > div > div > div:nth-child(4) > div > div > div.vs__actions > button')))
        back.click()
        wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, f'div:nth-child({i}) > table > tbody > tr')))

内容的提问来源于stack exchange,提问作者Alvanzah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 04:02:04