Python 爬虫项目 Playwright 异步编程、页面拦截与反爬基础绕过实战
2026/6/9 4:09:51 网站建设 项目流程

前言

在大规模动态页面采集、多站点并发爬虫业务中,同步执行模式存在明显性能瓶颈,单线程串行执行无法充分利用网络与服务器资源。Playwright 除基础的同步 API 外,原生支持异步编程模型,依托 Pythonasyncio实现高并发页面访问,可大幅提升批量采集效率。同时,现代网站普遍搭载基础反爬策略,结合请求头校验、资源加载检测、浏览器特征识别等手段拦截自动化工具,单纯的页面渲染采集难以稳定运行。

本文聚焦 Playwright 三大核心进阶能力:异步并发架构实现、网络请求与静态资源拦截、主流基础反爬策略绕过。结合资讯、综合门户类站点实战案例,拆解异步运行原理、拦截规则配置、指纹伪装、请求头优化等关键技术,搭配完整可落地代码与底层逻辑解析。同时对比同步与异步模式的性能差异,梳理生产环境下的编码规范与风险规避要点,帮助开发者将 Playwright 从单机小型采集工具升级为支持高并发、具备基础反爬抗性的工程化爬虫组件。

本文涉及核心依赖库与官方文档:

  1. Playwright Python 官方文档
  2. Python asyncio 异步标准库文档
  3. Playwright 网络拦截专题文档
  4. Python 异常处理与协程编程指南

一、Playwright 同步与异步架构核心解析

1.1 同步与异步模式运行机制

Playwright 提供两套完整的 API 体系,分别为同步 API 与异步 API,两套接口功能完全对等,仅运行模型存在差异。同步 API 基于单线程串行执行,代码自上而下依次运行,发起页面请求、等待渲染、数据解析等流程均会阻塞当前线程,同一时间仅能处理单个页面任务,优势是代码逻辑简单、调试便捷,适用于单站点、小数据量采集场景。

异步 API 基于 Python 原生asyncio协程模型运行,协程属于轻量级并发单元,无需创建系统线程与进程,资源开销极低。在异步模式下,程序不会因页面加载、网络请求等 IO 操作阻塞整体流程,可在等待一个页面渲染的间隙,同时发起多个页面访问请求,实现同一进程内的大规模并发采集,是海量数据批量抓取、多站点轮询采集的最优选择。

Playwright 底层基于 Node.js 驱动通信,异步 API 与底层通信模型天然适配,并发调度效率高于传统多线程爬虫,同时规避了多线程下线程安全、资源竞争等问题。

1.2 核心语法规则与环境区分

异步编程存在专属语法标识,async用于定义异步函数,await用于挂起协程,等待 IO 操作完成后再继续执行后续逻辑。Playwright 异步 API 所有页面操作、浏览器操作方法均为异步方法,调用时必须搭配await关键字,这是异步代码编写的基础规范。

同步模式使用sync_playwright上下文管理器,异步模式则对应async_playwright,二者不可混用。基础结构对比如下表:

表格

运行模式上下文管理器语法标识并发能力适用场景
同步模式sync_playwright()无 async/await单任务串行,无并发单页面采集、功能调试、新手入门
异步模式async_playwright()async 函数 + await 调用协程高并发,轻量高效多站点批量采集、定时轮询、大规模任务

1.3 异步模式基础环境搭建

异步模式依赖asyncio标准库,Python 3.7 及以上版本内置该库,无需额外安装。首先确认 Playwright 完整安装,执行基础环境命令:

bash

运行

pip install playwright playwright install

基础异步代码框架为行业通用模板,所有异步爬虫均基于该结构扩展:

python

运行

import asyncio from playwright.async_api import async_playwright # 定义异步主函数 async def main(): async with async_playwright() as p: # 异步启动浏览器、页面,所有操作加 await browser = await p.chromium.launch(headless=True) page = await browser.new_page() await page.goto("目标URL") # 业务逻辑代码 await browser.close() # 启动异步事件循环 if __name__ == "__main__": asyncio.run(main())

