第4章 动态数据采集:Selenium入门、浏览器自动化、处理JavaScript渲染页面
4.1 为什么需要Selenium?
说实话,做数据采集最头疼的不是数据量大,而是页面不配合。你想想看,很多网站现在都是JavaScript渲染的——数据不是写在HTML里,而是通过JS动态加载出来的。你用requests去抓,拿到的只是一堆空壳子。
我刚开始做采集时,就踩过这个坑。一个电商网站的商品列表,用requests死活抓不到数据,折腾了两天才发现是JS渲染的。后来改用Selenium,十分钟搞定。嗯,工具选对了,事半功倍。
Selenium说白了就是一个浏览器自动化工具。它能模拟真实用户的操作——打开网页、点击按钮、滚动页面、填写表单。最关键的是,它能等JavaScript执行完再拿数据。
核心概念:Selenium通过WebDriver驱动真实的浏览器(Chrome、Firefox等),让浏览器像真人一样操作页面。这样JS渲染的内容自然就能拿到了。
4.2 环境搭建
安装其实很简单,就两步。我个人习惯用Chrome,因为调试工具顺手。
# 第一步:安装Selenium库
pip install selenium
# 第二步:下载ChromeDriver
# 注意版本要和你的Chrome浏览器一致
# 下载地址:https://chromedriver.chromium.org/
避坑指南:我曾经因为ChromeDriver版本不对,折腾了一下午。Chrome浏览器会自动更新,但Driver不会。建议用代码自动匹配版本,或者定期检查。
写个简单的启动代码,验证环境是否正常:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 配置浏览器选项
options = Options()
options.add_argument('--headless') # 无头模式,不显示浏览器窗口
# 启动浏览器
driver = webdriver.Chrome(options=options)
driver.get('https://www.baidu.com')
print(driver.title) # 打印页面标题
driver.quit() # 关闭浏览器
跑通了?好,那我们继续。
4.3 核心操作:定位元素
Selenium最常用的就是定位页面元素。说白了,就是告诉浏览器「我要找那个按钮」、「我要找那个输入框」。
我常用的定位方式有8种,但实际工作中80%的情况只用两种:
| 定位方式 | 写法示例 | 适用场景 |
|---|---|---|
| ID定位 | find_element(By.ID, 'username') | 元素有唯一ID时,最快最稳 |
| CSS选择器 | find_element(By.CSS_SELECTOR, '.class-name') | 没有ID时,用class或属性定位 |
| XPath | find_element(By.XPATH, '//div[@class="item"]') | 结构复杂时,XPath最灵活 |
| 文本定位 | find_element(By.LINK_TEXT, '下一页') | 定位链接或按钮文本 |
我的经验:优先用ID,没有ID用CSS选择器。XPath虽然强大,但写起来啰嗦,而且页面结构一变就容易失效。我在项目中吃过这个亏,后来尽量少用XPath。
4.4 等待机制:别急着拿数据
这是新手最容易犯的错误。你让浏览器打开一个页面,然后立刻去拿数据——但JS还没执行完呢!
为什么会这样?因为浏览器加载页面是异步的。HTML先到,JS后执行。如果你不等JS渲染完,拿到的就是空数据。
Selenium提供了两种等待方式:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 方式一:显式等待(推荐)
# 等到某个元素出现再继续
wait = WebDriverWait(driver, 10) # 最多等10秒
element = wait.until(
EC.presence_of_element_located((By.ID, 'data-list'))
)
# 方式二:隐式等待(全局设置)
# 每次查找元素时,如果没找到就等一会儿
driver.implicitly_wait(5) # 全局等5秒
核心建议:我个人习惯用显式等待。隐式等待虽然方便,但有时候会掩盖真正的问题。比如页面加载失败,隐式等待会傻等5秒,而显式等待能立刻报错。
4.5 实战:采集动态加载的新闻列表
来,我们写个完整的例子。假设有个新闻网站,列表是JS动态渲染的:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 启动浏览器
driver = webdriver.Chrome()
driver.get('https://example-news.com/list')
# 等待列表加载完成
wait = WebDriverWait(driver, 10)
news_list = wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'news-items'))
)
# 获取所有新闻条目
items = driver.find_elements(By.CSS_SELECTOR, '.news-item')
data = []
for item in items:
title = item.find_element(By.CSS_SELECTOR, '.title').text
link = item.find_element(By.CSS_SELECTOR, 'a').get_attribute('href')
date = item.find_element(By.CSS_SELECTOR, '.date').text
data.append({
'title': title,
'link': link,
'date': date
})
# 翻页采集
while True:
try:
next_btn = driver.find_element(By.LINK_TEXT, '下一页')
next_btn.click()
time.sleep(2) # 等页面加载
# 继续采集...
items = driver.find_elements(By.CSS_SELECTOR, '.news-item')
for item in items:
# 同上,采集数据
pass
except:
print("没有下一页了")
break
driver.quit()
print(f"共采集 {len(data)} 条数据")
避坑指南:我曾经在翻页时没加time.sleep,结果点击太快,页面还没加载完就报错了。虽然显式等待能解决大部分问题,但翻页这种操作,加个1-2秒的固定等待更稳妥。
4.6 处理常见问题
做自动化采集,总会遇到各种幺蛾子。我整理了几个高频问题:
- 元素被遮挡:有时候元素在页面上,但被其他元素挡住了。用
ActionChains模拟鼠标移动过去再点击。 - 弹窗干扰:突然弹出广告或验证码。用
driver.switch_to.alert处理弹窗,或者用try-except跳过。 - 页面滚动加载:有些页面是滚动到底部才加载新内容。用
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')模拟滚动。 - 反爬检测:网站检测到是自动化工具,直接封IP。可以加
options.add_argument('--disable-blink-features=AutomationControlled')隐藏特征。
重要提醒:采集数据要遵守网站的robots.txt协议,不要频繁请求。我一般会在每次请求之间加2-5秒的延迟,既保护自己,也不给服务器造成压力。
4.7 本章知识体系
下面这张图,是我自己总结的Selenium采集流程。你照着这个思路走,基本不会出错:
4.8 小结
Selenium入门其实不难,核心就三点:
- 等渲染:别急着拿数据,等JS跑完再说
- 准定位:选对定位方式,优先ID和CSS
- 稳操作:加延迟、处理异常、隐藏自动化特征
我在实际项目中,用这套方法采集过几十个动态网站的数据。只要不是遇到特别变态的反爬,基本都能搞定。记住,做采集不是比谁快,而是比谁稳。慢一点,反而能跑得更远。