一、什么是网页自动化爬虫?为什么需要它?

传统接口爬虫靠模拟API请求获取数据,遇到验证码、JS动态渲染、登录校验、无限滚动等场景极易失效。

网页自动化爬虫:用程序控制真实浏览器,完全模拟人类操作(打开、点击、输入、滚动、等待),能拿到渲染后的完整页面,应对严苛反爬。

适用场景

  • 登录带验证码/滑块验证

  • 数据由JS异步渲染、源码无数据

  • 需滚动/点击加载更多内容

  • 接口加密混淆、难以抓包分析

优缺点速览

✅ 优点:适配复杂交互、获取完整渲染HTML、绕过接口加密

❌ 缺点:资源占用高、速度慢于接口请求、页面改版需维护

二、Selenium环境搭建

Selenium是主流Web自动化工具,支持Python控制Chrome/Firefox等浏览器。

1. 安装依赖

pip install selenium

2. 浏览器驱动配置

先确定浏览器的版本号,程序员主流用的浏览器是Chrome,所以这里以Chrome为例,打开路径为是浏览器右上角的 ... -> 设置 -> 关于Chrome

Chrome浏览器的驱动程序可以在https://registry.npmmirror.com/binary.html?path=chrome-for-testing/网站下载(用其他浏览器,可以在搜索引擎里搜索一下关键字,比如“Selenium Firefox浏览器驱动下载”)

查找自己浏览器版本对应的驱动程序安装文件就可以了。以138.0.7204.169为例子,138为主版本号,0为次版本号,7204修订版本号,101是构建版本号。如果找不到对应到构建版本号的驱动程序,也可以下载和修订版本号匹配的文件,如138.0.7204.~。如果修订版本号也没有匹配的,就找与主版本号和次版本号匹配的,依此类推

要下载带driver关键字的文件,另外两个并不是咱们所需要的驱动程序。下载好后解压进入到文件夹里,可以看到一个叫“chromedriver”的可执行文件,可以把驱动文件放到某个固定的路径下。

通过python代码来启动Chrome浏览器。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
service = Service("D:/SeleniumDriver/chromedriver.exe")
driver = webdriver.Chrome(service=service)

如何程序报错,找不到 Chrome(而实际已安装了Chrome),则手动指定 Chrome 位置

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options  # 新增:导入配置项

# 配置 Chrome 选项,指定 Chrome 安装路径
chrome_options = Options()
# TODO:替换成你电脑上 chrome.exe 的实际路径(示例是默认路径,根据自己的改)
chrome_options.binary_location = r"C:\Program Files\Qoom Chrome\chrome.exe"

# 驱动路径(Windows用/或r字符串)
service = Service(r"D:\SeleniumDriver\chromedriver.exe")
driver = webdriver.Chrome(service=service, options=chrome_options)

三、Selenium核心:浏览器基础操作

四个必备模块与类:

使用Selenium编写爬虫时,常用的四个工具是:

  • webdriver:封装浏览器的操作,例如启动、访问URL、打开新标签页。

  • By:用于定位网页中的元素(如By.IDBy.CLASS_NAME)。

  • Keys:模拟键盘操作(如回车键、删除键)。

  • ActionChains:模拟鼠标动作链(如点击、拖拽)。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

启动浏览器常用操作

1.访问网页

  • 使用get(url)方法访问网页。

  • 使用title属性获取网页标题,使用current_url获取网页地址。

  • 使用time.sleep()保持浏览器窗口一段时间。

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.chrome.options import Options  # 新增:导入配置项
    from time
    
    # 配置 Chrome 选项,指定 Chrome 安装路径
    chrome_options = Options()
    # TODO:替换成你电脑上 chrome.exe 的实际路径(示例是默认路径,根据自己的改)
    chrome_options.binary_location = r"C:\Program Files\Qoom Chrome\chrome.exe"
    
    # 驱动路径(Windows用/或r字符串)
    service = Service(r"D:\SeleniumDriver\chromedriver.exe")
    driver = webdriver.Chrome(service=service, options=chrome_options)
    
    driver.get("https://www.douban.com")
    print(driver.title, driver.current_url)
    time.sleep(3)

2.最大化浏览器窗口

使用maximize_window()方法让浏览器窗口全屏,以避免后续操作因分辨率不同产生误差。

