恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Selenium爬虫实战:从动态网页到招聘网站数据抓取
首页
资讯中心
/
Selenium爬虫实战:从动态网页到招聘网站数据抓取
Selenium爬虫实战:从动态网页到招聘网站数据抓取
发布时间:2026/10/21 8:58:32
做爬虫这么多年我一直觉得 Requests 加 BeautifulSoup 是性价比最高的组合直到需要批量抓取招聘网站。打开页面一看HTML 里空荡荡的岗位信息关键数据全靠 JavaScript 动态渲染Requests 拿到的源码跟浏览器看到的根本不是一回事。这时候 Selenium 就成了那个能救场的东西——它直接驱动真实浏览器页面渲染多少它就爬多少不需要猜接口也不需要逆向分析对新手来说是最容易理解的一条路。这篇文章是系列的第一部分我会从零开始讲 Selenium 的安装、浏览器驱动的配对、第一个能跑的脚本再到一个完整可扩展的招聘网站基础爬虫覆盖环境搭建、元素定位、翻页循环、数据落盘和常见坑的排查思路。如果你想学爬虫又不想一上来就啃反爬和加密参数那用 Selenium 打底是最好的切入点。1. 为什么招聘类网站是 Requests 的滑铁卢1.1 动态渲染的页面结构招聘网站这种平台岗位列表、薪资范围、公司规模这些数据几乎都是前端通过 JavaScript 异步加载的。你用 Requests 去请求首页拿回来的只是页面的空壳子。真正的岗位数据是页面加载后又调用了好几个内部接口把 JSON 数据填进 DOM 里的。这个流程在浏览器里是自动完成的但在 Requests 的世界里你得先找到那些内部接口的 URL、分析它们的参数、处理 token 或者签名工作量大得惊人。我曾经花了整整一下午去抓某个招聘平台的接口发现它的参数里有时间戳和固定算法。好不容易模拟出来跑了一百多条请求突然 IP 就被限制了。后面换思路直接用 Selenium半小时就把同样的数据抓下来了还没触发任何风控。倒不是说接口方案不行而是对新手来说接口逆向的复杂度太高不值得为了一个学习项目这么折腾。1.2 Selenium 是什么它能做什么Selenium 是一个浏览器自动化框架最开始是给测试人员做自动化测试用的。它通过 WebDriver 协议跟浏览器通信可以模拟真实用户的一切操作打开网址、输入文字、点击按钮、滚动页面、切换标签页甚至处理弹窗和下载文件。因为它是真实浏览器在跑所以网站服务器看到的就是一个正常的用户在访问反爬难度比纯请求大很多。拿这个特性来做爬虫意味着你不需要关心数据是怎么加载的也不需要分析接口和加密参数。你只需要像看浏览器一样看页面找到目标元素然后告诉 Selenium 去点击、去提取数据自然就拿到了。这就是为什么招聘网站这种重度前端渲染的站点更适合用 Selenium 兜底。1.3 什么时候该用什么时候该放弃Selenium 不是万能钥匙它最大的缺点就是慢。每启动一个浏览器进程开销都不小跑一千条数据可能要好几分钟。如果你的目标站点的数据是服务端渲染的直接用 Requests 就行如果能找到公开的 API 接口也优先用请求方式因为效率和稳定性都更好。我的选择标准很简单列表页能用 Requests 拿到完整数据就不碰 Selenium如果页面必须等几秒才能出现内容或者需要点击、登录、滚动才能加载才考虑 Selenium。换句话说Selenium 是兜底方案是前面所有轻量方案都走不通之后的上策。本系列的实战题目主要讲这类动态页面场景所以会全程围绕 Selenium 展开。2. Selenium 安装与环境配对卡住大部分新人的第一道坎2.1 Python 环境与 pip 的预备工作在安装 Selenium 之前先确认你的电脑上有 Python 环境。Windows 上打开终端输入python --versionmacOS 或 Linux 输入python3 --version能看到版本号就行。我建议用 3.8 以上的版本太老的版本有些新特性不支持。其次确认 pip 可用终端执行pip --version或pip3 --version如果提示找不到命令检查一下 Python 安装时是否有添加到系统环境变量。这里说一个常见坑很多人同时装了多个 Python 版本结果python指向 2.7 而pip指向 3.x安装的库不能通用。最简单的处理方式是统一用python -m pip install方式而不是直接敲pip这样能确定包装在当前的 Python 解释器里。2.2 安装 Selenium 库Selenium 从 4.0 版本开始安装和使用方式简化了很多。终端执行python -m pip install selenium如果想指定版本比如用 4.10 版本可以写python -m pip install selenium4.10.0。安装完成后验证一下python -c import selenium; print(selenium.__version__)能打印出版本号说明安装成功。这里额外提醒一句Selenium 库本身只是客户端它需要连接浏览器驱动才能控制浏览器。所以更准确地说安装分成两步装这个 Python 库再装浏览器驱动。2.3 浏览器驱动版本匹配是最大的坑我用的是 Chromium 内核的浏览器对应需要一个叫 chromedriver 的驱动文件。这个东西的工作机制是Selenium 启动驱动驱动再去启动浏览器三者版本必须匹配否则会直接报session not created或This version of ChromeDriver only supports Chrome version XX。最准确的方法是先看清楚浏览器版本号。在浏览器地址栏输入chrome://version记下主版本号比如 120。然后去下载对应版本的驱动文件。这里不建议用外部工具直接用符合版本号的驱动文件更可靠。完整流程分四步查浏览器版本号记下大版本下载对应版本的 chromedriver 压缩包解压后把可执行文件放到一个固定目录比如C:\webdrivers把这个目录加入系统 PATH 环境变量。其实 Selenium 4 开始支持自动管理驱动Selenium Manager当你安装浏览器后它可以自动下载匹配的驱动省去很多麻烦。但对于生产级脚本我还是建议手动控制驱动版本避免某天自动下载的驱动和浏览器版本不一致导致全盘崩掉。2.4 用一个小脚本验证环境是否正常环境配好之后跑一个最基础的脚本能打开浏览器并访问一个空白页面就说明全流程没问题。代码很简单from selenium import webdriver driver webdriver.Chrome() driver.get(https://example.com) print(driver.title) driver.quit()如果打开了浏览器并输出Example Domain恭喜你的环境已经通了。如果这一步报错先把驱动版本和浏览器版本对齐再检查驱动路径和权限。3. 第一个可跑的脚本让浏览器自动打开、搜索并返回结果3.1 配置浏览器选项关闭自动控制提示环境跑通之后别忘了做一个重要配置。Selenium 控制的浏览器左上角会显示正在受到自动软件的控制这样的提示条。某些招聘网站会通过 JavaScript 检测这个特征进而拦截。虽然不至于立刻封 IP但会影响行为模拟效果。用options可以去掉部分提示from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) driver webdriver.Chrome(optionsoptions)这段配置只推荐用于合法合规的爬虫学习。记住尊重目标网站的声明和 robots 协议是所有爬虫项目的前提。方法本身没有对错但使用意图必须正当。3.2 打开招聘网站首页等待关键元素出现我以某招聘网站的首页搜索为例。这类网站的结构通常是顶部有一个搜索框、一个搜索按钮下面热门搜索关键词。我们想让浏览器自动打开首页在搜索框里输入Python 开发然后点击搜索。第一次打开页面的时候网络慢的话会出现加载不完整。直接定位元素很容易找不到。解决办法是显式等待等待某个元素出现最多等十秒。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver.get(https://example.com/job-search) wait WebDriverWait(driver, 10) search_input wait.until(EC.presence_of_element_located((By.ID, search-keyword)))为什么用 ID因为招聘网站的搜索框一般都有一个唯一的 id像search-keyword或keyword-input这是整个页面里最稳定的定位方式。相比之下class 可能同时用于多个元素XPath 也可能因为页面结构调整而失效。3.3 输入关键词并执行搜索拿到输入框元素之后用send_keys()方法输入内容用click()方法点击按钮。代码很简单search_input.send_keys(Python) search_button driver.find_element(By.CLASS_NAME, search-btn) search_button.click()有个细节值得注意输入中文的时候如果页面有输入法干扰建议在send_keys之前先点击输入框再清空已有的内容。用clear()方法search_input.clear() search_input.send_keys(Python开发)搜索之后浏览器会跳转到列表页。这时可以用WebDriverWait等待列表容器加载出来再开始真正的数据提取。3.4 打印当前页面标题和 URL确认跳转成功有时候点击了搜索按钮但页面可能是因为新标签页打开也可能是原页面跳转你需要确认当前到底在哪。打印一下基本信息print(driver.current_url) print(driver.title)如果点击响应的元素不同甚至会打开新标签页。这种情况下你需要切换窗口句柄driver.switch_to.window(driver.window_handles[-1])这一节内容足以应付招聘网站搜索的常见布局。实际项目里第一步就是跑通搜索路径后续的数据提取和翻页都是在这个基础上叠加的。4. 列表页实战从岗位卡片到 CSV 文件4.1 先看懂页面结构再想怎么写代码很多人一上来就写find_elements代码跑完发现提取的是空列表原因就是没有先分析页面结构。正确做法是打开目标列表页按 F12 打开开发者工具找到一条岗位记录对应的 HTML 区域。招聘网站的列表页每条岗位通常用一个div或li包起来里面有岗位名称、公司名称、薪资、地区、经验要求等。这些内容在页面刚加载时可能还没渲染完所以第一步依然是等待整个列表容器出现wait.until(EC.presence_of_element_located((By.CLASS_NAME, job-list)))之后再用find_elements获取所有岗位卡片job_cards driver.find_elements(By.CLASS_NAME, job-item)注意这里用的是find_elements返回的是元素列表不是单个元素。4.2 用相对定位提取岗位名称、公司、薪资拿到每个卡片元素后在卡片内部继续定位它的子元素。这里我不建议从全局用 XPath 定位因为很容易匹配到页面上其他同样结构的元素。最好在card这个局部元素下面继续查找job_title card.find_element(By.CSS_SELECTOR, .job-name).text company card.find_element(By.CSS_SELECTOR, .company-name).text salary card.find_element(By.CSS_SELECTOR, .salary).text使用 CSS 选择器比 XPath 更简洁.job-name就是选择 class 为job-name的元素。还有一个小细节text属性获取的是元素可见文本如果元素隐藏了返回空字符串。遇到提取不到内容时优先检查选择器是否命中其次检查元素是否可见。4.3 翻页循环的两种实现思路招聘网站的列表页通常底部有页码按钮也有下一页按钮。翻页逻辑有两种思路第一种直接点击下一页按钮适用于按钮一直存在且始终可点击的页面next_btn driver.find_element(By.CSS_SELECTOR, .next-page) if next_btn.is_enabled(): next_btn.click()第二种循环点击具体页码数字直到页码超出范围for page in range(2, 6): page_btn driver.find_element(By.XPATH, f//a[text(){page}]) page_btn.click() wait.until(EC.staleness_of(page_btn))这里的staleness_of是等待上一个元素失效等价于页面已经跳转比固定的time.sleep()更稳定。我个人更推荐这种方式因为支持直接跳到任意页码也更容易控制最大页数防止爬虫失控。4.4 存储到 CSV编码与去重数据提取到内存之后写 CSV 是常规操作。这里有几个必须注意的坑文件打开时要指定utf-8-sig编码不然用 Excel 打开中文会乱码表头要提前定义好方便后续读取重复岗位需要按 URL 或岗位名加公司名做去重。简单实现一个去重集合import csv seen set() with open(jobs.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([岗位名称, 公司, 薪资]) for i, card in enumerate(job_cards[:20]): title card.find_element(...).text key (title, company) if key not in seen: seen.add(key) writer.writerow([title, company, salary])这种写法只处理当前页。要爬多页把翻页逻辑包在外层循环里每翻一页重新获取一次job_cards就行了。5. 实战中的四大拦路虎与排查思路5.1 元素定位不稳定等待策略的选择新手遇到最多的报错是NoSuchElementException。表面意思是找不到元素实际原因一般是页面还没加载完。我见过很多人在time.sleep(3)之后还是报错于是把时间加到 5 秒、10 秒这是笨办法。网络慢的时候等再久也没用网络快的时候白白浪费时间。正确做法是用WebDriverWait配合条件比如WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, .search-btn)) )可以用的条件很多presence_of_element_located表示元素出现在 DOM 中visibility_of_element_located表示元素可见element_to_be_clickable表示可点。具体用哪个取决于后面的操作是读取文本还是点击按钮。5.2 浏览器被识别常见检测与应对原则有些招聘网站会检测当前窗口是否是自动化环境常见手段包括查看navigator.webdriver属性或者检测窗口尺寸是否有异常。你可以尝试在启动浏览器前加上一些配置比如去掉自动化提示设置常规的用户代理options.add_argument(--user-agentMozilla/5.0 ... Chrome/120.0.0.0 Safari/537.36)但要坦诚地讲没有一种配置能百分百伪装成真实用户。更重要的是招聘网站一般对浏览行为不太敏感你只要控制请求频率、不要并发开太多页面触发风控的概率很低。我在实践中普通的一页一页点击跑完几十页也没事。如果你非要用 10 个线程同时开 10 个浏览器去爬那被拦截只能说不冤。5.3 崩溃后的信息收集别急着改代码脚本报错之后最怕的就是看了一眼错误就说又失败了然后随手改个选择器再试。正确的排查思路是先收集信息再定位问题。收集信息包括三块当前页面的截图用driver.get_screenshot_as_file(debug.png)当前页面的源码用driver.page_source存成 HTML 文件当时的 URL 和窗口句柄。有了这三样东西你能很快判断是元素变了还是页面没加载或者是跳到了一个登录页。如果你只是盯着控制台的一行报错很可能漏掉真正的上下文。5.4 做一个有礼貌的爬虫频率控制与异常兜底最后也是最重要的是学会节制。招聘网站的服务器资源有限你的脚本应该像一个人一样慢慢看而不是像一个程序一样猛烈袭击。建议每次操作间隔 1 到 3 秒import time time.sleep(1.5)如果中途遇到验证码不要尝试破解。正规做法是停止脚本人工过一下验证码或者干脆换一个源。爬虫的意义在于帮你节省重复劳动而不是教你突破安全机制。我还习惯在整个循环外面套一个try/except把单条数据抓取失败的情况记录下来但不中断整个脚本。比如某一条岗位卡片里没有薪资就不写入这条字段而是打印一行日志继续下一条。这样即使页面局部结构有变化脚本也能跑完大部分数据不会因为一条脏数据全盘崩溃。实际上把 Selenium 用熟之后你会发现它不只是爬虫工具更是一个网页自动化的瑞士军刀。批量登录、定时截图、自动填表、重复性测试都能用它解决。这个系列的第一部分到这里刚好完成一个最小闭环安装环境、打开浏览器、搜索关键词、抓取列表、翻页、存储 CSV。我在最初跑这个项目的时候最大的体会是不要一开始就追求代码多华丽先把一条数据从页面里抠出来再考虑循环和扩展。一个能跑的简单脚本比十段写了一半的高级框架有用得多。下一步如果你感兴趣可以试着把抓取从列表页延伸到职位详情页那样就能拿到完整的岗位描述和任职要求也能练习延时加载和切换标签页。这些内容就留到系列的第二部分再聊了。