别再手写Selenium了!用AI生成动态页面爬虫脚本,Python自动点击/滚动实战指南

别再手写Selenium了!用AI生成动态页面爬虫脚本,Python自动点击/滚动实战指南

摘要:面对Vue/React渲染的动态页面,还在一行行调试XPath和等待策略?本文分享一套“AI辅助生成 + 人工校验修正”的Selenium爬虫工作流。以某主流招聘网站职位列表页为例,演示如何让AI理解页面结构后自动生成包含智能等待、无限滚动、弹窗处理的完整脚本。重点不是“让AI替你写代码”,而是如何把AI变成你的“爬虫结对编程搭档”。文末附Prompt模板与避坑清单。


一、为什么动态页面爬虫这么痛?

做过Web爬虫的都懂这个绝望循环:

  1. 打开F12看到完美的HTML结构,复制XPath;
  2. 塞进Selenium,运行,报NoSuchElementException
  3. time.sleep(5),能跑了但慢得像蜗牛;
  4. 换个网络环境又挂了,因为页面加载时间变了;
  5. 好不容易稳定了,网站前端重构,所有定位器全部失效。

动态渲染页面的核心难点从来不是“怎么点”,而是“什么时候点”。SPA框架的异步渲染、懒加载、虚拟滚动、Toast弹窗遮挡……这些都不是一个静态选择器能解决的。传统做法是靠经验堆显式等待,而AI的价值在于:它能根据你的自然语言描述,快速生成一套“防御性”操作序列,你再基于实际DOM做精准校准

这不是“AI一键生成可用代码”的童话,而是一种效率翻倍的新工作范式。


二、工作流设计:人机协作的正确姿势

在动手之前,先建立一个关键认知:AI不懂你目标网站的实时DOM,它只懂通用模式。所以我们的流程必须是闭环的:

人工: 分析页面结构
截图/F12/DOM片段

Prompt工程:
结构化描述+约束条件

AI生成:
Selenium脚本初稿

人工: 本地运行
+断点调试

通过?

反馈错误信息
给AI修正

脚本固化
+异常处理增强

三个原则

  • 喂上下文,不喂URL:AI无法访问网页,你必须提供DOM片段、CSS类名规律、交互行为描述;
  • 要策略,不要硬编码:要求AI生成“等待元素可点击”而非“sleep 3秒”;
  • 迭代修正,不求一步到位:第一轮生成的是骨架,第二轮修边界case,第三轮加健壮性。

三、实战案例:爬取动态加载的职位列表

我们选择一个典型的SPA招聘站点职位搜索页作为靶标(文中隐去具体域名,方法通用)。该页面特征:

  • React渲染,首屏仅加载20条;
  • 滚动到底部触发AJAX加载更多;
  • 职位卡片有“展开详情”按钮,点击后弹出Modal;
  • 偶发Cookie同意弹窗遮挡操作;
  • 列表容器使用虚拟滚动,DOM节点会复用。

3.1 第一步:人工采集页面指纹

打开F12,记录以下关键信息(这是给AI的“弹药”):

观察项记录内容用途
列表容器div.job-list-container滚动监听目标
单条职位article.job-card[data-job-id]数据提取锚点
展开按钮button.expand-btn(卡片内)触发Modal
Modal内容div.modal-overlay .job-detail详情提取
关闭按钮button.modal-close[aria-label="关闭"]弹窗清理
Cookie弹窗div.cookie-banner button.accept前置拦截
加载指示器div.loading-spinner(消失=加载完)滚动等待信号
无更多数据p.no-more-data(出现=终止)终止条件

💡技巧:在F12 Console里执行document.querySelector('xxx')验证选择器是否唯一有效,再喂给AI。别把猜测当事实。

3.2 第二步:构造结构化Prompt

以下是经过多轮迭代验证的高效Prompt模板(可直接复用):

你是一个资深Python自动化测试工程师。请为我生成一个Selenium WebDriver脚本,用于爬取一个React SPA招聘网站的职位列表。 ## 页面结构(已验证) - 列表容器: div.job-list-container - 职位卡片: article.job-card[data-job-id] - 展开详情按钮: button.expand-btn (在每个卡片内部) - 详情Modal: div.modal-overlay .job-detail - Modal关闭按钮: button.modal-close[aria-label="关闭"] - Cookie同意弹窗: div.cookie-banner button.accept (可能出现) - 加载中指示器: div.loading-spinner - 无更多数据标记: p.no-more-data ## 功能需求 1. 启动浏览器,访问指定URL 2. 检测并处理Cookie弹窗(如果出现则点击接受,超时3秒忽略) 3. 无限滚动加载:每次滚动到底部,等待loading-spinner消失或no-more-data出现 4. 对每个职位卡片:提取data-job-id、标题、公司、薪资;点击expand-btn获取详情文本;关闭Modal 5. 收集所有数据到列表,最终打印JSON ## 技术约束(必须遵守) - 使用selenium 4.x + webdriver-manager - 禁止使用time.sleep()作为等待策略 - 所有等待必须用WebDriverWait + expected_conditions - 滚动使用JavaScript execute_script,不用ActionChains - Modal操作前后加try-except,失败时记录日志跳过 - 添加合理的隐式等待和页面加载超时 - 代码注释用中文,解释每段等待策略的设计意图 - 返回完整可运行的单文件脚本

