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
相关产品推荐
相关产品推荐

