尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Selenium+Python爬虫实战:从环境搭建到高级技巧全解析

Selenium+Python爬虫实战:从环境搭建到高级技巧全解析
📅 发布时间:2026/7/29 6:39:57

1. 从“点击”到“数据”:为什么Selenium+Python是爬虫的终极利器?

如果你尝试过用requests和BeautifulSoup写爬虫,大概率会遇到过这样的场景:目标网站的数据,在你用浏览器访问时明明就在那里,但用代码一抓,返回的要么是空页面,要么是一堆看不懂的JavaScript代码。你检查了请求头,加了Cookie,甚至模拟了Ajax请求,但数据就像跟你捉迷藏一样,怎么也抓不到。这时候,一个念头就会冒出来:要是能让代码像真人一样去操作浏览器,看到什么就抓什么,该多好?

这个念头,就是Selenium+Python爬虫方案的核心价值。它不再局限于传统的HTTP请求-响应模式,而是直接驱动一个真实的浏览器(比如Chrome)去访问网页,执行页面上的JavaScript,渲染出完整的DOM树,然后你再像在浏览器开发者工具里一样,精准地定位和提取数据。简单来说,它把爬虫从“网络协议层”提升到了“用户交互层”。我最初接触这个组合,就是为了搞定一个需要先登录、再点击多个选项卡、最后在动态表格里翻页查询数据的内部系统。用传统方法几乎无从下手,但用Selenium,我写出的脚本就像有个隐形的员工在帮我操作电脑,稳定运行了半年多,抓取了上百万条记录。

这套方案特别适合三类人:一是爬虫新手,面对复杂网站无从下手时,Selenium提供了最直观的“所见即所得”的解决方案;二是需要处理大量动态内容(如由React、Vue等框架构建的单页应用)的数据采集者;三是需要模拟完整用户行为流(登录、搜索、翻页、下单)的自动化测试或业务流模拟工程师。它可能不是最快的方案,但在成功率和易用性上,往往是那个最可靠的“保底”选择。

2. 环境搭建:别在第一步就踩坑

万事开头难,环境配置是劝退很多新手的第一个门槛。网上教程五花八门,但很多都忽略了版本兼容性这个致命细节。我见过太多人照着过时的教程,安装了最新版的Selenium和Chrome浏览器,结果代码一跑就报错,折腾半天找不到原因。所以,我们的第一步必须稳扎稳打。

2.1 Python与Selenium库安装

首先确保你有一个Python环境。我个人推荐使用Python 3.8或3.9版本,这两个版本在生态兼容性上最为成熟稳定。安装Selenium库非常简单,打开你的命令行(CMD或终端),使用pip命令即可:

pip install selenium

这里有个关键点:不要盲目追求最新版本。截至我写这篇文章时,selenium 4.x是主流版本,其API相比3.x有一些变化,但核心用法一致。我们以4.x版本为基础进行讲解,如果你安装的是3.x,大部分代码也兼容,但部分导入语句或选项设置可能需要微调。

2.2 浏览器驱动的“配对”哲学

这是Selenium工作的核心,也是最容易出错的地方。Selenium库本身只是一个发出指令的“遥控器”,它需要另一个叫做“浏览器驱动”的程序来实际控制浏览器。这个驱动必须和你的浏览器主程序版本严格匹配。

以Chrome为例,你需要的是ChromeDriver。

  1. 查看你的Chrome版本:打开Chrome浏览器,点击右上角三个点 -> 帮助 -> 关于Google Chrome。记下版本号(例如:109.0.5414.120)。
  2. 下载对应版本的ChromeDriver:访问ChromeDriver的官方下载站或国内镜像站。找到与你的Chrome版本号完全一致的驱动进行下载。如果官网只有接近的版本(比如你是109.0.5414.120,但官网只有109.0.5414.*),通常小版本号差异可以兼容,但最好还是寻找完全一致的。
  3. 放置驱动并配置路径:下载后是一个可执行文件(如chromedriver.exeon Windows)。你有两种方式让Python找到它:
    • 方法A(推荐,尤其对新手):将这个驱动文件直接放在你的Python脚本所在的同一个目录下。Selenium会优先在当前目录查找。
    • 方法B(一劳永逸):将驱动文件放在某个固定目录(如C:\WebDriver\或/usr/local/bin),然后将该目录路径添加到系统的环境变量PATH中。