为什么这样写有效?

  • 角色设定:限定“自动化测试工程师”而非“爬虫开发者”,AI会更注重稳定性而非激进抓取;
  • 已验证标签:明确告知选择器是经过人工确认的,避免AI编造;
  • 负面约束:“禁止sleep”比“请用显式等待”更有强制力;
  • 容错要求:提前声明异常处理粒度,减少后期返工。

3.3 第三步:AI生成脚本的核心逻辑解析

AI通常会生成如下结构的代码(展示关键片段与设计思路):

fromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECfromselenium.common.exceptionsimportTimeoutException,NoSuchElementExceptionimportjsonimportlogging logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s] %(message)s')logger=logging.getLogger(__name__)classJobScraper:def__init__(self,url):self.url=url self.driver=webdriver.Chrome()self.driver.set_page_load_timeout(30)self.wait=WebDriverWait(self.driver,10)self.jobs=[]def_dismiss_cookie_banner(self):""" Cookie弹窗是可选的前置障碍。 用短超时探测,存在则点击,不存在则静默跳过。 不使用全局隐式等待,避免影响后续正常元素的等待节奏。 """try:banner_wait=WebDriverWait(self.driver,3)accept_btn=banner_wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,"div.cookie-banner button.accept")))accept_btn.click()logger.info("✓ Cookie弹窗已处理")exceptTimeoutException:logger.debug("未检测到Cookie弹窗,继续执行")def_scroll_to_load_all(self):""" 无限滚动核心逻辑。 终止条件双重保险:要么出现"无更多"标记,要么连续两次滚动后高度不变。 等待loading-spinner消失作为单次加载完成的信号,而非固定延时。 """last_height=0stall_count=0whileTrue:# 检查终止标志ifself.driver.find_elements(By.CSS_SELECTOR,"p.no-more-data"):logger.info("✓ 已到达列表底部(检测到终止标记)")break# 执行滚动self.driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight;",self.driver.find_element(By.CSS_SELECTOR,"div.job-list-container"))# 等待加载完成:spinner消失 OR 超时兜底try:self.wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR,"div.loading-spinner")))exceptTimeoutException:logger.warning("加载指示器等待超时,尝试继续...")# 防死循环:高度停滞检测new_height=self.driver.execute_script("return arguments[0].scrollHeight;",self.driver.find_element(By.CSS_SELECTOR,"div.job-list-container"))ifnew_height==last_height:stall_count+=1ifstall_count>=2:logger.info("✓ 滚动高度停滞,判定加载完毕")breakelse:stall_count=0last_height=new_heightdef_extract_job_detail(self,card):""" 单条职位的数据提取 + Modal交互。 Modal操作包裹在独立try-except中, 失败不影响外层循环,仅记录警告。 """job={'id':card.get_attribute('data-job-id'),'title':card.find_element(By.CSS_SELECTOR,".job-title").text,'company':card.find_element(By.CSS_SELECTOR,".company-name").text,'salary':card.find_element(By.CSS_SELECTOR,".salary-tag").text,}try:expand_btn=card.find_element(By.CSS_SELECTOR,"button.expand-btn")expand_btn.click()detail_el=self.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,"div.modal-overlay .job-detail")))job['detail']=detail_el.text close_btn=self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'button.modal-close[aria-label="关闭"]')))close_btn.click()# 等待Modal完全消失再继续下一条,避免DOM冲突self.wait.until(EC.invisibility_of_element(detail_el))except(TimeoutException,NoSuchElementException)ase:logger.warning(f"职位{job['id']}详情提取失败:{e}")job['detail']=None# 尝试兜底关闭可能残留的Modaltry:self.driver.find_element(By.CSS_SELECTOR,'button.modal-close[aria-label="关闭"]').click()exceptException:passreturnjobdefrun(self):try:self.driver.get(self.url)self._dismiss_cookie_banner()self._scroll_to_load_all()cards=self.driver.find_elements(By.CSS_SELECTOR,"article.job-card[data-job-id]")logger.info(f"共发现{len(cards)}个职位卡片")fori,cardinenumerate(cards):logger.info(f"正在处理第{i+1}/{len(cards)}条...")job=self._extract_job_detail(card)self.jobs.append(job)print(json.dumps(self.jobs,ensure_ascii=False,indent=2))returnself.jobsfinally:self.driver.quit()

