一、Playwright 基本介绍
1、框架介绍
Playwright 是一个开源的自动化库,由 Microsoft 开发,用于自动化基于 Chromium,Firefox 和WebKit 的浏览器。它支持多种语言,包括 Python,并能在 Windows,Linux 和 macOS 上运行。
2、框架安装
安装Playwright:
pip install playwright
安装浏览器驱动:
python -m playwright install
3、基本使用
# -*- coding: utf-8 -*-# @Time : 2026/1/21# @Author : 富富import timefrom playwright.sync_api import sync_playwright # 同步执行# 创建一个playwright上下文管理器with sync_playwright() as p: # 创建一个浏览器 browser = p.chromium.launch(headless=False) # 有界面,默认开启无头模式 # 打开一个标签页 page = browser.new_page() # 标签页中访问百度页面 # 设置页面加载的超时时间,单位为毫秒。如果页面在指定的时间内未加载完成,将抛出超时异常 # page.goto("https://www.baidu.com/", timeout=5000) # 设置超时时间为5秒 # 设置页面导航的引用页面URL,模拟用户从某个特定页面跳转而来 page.goto("https://news.baidu.com/", referer="https://www.baidu.com/") # 获取页面标题 title = page.title() print(title) # 获取页面的链接 current_url = page.url print(current_url) # 对当前页面进行截图 page.screenshot(path='baidu.png') # 获取当前页面的HTML源码 html = page.content() print(html) # 等待3秒 time.sleep(3) # 关闭浏览器 browser.close()
二、元素定位与操作方式
# -*- coding: utf-8 -*-# @Time : 2026/1/21# @Author : 富富# 元素定位与操作方式import timefrom playwright.sync_api import sync_playwright # 同步执行# 创建一个playwright上下文管理器with sync_playwright() as p: # 创建一个浏览器 browser = p.chromium.launch(headless=False) # 有界面,默认开启无头模式 # 打开一个标签页 page = browser.new_page() # 标签页中访问百度页面 page.goto("https://www.baidu.com/") # 使用locator方法通过元素中的文本内容来定位元素(不建议使用这个方法,容易定位失败) # page.locator("text=按钮文本").click() # page.locator("text=新闻").first.click() # 如果有多个元素,可以指定第一个输出 # 定位百度首页的输入框元素标签,并输入文字 # 使用query_selector方法通过xpath表达式来定位元素,如果有多个元素只获取第一个 page.query_selector('//textarea[@id="chat-textarea"]').fill("烤肉") # 定位百度首页的搜索按钮,并点击这个按钮 page.query_selector('//button[@id="chat-submit-button"]').click() # 定位多个元素标签 element_list = page.query_selector_all('//span[@class="title-content-title"]') for element in element_list: # 获取元素标签的文本内容 print(element.inner_text()) # 提取属性的值 print(element.get_attribute('class')) # 等待3秒 time.sleep(3) # 关闭浏览器 browser.close()
三、标签页的切换与滚动
介绍如何切换到 iframe 或处理模态弹窗。
# -*- coding: utf-8 -*-# @Time : 2026/1/22# @Author : 富富# 切换iframe页面import timefrom playwright.sync_api import sync_playwright# 创建一个playwright上下文管理器with sync_playwright() as p: # 创建一个浏览器 browser = p.chromium.launch(headless=False) # 有界面 # 打开一个标签页 page = browser.new_page() # 标签页中访问页面 page.goto("https://mail.163.com/") # 定位iframe标签 iframe = page.query_selector('//iframe[@frameborder="0"]') # 通过content_frame加载iframe中的内容,并再次定位输入 # 如果你需要模拟真实用户的输入行为,或者页面有依赖于键盘事件的逻辑,使用type。 # 如果你只是需要快速填充表单,并且页面没有依赖于键盘事件的逻辑,使用fill。 iframe.content_frame().query_selector('//input[@name="email"]').type('fufu') iframe.content_frame().query_selector('//input[@name="password"]').type('fufu') # 等待3秒 time.sleep(3) # 关闭浏览器 browser.close()
四、隐藏驱动检测
网站会针对 navigator.webdriver 属性进行检测反爬,可以通过在初始化脚本中修改浏览器的webdriver 的值,避免被检测;
# -*- coding: utf-8 -*-# @Time : 2026/1/22# @Author : 富富# 测试网站是否有自动化特性检测import timefrom playwright.sync_api import sync_playwright# 创建一个playwright上下文管理器with sync_playwright() as p: # 创建一个浏览器 browser = p.chromium.launch(headless=False) # 有界面 # 打开一个标签页 page = browser.new_page() # 添加初始化js脚本代码,隐藏webdriver属性,防止检测出来 js = "Object.defineProperties(navigator, {webdriver:{get:()=>undefined}});" page.add_init_script(js) # 标签页中打开对应的网址 page.goto("https://www.qidian.com/") # 等待3秒 time.sleep(3) # 关闭浏览器 browser.close()
五、异步执行
# -*- coding: utf-8 -*-# @Time : 2026/1/22# @Author : 富富# 异步执行import asynciofrom playwright.async_api import async_playwright # 异步执行async def main(): # 创建一个playwright上下文管理器 async with async_playwright() as p: # 创建一个浏览器 browser = await p.chromium.launch(headless=False) # 有界面 # 打开一个标签页 page = await browser.new_page() # 标签页中访问百度页面 await page.goto("https://www.baidu.com") # 异步等待3秒 await asyncio.sleep(3) # 关闭浏览器 await browser.close()if __name__ == '__main__': asyncio.run(main())
六、抓取当当图书网案例
使用 playwright 异步抓取工具来批量抓取当当图书网的商品数据信息。
# -*- coding: utf-8 -*-# @Time : 2026/1/22# @Author : 富富import csvimport timeimport asynciofrom lxml import etreefrom playwright.async_api import async_playwright# 定义抓取每页数据的函数async def get_page_data(browser, semaphore, page_number): # 控制并发量 async with semaphore: # 创建标签页 page = await browser.new_page() # 格式化每页的网址并打开网址 url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1{page_number}' await page.goto(url) # 获取当前页面的HTML源码 html_text = await page.content() # 将网页html代码转换成树形结构 html_tree = etree.HTML(html_text) # 使用xpath表达式来筛选书籍的节点标签元素 book_info_list = html_tree.xpath('//ul[@class="bang_list clearfix bang_list_mode"]/li') # 定义每页数据列表 page_book_info = [] # 使用循环遍历出每本书的节点信息 for book_info in book_info_list: # 使用xpath表达式来筛选书籍的名称 book_title = book_info.xpath('./div[@class="name"]/a/@title')[0] print(book_title) # 使用xpath表达式来筛选书籍的价格 book_price = book_info.xpath('./div[@class="price"]/p[1]/span[@class="price_n"]/text()')[0] print(book_price) page_book_info.append([book_title, book_price]) print(f'==================第{page_number}页的数据抓取完毕==================') # 返回每页筛选的数据 return page_book_info# 定义保存数据的函数def save_data(book_info_list): # 创建一个csv文件 # office excel不支持utf-8字符串,所以如果用utf-8字符串会出现乱码,需要修改为utf-8-sig with open('当当图书.csv', 'w', newline='', encoding='utf-8-sig') as file: # 将文件对象绑定到csv的写入对象 writer = csv.writer(file) # 写入一行表头数据 writer.writerow(['书籍名称', '书籍价格']) # 写入多行数据 writer.writerows(book_info_list)# 运行多个爬虫任务async def spider_main(pages): # 异步的方式实例化playwright async with async_playwright() as p: # 实例化浏览器对象,并执行为有界面模式执行 browser = await p.chromium.launch(headless=False) # 使用信号量限制最大并发为5个 semaphore = asyncio.Semaphore(5) # 定义协程任务列表 task_list = [] # 遍历页数 for i in range(1, pages + 1): # 创建协程对象执行get_page_data函数 task = asyncio.create_task(get_page_data(browser, semaphore, i)) # 添加协程对象到列表中 task_list.append(task) # 挂起并执行协程任务列表中的所有任务,并接收get_page_data函数返回的数据,获取的数据类型是列表。 page_book_list = await asyncio.gather(*task_list) print(page_book_list) # 定义空列表用于整理最终的数据格式 all_book_list = [] # 循环遍历多页数据的三维列表 for page_book in page_book_list: # 遍历一页数据中每本书的列表数据 for book in page_book: # 将每本书的数据追加到汇总的列表中 all_book_list.append(book) # 调用save_data函数来保存所有数据 save_data(all_book_list) # 关闭浏览器 await browser.close()if __name__ == '__main__': # 从键盘输入抓取的页数 pages = int(input('请输入抓取的页数:')) # 开始计时 start_time = time.time() # 运行爬虫抓取数据 asyncio.run(spider_main(pages)) # 结束计时,并输出运行时间 end_time = time.time() print('程序耗时:', end_time - start_time)