注意:很多教程会教你用webdriver-manager这类库自动管理驱动,这对于快速测试和学习是方便的。但在生产环境或需要长期稳定运行的脚本中,我强烈建议手动指定确定版本的驱动。自动下载可能因为网络问题失败,也可能在你不知情时更新到不兼容的版本,导致线上任务突然崩溃。

2.3 编写你的第一个“Hello World”脚本

环境就绪,我们来写一个最简单的脚本,打开百度并搜索一个关键词。创建一个新的Python文件,比如first_crawl.py。

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 1. 创建浏览器驱动对象,这将启动一个Chrome浏览器窗口 driver = webdriver.Chrome() # 如果chromedriver不在当前目录或PATH,需要指定路径:webdriver.Chrome(executable_path='你的路径/chromedriver') # 2. 打开百度首页 driver.get("https://www.baidu.com") # 3. 等待页面加载一下(实际项目中会用更智能的等待方式,这里先用time.sleep) time.sleep(2) # 4. 定位搜索框。通过检查元素,发现搜索框的id是'kw' search_box = driver.find_element(By.ID, "kw") # 5. 在搜索框中输入文本 search_box.send_keys("Selenium自动化测试") # 6. 模拟按下回车键进行搜索 search_box.send_keys(Keys.RETURN) # 7. 等待搜索结果加载 time.sleep(3) # 8. 打印当前页面的标题 print("当前页面标题是:", driver.title) # 9. 关闭浏览器窗口 driver.quit()

运行这个脚本,你会看到一个Chrome浏览器窗口自动打开,访问百度,输入关键词并搜索,最后在控制台打印出标题,然后关闭。恭喜,你已经成功迈出了第一步!这个脚本包含了Selenium最核心的几个操作:启动驱动、访问URL、定位元素、模拟输入、模拟键盘事件、获取页面属性、关闭驱动。

3. 核心操作详解:像侦探一样定位元素

Selenium爬虫的绝大部分工作,都可以归结为“定位元素”和“与元素交互”。网页上的每一个按钮、输入框、链接、文本块,都是一个HTML元素。我们的脚本要做的就是找到它们,然后点击、输入或读取。

3.1 八种定位器:找到元素的“钥匙”

Selenium提供了8种主要的定位方式,通过By类来调用。掌握它们是你精准抓取数据的基础。

from selenium.webdriver.common.by import By # 假设 driver 已经创建,并打开了一个网页 # 1. 通过ID定位 (最优先选择,通常唯一且稳定) element = driver.find_element(By.ID, “login-button”) # 2. 通过NAME定位 (常用于表单元素) element = driver.find_element(By.NAME, “username”) # 3. 通过CLASS_NAME定位 (注意:class可能有多个,用空格分隔,这里只能匹配其中一个) element = driver.find_element(By.CLASS_NAME, “btn-primary”) # 4. 通过TAG_NAME定位 (如 ‘input’, ‘a’, ‘div’,通常不够精确,需结合其他条件) element = driver.find_element(By.TAG_NAME, “h1”) # 5. 通过LINK_TEXT定位 (精确匹配链接的完整可见文本) element = driver.find_element(By.LINK_TEXT, “忘记密码?”) # 6. 通过PARTIAL_LINK_TEXT定位 (匹配链接文本的一部分) element = driver.find_element(By.PARTIAL_LINK_TEXT, “密码”) # 7. 通过CSS_SELECTOR定位 (功能强大,语法灵活,是主力定位方式) element = driver.find_element(By.CSS_SELECTOR, “#content .list-item:nth-child(2)”) # 定位id为content下,第二个list-item类的元素 # 8. 通过XPATH定位 (功能最强大,但语法相对复杂,可以遍历XML/HTML文档树) element = driver.find_element(By.XPATH, “//div[@id=‘main’]//a[contains(text(), ‘下一页’)]”) # 定位id为main的div下,文本包含“下一页”的a标签

定位策略心得:

  • 优先级:ID>NAME>CSS_SELECTOR>XPATH> 其他。ID和NAME通常是开发人员赋予的具有语义的标识,最稳定。如果都没有,CSS Selector通常是首选,因为它性能好且语法简洁。
  • 避免脆弱的定位:不要使用绝对路径的XPATH(如/html/body/div[3]/div[2]/div[4]),页面结构稍有变动就会失效。尽量使用相对路径和属性组合。
  • 使用浏览器开发者工具:这是你最好的朋友。在网页上右键“检查”,可以查看元素的HTML代码。在“Elements”面板中,右键某个元素,选择“Copy” -> “Copy selector”或“Copy XPath”,可以快速获取定位表达式,但需要人工校验其稳定性。