二、Playwright 异步并发资讯爬虫实战

2.1 单协程异步页面采集案例

本案例实现单异步协程完成动态资讯页面数据抓取,对比同步代码,熟悉异步语法与基础页面操作逻辑。目标站点为资讯门户,数据通过 JS 动态渲染,依靠 Playwright 内置自动等待机制完成元素加载。

2.1.1 完整代码实现

python

运行

import asyncio from playwright.async_api import async_playwright async def single_news_spider(url): """单页面异步采集函数""" async with async_playwright() as p: # 启动无头浏览器,关闭图形界面降低资源占用 browser = await p.chromium.launch(headless=True) # 创建浏览器上下文,隔离Cookie、缓存等数据 context = await browser.new_context() page = await context.new_page() # 设置全局超时时间,单位毫秒 page.set_default_timeout(30000) try: # 访问目标页面,await 挂起协程等待页面加载完成 await page.goto(url) # 等待资讯列表元素加载完成 await page.wait_for_selector(".hotnews-item", timeout=15000) # 批量定位所有资讯元素 news_items = await page.query_selector_all(".hotnews-item") result = [] for index, item in enumerate(news_items): # 提取标题与链接 title_elem = await item.query_selector("a") title = await title_elem.inner_text() link = await title_elem.get_attribute("href") # 提取发布时间 time_elem = await item.query_selector(".date") publish_time = await time_elem.inner_text() if time_elem else "未知时间" data = { "序号": index + 1, "资讯标题": title.strip(), "资讯链接": link, "发布时间": publish_time.strip() } result.append(data) print(data) print(f"页面 {url} 采集完成,共获取 {len(result)} 条资讯") except Exception as e: print(f"页面 {url} 采集异常:{str(e)}") finally: # 逐级关闭资源,释放内存 await context.close() await browser.close() async def main(): target_url = "https://news.baidu.com/" await single_news_spider(target_url) if __name__ == "__main__": # 启动异步事件循环 asyncio.run(main())
2.1.2 代码原理拆解
  1. 异步函数封装:采集逻辑封装为async def定义的异步函数,所有页面操作方法gotowait_for_selectorquery_selector均添加await,协程在 IO 阶段主动让出资源。
  2. 上下文隔离:使用new_context创建独立浏览器上下文,每个上下文拥有独立的缓存、Cookie、存储数据,避免不同页面之间数据互相干扰。
  3. 超时机制:分层设置超时,全局页面超时与元素等待超时配合,防止页面加载卡死导致协程常驻。
  4. 资源释放finally代码块保证无论任务成功或失败,上下文与浏览器进程都会正常关闭,杜绝后台进程残留。

2.2 多协程并发批量采集实战

在实际业务中,通常需要同时采集多个资讯站点,利用asyncio.create_task创建多个协程任务,实现并行执行,大幅提升整体采集速度。本案例同时访问多个资讯门户,对比串行与并发的执行效率。

2.2.1 并发采集完整代码

python

运行

