爬虫框架playwright使用小技巧

playwright简介

playwright是微软开源的一款自动化测试框架,其以卓越的性能和强大的功能,在自动化测试和爬虫领域越来越流行,逐步开始替代selenium。本文记录在使用playwright时用到的一些小技巧

playwright 简单实用

安装

bash 复制代码
pip install playwright # 安装playwright 库
playwright install # 此时会自动化安装浏览器,非必须
playwright install-deps # 安装一些依赖,非必须

使用

ini 复制代码
import json
import time
import asyncio


chrome_path = 'chrome 路径'

with sync_playwright() as p:
    # 使用系统安装的 Chromium
    browser = p.chromium.launch(
        executable_path=chrome_path,
        headless=False
        args=[
            "--no-sandbox",
            "--disable-gpu",
            "--disable-setuid-sandbox",
            "--disable-dev-shm-usage",
            "--disable-extensions",
            "--disable-blink-features=AutomationControlled",
            "--start-maximized"
        ]
    )
    context = browser.new_context()
    page = context.new_page()
    page.goto('https://www.baidu.com')
    await asyncio.sleep(300)

执行上面的代码,会打开chrome浏览器,然后打开百度页面

playwright小技巧

1. 开启debug模式

在启动,增加参数headless=False,运行代码时,会看到浏览器被打开,可以很方面的看到代码所执行的操作

ini 复制代码
browser = p.chromium.launch(
        executable_path=chrome_path,
        headless=False
    )

2. 隐藏爬虫指纹

此处主要是将js对象navigator.webdriver 隐藏掉,同时设置使用的系统平台信息

ini 复制代码
init_script = """
    Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
    Object.defineProperty(navigator, 'platform', { get: () => 'MacIntel' });
    window.chrome = {
        runtime: {},
        // 添加其他 Chrome 对象属性
    }
"""

page = context.new_page(java_script_enabled=True)
page.add_init_script(script=init_script)
from playwright.sync_api import sync_playwright
stealth_sync(page)

3. 上下文信息传递和保存

大多数网站都要求登录,我们在使用playwright时经常需要让用户处于登录态才能进行下一步操作,此时我们可以通过上下文,在初始化时,让用户处于登录态,同时操作完成是,将上下文信息持久化存储

初始化上下文

csharp 复制代码
    with open('state.json') as f:
        state = json.load(f)
    context = browser.new_context(storage_state=state)

在初始化context时,可以通过storage_state参数将上下文信息传递进去,storage_state支持两种格式,一种是文件路径,另一种是python dict形式;因此我可以将上下文保存到文件中,通过文件的形式传递,也可以从缓存或者数据库中读取,构造成dict形式传递

保存上下文

perl 复制代码
    state = context.storage_state()
    with open('state.json', 'w') as f:
        json.dump(state, f)

通过context.storage_state()可以拿到当先上下文信息,然后可以保存到文件,或者持久化到缓存或者数据库中

4. 打印控制台信息

在遇到异常时,尤其是js执行异常时,排查非常困难,因此可以通过将控制台信息打印出来,帮助我们排查问题

python 复制代码
    def log_console(msg):
        print("Browser Console:", msg.text)
        print("Browser Console:", msg)
        location = msg.location
        if location:
            url = location.get("url")
            if url:
                print("Resource URL:", url)
            else:
                print("Resource URL not available")
        else:
            print("Location information not available")


    page.on('console', log_console)

通过定义方法,然后通过page.on 注册

5. 打印网络请求和响应信息

有些站点可能针对爬虫做了特殊处理,比如我知道的有些站点检测到爬虫时会返回400或者202,针对这种情况在排查时非常困难且耗时,因此可以将网络请求信息全部打印,协助排查

python 复制代码
    page.on("request", lambda request: print(
        f"请求: {request.method} {request.url} {request.headers}"))
    # 监听响应事件
    page.on("response",
            lambda response: print(
                f"响应: {response.status} {response.url} {response.headers}"))

也是通过page.on注册

6. 篡改请求或者响应

在某些时候,我们可能希望拦截请求,然后针对请求或者响应做一些特殊处理来模拟一些情况,此时如果可以篡改请求,将会非常有用

这里主要通过page.route方法实现

6.1 篡改请求

python 复制代码
   def route_interceptor(route):
        headers = dict(route.request.headers)
        # headers['cookie'] = ''
        print(headers)
        if 'cookie' in headers:
            print('移除cookie')
            del headers['cookie']
        print(
            f"拦截请求: {type(route)} {route.request.method} {route.request.url} {headers}")
        route.continue_(headers=headers)

    page.route('**/*', route_interceptor)

6.2 篡改响应

ini 复制代码
def handle_route(route):
    logger.info("handle_route call")
    request = route.request
    url = request.url

    # 跳过非静态资源
    if not is_static_resource(url):
        route.continue_()
        return

    logger.info("handle_route 开始缓存")
    # 生成基于 URL 的唯一缓存文件名
    url_hash = hashlib.sha256(url.encode()).hexdigest()
    cache_file = Path(os.path.join(GLOBAL_CACHE_DIR, url_hash))

    # 如果缓存存在,使用缓存响应
    if cache_file.exists():
        logger.info(f"使用全局缓存: {url}")
        content_type = get_content_type(url)
        route.fulfill(
            status=200,
            headers={"Content-Type": content_type},
            body=cache_file.read_bytes()
        )
        return

    # 否则请求并缓存
    logger.info(f"请求并缓存: {url}")
    route.continue_()
    response = route.request.response()

    # 只缓存成功的静态资源
    if response.status == 200:
        try:
            body = response.body()
            cache_file.write_bytes(body)
            route.fulfill(
                status=200,
                headers=dict(response.all_headers()),
                body=body
            )
            return
        except Exception as e:
            logger.info(f"缓存失败 {url}: {e}")

    route.fulfill(
        status=response.status,
        headers=dict(response.all_headers()),
        body=response.body()
    )

    page.route('**/*', handle_route)

笔者使用此方法实现了静态文件缓存

7. 屏幕截图

如果开启无头模式,将没有浏览器界面,此时发生了什么我们并不清楚,因此可以通过屏幕截图的方式来保存现场

arduino 复制代码
page.screenshot('图片路径')
相关推荐
m0_625686555 分钟前
day37
python
云天徽上18 分钟前
【目标检测】IOU的概念与Python实例解析
图像处理·人工智能·python·深度学习·目标检测·计算机视觉
是紫焅呢20 分钟前
I排序算法.go
开发语言·后端·算法·golang·排序算法·学习方法·visual studio code
lovebugs30 分钟前
Java线上死锁问题实战:从定位到解决的全链路指南
java·后端·面试
飞飞帅傅38 分钟前
go语言位运算
开发语言·后端·golang
灬芜湖丿39 分钟前
day36 复习日(信贷神经网络)
python·深度学习·神经网络
来两个炸鸡腿1 小时前
【Datawhale组队学习202506】YOLO-Master task02 YOLO系列发展线
python·深度学习·学习·yolo
19891 小时前
【Dify精讲】第14章:部署架构与DevOps实践【知识卡片】
运维·python·性能优化·架构·flask·ai编程·devops
Ven%1 小时前
Linux环境下使用WPS比较word文档并生成修订
linux·服务器·python·wps