3.2 元素交互:模拟真实用户操作

找到元素后,我们就可以与之交互了。最常用的操作如下:

# 点击元素(按钮、链接、复选框等) element.click() # 在输入框内输入文本(会先清空原有内容) element.send_keys(“你要输入的文字”) # 清空输入框内容 element.clear() # 获取元素的文本内容(即用户看到的文字) text = element.text print(text) # 获取元素的某个属性值(如href, src, value等) link_url = element.get_attribute(“href”) print(link_url) # 判断元素是否可见、是否可点击、是否被选中(用于复选框、单选框) is_displayed = element.is_displayed() is_enabled = element.is_enabled() is_selected = element.is_selected()

3.3 等待的艺术:告别time.sleep

在我们第一个脚本中,使用了time.sleep(3)来等待页面加载。这是显式等待(固定等待),简单但低效。如果页面2秒就加载完了,你白白等了1秒;如果网络慢5秒才加载完,你的代码就会因为找不到元素而报错。

Selenium提供了两种更优雅的等待方式:

  1. 隐式等待 (Implicit Wait):为整个driver会话设置一个全局的等待时间。当查找元素时,如果元素没有立即出现,WebDriver会轮询DOM一段时间,直到找到元素或超时。

    driver.implicitly_wait(10) # 单位:秒 # 此后,所有 driver.find_element 操作都会最多等待10秒
  2. 显式等待 (Explicit Wait):针对某个特定条件进行等待,更加灵活和精确。这是生产环境推荐的做法。

    from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待直到ID为‘result’的元素出现在DOM中并且可见,最多等10秒,每0.5秒检查一次 wait = WebDriverWait(driver, 10, poll_frequency=0.5) result_element = wait.until(EC.visibility_of_element_located((By.ID, “result”))) # 等待直到某个元素可以被点击 button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, “.submit-btn”))) button.click() # 等待直到页面标题包含特定文字 wait.until(EC.title_contains(“搜索结果”))

    expected_conditions模块提供了很多预置条件,如元素是否存在、是否可见、是否可点击、文本是否出现等。这能确保你的代码在元素真正就绪时才进行操作,极大提高了稳定性和运行效率。

4. 实战:构建一个健壮的爬虫流程

掌握了基本操作,我们把这些知识串联起来,构建一个处理常见爬虫场景的完整流程。我们以一个需要登录、然后查询并翻页抓取列表数据的模拟网站为例。

4.1 处理登录与验证码

很多网站的数据隐藏在登录之后。Selenium处理登录非常直观,就是模拟输入用户名密码并点击登录按钮。

