news 2025/12/26 10:54:52

Python 爬虫实战:Playwright 替代 Selenium 爬取动态页面

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python 爬虫实战:Playwright 替代 Selenium 爬取动态页面

前言

在网络爬虫开发领域,动态页面爬取一直是核心难点之一。传统的请求库(如 Requests)仅能获取静态 HTML 内容,无法处理由 JavaScript 渲染的动态数据;而 Selenium 作为老牌自动化测试工具,虽能解决动态页面爬取问题,但存在启动速度慢、资源占用高、稳定性不足等缺陷。Playwright 是微软推出的新一代自动化测试工具,凭借跨浏览器支持、异步编程友好、内置等待机制等优势,逐渐成为替代 Selenium 爬取动态页面的首选方案。本文将从实战角度出发,全面讲解 Playwright 的核心特性、使用方法,并通过完整案例演示如何用其高效爬取动态页面,帮助开发者掌握这一主流技术。

摘要

本文聚焦 Playwright 在动态页面爬虫开发中的应用,对比其与 Selenium 的核心差异,系统讲解 Playwright 的环境搭建、核心 API 使用、动态元素定位、异步爬取等关键技术点。通过实战案例(爬取豆瓣电影 Top250动态渲染的电影信息),完整展示从页面加载、元素提取到数据存储的全流程,同时剖析 Playwright 的底层工作原理,帮助开发者理解其高效爬取动态页面的核心逻辑。最终实现一套高性能、高稳定性的动态页面爬虫方案,为爬虫开发提供新的技术选型思路。

一、Playwright vs Selenium 核心差异对比

为清晰展示 Playwright 的优势,以下从核心维度对比两者的差异:

特性PlaywrightSelenium
开发团队微软开源社区(最初由 ThoughtWorks 开发)
浏览器支持Chrome、Firefox、Safari、Edge(原生支持)需要对应浏览器驱动,配置复杂
异步支持原生支持异步(async/await)需结合第三方库实现,体验差
等待机制自动等待元素加载,无需手动设置需手动设置显式 / 隐式等待,易出错
资源占用低,启动速度快高,启动及运行速度慢
元素定位支持更多定位方式(如 text、has-text)定位方式有限,依赖 XPath/CSS
稳定性高,内置防反爬适配易出现元素未加载完成导致的异常
调试体验内置代码生成器、调试工具调试工具需额外配置

二、Playwright 环境搭建

2.1 安装 Playwright

Playwright 的安装分为两个步骤:安装 Python 包和安装浏览器驱动(Playwright 会自动管理驱动,无需手动下载)。

执行以下命令完成安装:

bash

运行

# 安装Playwright Python包 pip install playwright # 安装浏览器驱动(Chrome、Firefox、WebKit) playwright install

2.2 验证安装

创建test_install.py文件,执行以下代码验证环境是否正常:

python

运行

from playwright.sync_api import sync_playwright def test_playwright_install(): with sync_playwright() as p: # 启动Chrome浏览器(无头模式) browser = p.chromium.launch(headless=True) page = browser.new_page() # 访问测试页面 page.goto("https://www.baidu.com") # 获取页面标题 title = page.title() print(f"页面标题:{title}") # 关闭浏览器 browser.close() if __name__ == "__main__": test_playwright_install()
输出结果:

plaintext

页面标题:百度一下,你就知道
原理说明:
  • sync_playwright():创建同步 Playwright 实例,适用于同步编程场景;若需异步,可使用async_playwright()
  • p.chromium.launch():启动 Chromium 内核浏览器(Chrome/Edge 基于该内核),headless=True表示无头模式(无界面运行),便于服务器部署。
  • page.goto():导航至指定 URL,Playwright 会自动等待页面加载完成(默认等待 DOMContentLoaded)。
  • page.title():获取页面标题,验证浏览器是否正常访问目标页面。

三、Playwright 核心 API 详解

3.1 浏览器与页面操作

API 方法功能说明
browser = p.chromium.launch()启动浏览器实例
context = browser.new_context()创建浏览器上下文(隔离的会话环境)
page = context.new_page()创建新页面
page.goto(url, wait_until="load")导航至 URL,wait_until 可选 load/domcontentloaded/networkidle
page.close()关闭页面
browser.close()关闭浏览器

