python版puppeteer——pyppeteerselenium的加强版——seleniumwire

news2024/11/18 23:25:10

目录

  • 前言
  • seleniumwire
    • 安装
    • 创建web driver
    • 设置代理
    • 反屏蔽
      • 修改window.navigator.webdriver关键字返回结果
      • options追加参数
  • pyppeteer
    • puppeteer
    • 安装
    • 快速入门
    • 参数配置
    • 隐藏浏览器特征
    • 拦截请求
    • 更多文档&博客
  • Playwright
    • 安装
    • 快速入门
    • 新概念:Context
    • 页面基本操作
    • 选择器表达式
    • 复用 Cookies 等认证信息
    • 监听事件
    • 拦截更改网络请求
    • 灵活设置代理
    • 杀手级功能:录制操作直接生成代码

前言

书接上文,selenium添加代理:

rom selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

# 代理供应商
proxy_host = 'your_host'
proxy_port = 'your_port'
proxy_username = 'your_username'
proxy_password = 'your_password'

# 创建代理对象
proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'
proxy.ssl_proxy = f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'

# 设置Chrome浏览器驱动程序使用代理
options = webdriver.ChromeOptions()
options.add_argument('--proxy-server=%s' % proxy.http_proxy)
options.add_argument('--disable-notifications')
options.add_argument('--disable-popup-blocking')
options.add_argument('--disable-infobars')
options.add_argument('--mute-audio')
driver = webdriver.Chrome(options=options)

对于无校验的代理,也就是代理形式为:

{proxy_host}:{proxy_port}

这样加没问题,但

对于需要校验用户名,密码的代理,也就是代理形式为:

proxy.http_proxy = f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'
proxy.ssl_proxy = f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'

访问页面就会出现"ERR_NO_SUPPORTED_PROXIES"

解决方法:

  1. 代理提供商通常会提供白名单校验:将你的主机ip加入白名单就可以替换username,password校验了。
  2. 使用selenium的扩展库:selenium-wire

seleniumwire

SeleniumWire 是一个 Python 库,它是在 Selenium WebDriver 的基础上扩展而来的。它提供了对浏览器的网络请求和响应进行拦截、修改和监控的功能,使得你可以更灵活地控制浏览器与服务器之间的通信过程。

以下是 SeleniumWire 提供的一些主要功能和特点:

  1. 请求和响应拦截:SeleniumWire 允许你拦截浏览器发送的请求和接收的响应,从而可以在请求发送前、发送后、响应接收前、接收后等时机对请求和响应进行修改、记录或拦截。

  2. 修改请求和响应:你可以修改请求头、请求体、查询参数、响应头、响应体等内容,以便在自动化测试过程中模拟不同的场景和测试条件。

  3. 请求过滤:SeleniumWire 支持根据 URL、请求方法、请求头等条件对请求进行过滤,使得你可以只关注感兴趣的请求,忽略其他请求。

  4. 请求记录和导出:SeleniumWire 可以记录浏览器发送和接收的所有网络请求,并且支持将请求记录导出为 HAR 文件,方便分析和调试。

  5. 网络性能分析:通过拦截和记录网络请求,你可以对页面加载时间、资源加载情况等进行性能分析和优化。

  6. 无需修改浏览器配置:SeleniumWire 是在 Selenium WebDriver 的基础上扩展的,因此无需额外的浏览器配置或插件即可使用。

以下是一个简单的示例代码,演示了如何使用 SeleniumWire 进行网络请求的拦截和修改:

from seleniumwire import webdriver

# 创建 Chrome WebDriver,并将日志级别设置为 INFO,用于查看网络请求日志
driver = webdriver.Chrome(seleniumwire_options={'log_level': 0})

# 访问目标网页
driver.get('https://www.example.com')

# 获取所有的网络请求
for request in driver.requests:
    if request.response:
        print(request.url, request.response.status_code)

# 关闭 WebDriver
driver.quit()

在上述示例代码中,创建了一个 Chrome WebDriver,并使用 seleniumwire_options 参数将日志级别设置为 0(INFO),以便在控制台查看网络请求日志。然后,我们访问了一个网页,并获取了所有的网络请求,打印了请求的 URL 和响应的状态码。

总体而言,SeleniumWire 提供了强大的网络请求拦截和修改功能,可以更灵活地控制和分析浏览器与服务器之间的通信过程。它在自动化测试、网络性能分析和数据采集等场景中都有很多实用价值。

安装

pip install selenium-wire

创建web driver

确保你从seleniumwire导入webdriver库:

from seleniumwire import webdriver