def login(driver, username, password): driver.get(“https://example.com/login”) wait = WebDriverWait(driver, 10) # 定位并输入用户名密码 user_input = wait.until(EC.presence_of_element_located((By.NAME, “username”))) pass_input = driver.find_element(By.NAME, “password”) user_input.send_keys(username) pass_input.send_keys(password) # 处理可能的验证码(这里以简单图片验证码为例,复杂验证码需要额外处理) # 1. 定位验证码图片元素 captcha_img = driver.find_element(By.ID, “captcha_image”) # 2. 截图保存验证码图片(实际项目中,这里可能需要对接打码平台) captcha_img.screenshot(“captcha.png”) print(“请查看当前目录下的captcha.png文件,输入验证码:”) captcha_code = input() # 等待手动输入,自动化需要接入OCR或打码API # 3. 输入验证码 captcha_input = driver.find_element(By.ID, “captcha”) captcha_input.send_keys(captcha_code) # 4. 点击登录按钮 login_button = driver.find_element(By.XPATH, “//button[@type=‘submit’]”) login_button.click() # 5. 等待登录成功后的页面跳转,例如等待用户头像出现 wait.until(EC.presence_of_element_located((By.CLASS_NAME, “user-avatar”))) print(“登录成功!”)

关于验证码的深入讨论:这是爬虫与反爬对抗的重点。除了简单的图形验证码,还有滑动拼图、点选文字、短信验证等。对于简单图形码,可以尝试使用开源的OCR库(如pytesseract,但识别率有限)。对于复杂验证码,商业打码平台是更可靠的选择,它们提供API接口,你上传图片,它们返回识别结果。如果网站验证码极其复杂或动态变化,可能需要考虑其他技术路线,如尝试寻找无需验证码的API接口,但这已超出Selenium的范畴。

4.2 数据提取与解析

登录成功后,进入数据列表页。我们需要提取每一行的数据。

假设列表页的HTML结构如下:

<table id=“data-table”> <tr> <th>姓名</th><th>年龄</th><th>城市</th> </tr> <tr> <td class=“name”>张三</td><td>28</td><td class=“city”>北京</td> </tr> <tr> <td class=“name”>李四</td><td>35</td><td class=“city”>上海</td> </tr> </table>
def extract_data_from_current_page(driver): data_list = [] # 定位到表格的所有行,跳过表头(索引从1开始) rows = driver.find_elements(By.CSS_SELECTOR, “#data-table tr”)[1:] # find_elements 返回列表 for row in rows: # 在每一行内,定位各个单元格 cells = row.find_elements(By.TAG_NAME, “td”) if len(cells) >= 3: item = { “name”: cells[0].text, # 获取文本 “age”: int(cells[1].text), # 转换为整数 “city”: cells[2].text } # 也可以获取特定属性,比如如果名字单元格里有链接 # link = cells[0].find_element(By.TAG_NAME, “a”).get_attribute(“href”) data_list.append(item) return data_list

关键技巧:注意find_element和find_elements的区别。前者返回第一个匹配的元素(一个WebElement对象),如果没找到会抛出异常;后者返回所有匹配元素的列表(一个列表对象),如果没找到则返回空列表。在遍历列表或不确定元素是否存在时,使用find_elements更安全。

4.3 自动翻页与循环终止

数据通常分页显示。我们需要自动点击“下一页”,直到没有下一页为止。

def crawl_all_pages(driver, start_url): driver.get(start_url) all_data = [] page_num = 1 while True: print(f“正在抓取第 {page_num} 页...”) # 提取当前页数据 page_data = extract_data_from_current_page(driver) all_data.extend(page_data) # 尝试定位‘下一页’按钮 try: # 下一页按钮可能是一个链接,也可能是一个按钮 # 示例1:链接文本是‘下一页’ next_button = driver.find_element(By.LINK_TEXT, “下一页”) # 示例2:一个带有‘next’类的按钮 # next_button = driver.find_element(By.CSS_SELECTOR, “.next-page:not(.disabled)”) # 检查按钮是否可点击(是否被禁用) if next_button.is_enabled(): next_button.click() # 等待新页面加载完成,例如等待表格刷新或某个元素出现 WebDriverWait(driver, 10).until( EC.staleness_of(next_button) # 等待旧按钮从DOM中消失,表示页面已跳转 ) # 或者等待新页面的某个标志性元素出现 # WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, “data-table”))) page_num += 1 else: print(“已是最后一页。”) break except Exception as e: # 如果找不到‘下一页’按钮,说明已经是最后一页 print(f“未找到下一页按钮,抓取结束。错误信息(可忽略):{e}”) break # 可选:防止意外无限循环,设置最大页数限制 if page_num > 50: print(“已达到最大页数限制,停止抓取。”) break return all_data

这个循环逻辑是爬虫的核心之一。关键在于如何可靠地判断“已是最后一页”。通常有几种方式:1)“下一页”按钮变为不可点击(is_enabled()为False或增加了disabled类);2)“下一页”按钮在最后一页直接消失(find_element会抛出NoSuchElementException);3)页面有明确的页码提示(如“当前第5页/共10页”)。你需要根据目标网站的具体情况来调整判断逻辑。

5. 高级技巧与性能优化

当你的爬虫需要长时间运行或处理大量页面时,基础操作可能不够用。下面是一些提升脚本稳定性、效率和隐蔽性的高级技巧。

5.1 绕过检测与浏览器指纹伪装

越来越多的网站会检测Selenium等自动化工具。它们通过检测浏览器环境中的一些特定JavaScript变量(如navigator.webdriver)来判断。我们可以通过添加Chrome选项来尝试绕过。