3.打开新标签页

使用execute_script("window.open('');")在浏览器中打开新标签页,execute_script()括号中为可执行的.js代码

使用window_handles属性查看所有标签页ID。

4.切换标签页

使用switch_to.window(handle)切换到指定标签页。补充:switch_to获得SwitchTo对象(上下文切换控制器),常用与处理标签页、iframe等上下文的切换。

可以在不同标签页中加载不同页面。

5.关闭标签页

使用close()方法关闭当前活跃的标签页。

若需关闭特定标签页,先切换至该标签页再关闭。

driver.maximize_window()   # 最大化浏览器窗口
driver.get("https://www.douban.com")  # 打开豆瓣首页

driver.execute_script("window.open('');")  # 新建空白标签页
window_handle_list = driver.window_handles  # 获取所有标签页句柄

time.sleep(3)  # 等待3秒
driver.switch_to.window(window_handle_list[0])  # 切换到第一个标签页
print("切换到第一个标签页")

time.sleep(3)  # 等待3秒
driver.switch_to.window(window_handle_list[1])  # 切换到第二个标签页
print("切换到第二个标签页")

driver.get("https://www.baidu.com")  # 第二个标签页打开百度
print(f"当前页面标题: {driver.title}")  # 打印页面标题
print(f"当前页面URl: {driver.current_url}")  # 打印当前页面URL

time.sleep(3)  # 等待3秒
driver.switch_to.window(window_handle_list[1])  # 切换到第二个标签页
driver.close()  # 关闭当前标签页
print("第一个标签页已关闭")

time.sleep(5)  # 等待5秒后程序继续

四、用Selenium获取静态网页数据(By类)

查找元素

使用 find_element 查找单个元素,找不到会报错。建议使用try...except 避免因找不到元素而报错。

# 找 1 个元素
item = driver.find_element(By.CLASS_NAME, "title")

# 推荐加 try except
try:
    item = driver.find_element(By.ID, "no_id")
except:
    print("元素没找到")

使用 find_elements 批量查找元素,找不到会返回空列表。

# 找多个元素
item_list = driver.find_elements(By.TAG_NAME, "div")
item = item_list[0].text if len(item_list) >= 1 else None

获取元素文本

使用 element.text 获取文本内容。

element = driver.find_element(By.ID, "name")
text = element.text   # 获取元素里的文字
print(text)

获取元素的属性值

使用 get_attribute("属性名") 获取指定属性的值。

link = driver.find_element(By.TAG_NAME, "a")
href = link.get_attribute("href")   # 获取链接地址
src = link.get_attribute("id")      # 获取id

获取元素HTML

get_attribute("outerHTML") 获取完整HTML,包括自身标签,子元素、文本、注释等。

get_attribute("innerHTML") 获取内部HTML,不含自身标签,仅含子元素、文本、注释等。

element = driver.find_element(By.ID, "box")

# 包含自己标签的完整 HTML
outer = element.get_attribute("outerHTML")

# 只包含内部内容,不带自己标签
inner = element.get_attribute("innerHTML")

查找属于多个类的元素

By.CLASS_NAME不支持多个类名。

可使用 By.CSS_SELECTOR 并传入组合选择器 .类1.类2。格式:.find_element(By.CSS_SELECTOR,"表达式")

# 不支持:By.CLASS_NAME("a b")
# 正确写法:CSS选择器 .类1.类2
element = driver.find_element(By.CSS_SELECTOR, ".item.active.red")

使用XPath查找元素

By.XPATH 可快速定位深层嵌套的元素。格式:.find_element(By.XPATH,"表达式")

# 找 div 下 class 为 title 的 span
element = driver.find_element(
    By.XPATH, 
    "//div[@class='box']/span[@class='title']"
)

模糊查找元素

使用 By.CSS_SELECTOR(CSS 选择器)支持模糊匹配。格式:.find_element(By.CSS_SELECTOR,"表达式")

语法规则:

包含子字符串: [属性名*="值"]

以某字符串开头: [属性名^="值"]

以某字符串结尾: [属性名$="值"]

# 包含 "btn" 字符串
driver.find_element(By.CSS_SELECTOR, "[class*='btn']")

# 以 "head" 开头
driver.find_element(By.CSS_SELECTOR, "[id^='head']")

