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

Selenium爬取时JavaScript事件触发异常:下拉框无法弹出求助

问题描述

我需要爬取某匹配捐赠网站的数据,流程是:在搜索框输入文本,选择下拉框首个选项,触发对应区块的数据更新,重复这个操作收集所有输入项的数据。

但用Selenium驱动浏览器时,输入文本后下拉框死活弹不出来,没法更新目标数据。手动操作的时候,搜索输入区域的div类名会变,还会生成新的下拉框区块,但Selenium环境里这些变化都没发生。我试过用代码修改类名,还是触发不了下拉框。

附上当前的爬取代码:

def search_for_company(driver, company_name):
    # find search input
    search_input = driver.find_element_by_css_selector(config.SEARCH_INPUT_SELECTOR)
    search_box_div = search_input.find_element_by_xpath('//*[@id="dd-container"]/div/div/div[3]')
    # Remove the readonly attribute
    driver.execute_script("arguments[0].removeAttribute('readonly')", search_input)
    # clear the current text and update with new text
    search_input.clear()
    #search_input.send_keys(company_name)
    # you can see here I tried experimenting with trying to mimic actually user input, to no avail...
    for char in company_name:
        search_input.send_keys(char)
    # Change the class of the div
    driver.execute_script("arguments[0].setAttribute('class', 'dtd-search-box is-open is-focused')", search_box_div)

    time.sleep(10)  # Allow time for potential results to load

    # Check if there are no results (skip if no dropdown results pop up)
    try:
        # this is an actual div class that pops up when there are no results
        no_results = driver.find_element_by_css_selector("div.dtd-noresults")
        if no_results.is_displayed():
            print("No results displayed >>> SKIPPED.")
            return False  # No results found
    except:
        # If no such element, proceed with pressing return
        search_input.send_keys(Keys.RETURN)
        time.sleep(config.REQUEST_DELAY)
        print("Returning results.")
        return True  # Results found

解决办法

1. 用ActionChains模拟真实用户操作

很多网站的下拉框依赖focus、input这类原生事件,单纯用send_keys或者改属性可能触发不全。试试用ActionChains模拟完整的用户交互流程:

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

def search_for_company(driver, company_name):
    search_input = driver.find_element_by_css_selector(config.SEARCH_INPUT_SELECTOR)
    
    # 移除readonly属性(如果需要)
    driver.execute_script("arguments[0].removeAttribute('readonly')", search_input)
    
    # 模拟鼠标移动到输入框、点击聚焦,还原真实操作
    ActionChains(driver).move_to_element(search_input).click().perform()
    
    # 清除原有内容
    search_input.clear()
    
    # 逐字符输入,加一点停顿模拟真实打字节奏
    for char in company_name:
        ActionChains(driver).send_keys(char).pause(0.1).perform()
    
    # 触发input事件(关键!有些网站只认这个事件来渲染下拉)
    driver.execute_script("arguments[0].dispatchEvent(new Event('input'))", search_input)
    
    # 用显式等待替代固定sleep,等下拉选项出现
    try:
        # 这里替换成你实际的下拉选项选择器,比如".dtd-search-results li:first-child"
        first_option = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "你的下拉选项选择器"))
        )
        first_option.click()
        
        # 等待数据区块更新,比如等原有区块失效或者新区块出现
        WebDriverWait(driver, config.REQUEST_DELAY).until(
            EC.staleness_of(driver.find_element_by_css_selector("原有数据区块选择器"))
        )
        print("数据加载完成")
        return True
    except:
        # 检查无结果状态
        try:
            no_results = WebDriverWait(driver, 5).until(
                EC.visibility_of_element_located((By.CSS_SELECTOR, "div.dtd-noresults"))
            )
            print("No results displayed >>> SKIPPED.")
            return False
        except:
            # 退而求其次用回车触发
            search_input.send_keys(Keys.RETURN)
            WebDriverWait(driver, config.REQUEST_DELAY).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "更新后的数据区块选择器"))
            )
            print("Returning results.")
            return True

2. 直接触发页面的JS事件

如果模拟操作还是不行,试试直接触发页面绑定的输入相关事件,比如input、keyup:

# 在输入完文本后,执行这段代码
driver.execute_script("arguments[0].dispatchEvent(new Event('input'))", search_input)
driver.execute_script("arguments[0].dispatchEvent(new Event('keyup'))", search_input)

这比修改类名更直接,因为很多前端框架是通过事件监听来更新DOM的,不是单纯依赖类名变化。

3. 检查是否被反爬识别

有些网站会检测Selenium的navigator.webdriver属性,试试隐藏这个特征:

# 初始化driver之后执行这段代码
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

4. 换掉固定sleep,用显式等待

time.sleep(10)太死板,页面加载快的时候浪费时间,加载慢的时候又不够。用WebDriverWait等元素出现或状态变化,更可靠。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:10:40