from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service chrome_options = Options() # 1. 添加常规反检测参数 chrome_options.add_argument(“--disable-blink-features=AutomationControlled”) chrome_options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) chrome_options.add_experimental_option(‘useAutomationExtension’, False) # 2. 修改 navigator.webdriver 属性(在Selenium 4中,这行代码可能需要在driver创建后执行CDP命令) # 更推荐使用下面的CDP命令方法 # 3. 使用 Chrome DevTools Protocol (CDP) 直接执行JavaScript,覆盖webdriver属性 # 这需要在创建driver后执行 def create_stealth_driver(): options = Options() options.add_argument(“--disable-blink-features=AutomationControlled”) options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) options.add_experimental_option(‘useAutomationExtension’, False) # 可以添加用户代理,模拟真实浏览器 options.add_argument(‘user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36’) driver = webdriver.Chrome(options=options) # 执行CDP命令,将 navigator.webdriver 设置为 undefined driver.execute_cdp_cmd(‘Page.addScriptToEvaluateOnNewDocument’, { ‘source’: ‘'' Object.defineProperty(navigator, ‘webdriver’, { get: () => undefined }); ‘'' }) return driver driver = create_stealth_driver()

此外,还可以考虑:

  • 禁用图片加载:加速页面加载。chrome_options.add_argument(‘--blink-settings=imagesEnabled=false’)
  • 使用无头模式:不显示浏览器GUI,节省资源。chrome_options.add_argument(‘--headless’)。注意,无头模式更容易被一些高级反爬系统识别。
  • 设置窗口大小:有些网站会检测窗口尺寸。driver.set_window_size(1920, 1080)

5.2 使用WebDriver Action Chains模拟复杂操作

对于拖拽、鼠标悬停、右键菜单、组合键等复杂交互,需要使用ActionChains类。

from selenium.webdriver.common.action_chains import ActionChains # 鼠标悬停 menu = driver.find_element(By.ID, “dropdown-menu”) ActionChains(driver).move_to_element(menu).perform() # 悬停后出现的子菜单 submenu = wait.until(EC.visibility_of_element_located((By.LINK_TEXT, “子选项”))) submenu.click() # 拖拽元素 source = driver.find_element(By.ID, “draggable”) target = driver.find_element(By.ID, “droppable”) ActionChains(driver).drag_and_drop(source, target).perform() # 组合键操作 ActionChains(driver).key_down(Keys.CONTROL).send_keys(‘c’).key_up(Keys.CONTROL).perform() # 模拟Ctrl+C

5.3 多线程与并发控制

Selenium的每个driver实例都是一个独立的浏览器进程,资源消耗较大。直接开上百个线程跑Selenium会导致系统崩溃。合理的做法是使用线程池,控制并发数量。

from concurrent.futures import ThreadPoolExecutor, as_completed import threading def worker(task_url): # 每个线程创建自己的driver实例,避免资源共享冲突 thread_local = threading.local() if not hasattr(thread_local, “driver”): options = Options() options.add_argument(“--headless”) # 无头模式节省资源 thread_local.driver = webdriver.Chrome(options=options) driver = thread_local.driver try: driver.get(task_url) # ... 执行抓取任务 ... data = extract_data(driver) return data except Exception as e: print(f“抓取 {task_url} 失败:{e}”) return None # 要抓取的URL列表 urls_to_crawl = [“url1”, “url2”, “url3”, …] # 使用最多3个线程的线程池 all_results = [] with ThreadPoolExecutor(max_workers=3) as executor: future_to_url = {executor.submit(worker, url): url for url in urls_to_crawl} for future in as_completed(future_to_url): url = future_to_url[future] try: data = future.result() if data: all_results.append(data) print(f“成功抓取:{url}”) except Exception as exc: print(f‘{url} 产生了异常:{exc}’) # 所有任务完成后,记得关闭各个线程的driver(这里示例简化,实际需在每个线程内或最后统一清理)

重要警告:多线程Selenium必须确保每个线程有自己独立的driver实例,绝不能在多个线程间共享同一个driver,这会导致不可预知的错误。使用threading.local()是一种简洁的管理方式。

6. 常见问题排查与实战避坑指南

即使按照教程一步步来,在实际操作中你还是会遇到各种各样的问题。下面是我在长期使用中总结的一些典型“坑”及其解决方案。

6.1 元素定位失败:NoSuchElementException

这是最常见的问题。脚本报错说找不到某个元素。