3.2 元素定位与操作

Playwright 支持多种元素定位方式,核心方法为page.locator(),常用定位策略:

python

运行

# 1. CSS选择器(推荐) locator = page.locator("#kw") # 定位百度搜索框 # 2. XPath locator = page.locator('//*[@id="kw"]') # 3. 文本匹配 locator = page.locator("text=百度热搜") # 精确匹配文本 locator = page.locator("has-text=热搜") # 包含文本 # 4. ID/Class locator = page.locator("[id='kw']") locator = page.locator(".s_ipt") # 元素操作 locator.fill("Playwright 爬虫") # 输入文本 locator.click() # 点击元素 locator.wait_for() # 等待元素可见

3.3 数据提取

python

运行

# 获取元素文本 text = page.locator(".title").text_content() # 获取文本内容(含子元素) inner_text = page.locator(".title").inner_text() # 获取可见文本 # 获取元素属性 href = page.locator("a").get_attribute("href") # 获取页面HTML html = page.content() # 执行JS代码 result = page.evaluate("() => document.body.scrollHeight") # 获取页面高度

四、实战:爬取豆瓣电影 Top250 动态页面

4.1 需求分析

豆瓣电影 Top250(https://movie.douban.com/top250)的电影列表通过 JavaScript 动态渲染,需模拟浏览器加载页面,提取每部电影的标题、评分、简介、链接等信息,并将数据保存至 CSV 文件。

4.2 完整代码实现

python

运行

import csv from playwright.sync_api import sync_playwright class DoubanMovieSpider: def __init__(self): self.base_url = "https://movie.douban.com/top250" self.movies = [] # 存储爬取的电影数据 def crawl_movie(self, page): """爬取单页电影数据""" # 定位所有电影条目 movie_items = page.locator(".item") # 获取条目数量 item_count = movie_items.count() print(f"当前页电影数量:{item_count}") for i in range(item_count): item = movie_items.nth(i) # 获取第i个条目 # 提取电影信息 rank = item.locator(".pic em").inner_text() # 排名 title = item.locator(".hd a span").first.inner_text() # 标题 score = item.locator(".rating_num").inner_text() # 评分 quote = item.locator(".inq").inner_text() if item.locator(".inq").count() > 0 else "" # 引言 link = item.locator(".hd a").get_attribute("href") # 链接 # 存储数据 self.movies.append({ "rank": rank, "title": title, "score": score, "quote": quote, "link": link }) print(f"已爬取:{rank}. {title} | 评分:{score}") def crawl_all_pages(self): """爬取所有页面""" with sync_playwright() as p: # 启动浏览器(无头模式,若需调试可设为False) browser = p.chromium.launch( headless=True, # 禁用图片加载,提升爬取速度 args=["--blink-settings=imagesEnabled=false"] ) context = browser.new_context( # 设置用户代理,模拟真实浏览器 user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ) page = context.new_page() # 遍历所有页面(共10页) for page_num in range(10): offset = page_num * 25 url = f"{self.base_url}?start={offset}&filter=" print(f"\n正在爬取第{page_num + 1}页:{url}") # 访问页面,等待网络空闲(确保动态内容加载完成) page.goto(url, wait_until="networkidle") # 爬取当前页数据 self.crawl_movie(page) # 关闭浏览器 browser.close() def save_to_csv(self): """将数据保存至CSV文件""" with open("douban_top250.csv", "w", encoding="utf-8-sig", newline="") as f: fieldnames = ["rank", "title", "score", "quote", "link"] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(self.movies) print(f"\n数据已保存至douban_top250.csv,共{len(self.movies)}条记录") if __name__ == "__main__": spider = DoubanMovieSpider() # 爬取所有页面 spider.crawl_all_pages() # 保存数据 spider.save_to_csv()

4.3 输出结果

控制台输出(部分):

plaintext

正在爬取第1页:https://movie.douban.com/top250?start=0&filter= 当前页电影数量:25 已爬取:1. 肖申克的救赎 | 评分:9.7 已爬取:2. 霸王别姬 | 评分:9.6 已爬取:3. 阿甘正传 | 评分:9.5 ... 正在爬取第2页:https://movie.douban.com/top250?start=25&filter= 当前页电影数量:25 已爬取:26. 蝙蝠侠:黑暗骑士 | 评分:9.2 已爬取:27. 教父2 | 评分:9.3 ... 数据已保存至douban_top250.csv,共250条记录
CSV 文件输出(部分):
ranktitlescorequotelink
1肖申克的救赎9.7希望让人自由。https://movie.douban.com/subject/1292052/
2霸王别姬9.6风华绝代。https://movie.douban.com/subject/1291546/
3阿甘正传9.5一部美国近现代史。https://movie.douban.com/subject/1292720/

4.4 核心原理剖析

  1. 动态页面加载处理:Playwright 的page.goto()方法默认等待 DOMContentLoaded,通过设置wait_until="networkidle"可等待网络请求基本完成(无新请求超过 500ms),确保 JavaScript 渲染的电影列表完全加载。

  2. 元素定位与遍历:使用page.locator(".item")定位所有电影条目,通过count()获取条目数量,nth(i)遍历每个条目;针对可能不存在的元素(如引言.inq),通过count() > 0判断是否存在,避免报错。

  3. 性能优化

    • 启用无头模式(headless=True)减少资源占用;
    • 添加--blink-settings=imagesEnabled=false禁用图片加载,提升爬取速度;
    • 设置真实用户代理(User-Agent),降低被反爬识别的概率。
  4. 数据存储:采用 Python 内置的csv模块,将爬取的电影信息写入 CSV 文件,encoding="utf-8-sig"确保中文正常显示,newline=""避免出现空行。

五、Playwright 高级特性(异步爬取)

对于高并发爬取场景,Playwright 支持异步编程,以下是异步版豆瓣 Top250 爬虫示例:

python

运行

import csv import asyncio from playwright.async_api import async_playwright class AsyncDoubanMovieSpider: def __init__(self): self.base_url = "https://movie.douban.com/top250" self.movies = [] async def crawl_movie(self, page): """异步爬取单页数据""" movie_items = page.locator(".item") item_count = await movie_items.count() for i in range(item_count): item = movie_items.nth(i) rank = await item.locator(".pic em").inner_text() title = await item.locator(".hd a span").first.inner_text() score = await item.locator(".rating_num").inner_text() quote = await item.locator(".inq").inner_text() if await item.locator(".inq").count() > 0 else "" link = await item.locator(".hd a").get_attribute("href") self.movies.append({ "rank": rank, "title": title, "score": score, "quote": quote, "link": link }) print(f"已爬取:{rank}. {title} | 评分:{score}") async def crawl_all_pages(self): """异步爬取所有页面""" async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ) page = await context.new_page() for page_num in range(10): offset = page_num * 25 url = f"{self.base_url}?start={offset}&filter=" print(f"\n正在爬取第{page_num + 1}页:{url}") await page.goto(url, wait_until="networkidle") await self.crawl_movie(page) await browser.close() def save_to_csv(self): with open("douban_top250_async.csv", "w", encoding="utf-8-sig", newline="") as f: fieldnames = ["rank", "title", "score", "quote", "link"] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(self.movies) print(f"\n异步爬取完成,数据已保存至douban_top250_async.csv,共{len(self.movies)}条记录") if __name__ == "__main__": spider = AsyncDoubanMovieSpider() asyncio.run(spider.crawl_all_pages()) spider.save_to_csv()
原理说明:
  • async_playwright():创建异步 Playwright 实例,结合async/await实现异步编程。
  • asyncio.run():执行异步主函数,是 Python 3.7 + 推荐的异步执行方式。
  • 异步爬取的优势:在爬取多页面 / 多网站时,可并发执行请求,大幅提升爬取效率(相比同步爬取,效率提升 3-5 倍)。