然后,只需像直接使用Selenium一样实例化WebDriver。你可以传递任何所需的功能或浏览器特定选项,比如可执行路径、无头模式等。Selenium Wire也有自己的选项,可以通过seleniumwire_options属性传递进去。

# Create the driver with no options (use defaults)
driver = webdriver.Chrome()

# Or create using browser specific options and/or seleniumwire_options options
driver = webdriver.Chrome(
    options = webdriver.ChromeOptions(...),
    seleniumwire_options={...}
)

设置代理

options = {
    'proxy': {
        'http': 'http://username:password@host:port', 
        'https': 'https://username:password@host:port',
    }
}
browser = webdriver.Chrome(path_to_driver, seleniumwire_options=options)

而且最最最最最最重要的是,它支持热加载代理,如下,内部实现了proxy的setter和getter:

@property
def proxy(self) -> Dict[str, Any]:
   	...
    return conf

@proxy.setter
def proxy(self, proxy_conf: Dict[str, Any]):
    ...

牛刀小试:

from seleniumwire import webdriver
proxy = next_proxy()
options = {
    'proxy': {
        'http': proxy["http"],
        'https': proxy["https"],
    }
}

browser = webdriver.Chrome(seleniumwire_options=options)
browser.get("https://myip.ipip.net/")
print(browser.proxy)
print(browser.page_source)

proxy = next_proxy()
browser.proxy = {"http": proxy["http"], "https": proxy["https"]}
browser.get("https://myip.ipip.net/")
print(browser.proxy)
print(browser.page_source)

--------------------------------------------------------------------
当前 IP:xxxxxxxx  来自于:中国 安徽
当前 IP:xxxxxxxx  来自于:中国 陕西

反屏蔽

我们使用https://bot.sannysoft.com/来查看请求是否完全被判定为用户浏览器:
在这里插入图片描述
先不加任何参数直接访,100%会被检测出来web driver

from seleniumwire import webdriver
browser = webdriver.Chrome()
browser.get("https://bot.sannysoft.com/")
# 此处用于阻塞解释器
input(">>>")

在这里插入图片描述
除了我关于selenium的另一篇博客:https://blog.csdn.net/General_zy/article/details/128712331 介绍的反屏蔽方法,此处再介绍一些方法:
(或者也可以使用另一个库:pip install undetected-chromedriver)

修改window.navigator.webdriver关键字返回结果