排查步骤:

  1. 等待不足:这是首要原因。页面还没加载完你就去找元素。解决方案:使用WebDriverWait配合expected_conditions进行显式等待,而不是time.sleep。
  2. 页面内有iframe/框架页:你要找的元素嵌套在<iframe>里。解决方案:需要先切换到对应的iframe。
    # 通过ID或索引切换到iframe iframe = driver.find_element(By.ID, “iframe_id”) driver.switch_to.frame(iframe) # 现在可以定位iframe内的元素了 # 操作完成后,切回主页面 driver.switch_to.default_content()
  3. 定位表达式写错了:可能是复制出来的CSS Selector或XPath不稳定。解决方案:在浏览器的开发者工具Console里测试你的表达式。例如,在Console里输入$$(“你的CSS选择器”)或$x(“你的XPath表达式”),看是否能正确选中元素。
  4. 元素是动态生成的:有些元素是在你执行了某个操作(如点击、滚动)后才出现的。解决方案:确保在触发元素出现的操作执行后,再等待并定位它。
  5. 页面发生了跳转或刷新:你获取到的元素对象在页面刷新后已经“过期”(stale)。解决方案:在页面刷新或跳转后,需要重新定位元素。使用EC.staleness_of(element)可以等待一个旧元素“失效”。

6.2 页面加载异常或空白

用driver.get(url)后,页面一片空白或加载不全。

排查步骤:

  1. 检查网络和URL:确保网络通畅,URL正确。
  2. 浏览器控制台报错:在脚本中启动浏览器时,不要立即关闭,查看浏览器控制台(F12 -> Console)是否有JavaScript错误。有时网站JS报错会导致页面渲染失败。
  3. 证书或安全警告:访问HTTPS网站可能遇到证书问题。解决方案:添加选项忽略证书错误(仅用于测试环境)。
    chrome_options.add_argument(‘--ignore-certificate-errors’) chrome_options.add_argument(‘--allow-insecure-localhost’)
  4. 网站屏蔽了自动化访问:返回了验证页面或错误信息。解决方案:尝试上述的“绕过检测”技巧,添加更真实的浏览器指纹和用户代理。

6.3 脚本运行速度慢

Selenium因为要启动和渲染完整浏览器,天生就比requests慢。但我们可以优化。

优化策略:

  1. 启用无头模式:chrome_options.add_argument(‘--headless’)。去掉GUI渲染能快不少。
  2. 禁用图片、CSS、字体等非必要资源:
    prefs = {“profile.managed_default_content_settings.images”: 2} chrome_options.add_experimental_option(“prefs”, prefs)
  3. 优化等待策略:用精确的显式等待替代固定的time.sleep和过长的隐式等待。
  4. 复用浏览器会话:对于需要多次登录或保持会话的场景,可以考虑使用Chrome DevTools Protocol更底层地控制浏览器,或者使用selenium-wire这类库来直接复用Cookie,避免每次重新登录。但这属于更高级的用法。

6.4 内存泄漏与浏览器进程残留

长时间运行爬虫脚本后,可能会发现电脑内存被大量Chrome进程占用。

解决方案:

  1. 确保driver.quit()被调用:这是最重要的。driver.quit()会关闭浏览器并结束WebDriver进程。而driver.close()只关闭当前标签页。务必在try…except…finally块中或在脚本最后调用driver.quit()。
    driver = None try: driver = webdriver.Chrome() # … 你的爬虫逻辑 … except Exception as e: print(f“运行出错:{e}”) finally: if driver: driver.quit() # 确保无论如何都会执行清理
  2. 定期重启:对于需要7x24小时运行的爬虫,可以设计一个机制,在抓取一定数量页面或运行一段时间后,主动调用driver.quit()并重新创建新的driver实例,以释放积累的内存碎片。
  3. 监控进程:在任务管理器中监控chromedriver.exe和chrome.exe进程的数量。如果发现只增不减,说明存在资源未释放的问题。

7. 项目结构与代码封装:从脚本到工程

当你的爬虫逻辑越来越复杂,不再是一个简单的.py文件时,良好的代码组织就至关重要了。这能提升代码的可读性、可维护性和复用性。

一个建议的项目结构如下:

your_crawler_project/ ├── config.py # 配置文件,存放URL、账号密码、数据库连接信息等 ├── main.py # 主程序入口 ├── core/ │ ├── __init__.py │ ├── browser.py # 封装浏览器创建、配置、关闭等逻辑 │ ├── login.py # 封装登录逻辑 │ └── parser.py # 封装页面解析和数据提取逻辑 ├── utils/ │ ├── __init__.py │ ├── logger.py # 日志记录模块 │ └── file_io.py # 文件读写辅助函数 ├── storage/ │ ├── __init__.py │ └── data_handler.py # 数据处理和存储(如存到CSV、数据库) └── requirements.txt # 项目依赖包列表

