兄弟们,用Selenium搞爬虫是不是经常被气得想砸键盘?明明代码看着没问题,浏览器却突然闪退;好不容易定位到元素,一翻页就报错失效;还有那阴魂不散的验证码和永远加载不完的页面!别慌,这些坑我都踩过,今天就用大白话给你总结一套防坑指南,让你爬虫效率直接起飞!

Selenium 是强大的自动化工具,但在爬虫过程中常常会遇到一些“坑”。我会为你梳理常见的错误及其解决方案,希望能帮助你更顺利地完成爬虫任务。
下面这个表格汇总了 Selenium 爬虫时你可能遇到的常见问题、原因及快速解决办法。
错误类型/问题现象
主要原因
推荐解决方案
引用来源
浏览器闪退、页面立即关闭
被网站检测到自动化工具(如 navigator.webdriver属性存在)
使用 undetected-chromedriver 或通过 CDP 命令修改 navigator.webdriver 属性为 undefined。
StaleElementReferenceException(元素过期)
页面刷新或重新加载后,之前获取的元素引用失效
等待页面加载完成后再重新定位元素,或尝试在新标签页中打开页面。
InvalidElementStateException(元素状态无效)
尝试与不可交互的元素(如不可见、被禁用、被覆盖、只读)进行操作
操作前等待元素变为可交互状态(可见、启用),并检查元素状态。
爬取的文本内容错误、缺失或为空
元素定位方式不准确、页面结构变化、动态加载内容未完全加载
确保选择器准确,使用显式等待(WebDriverWait) 等待特定元素加载完成。
页面加载超时 (TimeoutException)
网络问题、网站拦截、资源加载缓慢
合理设置 set_page_load_timeout(),并考虑使用代理IP。
多线程爬虫时数据竞争或崩溃
多线程共享 WebDriver 实例或数据未加锁
为每个线程创建独立 WebDriver 实例,或使用 threading.Lock保护共享数据。
遇到验证码(特别是滑动验证码)
网站反爬机制触发
可考虑专业验证码处理服务,或模拟人工滑动(注意轨迹和速度)。
翻页后无法获取新内容或重复旧内容
翻页操作后未等待新页面完全加载,代码执行速度比页面加载快
翻页后使用显式等待条件等待新页面关键元素加载完成,再提取内容。
1、绕过浏览器检测与反爬机制
网站通过检测 navigator.webdriver 等属性识别自动化脚本。
• 使用 undetected-chromedriver (推荐) 这是一个专门为绕过检测而设计的库。

import undetected_chromedriver as uc
driver = uc.Chrome(version_main=114, headless=False) # 匹配你的 Chrome 版本,慎用无头模式
driver.get("https://目标网站.com")