import asyncio from playwright.async_api import async_playwright # 待采集的多资讯站点列表 URL_LIST = [ "https://news.baidu.com/", "https://news.sina.com.cn/", "https://www.techweb.com.cn/", "https://www.donews.com/" ] async def batch_spider(url): """单站点采集逻辑""" browser = None context = None try: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() page.set_default_timeout(30000) await page.goto(url) await page.wait_for_selector("a", timeout=15000) # 提取页面标题与前5条资讯标题 page_title = await page.title() news_items = await page.query_selector_all("a")[:5] news_titles = [await item.inner_text() for item in news_items] print(f"【站点】{url} 页面标题:{page_title}") print(f"资讯摘要:{news_titles}\n") except Exception as e: print(f"站点 {url} 采集失败:{str(e)}\n") finally: if context: await context.close() if browser: await browser.close() async def main(): # 创建协程任务列表 tasks = [] for url in URL_LIST: task = asyncio.create_task(batch_spider(url)) tasks.append(task) # 等待所有协程任务执行完成 await asyncio.gather(*tasks) print("所有站点并发采集任务全部结束") if __name__ == "__main__": asyncio.run(main())
2.2.2 并发核心知识点
  1. 协程任务创建asyncio.create_task将异步函数封装为可调度的协程任务,加入事件循环等待执行。
  2. 任务聚合执行asyncio.gather接收多个任务对象,统一等待所有任务运行完毕,是并发场景的核心方法。
  3. 性能优势:串行执行多个站点时,总耗时为所有站点加载时间之和;并发模式下,总耗时近似等于单个站点最长加载时间,站点数量越多,性能提升越明显。
  4. 并发数量控制:无限制创建协程会同时发起大量网络请求,容易触发目标站点限流、IP 封禁,生产环境需通过信号量asyncio.Semaphore限制最大并发数。

2.3 并发数量限制(信号量)实战

信号量用于控制同一时间运行的协程数量,避免并发过高造成风险,是工程化异步爬虫的必备配置。

python

运行

import asyncio from playwright.async_api import async_playwright # 限制最大并发数为3 SEMAPHORE = asyncio.Semaphore(3) URL_LIST = [ "https://news.baidu.com/", "https://news.sina.com.cn/", "https://www.techweb.com.cn/", "https://www.donews.com/", "https://www.163.com/dy/" ] async def limited_spider(url): async with SEMAPHORE: # 信号量内同一时间最多3个协程执行 async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() await page.goto(url) title = await page.title() print(f"正常采集:{url} 标题:{title}") await context.close() await browser.close() async def main(): tasks = [asyncio.create_task(limited_spider(url)) for url in URL_LIST] await asyncio.gather(*tasks) print("限流并发任务执行完毕") if __name__ == "__main__": asyncio.run(main())

三、Playwright 网络请求拦截与资源优化

3.1 网络拦截应用场景与原理

现代网页包含图片、视频、字体、CSS、JS、广告脚本等大量静态资源,爬虫核心目标仅为文本、链接等有效数据,加载无关资源会延长页面渲染时间、增加网络流量与内存消耗。Playwright 提供强大的网络路由拦截功能,可拦截、修改、放行、终止任意类型的网络请求,实现资源裁剪、请求篡改、接口监听等功能。

路由拦截基于page.route()方法实现,工作原理为:浏览器发起网络请求前,Playwright 拦截请求并匹配预设规则,根据规则决定继续转发请求、终止请求、替换请求内容或修改请求头。该功能同步、异步模式均可使用,是优化爬虫性能的核心手段。

3.2 静态资源拦截优化加载速度

针对图片、视频、音频、字体等非必要资源进行拦截终止,大幅缩减页面加载体积,提升采集效率。支持通配符匹配资源后缀、请求地址,适配全站统一拦截规则。

3.2.1 资源拦截完整代码

python

运行