核心模块browser.py示例:

# core/browser.py from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait import logging logger = logging.getLogger(__name__) class BrowserDriver: def __init__(self, headless=False, stealth=True): self.options = Options() if headless: self.options.add_argument(“--headless”) if stealth: self._add_stealth_options() # 其他通用配置 self.options.add_argument(“--disable-gpu”) self.options.add_argument(“--window-size=1920,1080”) self.driver = None def _add_stealth_options(self): self.options.add_argument(“--disable-blink-features=AutomationControlled”) self.options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) self.options.add_experimental_option(‘useAutomationExtension’, False) def start(self): “”“启动浏览器”“” try: self.driver = webdriver.Chrome(options=self.options) if self.options.arguments.get(“--disable-blink-features”): self.driver.execute_cdp_cmd(‘Page.addScriptToEvaluateOnNewDocument’, { ‘source’: ‘'' Object.defineProperty(navigator, ‘webdriver’, { get: () => undefined }); ‘'' }) logger.info(“浏览器启动成功。”) return self.driver except Exception as e: logger.error(f“启动浏览器失败:{e}”) raise def get_wait(self, timeout=10): “”“获取一个WebDriverWait对象”“” return WebDriverWait(self.driver, timeout) def quit(self): “”“安全关闭浏览器”“” if self.driver: self.driver.quit() logger.info(“浏览器已关闭。”)

这样,在你的主程序main.py中,代码会非常清晰:

# main.py from core.browser import BrowserDriver from core.login import login from core.parser import DataParser from storage.data_handler import save_to_csv import config def main(): browser_manager = BrowserDriver(headless=True, stealth=True) driver = browser_manager.start() try: # 登录 login(driver, config.USERNAME, config.PASSWORD) # 进入目标页面 driver.get(config.TARGET_URL) # 初始化解析器 parser = DataParser(driver) all_data = [] while parser.has_next_page(): page_data = parser.parse_current_page() all_data.extend(page_data) parser.go_to_next_page() # 保存数据 save_to_csv(all_data, “output.csv”) print(f“共抓取 {len(all_data)} 条数据。”) except Exception as e: print(f“程序运行出错:{e}”) finally: browser_manager.quit() if __name__ == “__main__”: main()

这种模块化的设计,使得登录逻辑、解析逻辑、浏览器管理、数据存储都分离开来。未来如果登录方式变了,你只需要修改login.py;如果网站改版导致解析规则变了,你只需要修改parser.py。代码的维护性和可测试性都大大增强。

最后,再分享一个我个人的小习惯:在编写复杂的页面解析逻辑时,我会先用Selenium打开页面,手动操作一遍,然后用driver.page_source将完整的HTML源码保存到本地文件,再用编辑器慢慢研究结构、编写和测试XPath或CSS选择器。这比反复运行脚本调试要高效得多。Selenium+Python爬虫是一个强大的工具,它的价值在于能处理那些“传统爬虫”束手无策的复杂场景。虽然速度不是它的强项,但它的高成功率和模拟真实性,在很多时候是无价的。掌握它,意味着你打开了一扇处理动态网页数据的新大门。

相关新闻

  • Simulink核心原理与工程实践:从信号求解器到代码生成全解析
  • 电路分析实战:从静态工作点到动态响应,打通硬件设计核心脉络
  • C++对象内存布局与字节对齐:从原理到实战优化

最新新闻

  • 知识库与AI写作融合提升公文写作效率
  • 嵌入式外部中断实战:从轮询到事件驱动的设计思维转变
  • 在信息洪流中修筑巴别塔:WaytoAGI与千万人的“通往通用人工智能之路”
  • 99 年清华博士创业:AI 赋能电池行业,2 - 3 天完成 3 个月项目!
  • TikTok IM协议逆向实战:解密WSS通信与模拟商品卡片发送
  • Processing粒子系统实战:从零构建动态雨景模拟与交互优化

日新闻

  • 金融舆情监测系统:多语言情感分析与实时可视化技术解析
  • QT C++调用Python异常处理:PyBind11实战与跨语言编程指南
  • A-47双麦回音消除模块:主次麦空间分布与差分连接对ENC性能的影响

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号