# 以 "img" 结尾
driver.find_element(By.CSS_SELECTOR, "[src$='.img']")

五、用Selenium获取动态网页数据

动态网页的挑战

  • 静态网页:HTML源码里直接包含数据。

  • 动态网页:HTML源码中没有目标数据,需要等待页面加载完成才能获取。

方法1:固定时间等待

最简单的方式是调用 time.sleep(N) 让程序暂停几秒,等待页面完成加载。

方法2:隐式等待

使用 driver.implicitly_wait(N) 设置全局等待时间。

在查找元素时,如果元素存在-->找出元素-->执行下一步,如果元素不存在-->在指定时间内重复查找-->(直到出现或超时)

方法3:显式等待

显式等待更灵活,可以为特定操作单独设置等待条件。

需要用到 WebDriverWait expected_conditions函数(常简写为 EC)。

常用条件:

presence_of_element_located

元素出现在DOM中(不一定可见)

等待一个匹配的元素达成条件

visibility_of_element_located

元素存在并可见(高度/宽度大于0)

presence_of_all_element_located

~

等待所有匹配的元素达成条件

visibility_of_all_element_located

~

element_to_be_clickable

元素可点击(存在、可见且可用)

title_contains

标题包含指定文本

url_changes

URL发生变化

test_to_be_present_in_element

元素包含指定文本

隐式等待与显示等待具体区别

对比维度

隐式等待(implicitly_wait)

显式等待(WebDriverWait)

等待范围

全局设置,影响所有元素定位

针对特定元素或条件设置

等待条件

仅等待元素存在于 DOM 中

支持多种条件(可见、可点击、文本出现等)

灵活性

固定时间,无法动态调整

可根据元素特性定制等待时间和条件

推荐场景

简单页面,元素加载时间较一致

复杂交互,元素加载时间差异大

三种方法对比

  • 固定时间等待:简单直观,但效率低。

  • 隐式等待:自动应用于所有元素查找,适合简单场景。

  • 显式等待:灵活可控,适合复杂场景和动态网页。

案例演示

前程无忧这个网站演示。

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options  # 新增:导入配置项

# 配置 Chrome 选项,指定 Chrome 安装路径
chrome_options = Options()
# TODO:替换成你电脑上 chrome.exe 的实际路径(示例是默认路径,根据自己的改)
chrome_options.binary_location = r"C:\Program Files\Qoom Chrome\chrome.exe"

# TODO:替换成你电脑上驱动文件的路径 
service = Service(r"D:\SeleniumDriver\chromedriver.exe")
driver = webdriver.Chrome(service=service, options=chrome_options)
driver.maximize_window()   #最大化浏览器窗口
driver.get("https://we.51job.com/pc/search")

start = time.time()

# 方法1:暂停运行
# time.sleep(5)

# 方式2:隐式等待
# driver.implicitly_wait(10)
# job_element_list = driver.find_elements(By.CSS_SELECTOR, ".jname.text-cut")

# 方式3:显式等待
wait = WebDriverWait(driver, timeout=10, poll_frequency=0.5)
job_element_list = wait.until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".jname.text-cut"))
)

duration = time.time()-start
print(f"程序等待了 {duration:.2f} 秒")

print(f"共找到 {len(job_element_list)} 个岗位")
for job_element in job_element_list:
    print(job_element.text)

六、用Selenium自动控制键盘

为什么要学习键盘输入

在爬虫与自动化场景中,多数网站需要模拟真实用户操作才能正常获取数据,而登录账号、填写验证码、搜索关键词、提交表单等核心操作,都依赖键盘输入完成。

基础文本输入

  1. 操作步骤:先定位输入框元素,再调用 send_keys() 方法输入内容

  2. 特殊情况:若输入框位于 frame 框架内,需先切换框架再定位操作

# 定位并切换 iframe
iframe_elem = driver.find_element(By.XPATH, "//iframe[@id='login-frame']")
driver.switch_to.frame(iframe_elem)

# 定位输入框并输入内容
username_input = driver.find_element(By.NAME, "username")
password_input = driver.find_element(By.ID, "password")

username_input.send_keys("test_user123")
password_input.send_keys("mypassword456")

模拟键盘按键

需先导入 Keys 类,通过 send_keys() 搭配按键常量实现模拟按键。

