先说一个我踩过的坑。有天下午,朋友发来一个网站,说之前写好的爬虫崩了,让我帮忙看看。我打开他的代码,requests写得挺规整,headers、cookie、代理都带了,但拿回来的HTML源码里,硬是没有他说的那个价格字段。我打开浏览器按F12看了一眼,瞬间就明白了——这是个JavaScript渲染的页面,数据根本不是服务端直接吐出来的,而是浏览器加载完页面后,由脚本再发起请求、把数据插进DOM里的。
这个场景,做爬虫的人应该都不陌生。现在前端框架越来越重,页面结构越来越“壳化”,很多网页打开就是一个空荡荡的div壳子,里面所有可见内容全靠JavaScript异步填充。你拿requests去扒源码,扒回来的只是一副没有血肉的骨架。这时候,Selenium就是绕不开的一件工具。这篇文章就把我过去处理这类页面时踩过的坑、用过的方案、沉淀下来的思路,一次性整理出来。内容偏实战,适合那种“requests已经写得挺熟、但突然面对动态页面不知道从哪下手”的朋友。
1. 先搞清楚:目标页面到底是不是JavaScript渲染的
1.1 从一次“假静态页”说起
很多人在遇到“爬不到数据”时,第一反应是加headers、加cookie、换代理,折腾一圈发现没用,最后才怀疑到动态渲染头上。其实这个问题前置一点,能省下大量无效劳动。
我那位朋友的失误在于:他拿到需求后,直接在浏览器里右键“查看网页源代码”,看到里面有部分数据,就以为整个页面是静态的。但他忽略了一点——浏览器里看到的完整页面,和服务器返回的原始HTML,根本不是一回事。浏览器会执行HTML中的script标签,拉取更多数据再渲染成最终页面,这个过程叫“客户端渲染”。而“查看网页源代码”看到的是服务器返回的原样文本,其中很多字段只是JS模板里的占位符,或者干脆只有一段加密的JSON配置文件。
所以第一件事,就是确认页面到底是怎么渲染的。别急着写Selenium代码,先花五分钟做个判断。
1.2 快速判断的四种方法
我常用的判断方式有四种,从最快到最稳排列:
-
看服务器返回的HTML里有没有目标字段。直接用requests请求目标URL,把返回的response.text存成文件,Ctrl+F搜索你要的数据字段。如果搜得到,说明是服务端渲染,直接解析就行。如果搜不到,说明数据是后续动态加载的。
-
对比“查看网页源代码”和开发者工具中的Elements。打开浏览器按F12,在Elements面板看到的内容是“渲染后的DOM”,它可能已经被JavaScript修改过。而右键查看网页源代码,是“原始HTML”。两份内容一对比,如果差别很大,基本上可以断定有脚本在起作用。
-
抓包看XHR/ Fetch请求。打开开发者工具切到Network面板,勾选Fetch/XHR,刷新页面,观察网络请求列表。如果看到几个返回JSON的接口,那思路就清晰了:Selenium顶多是帮你触发这些接口,真正要解析的数据在JSON里。这一步还能顺便拿到接口地址和参数,我后面会说“能直接调接口就别上Selenium”的逻辑,就来自这里。
-
禁用JavaScript后再打开页面。Chrome浏览器设置里可以临时把JavaScript关掉,再刷新目标页面。如果页面变成一片空白或只剩框架,说明内容几乎全依赖JS渲染。这个办法最直观,一看便知。
1.3 能直接调接口就别上Selenium
这里要插一段非常重要的话,也是我给所有新手的忠告:Selenium不是万能钥匙,它只是最后一招。
原因很简单——Selenium要启动一个完整的浏览器,加载页面、执行脚本、渲染样式,开销远大于一次普通的HTTP请求。别人300毫秒能跑完的请求,它可能要用3到5秒,性能差一个数量级。而且真实浏览器还会暴露更多指纹特征,更容易触发反爬。
所以,如果Network面板里发现了明确的JSON接口,优先试试直接用requests复现这个接口。找到需要的参数和鉴权方式,你就能用最轻量的方式拿数据。Selenium适合的是“接口参数做了签名加密、直接调用完全推导不出来”的场景,或者“数据在渲染过程中经过复杂计算才写入DOM”的场景。在这些情况里,与其死磕JS逆向,不如让浏览器替你把活干完。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Selenium是只“重剑”:环境搭建中几个讨厌的坑
2.1 ChromeDriver版本匹配:90%的新手报错源头
决定用Selenium之后,第一个坑就是环境。Selenium本身是一个自动化协议库,它不直接操作浏览器,而是通过一个驱动中间层来发指令。Chrome就对应ChromeDriver,Firefox对应GeckoDriver。驱动版本和浏览器版本必须匹配,否则直接报SessionNotCreatedException。
这是我见过最频繁的入门报错。解决办法其实很简单:
- 打开Chrome浏览器,点右上角三个点,进“帮助 -> 关于Google Chrome”,记下完整版本号,比如“120.0.6099.130”。
- 去ChromeDriver的官方下载站,找Major Version(也就是版本号第一个数字)相同的版本。比如浏览器是120.x,就下载120开头的驱动。
- 下载后把驱动文件放到一个固定目录,或者直接放进Python的Scripts目录,确保系统PATH里能找到。
一个很容易忽略的细节是:Chrome会自动更新,今天匹配好的版本,过两周浏览器一变,驱动就失效。所以很多时候“昨天还好好的,今天突然崩了”,一查就是Chrome偷偷升级了。建议在代码里先打印一下driver的版本,或者用webdriver-manager这个库实现驱动版本自动识别和管理。这个库会自动匹配本地浏览器版本去下载驱动,能省掉很多重复操作的麻烦。
2.2 headless模式不是加个参数就行
环境跑通之后,大多数人会立刻打开headless模式,也就是“无头浏览”。这个模式的好处是不弹出浏览器窗口,节省资源,也适合部署在服务器上。但很多人的第一版headless代码,跑起来要么被网站识别,要么页面渲染不完整。
先说基本配置:
python复制from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless=new')
options.add_argument('--disable-gpu')
options.add_argum