options = webdriver.ChromeOptions()
# 此步骤很重要,设置为开发者模式,防止被各大网站识别出来使用了Selenium
driver = webdriver.Chrome(options=options)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
    Object.defineProperty(navigator, 'webdriver', {
      get: () => undefined
    })
    """
})

options追加参数

add_experimental_optionadd_argument 都是用于向 ChromeOptions 添加参数的方法,但它们在使用上有一些区别:

  1. add_experimental_option

    • 用途:用于向 ChromeOptions 添加一些实验性的选项或特性。
    • 适用对象:通常用于添加一些不常用的或特定于浏览器版本的选项。
    • 示例:在 Selenium 中,使用 add_experimental_option 可以添加一些实验性的 Chrome 参数,比如启用/禁用某些功能、设置浏览器性能优化选项等。
  2. add_argument

    • 用途:用于向 ChromeOptions 添加普通的命令行选项或配置参数。
    • 适用对象:通常用于添加一些常规的浏览器配置参数,比如设置 User-Agent、启用/禁用 JavaScript、设置代理服务器等。
    • 示例:在 Selenium 中,使用 add_argument 可以添加一些普通的 Chrome 命令行选项,比如设置 User-Agent,可以使用 options.add_argument("--user-agent=Your_User_Agent")

总的来说,add_experimental_option 更多用于添加一些不常用或实验性的选项,而 add_argument 则更常用于添加普通的浏览器配置参数。在实际使用中,可以根据具体需求选择使用哪种方法。

通过 options = webdriver.ChromeOptions() 来添加参数的方式主要是在 Selenium 中模拟浏览器操作,以尽量避免被网站屏蔽。以下是一些常用参数和选项的介绍:

  1. User-Agent:通过 options.add_argument("--user-agent=Your_User_Agent") 可以设置自定义的 User-Agent 字段,以伪装成不同的浏览器或设备。

  2. 代理:通过 options.add_argument("--proxy-server=your_proxy_ip:your_proxy_port") 可以设置代理服务器,通过不同的代理 IP 来访问网站,避免频繁使用相同的 IP 地址。

  3. 禁用图片加载:通过 options.add_argument("--blink-settings=imagesEnabled=false") 可以禁用图片加载,减少页面请求,提高访问速度。

  4. 禁用 JavaScript:通过 options.add_argument("--disable-javascript") 可以禁用 JavaScript 的执行,有些网站的屏蔽机制可能依赖于 JavaScript 的操作。

  5. 无头模式:通过 options.add_argument("--headless") 可以启用 Chrome 的无头模式,即在后台运行 Chrome,无界面显示,模拟真实浏览器行为。

  6. 禁用 GPU 加速:通过 options.add_argument("--disable-gpu") 可以禁用 GPU 加速,有些网站可能会检测 GPU 信息,通过禁用 GPU 可以降低被检测的概率。

  7. 禁用扩展插件:通过 options.add_argument("--disable-extensions") 可以禁用 Chrome 的扩展插件,减少特定插件对网站访问的影响。

  8. 禁用自动化提示:通过 options.add_experimental_option("excludeSwitches", ["enable-automation"]) 可以禁用自动化提示,模拟真实用户行为。

  9. options.add_argument(‘–ignore-certificate-errors’),options.add_argument(‘–ignore-ssl-errors’)忽略 SSL 证书错误和 SSL 错误,用于处理在使用自签名证书或过期证书的网站时避免弹出警告。

  10. options.add_argument(‘–disable-logging’),options.add_argument(‘–log-level=3’) 禁用日志记录,以减少日志文件的输出。

options = webdriver.ChromeOptions()
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    options.add_experimental_option('useAutomationExtension', False)
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_argument("--disable-gpu")
    browser = webdriver.Chrome(options=options)
    with open('stealth.min.js') as f:
        js = f.read()
    browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
        'source': js
    })

    browser.get("https://bot.sannysoft.com/")
    # 此处用于阻塞解释器
    input(">>>")

在这里插入图片描述

pyppeteer

是 Puppeteer 的 Python 版本的实现,但他不是 Google 开发的,是一位来自于日本的工程师依据 Puppeteer 的一些功能开发出来的非官方版本。

在 Pyppetter 中,实际上它背后也是有一个类似 Chrome 浏览器的 Chromium 浏览器在执行一些动作进行网页渲染。

Chromium 是谷歌为了研发 Chrome 而启动的项目,是完全开源的。二者基于相同的源代码构建,Chrome 所有的新功能都会先在 Chromium 上实现,待验证稳定后才会移植,因此 Chromium 的版本更新频率更高,也会包含很多新的功能,但作为一款独立的浏览器,Chromium 的用户群体要小众得多。两款浏览器“同根同源”,它们有着同样的 Logo,但配色不同,Chrome 由蓝红绿黄四种颜色组成,而 Chromium 由不同深度的蓝色构成。

在这里插入图片描述

puppeteer

Puppeteer是一个基于Node.js的库,它提供了一个高级API,可以通过DevTools协议控制Chrome/Chromium浏览器。Puppeteer默认以无界面模式运行,但可以配置为在完整(“headful”)Chrome/Chromium浏览器中运行。

中文文档:https://puppeteer.bootcss.com/

安装

pip install pyppeteer

官方github库提示:

注意:此存储库未维护,并且已经很长时间没有进行任何重大更改。请考虑使用 playwright-python 作为替代方案。

如果您希望对此代码进行全面更新,请与我联系。

pyppeteer的语法和puppeteer相同,可以参考puppeteer文档。

快速入门

pyppeteer是基于asyncio的,所以需要引入async,await关键字:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('https://example.com')
    await page.screenshot({'path': 'example.png'})
    await browser.close()

# python3.10后run函数成为一个稳定的版本
# 推荐使用run函数
asyncio.run(main())

更多demo:https://www.jianshu.com/p/f1a8fb7037d7

在页面上执行Js:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('https://example.com')
    await page.screenshot({'path': 'example.png'})

    dimensions = await page.evaluate('''() => {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            deviceScaleFactor: window.devicePixelRatio,
        }
    }''')

    print(dimensions)
    # >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
    await browser.close()

asyncio.run(main())

参数配置

属性参数描述
executablePathstrchrome.exe运行的路径
ignorehttpserrrorsbool忽略https错误,默认false
headlessboolTrue 开始无头浏览器 False关闭无头
dumpiobool设置True 解决浏览器多开卡死 (没有测试过)
下面是args的参数设置下面是args的参数设置下面是args的参数设置
–disable-infobars-关闭自动化提示框
–window-size=1920,1080str设置浏览器大小吗,1920是宽,1080是宽
–log-level=30str日志保存等级, 建议设置越好越好,要不然生成的日志占用的空间会很大 30为warning级别
–start-maximized-窗口最大化模式
–proxy-server=http://localhost:1080str设置代理
userDataDir=D:\userData\str用户文件保存地址

其他参数:

  • ignoreHTTPSErrors (bool): 是否要忽略 HTTPS 的错误,默认是 False。
  • headless (bool): 是否启用 Headless 模式,即无界面模式,如果 devtools 这个参数是 True 的话,那么该参数就会被设置为 False,否则为 True,即默认是开启无界面模式的。
  • executablePath (str): 可执行文件的路径,如果指定之后就不需要使用默认的 Chromium 了,可以指定为已有的 Chrome 或 Chromium。
  • slowMo (int|float): 通过传入指定的时间,可以减缓 Pyppeteer 的一些模拟操作。
  • args (List[str]): 在执行过程中可以传入的额外参数。
  • ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默认参数,如果使用了这个参数,那么最好通过 args 参数来设定一些参数,否则可能会出现一些意想不到的问题。这个参数相对比较危险,慎用。
  • handleSIGINT (bool): 是否响应 SIGINT 信号,也就是可以使用 Ctrl + C 来终止浏览器程序,默认是 True。
  • handleSIGTERM (bool): 是否响应 SIGTERM 信号,一般是 kill 命令,默认是 True。
  • handleSIGHUP (bool): 是否响应 SIGHUP 信号,即挂起信号,比如终端退出操作,默认是 True。
  • dumpio (bool): 是否将 Pyppeteer 的输出内容传给 process.stdout 和 process.stderr 对象,默认是 False。
  • userDataDir (str): 即用户数据文件夹,即可以保留一些个性化配置和操作记录。
  • env (dict): 环境变量,可以通过字典形式传入。
  • devtools (bool): 是否为每一个页面自动开启调试工具,默认是 False。如果这个参数设置为 True,那么 headless 参数就会无效,会被强制设置为 False。
  • logLevel (int|str): 日志级别,默认和 root logger 对象的级别相同。
  • autoClose (bool): 当一些命令执行完之后,是否自动关闭浏览器,默认是 True。
  • loop (asyncio.AbstractEventLoop): 时间循环对象。
async def main():
    proxy = next_proxy()
    # 创建浏览器启动选项
    browser_options = {
        'args': [
            # f'--proxy-server={proxy.get("https")}',  # 设置代理,替换为你的代理IP和端口
            '--no-sandbox',  # 非沙盒模式,
            '--disable-infobars',  # 关闭自动化提示框
            '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',
        ],
        'headless': True  # 无头模式,不显示浏览器窗口
    }

    # 启动浏览器并创建页面
    browser = await launch(options=browser_options)
    page = await browser.newPage()
    resp = await page.goto('https://myip.ipip.net/')
    print(await resp.text())
    await browser.close()


if __name__ == '__main__':
    # test()
    # test_with_proxy()
    asyncio.run(main())

注意,pyppeteer也只支持无认证的代理。

隐藏浏览器特征

pyppeteer跟selenium一样会有浏览器特征,所以需要修改,隐藏特征防止被识别。

主要有下面两点:

  1. 去除浏览器自动化参数 --enable-automation
  2. 去除window.navigator.webdriver等检测
from pyppeteer import launcher
# 第一步 去除浏览器自动化参数
# 必须在 from pyppeteer import launch 前去除参数
# 去除自动化 启动参数
launcher.AUTOMATION_ARGS.remove("--enable-automation")


# 第二步,修改 navigator.webdriver检测
# 其实各种网站的检测js是不一样的,这是比较通用的。有的网站会检测运行的电脑运行系统,cpu核心数量,鼠标运行轨迹等等。
# 反爬js
js_text = """
	() =>{ 
	Object.defineProperties(navigator,{ webdriver:{ get: () => false } });
	window.navigator.chrome = { runtime: {},  };
	Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
	Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], });
	}