3.4 第四步:人工校验与修正要点

AI生成的脚本通常能达到80%可用度,剩下20%需要你根据真实环境修补:

AI常见缺陷修正方法原因
滚动目标选错容器F12确认实际可滚动元素,可能是父级wrapperAI只能猜,不能看
Modal关闭后DOM未刷新增加invisibility_of_element等待AI常忽略过渡动画耗时
虚拟滚动导致卡片复用改用data-job-id去重,而非索引这是SPA特有陷阱
异常处理粒度过粗拆分try块,区分“找不到”和“点了没反应”便于定位故障点
缺少浏览器参数补充--disable-blink-features=AutomationControlled反检测基础配置

特别强调虚拟滚动问题:很多现代列表组件(如react-virtualized)只渲染可视区DOM。如果你滚动后再回头找之前的卡片,它们可能已被销毁。解决方案是在滚动过程中边滚边提取,而非滚完再统一提取。这需要修改AI生成的流程顺序,是人机协作中典型的“AI给骨架、人改血肉”场景。


四、进阶:让AI帮你写“自愈”逻辑

当页面频繁改版时,可以让AI生成多策略定位器降级链

# Prompt追加要求:# 为每个关键元素生成至少3种定位策略,按优先级尝试,# 全部失败时抛出带有页面截图路径的自定义异常def_safe_find(self,strategies,timeout=8):""" 多策略元素查找降级器。 strategies: [(By.CSS_SELECTOR, "xxx"), (By.XPATH, "//yyy"), ...] """forby,valueinstrategies:try:el=WebDriverWait(self.driver,timeout).until(EC.presence_of_element_located((by,value)))returnelexceptTimeoutException:continue# 全部失败,保存现场供分析screenshot_path=f"debug_{int(time.time())}.png"self.driver.save_screenshot(screenshot_path)raiseElementNotFoundError(f"所有定位策略均失败:{strategies}\n截图已保存:{screenshot_path}")

这种模式让脚本在网站小改版时仍能存活,大幅降低维护成本。AI擅长生成这种“模式化防御代码”,而你负责定义策略优先级和失败后的诊断动作。


五、合规与伦理红线

技术可行不等于应该做。请务必遵守:

  • 尊重robots.txt与ToS:若网站明确禁止自动化访问,立即停止;
  • 控制频率:单次操作间隔≥2秒,避免对服务器造成压力;
  • 不绕过付费墙/登录态:仅爬取公开可见内容;
  • 不采集个人信息:职位爬虫只取岗位描述,不碰简历、联系方式;
  • 数据用途受限:仅用于个人学习、学术研究或内部分析,不得商用或再分发;
  • 优先使用官方API:如果平台提供数据接口,永远优先于模拟浏览器。

Selenium是最后手段,不是首选方案。能用API解决的,绝不开浏览器。


六、总结:AI是放大器,不是替代品

回顾整个流程,AI真正加速的环节是:

  • 将零散的页面观察转化为结构化代码骨架;
  • 生成符合最佳实践的等待策略模板;
  • 补全异常处理、日志、资源清理等“无聊但必要”的代码;
  • 在你卡住时提供替代实现思路。

但它无法替代的是:

  • 对目标页面真实行为的观察与理解;
  • 对业务需求的判断(哪些数据值得爬、爬到什么程度够用);
  • 对法律与伦理边界的把握;
  • 对运行时异常的根因分析。

把AI当作一个经验丰富但不了解你项目的同事:你提供详尽的需求文档和现场照片,它给出专业建议和代码草稿,你审核、测试、调整,最终交付可靠的产品。这才是“AI+爬虫”在2026年应有的成熟姿态。

下次遇到棘手的动态页面,别再对着空白编辑器发呆。花10分钟整理页面指纹,写一段结构化Prompt,让AI先跑起来——然后你来把它变好。


免责声明:本文所述技术仅用于编程学习与自动化测试研究。使用者应自行评估目标网站的访问政策与法律法规,确保行为合规。因不当使用引发的任何法律纠纷或服务封禁,责任由使用者承担。文中示例代码已做脱敏与泛化处理,不构成对任何特定网站的爬取指导。