import asyncio from playwright.async_api import async_playwright async def intercept_resource_spider(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() # 规则1:拦截所有图片资源 await page.route("**/*.{png,jpg,jpeg,gif,webp,bmp}", lambda route: route.abort()) # 规则2:拦截视频、音频资源 await page.route("**/*.{mp4,flv,mp3,wav}", lambda route: route.abort()) # 规则3:拦截字体文件 await page.route("**/*.{woff,woff2,ttf}", lambda route: route.abort()) # 访问资讯页面 url = "https://news.baidu.com/" await page.goto(url) print("页面加载完成,已拦截所有图片、视频、字体资源") # 正常采集文本数据 news_items = await page.query_selector_all(".hotnews-item") for item in news_items[:8]: title = await item.query_selector("a") text = await title.inner_text() print(f"资讯标题:{text.strip()}") await context.close() await browser.close() if __name__ == "__main__": asyncio.run(intercept_resource_spider())
3.2.2 路由核心方法说明
  • route.abort():终止当前请求,不再向服务器发送,资源直接放弃加载,最常用;
  • route.continue_():放行请求,按照原始配置正常转发;
  • route.fulfill():伪造响应数据,自定义返回内容,适用于 mock 测试场景;
  • 通配符**:匹配域名下任意路径,实现全站统一拦截。

3.3 XHR/Fetch 请求监听与捕获

除静态资源拦截外,路由功能还可监听页面所有异步 XHR、Fetch 请求,自动抓取接口地址、请求参数、响应数据,替代手动抓包流程,实现自动化接口逆向。

python

运行

import asyncio from playwright.async_api import async_playwright async def listen_xhr_request(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) page = await browser.new_page() # 监听所有XHR、Fetch异步请求 async def handle_request(route, request): # 打印异步接口信息 if request.resource_type == "xhr" or request.resource_type == "fetch": print(f"接口地址:{request.url}") print(f"请求方式:{request.method}") print(f"请求参数:{request.post_data}\n") await route.continue_() await page.route("**/*", handle_request) await page.goto("https://news.baidu.com/") await page.wait_for_timeout(5000) await browser.close() if __name__ == "__main__": asyncio.run(listen_xhr_request())

四、Playwright 基础反爬策略绕过实战

4.1 自动化工具识别原理

主流站点基础反爬系统会检测浏览器指纹、请求头、运行特征,以此区分真实用户与自动化爬虫。常见识别点包含:默认请求头缺失、浏览器特征指纹异常、无头模式特征标记、页面操作行为机械化等。Playwright 原生模拟正规浏览器,但默认配置仍存在部分可识别特征,需要针对性伪装优化。

4.2 请求头与浏览器上下文伪装

请求头是最基础的反爬校验项,User-AgentRefererAccept等字段缺失或异常,会直接导致请求被拦截。通过new_context初始化时自定义请求头,模拟真实浏览器访问行为。

4.2.1 自定义请求头代码

python

运行

import asyncio from playwright.async_api import async_playwright async def fake_header_spider(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) # 自定义请求头,模拟真实Chrome浏览器 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", "Accept-Language": "zh-CN,zh;q=0.9", "Referer": "https://www.baidu.com/", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } # 加载自定义请求头 context = await browser.new_context(extra_http_headers=headers) page = await context.new_page() await page.goto("https://news.baidu.com/") title = await page.title() print(f"页面标题:{title},请求头伪装生效") await context.close() await browser.close() if __name__ == "__main__": asyncio.run(fake_header_spider())

4.3 无头模式特征消除

原生 Chromium 无头模式存在固定特征标识,部分站点可精准识别。Playwright 支持通过启动参数关闭无头特征、补充浏览器插件标识,进一步隐藏自动化属性。

python

运行

import asyncio from playwright.async_api import async_playwright async def hide_headless_feature(): # 配置浏览器启动参数,消除无头特征 launch_options = { "headless": True, "args": [ "--no-sandbox", "--disable-blink-features=AutomationControlled", "--disable-dev-shm-usage" ] } async with async_playwright() as p: browser = await p.chromium.launch(**launch_options) context = await browser.new_context() page = await context.new_page() # 执行JS脚本,修改页面自动化检测标识 await page.add_init_script(""" Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) """) await page.goto("https://news.baidu.com/") print("无头浏览器特征已清除") await browser.close() if __name__ == "__main__": asyncio.run(hide_headless_feature())

参数说明:--disable-blink-features=AutomationControlled关闭浏览器自动化标记,JS 脚本删除navigator.webdriver检测字段,二者配合可绕过绝大多数基础无头检测。

4.4 模拟人工操作行为

纯自动化快速操作容易被行为风控识别,通过添加随机延时、模拟鼠标滑动、随机点击等行为,模仿真人浏览节奏。

python

运行

import asyncio import random from playwright.async_api import async_playwright async def human_behavior_spider(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) page = await browser.new_page() await page.goto("https://news.baidu.com/") # 随机等待1~3秒,模拟真人浏览停顿 await page.wait_for_timeout(random.randint(1000, 3000)) # 模拟鼠标滑动页面 await page.mouse.wheel(0, random.randint(200, 500)) await page.wait_for_timeout(random.randint(500, 1500)) print("模拟人工操作完成,正常采集数据") await browser.close() if __name__ == "__main__": asyncio.run(human_behavior_spider())

五、综合案例:高并发轻量化反爬爬虫整合

整合异步并发、资源拦截、请求头伪装、特征隐藏、人工行为模拟所有技术点,构建一套生产环境可用的综合爬虫,适配多站点批量动态资讯采集。

python

运行

import asyncio import random from playwright.async_api import async_playwright # 全局并发限制 MAX_SEMAPHORE = asyncio.Semaphore(4) # 目标站点列表 TARGET_URLS = [ "https://news.baidu.com/", "https://news.sina.com.cn/", "https://www.techweb.com.cn/" ] # 通用请求头 COMMON_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36", "Accept-Language": "zh-CN,zh;q=0.9" } # 浏览器启动参数 LAUNCH_ARGS = { "headless": True, "args": ["--no-sandbox", "--disable-blink-features=AutomationControlled"] } async def full_function_spider(url): async with MAX_SEMAPHORE: async with async_playwright() as p: browser = await p.chromium.launch(**LAUNCH_ARGS) context = await browser.new_context(extra_http_headers=COMMON_HEADERS) page = await context.new_page() page.set_default_timeout(30000) # 拦截无关资源 await page.route("**/*.{png,jpg,gif,mp4,woff}", lambda r: r.abort()) # 隐藏webdriver标识 await page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") try: await page.goto(url) # 模拟人工延时与滑动 await page.wait_for_timeout(random.randint(800, 2000)) await page.mouse.wheel(0, 300) await page.wait_for_selector(".hotnews-item,a[href*='news']", timeout=15000) # 采集数据 items = await page.query_selector_all("a")[:6] titles = [await item.inner_text() for item in items] print(f"【{url}】采集资讯:{titles}\n") except Exception as e: print(f"【{url}】任务异常:{str(e)}\n") finally: await context.close() await browser.close() async def main(): tasks = [asyncio.create_task(full_function_spider(url)) for url in TARGET_URLS] await asyncio.gather(*tasks) print("全部高并发采集任务执行结束") if __name__ == "__main__": asyncio.run(main())

六、全文总结

本文围绕 Playwright 进阶核心能力展开,从异步协程编程入手,讲解同步与异步模型的差异、协程任务创建、并发数量限制,解决大规模采集的性能问题;依托网络路由拦截功能,实现静态资源裁剪、异步接口监听,从加载层面优化爬虫运行效率;结合浏览器特征伪装、请求头配置、人工行为模拟,完成基础反爬策略绕过,提升爬虫稳定性。

异步编程是 Playwright 进阶的核心标志,协程并发相比传统多线程、多进程具备资源轻量、调度高效的优势,是批量爬虫的首选架构。网络拦截功能不仅可以优化加载速度,还能辅助接口逆向分析,打通浏览器渲染爬虫与接口爬虫的技术链路。而请求头、无头特征、行为模拟等伪装手段,是爬虫突破基础反爬的必要配置,也是生产环境稳定运行的保障。

将以上技术组合使用,可构建出高性能、低消耗、具备基础反爬能力的工程化 Playwright 爬虫系统。结合前文的基础用法、元素等待、批量采集等内容,已形成完整的 Playwright 技术体系,后续可继续拓展代理池对接、会话保持、分布式任务调度、高阶 JS 逆向等深度内容,进一步提升爬虫项目的落地能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询