六、防反爬与最佳实践

6.1 防反爬策略

  1. 设置合理的请求间隔:在页面跳转时添加随机延迟,避免高频请求被封禁:

    python

    运行

    import random import time # 同步版延迟 time.sleep(random.uniform(1, 3)) # 异步版延迟 await asyncio.sleep(random.uniform(1, 3))
  2. 使用代理 IP

    python

    运行

    # 配置代理 context = browser.new_context( proxy={"server": "http://127.0.0.1:7890"}, # 替换为实际代理地址 user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" )
  3. 避免指纹识别:Playwright 默认会隐藏自动化特征,但可进一步配置:

    python

    运行

    browser = p.chromium.launch( headless=True, args=[ "--disable-blink-features=AutomationControlled", # 禁用自动化检测 "--no-sandbox", # 禁用沙箱(服务器环境必需) "--disable-dev-shm-usage" # 解决/dev/shm内存不足问题 ] ) # 清除webdriver标识 page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

6.2 最佳实践

  1. 使用浏览器上下文隔离会话:每个爬虫任务使用独立的context,避免 Cookie / 缓存冲突。
  2. 异常处理:添加 try-except 捕获页面加载、元素定位异常,确保爬虫鲁棒性。
  3. 断点续爬:记录已爬取页面,重启时从上次中断位置继续(下一篇实战文章将详细讲解)。
  4. 调试技巧:启动浏览器时设置headless=False,并添加slow_mo=500(慢动作执行),便于调试元素定位问题。

七、总结

Playwright 作为新一代自动化工具,在动态页面爬取场景中展现出远超 Selenium 的性能和易用性:其原生支持异步、自动等待机制、丰富的元素定位方式,大幅降低了动态页面爬虫的开发难度;同时,通过合理的防反爬配置和性能优化,可实现高效、稳定的爬虫开发。本文通过豆瓣电影 Top250 的实战案例,完整覆盖了 Playwright 的环境搭建、核心 API、同步 / 异步爬取、数据存储等关键环节,希望能为开发者提供一套可直接落地的动态页面爬虫解决方案。

在实际开发中,可结合 Playwright 的截图、录屏、网络请求拦截等高级功能,进一步拓展爬虫的能力边界,应对更复杂的反爬场景。后续将继续讲解爬虫请求签名破解、JS 加密逆向等进阶技术,敬请关注。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/18 19:25:54

Python 爬虫实战:aiohttp 实现异步高并发爬虫

前言 传统同步爬虫受限于 “请求 - 等待 - 响应” 的串行执行模式,在面对海量 URL 采集场景时,I/O 等待时间占比极高,采集效率难以满足业务需求。异步编程通过事件循环机制,可在单个线程内同时处理多个网络请求,最大化…

作者头像 李华
网站建设 2025/12/18 19:25:46

Python 爬虫实战:asyncio 异步爬虫任务调度

前言 在基于 aiohttp 实现的异步爬虫中,单纯依靠 asyncio.gather 批量执行协程虽能实现高并发,但面对复杂场景(如任务优先级调度、动态任务添加、任务失败重试、资源限流)时,缺乏灵活的任务调度能力。asyncio 作为 Py…

作者头像 李华
网站建设 2025/12/18 19:25:39

必学收藏|AI Agent架构全解析:从ReAct到LangGraph设计模式

本文全面介绍了AI Agent的五大架构类型(反应型、审议式、混合、神经符号和认知)及LangGraph中的三大设计模式(多Agent系统、规划Agent、反思批判)。详细阐述了各架构特点、应用场景和优缺点,从基础到高级展示了AI Agent构建方法,强调选择合适架构的重要性…

作者头像 李华
网站建设 2025/12/18 19:24:45

COMSOL各向异性黑磷

comsol各向异性黑磷。搞黑磷模拟的朋友应该都懂,这玩意儿在不同方向上导电性能差异大到离谱。上次有个哥们拿着实验数据找我,说在COMSOL里死活复现不出黑磷的电流分布,我一看他的模型设置——好家伙,材料属性直接用了各向同性导电…

作者头像 李华
网站建设 2025/12/26 5:01:27

ORACLE学习笔记总结(数据库维护联机重做日志)

Oracle数据库联机重做日志详解一、什么是联机重做日志?1.1 基本概念联机重做日志(Online Redo Log)是Oracle数据库中用于记录所有数据变化的物理文件。它记录了数据库的所有修改操作,确保数据的一致性和可恢复性。1.2 核心作用数据…

作者头像 李华