常用按键:

  • Keys.ENTER:回车键

  • Keys.BACKSPACE:退格键

  • Keys.CONTROL:Windows系统Ctrl键

  • Keys.COMMAND:Mac系统Command键

from selenium.webdriver.common.keys import Keys
# 模拟按下回车
code_element.send_keys(Keys.ENTER)

键盘快捷键操作

  • 全选:send_keys(Keys.CONTROL + "a")

  • 复制:send_keys(Keys.CONTROL + "c")

  • 粘贴:send_keys(Keys.CONTROL + "v")

注意:Mac系统需用 Keys.COMMAND 替换 Keys.CONTROL

输入内容模式

  1. 追加输入(默认)

send_keys() 不会清空原有内容,直接在文本末尾追加新内容。

# 默认追加输入
search_input = driver.find_element(By.NAME, "search")
search_input.send_keys("python")
search_input.send_keys(" 教程")  # 结果:python 教程
  1. 覆盖输入

方法一:全选 + 删除 + 输入新内容

search_input.send_keys(Keys.CONTROL + "a")
search_input.send_keys(Keys.BACKSPACE)
search_input.send_keys("java 教程")

方法二(推荐):使用clear ()清空输入框,再输入新内容(由于电脑操作系统的不同,更推荐用这种方法)

code_element.clear()
code_element.send_keys("789")

七、用Selenium自动控制鼠标

什么要学习鼠标操作

网页中点击、双击、右键、悬停、拖拽等复杂交互,需要通过模拟鼠标操作实现,常用于点击按钮、拖动滑块验证码、拖拽上传文件等场景。

页面滚动操作

通过.execute_script( )执行JavaScript代码实现页面滚动,无需定位鼠标即可控制滚动效果。

  • 按像素滚动:driver.execute_script("window.scrollBy(横向滚动的像素数, 纵向滚动的像素数);") (正数表示向右或向下滚动,负数表示向左或向上滚动)

  • 滚动到某元素可见:driver.execute_script("arguments[0].scrollIntoView();", 元素) (arguments[0]表示传入的第一个参数,scrollIntoView()表示让传入的元素,滚动到页面的可视区域中)

# 向下滚动300像素
driver.execute_script("window.scrollBy(0, 300);")

# 滚动至指定元素可见位置
anony_book_element = driver.find_element(By.ID, "anony-book")
driver.execute_script("arguments[0].scrollIntoView();", anony_book_element)

基础鼠标点击

直接调用元素的 click() 方法,实现鼠标单击操作。

tab_account_element = driver.find_element(By.CLASS_NAME, "account-tab-account")
tab_account_element.click()

高级鼠标动作(ActionChains)

需导入 ActionChains 类,遵循「实例化对象→添加动作→执行perform()」的流程完成操作。

from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)  #创建实例化对象
~
  1. 鼠标双击 .double_click()

actions.double_click(username_element).perform()  #添加动作→执行perform()
  1. 鼠标三击 .double_click().click()

actions.double_click(username_element).click(username_element).perform()  #添加动作→执行perform()
  1. 元素拖拽 .drag_and_drop()

# 将源元素拖拽至目标元素
actions.drag_and_drop(link_fwd_element, username_element).perform()  #添加动作→执行perform()
  1. 滑动验证码模拟: 按住.click_and_hold()移动.move_by_offset(, )松开.release()

# 按住滑块→向右移动50像素→松开鼠标
actions.click_and_hold(puzzle_piece_element).move_by_offset(50, 0).release().perform()  #添加动作→执行perform()

获取元素基础信息

  1. 获取元素坐标 .location

location = puzzle_piece_element.location
print(location["x"], location["y"])    #location["x"]为元素左上角的坐标,location["y"]为元素右上角的坐标
  1. 获取元素尺寸 .size

size = puzzle_piece_element.size
print(size["width"], size["height"])
  1. 获取元素CSS样式 .value_of_css_property()

background_image = background_element.value_of_css_property("background-image")

八、总结与选型建议

  1. 简单静态页面:用requests+解析库,高效低成本

  2. 动态/反爬严格页面:用Selenium自动化,稳定可靠

  3. 大规模爬取:可结合无头模式+代理池,平衡效率与风控

Selenium的核心是模拟真人,用好等待、元素定位与交互操作,绝大多数网页数据都能轻松获取。