• 修改 navigator.webdriver 属性 在普通 Selenium 中通过 CDP 命令修改属性。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
​
chrome_options = Options()
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option("useAutomationExtension", False)
​
driver = webdriver.Chrome(options=chrome_options)
​
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        })
    """
})


• 模拟人类操作行为 随机延迟、模拟鼠标移动等行为有助于降低被检测的风险。

import time
import random
from selenium.webdriver.common.action_chains import ActionChains
​
time.sleep(random.uniform(1, 3)) # 随机延迟
​
actions = ActionChains(driver)
actions.move_by_offset(random.randint(10, 50), random.randint(10, 50)) # 随机移动鼠标
actions.perform()


2、处理动态加载与元素等待
页面元素是异步加载的,必须等待其出现后再操作。
• 显式等待 (Explicit Wait) 显式等待是针对特定条件进行的等待,比如等待某个元素存在、可见、可点击等。

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
​
try:
    # 等待最多10秒,直到ID为 'dynamicContent' 的元素出现并可见
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "dynamicContent"))
        # 也可用 EC.visibility_of_element_located
    )
    print(element.text)
except Exception as e:
    print("元素未找到或超时:", e)
​
# 等待元素可点击
clickable_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "myButton"))
)
clickable_element.click()


• 隐式等待 (Implicit Wait) 隐式等待是设置一个全局的等待时间,针对所有元素定位操作。

driver.implicitly_wait(10) # 设置隐式等待10秒
driver.find_element(By.ID, "someElement")


3、处理页面刷新与导航后的“元素过期”
页面刷新或跳转后,之前获取的元素引用会失效,需要重新定位。
• 显式等待新页面或元素 在操作后(如点击翻页)等待新页面的某个特定元素加载完成,然后再进行后续操作。

# 点击翻页按钮
next_page_button = driver.find_element(By.ID, "nextPage")
next_page_button.click()
​
# 等待新页面的特定元素(例如第二页的某个独有元素)加载完成
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "contentOnPage2"))
)
​
# 重新获取当前页面的元素列表
job_elements_list = driver.find_elements(By.CLASS_NAME, "job-item")


4、处理翻页问题
翻页时,除了等待新页面加载,有时网站结构可能导致翻页困难。
• 直接分析翻页URL规律 如果翻页是通过URL变化实现的(如 page=2),直接构造URL可能是最稳定的方式。
• 确保点击动作成功 有时点击翻页按钮需要用 JavaScript 直接执行。

5、多线程爬虫注意事项
在多线程环境中使用 Selenium 需要格外小心。
• 为每个线程创建独立的 WebDriver 实例 这是最简单也是最安全的方式,避免资源共享冲突。

import threading
from selenium import webdriver
​
def crawl_task(url):
    # 每个线程有自己的driver实例
    local_driver = webdriver.Chrome()
    local_driver.get(url)
    # ... 进行爬取操作
    # 操作完成后关闭
    local_driver.quit()
​
threads = []
for url in url_list:
    thread = threading.Thread(target=crawl_task, args=(url,))
    threads.append(thread)
    thread.start()
​
for thread in threads:
    thread.join()


• 使用锁 (Lock) 保护共享资源 如果必须共享某些资源(如写入同一个文件),使用锁来确保线程安全。

from threading import Lock

write_lock = Lock()
shared_file = open("data.txt", "a", encoding="utf-8")

def safe_write(data):
    with write_lock: # 获取锁
        shared_file.write(data + "\n") # 写入数据
    # 离开with块后自动释放锁

# 在爬虫线程中调用
safe_write("Some crawled data")


6、应对验证码
遇到验证码,特别是滑动验证码,处理起来比较复杂。
• 专业验证码处理服务 对于复杂验证码,可以考虑使用第三方服务(如 2Captcha、DeathByCaptcha)。
• 简单图形验证码 可以下载图片并使用 OCR 库(如 Tesseract,配合 pytesseract 库)识别。
• 滑动验证码 处理滑动验证码通常需要分析缺口位置、生成模拟人的滑动轨迹。

# 示例:生成滑动轨迹(部分代码,源自搜索结果)
def calculate_tracks(distance):
    # 模拟加速和减速过程,生成滑动轨迹列表
    v = 0
    t = 0.2
    forward_tracks = []
    current = 0
    mid = distance * 3 / 5
    while current < distance:
        if current < mid:
            a = 2  # 加速度
        else:
            a = -3 # 减速度
        s = v * t + 0.5 * a * (t ** 2)
        v = v + a * t
        current += s
        forward_tracks.append(round(s))
    return forward_tracks

# 使用 ActionsChains 执行滑动
slider = driver.find_element(By.ID, "slider")
tracks = calculate_tracks(150) # 假设需要滑动150像素

ActionChains(driver).click_and_hold(slider).perform()
for track in tracks:
    ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform()
ActionChains(driver).release().perform()


请注意,这只是一个示例,实际处理需要根据具体网站的验证码机制进行调整。滑动验证码的破解越来越难,很多网站会有更复杂的防护。
7、网络问题与超时处理
• 设置页面加载超时

from selenium.common.exceptions import TimeoutException

try:
    driver.set_page_load_timeout(30) # 设置页面加载超时为30秒
    driver.get("https://目标网站.com")
except TimeoutException:
    print("页面加载超时,可能是网络问题或网站拦截")
    driver.quit()


• 使用代理IP 如果IP被封锁,可以考虑使用代理IP。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument("--proxy-server=http://你的代理IP:端口") # 例如 http://123.45.67.89:8080
driver = webdriver.Chrome(options=chrome_options)


请注意,代理IP的稳定性和匿名性需要自行确保。
总之记住几个核心要领:爬虫别用无头模式容易暴露,元素定位要多等别硬睡,每个线程单独开浏览器最稳妥。遇到验证码别头铁,该用专业服务就别硬刚。只要把这些技巧摸透,Selenium爬虫基本就能横着走了!如果还遇到新问题,欢迎随时来交流~

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