"""
await page.evaluateOnNewDocument(js_text)  # 本页刷新后值不变,自动执行js

拦截请求

from pyppeteer import launcher

launcher.AUTOMATION_ARGS.remove("--enable-automation")

from pyppeteer import launch

from pyppeteer.network_manager import Request, Response


async def intercept_request(req:Request):
    await req.continue_()  # 请求,看源码可以重新编写请求


async def intercept_response(res:Response):
    if 'ext2020/apub/json/prevent.new' in res.url:
        print('拦截到请求')
        json_text = await res.text()
        title_li = jsonpath(json.loads(json_text), '$..title')
        for title in title_li:
            print(title)
    pass


async def main():
    # 浏览器 启动参数
    start_parm = {
        # 启动chrome的路径
        "executablePath": r"C:\Users\yq\AppData\Local\pyppeteer\pyppeteer\local-chromium\722234\chrome-win\chrome.exe",
        # 关闭无头浏览器 默认是无头启动的
        "headless": False,
        "args": [
            '--disable-infobars',  # 关闭自动化提示框
            # '--no-sandbox',  # 关闭沙盒模式
            '--start-maximized',  # 窗口最大化模式
            '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',
            # UA

        ],

    }
    # 创建浏览器对象,可以传入 字典形式参数
    browser = await launch(**start_parm)

    # 创建一个页面对象, 页面操作在该对象上执行
    page = await browser.newPage()
    await page.setJavaScriptEnabled(enabled=True)

    # 启用拦截器
    await page.setRequestInterception(True)
    page.on('request', intercept_request) 
    page.on('response', intercept_response)
    
    js_text = """
    () =>{ 
        Object.defineProperties(navigator,{ webdriver:{ get: () => false } });
        window.navigator.chrome = { runtime: {},  };
        Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
        Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], });
     }
        """
    await page.evaluateOnNewDocument(js_text)  # 本页刷新后值不变,自动执行js
    await page.goto('https://news.qq.com/')  # 页面跳转


    await browser.close()


asyncio.run()   # 创建异步池并执行main函数。

更多文档&博客

  1. https://blog.csdn.net/qq_27900321/article/details/129402456
  2. https://github.com/pyppeteer/pyppeteer

Playwright

迁移到 Playwright的原因:

  1. Pyppeteer 是第三方的,好久没有更新了,bug 也不少
  2. Puppeteer 的原生 JS 版本本身就很不稳定,亲测 Playwright 更稳定一些
  3. Playwright 原生支持 Python,而且支持 Google Chrome/Firefox/Safari 三大浏览器

Playwright 是微软出品的浏览器自动化工具,代码质量应该是有足够保证的。而且它还官方支持同步版的 Python API, 同时支持三大浏览器。

安装

  1. pip install playwright
  2. 安装浏览器:python -m playwright install

文档地址:https://playwright.dev/python/docs/api/class-playwright

快速入门

在这里插入图片描述

同步接口:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
	# 打开三种浏览器并截屏
    for browser_type in [p.chromium, p.firefox, p.webkit]:
        browser = browser_type.launch()
        page = browser.new_page()
        page.goto('http://playwright.dev')
        page.screenshot(path=f'example-{browser_type.name}.png')
        browser.close()

异步接口:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        for browser_type in [p.chromium, p.firefox, p.webkit]:
            browser = await browser_type.launch()
            page = await browser.new_page()
            await page.goto('http://playwright.dev')
            await page.screenshot(path=f'example-{browser_type.name}.png')
            await browser.close()

asyncio.run(main())

新概念:Context

和 Puppeteer 不同的是,Playwright 新增了 context 的概念,每个 context 就像是一个独立的匿名模式会话,非常轻量,但是又完全隔离。比如说,可以在两个 context 中登录两个不同的账号,也可以在两个 context 中使用不同的代理。

通过 context 还可以设置 viewport, user_agent 等。

context = browser.new_context(
  user_agent='My user agent'
)
context = browser.new_context(
  viewport={ 'width': 1280, 'height': 1024 }
)
context = browser.new_context(
    http_credentials={"username": "bill", "password": "pa55w0rd"}
)

# new_context 其他几个比较有用的选项:
ignore_https_errors=False
proxy={"server": "http://example.com:3128", "bypass": ".example.com", "username": "", "password": ""}
extra_http_headers={"X-Header": ""}

context 中有一个很有用的函数context.add_init_script, 可以让我们设定在调用 context.new_page 的时候在页面中执行的脚本。

# hook 新建页面中的 Math.random 函数,总是返回 42
context.add_init_script(script="Math.random = () => 42;")
# 或者写在一个文件里
context.add_init_script(path="preload.js")

还可以使用 context.expose_binding 和 context.expose_function 来把 Python 函数暴露到页面中,还是使用 add_init_script 暴露 JS 函数方便一些。

和 Puppeteer 一样,Playwright 的核心概念依然是 page, 核心 API 几乎都是 page 对象的方法。可以通过 context 来创建 page.

页面基本操作

按照官网文档,调用 page.goto(url) 后页面加载过程:

  1. 设定 url
  2. 通过网络加载解析页面
  3. 触发 page.on(“domcontentloaded”) 事件
  4. 执行页面的 js 脚本,加载静态资源
  5. 触发 page.on(“laod”) 事件
  6. 页面执行动态加载的脚本
  7. 当 500ms 都没有新的网络请求的时候,触发 networkidle 事件

page.goto(url) 会跳转到一个新的链接。默认情况下 Playwright 会等待到 load 状态。如果我们不关心加载的 CSS 图片等信息,可以改为等待到 domcontentloaded 状态,如果页面是 ajax 加载,那么我们需要等待到 networkidle 状态。如果 networkidle 也不合适的话,可以采用 page.wait_for_selector 等待某个元素出现。不过对于 click 等操作会自动等待。

page.goto(url, referer="", timeout=30, wait_until="domcontentloaded|load|networkidle")

Playwright 会自动等待元素处于可操作的稳定状态。当然也可以用 page.wait_for_* 函数来手工等待:

page.wait_for_event("event", event_predict, timeout)
page.wait_for_function(js_function)
page.wait_for_load_state(state="domcontentloaded|load|networkidle", timeout)
page.wait_for_selector(selector, timeout)
page.wait_for_timeout(timeout)  # 不推荐使用

对页面的操作方法主要有:

# selector 指的是 CSS 等表达式
page.click(selector)
page.fill(selector, value)  # 在 input 中填充值

# 例子
page.click("#search")

获取页面中的数据的主要方法有:

page.url  # url
page.title()  # title
page.content()  # 获取页面全文
page.inner_text(selector)  # element.inner_text()
page.inner_html(selector)
page.text_content(selector)
page.get_attribute(selector, attr)

# eval_on_selector 用于获取 DOM 中的值
page.eval_on_selector(selector, js_expression)
# 比如:
search_value = page.eval_on_selector("#search", "el => el.value")

# evaluate 用于获取页面中 JS 中的数据,比如说可以读取 window 中的值
result = page.evaluate("([x, y]) => Promise.resolve(x * y)", [7, 8])
print(result) # prints "56"

选择器表达式

在上面的代码中,使用了 CSS 表达式(比如#button)来选取元素。实际上,Playwright 还支持 XPath 和自己定义的两种简单表达式,并且是自动识别的。

# 通过文本选择元素,这是 Playwright 自定义的一种表达式
page.click("text=login")

# 直接通过 id 选择
page.click("id=login")

# 通过 CSS 选择元素
page.click("#search")
# 除了常用的 CSS 表达式外,Playwright 还支持了几个新的伪类
# :has 表示包含某个元素的元素
page.click("article:has(div.prome)")
# :is 用来对自身做断言
page.click("button:is(:text('sign in'), :text('log in'))")
# :text 表示包含某个文本的元素
page.click("button:text('Sign in')")  # 包含
page.click("button:text-is('Sign is')")  # 严格匹配
page.click("button:text-matches('\w+')")  # 正则
# 还可以根据方位匹配
page.click("button:right-of(#search)")  # 右边
page.click("button:left-of(#search)")  # 左边
page.click("button:above(#search)")  # 上边
page.click("button:below(#search)")  # 下边
page.click("button:near(#search)")  # 50px 之内的元素

# 通过 XPath 选择
page.click("//button[@id='search'])")
# 所有 // 或者 .. 开头的表达式都会默认为 XPath 表达式

对于 CSS 表达式,还可以添加前缀css=来显式指定,比如说 css=.login 就相当于 .login.

除了上面介绍的四种表达式以外,Playwright 还支持使用 >> 组合表达式,也就是混合使用四种表达式。

page.click('css=nav >> text=Login')

复用 Cookies 等认证信息

在 Puppeteer 中,复用 Cookies 也是一个老大难问题了。这个是 Playwright 特别方便的一点,他可以直接导出 Cookies 和 LocalStorage, 然后在新的 Context 中使用。

# 保存状态
import json
storage = context.storage_state()
with open("state.json", "w") as f:
    f.write(json.dumps(storage))

# 加载状态
with open("state.json") as f:
    storage_state = json.loads(f.read())
context = browser.new_context(storage_state=storage_state)

监听事件

通过 page.on(event, fn) 可以来注册对应事件的处理函数:

def log_request(intercepted_request):
    print("a request was made:", intercepted_request.url)
page.on("request", log_request)
# sometime later...
page.remove_listener("request", log_request)

其中比较重要的就是 request 和 response 两个事件

拦截更改网络请求

可以通过 page.on(“request”) 和 page.on(“response”) 来监听请求和响应事件。

from playwright.sync_api import sync_playwright as playwright

def run(pw):
    browser = pw.webkit.launch()
    page = browser.new_page()
    # Subscribe to "request" and "response" events.
    page.on("request", lambda request: print(">>", request.method, request.url))
    page.on("response", lambda response: print("<<", response.status, response.url))
    page.goto("https://example.com")
    browser.close()

with playwright() as pw:
    run(pw)

其中 request 和 response 的属性和方法,可以查阅文档:https://playwright.dev/python/docs/api/class-request

通过 context.route, 还可以伪造修改拦截请求等。比如说,拦截所有的图片请求以减少带宽占用:

context = browser.new_context()
page = context.new_page()
# route 的参数默认是通配符,也可以传递编译好的正则表达式对象
context.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
context.route(re.compile(r"(\.png$)|(\.jpg$)"), lambda route: route.abort())
page.goto("https://example.com")
browser.close()

其中 route 对象的相关属性和方法,可以查阅文档:https://playwright.dev/python/docs/api/class-route

灵活设置代理

Playwright 还可以很方便地设置代理。Puppeteer 在打开浏览器之后就无法在更改代理了,对于爬虫类应用非常不友好,而 Playwright 可以通过 Context 设置代理,这样就非常轻量,不用为了切换代理而重启浏览器。

context = browser.new_context(
    proxy={"server": "http://example.com:3128", "bypass": ".example.com", "username": "", "password": ""}
)

示例:

from playwright.sync_api import sync_playwright

if __name__ == '__main__':
    with sync_playwright() as p:
        browser = p.chromium.launch(proxy={"server": "per-context"})

        pxy = next_proxy()
        context1 = browser.new_context(
            proxy={"server": pxy["http"], "username": "", "password": ""})
        page1 = context1.new_page()
        page1.goto("https://myip.ipip.net/")
        print(page1.content())

        pxy2 = next_proxy()
        context2 = browser.new_context(
            proxy={"server": pxy2["http"], "username": "", "password": ""})
        page2 = context2.new_page()
        page2.goto("https://myip.ipip.net/")
        print(page2.content())

        browser.close()

在这里插入图片描述

杀手级功能:录制操作直接生成代码

Playwright 的命令行还内置了一个有趣的功能:可以通过录制你的点击操作,直接生成 Python 代码。

python -m playwright codegen http://example.com/

Playwright 还有很多命令行功能,比如生成截图等等,可以通过 python -m playwright -h查看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/831088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wolfSSL5.6.3移植至ZYNQ(XC7Z010)记录

上篇文章主要描述了如何从源工程中将configure.ac转变为configure&#xff0c;并成功使其在Ubuntu上运行&#xff0c;此篇主要描述将其移植到嵌入式arm开发板上&#xff08;在官方手册中有列举支持的硬件平台架构&#xff09;。 交叉编译 1. 解压源工程代码&#xff1b; 2.进…

XUbuntu22.04之统计屏幕使用时间activitywatch(一百八十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Node.js-http模块服务端请求与响应操作,请求报文与响应报文

简单案例创建HTTP服务端&#xff1a; // 导入 http 模块 const http require("http"); // 创建服务对象 const server http.createServer((request, response) > {// 设置编码格式&#xff0c;解决中文乱码问题response.setHeader("content-type", &…

WAF独木难支 RASP与ADR将成应用安全防护2.0时代新宠

曾几何时&#xff0c;黑客攻击大多通过网络层进行&#xff0c;但随着基于网络层的基础安全防护措施趋于严密&#xff0c;防火墙、入侵防御、防病毒等安全软硬件构建起了相对完善的防护体系&#xff0c;想再从网络层钻空子的难度增大。如今&#xff0c;黑客攻击从网络层转入Web为…

学无止境·运维高阶③(Mysqldump脚本)

Mysqldump脚本 1、详细脚本2、执行 1、详细脚本 #!/bin/bash mysql_cmd‘-uroot -pRedHat123’ exclude_db‘information_schema|performance_schema|sys’ bak_path/backup/db mysql m y s q l c m d − e ′ s h o w d a t a b a s e s ′ − N ∣ e g r e p − v " {m…

网络电视盒子哪个品牌好?2023最新性价比电视盒子排行榜

电视盒子是电视机成本最低的更新方案&#xff0c;但面对众多品牌和机型时往往不知道网络电视盒子哪个品牌好&#xff0c;踩雷的消费者非常多&#xff0c;想要小编分享电视盒子推荐&#xff0c;今天带新手们了解一下业内最新的性价比电视盒子排行榜&#xff0c;看看哪些电视盒子…

Docker 安装 Tomcat

目录 一、查看 tomcat 版本 二、拉取 Tomcat Docker 镜像 三、创建 Tomcat 容器 四、访问 Tomcat 五、停止和启动容器 一、查看 tomcat 版本 访问 tomcat 镜像库地址&#xff1a;https://hub.docker.com/_/tomcat&#xff0c;可以通过 Tags 查看其他版本的 tomcat; 二、拉…

聊聊我的故事-悲惨的童年

目录 前言一、介绍二、17年回顾1.出生2.上幼儿园3.上小学4.上初中 高中总结 前言 本人是06年生的&#xff0c;快18了&#xff0c; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、介绍 本人已经17了&#xff0c;在这17年过的很悲惨&#xff0c;也…

【Linux多线程】设计一个线程池(代码+讲解)

线程池 Lock.hpp说明 Task.hpp代码代码说明 Threadpool.hpp代码说明 Threadpool.cc代码说明 Log.hpp代码说明 Makefile运行结果 Lock.hpp #pragma once#include <iostream> #include <pthread.h>class Mutex { public:Mutex(){pthread_mutex_init(&lock_, nul…

百度离线地图(包含轨迹回放)

先来个效果图&#xff08;demo没那么仔细&#xff09; 现在下载资源包文件放至static文件内 如果需要新的瓦片则添加至static/tiles(我用的是png也可以是jpg) 在此处修改即可 但是&#xff0c;最好还是放在服务器内&#xff0c;因为瓦片很多&#xff0c;项目是无法运行的&…

FTP Cadence中心库的创建与配置 (中)

在上节介绍的ftp服务器根目录下 一、创建一个文件夹 Cadence_lib&#xff0c;并把其设置为 共享。 共享给用户hw 和用户layout&#xff0c;并设置对应权限。 点击“共享”按钮。 二、在Cadence_lib文件夹下&#xff0c;添加如下文件夹&#xff0c;及文件。 其中 “00DataShee…

Vue-函数式组件

最近在开发项目的时候&#xff0c;定制了一个公司内部样式的Modal模态框组件。 Modal组件伪代码 <!-- Modal/index.vue--> <template><div class"modal-container" id"modalContainer"><!-- Modal Content --><div class&quo…

MySQL的索引使用的数据结构,事务知识

一、索引的数据结构&#x1f338; 索引的数据结构&#xff08;非常重要&#xff09; mysql的索引的数据结构&#xff0c;并非定式&#xff01;&#xff01;&#xff01;取决于MySQL使用哪个存储引擎 数据库这块组织数据使用的数据结构是在硬盘上的。我们平时写的代码是存在内存…

编译原理陈火旺第三版第九章课后题答案

下面的答案仅供参考&#xff01; 1. 有哪些存储分配策略&#xff1f;并叙述何时用何种存储分配策略&#xff1f; 答&#xff1a;存储分配策略分为静态分配策略和动态分配策略两大类,而动态分配策略又可分为栈式动态分配策略和堆式动态分配策略两类。 在一个的具体的编译…

软件测试需求分析的常用方法

软件测试需求分析时&#xff0c;应要求产品人员对需求进行讲解&#xff0c;并使用相对应的方法进行科学分析&#xff0c;否则无法保障软件测试的完整性和科学性&#xff0c;从而造成在项目中后期Bug频出、风险增大等问题。 而常用的测试需求分析的方法&#xff1a; 1、功能分解…

腾讯云MSS多项能力获IDC五星评价,综合实力位列第一

近日&#xff0c;IDC发布了《IDC Technology Assessement: 中国公有云托管安全服务能力&#xff0c;2023》报告&#xff08;以下简称“报告”&#xff09;。腾讯云安全托管服务MSS凭借多年的技术积累和出色的服务能力&#xff0c;在报告的专家能力、漏洞及威胁检测、事件分析、…

二叉搜索树(BST)的模拟实现

序言&#xff1a; 构造一棵二叉排序树的目的并不是为了排序&#xff0c;而是为了提高查找效率、插入和删除关键字的速度&#xff0c;同时二叉搜索树的这种非线性结构也有利于插入和删除的实现。 目录 &#xff08;一&#xff09;BST的定义 &#xff08;二&#xff09;二叉搜…

刘汝佳の树状数组详解

引入 二叉索引树&#xff0c;也叫树状数组是一种便于数组单点修改和区间求和的数据结构 主要根据下标的lowbit值来建树 至于lowbit(x)&#xff0c;则是(x)&(-(x))&#xff0c;也就是一个二进制数从右边数第一个1代表的数 #define lowbit(x) ((x)&(-(x)))基础树状数组…

GF(2)上矩阵秩的快速计算

https://github.com/mhostetter/galois/issues

uniapp发布插件显示components/xxx文件没找到,插件格式不正确

uniapp发布插件显示components/xxx文件没找到&#xff0c;插件格式不正确 将插件文件这样一起选中&#xff0c;然后右键压缩成zip文件&#xff0c;而不是外层文件压缩