当前位置:首页>python>Python Playwright模块深度解析:现代浏览器自动化测试框架全攻略

Python Playwright模块深度解析:现代浏览器自动化测试框架全攻略

  • 2026-07-02 00:44:51
Python Playwright模块深度解析:现代浏览器自动化测试框架全攻略

这就是今天要聊的主角——微软出品的 Playwright,一个让 Web 自动化测试从"体力活"变成"技术活"的现代框架。

 01. 

一、浏览器自动化的前世今生

要理解 Playwright 带来的变革,得先说说这个领域的老前辈们。

Selenium 诞生于 2004 年,靠着 WebDriver 协议一路打天下,至今仍是市场占有率最高的选择。它的原理是通过浏览器驱动(ChromeDriver、GeckoDriver)作为中间层,向浏览器发送指令。这个架构在当时很合理,但问题也很明显:

协议开销大。每次操作都要经过驱动中转,延迟累积起来很可观。

版本强耦合。Chrome 升级了,ChromeDriver 必须跟着升,否则各种奇奇怪怪的兼容问题。

等待机制反人类。开发者需要手动处理隐式等待、显式等待,写出来的代码又臭又长。

我见过最夸张的 Selenium 用例,光等待逻辑就占了 60% 的代码量。测试用例本身反而被淹没了。

Cypress 在 2015 年杀出来,带来了"开发者体验优先"的理念。它的自动等待、智能断言确实让人眼前一亮。但问题也很致命:只支持 Chromium 内核,多标签页操作像噩梦一样,文件下载更是老大难。

直到 2020 年,微软扔出了 Playwright 这个王炸。

团队背景很有意思——核心成员来自 Puppeteer(Google 开发的 Chrome 自动化库)。他们不满足于 Puppeteer 只支持 Chromium 的局限,决心做真正跨浏览器的自动化框架。靠着对浏览器内核的深刻理解,Playwright 从第一天起就走上了不同的技术路线。

 02. 

二、Playwright 的核心优势

1.1 自动等待:告别所有 sleep

这是 Playwright 最核心的特性,没有之一。

我们之前写 Selenium,最痛苦的就是等待。页面加载要等、接口返回要等、动画完成要等。不写等待,脚本分分钟报错;写了等待,要么等太久拖慢速度,要么等不够还是报错。

Playwright 的做法是:所有操作都内置自动等待机制。

当你执行 page.click("#submit") 时,Playwright 会在底层自动完成这些检查:

元素是否已经挂载到 DOM 树(Attached)

元素是否可见,没有被 display:none 或 visibility:hidden 隐藏(Visible)

元素的位置是否稳定,没有还在晃动的动画(Stable)

元素是否可交互,没有被 disabled 属性禁用(Enabled)

只有当这些条件全部满足,Playwright 才会执行点击操作。如果 30 秒内元素始终不满足条件,才会抛出超时错误。

我第一次看到这个机制时,心里是存疑的:"真的什么都不用管?"结果我专门写了一个测试,故意让按钮延迟 5 秒才出现,跑了一下午,脚本居然真的乖乖等够了 5 秒才点击。测试通过的那一刻,我直接对 Playwright 路转粉。

1.2 跨浏览器统一 API

Selenium 给人最深的印象就是"同一个功能,不同浏览器写法可能不一样"。Chromium 有 Chromium 的特性,Firefox 有 Firefox 的坑,Safari 更是独树一帜。维护多浏览器测试时,那个酸爽谁用谁知道。

Playwright 统一了所有浏览器的 API。无论你用 Chromium、Firefox 还是 WebKit,代码写法完全一致:

PYTHON# Chromiumbrowser = p.chromium.launch()# Firefoxbrowser = p.firefox.launch()# WebKitbrowser = p.webkit.launch()

我之前做过一个电商项目,需要在 Chrome、Firefox、Safari 上跑兼容性测试。用 Selenium 时,三套代码各自为政,光维护浏览器兼容性就占了我 30% 的时间。迁移到 Playwright 后,一套代码跑三个浏览器,测试用例从 900 行砍到 600 行,代码可读性反而更高了。

1.3 语义化定位器

Selenium 的元素定位一直是个痛点。ID 可能变、Class 可能变、XPath 写出来像天书一样难维护。我见过最长的 XPath 定位器://div[@class='container']//form[@id='login-form']//div[contains(@class,'field')]//input[@name='username'],看到这种代码我都替原作者捏把汗。

Playwright 官方推荐了一套语义化定位器,核心原则是"用功能定位,而不是用结构定位":

PYTHON# 根据角色定位,最推荐page.get_by_role("button", name="登录").click()# 根据标签文本定位page.get_by_text("用户名").fill("admin")# 根据输入框的 label 定位page.get_by_label("密码").fill("123456")# 根据占位符定位page.get_by_placeholder("请输入手机号").fill("13800138000")# 根据 alt 文本定位(图片)page.get_by_alt_text("验证码图片").click()

这些定位器的共同特点是:它们描述的是"功能"而非"结构"。只要页面上这个按钮的文字还是"登录",只要这个输入框的 label 还是"用户名",无论前端怎么重构,代码都不用改。

我之前维护一个 Vue 项目,前端团队喜欢频繁改结构。用了 Playwright 的语义化定位器后,每次改版需要调整的测试代码从平均 15 处降到了 2-3 处。我的头发保住了。

1.4 上下文隔离:测试隔离的正确姿势

Playwright 的架构里有一个核心概念叫"Browser Context"。每个 Context 相当于一个全新的浏览器配置文件——独立的 Cookie、独立的 LocalStorage、独立的会话。

这带来了一个巨大的好处:测试隔离

用 Selenium 时,如果测试之间没有做好数据清理,很容易互相影响。A 测试登录了用户甲,B 测试可能继承了这个登录状态,导致奇怪的 Bug。

Playwright 的做法是,每个测试创建一个全新的 Context,测试结束后直接销毁。零污染,零干扰。

PYTHON# 每个测试都这样创建全新的上下文context = browser.new_context()page = context.new_page()# ... 执行测试 ...context.close()  # 测试结束,干净利落

进阶用法是"一次登录,到处复用"。你可以先手动登录一次,把认证状态保存下来,后续所有测试直接加载这个状态,省掉重复登录的麻烦:

PYTHON# 保存登录状态context = browser.new_context()page = context.new_page()# ... 手动登录 ...context.storage_state(path="auth.json")# 后续测试直接加载context = browser.new_context(storage_state="auth.json")page = context.new_page()  # 已经处于登录状态

这个功能在做爬虫时也特别有用。登录一次,后续所有请求都带着登录态,不用每次都走登录流程,效率提升明显。

 03. 

三、Playwright 基础操作

3.1 安装与快速上手

Playwright 的安装比我用过的任何自动化框架都简单。三条命令搞定:

BASHpip install playwrightplaywright install  # 安装浏览器驱动

如果你在国内,网络不太好,可以指定国内镜像加速:

BASHplaywright install --force --with-deps chromium

默认安装 Chromium、Firefox、WebKit 三种浏览器。如果你只需要一种,可以指定:

BASHplaywright install chromium

第一个脚本简单到令人发指:

PYTHONfrom playwright.sync_api import sync_playwrightwith sync_playwright() as p:    browser = p.chromium.launch()  # 启动浏览器    page = browser.new_page()     # 创建新页面    page.goto("https://example.com")  # 访问网址    print(page.title())            # 打印标题    browser.close()                # 关闭浏览器

如果你用的是 asyncio 风格的异步代码,Playwright 也支持:

PYTHONimport asynciofrom playwright.async_api import async_playwrightasync def main():    async with async_playwright() as p:        browser = await p.chromium.launch()        page = await browser.new_page()        await page.goto("https://example.com")        print(await page.title())        await browser.close()asyncio.run(main())

我建议新手先用同步 API,上手更简单。等对框架熟悉了,再根据项目需求决定是否切换到异步。

3.2 页面导航与等待

页面导航是所有自动化操作的基础。Playwright 提供了完善的导航 API:

PYTHON# 基本跳转page.goto("https://example.com")# 等待页面加载完成再继续page.goto("https://example.com", wait_until="networkidle")# 支持的等待策略:# - load: 默认,等待 load 事件# - domcontentloaded: 等待 DOMContentLoaded 事件# - networkidle: 等待网络空闲(无活动请求超过 500ms)# 前进、后退、刷新page.go_back()page.go_forward()page.reload()

wait_until 参数是我最常用的一个。默认的 load 策略有时候页面还在渲染后续内容,自动化脚本就开始点击了,很容易出问题。我一般用 networkidle,虽然会慢一点,但稳定性好很多。

3.3 元素交互

Playwright 的元素交互 API 设计得很优雅,基本覆盖了所有常用场景:

PYTHON# 点击操作page.get_by_role("button", name="提交").click()page.get_by_text("删除").dblclick()  # 双击page.get_by_text("复制").click(button="right")  # 右键# 输入操作page.get_by_label("用户名").fill("admin")  # 填入内容(自动清空)page.get_by_label("搜索").type("hello", delay=100)  # 模拟逐字输入# 选择操作page.select_option("#city", value="beijing")  # 通过 value 选择page.select_option("#city", label="北京")    # 通过显示文本选择page.select_option("#city", index=2)          # 通过索引选择# 勾选/取消勾选page.get_by_role("checkbox", name="同意协议").check()page.get_by_role("checkbox", name="同意协议").uncheck()# 文件上传page.set_input_files("#upload", "path/to/file.pdf")# 键盘操作page.keyboard.press("Enter")page.keyboard.press("Control+A")page.keyboard.press("Control+C")page.keyboard.type("hello world")

我最喜欢的是 fill 和 type 的区别。fill 直接设置值,适合不需要触发输入事件的场景;type 模拟逐字输入,会触发 input 事件,适合需要前端做实时校验的场景。根据业务需求选对方法,能省很多调试时间。

3.4 获取元素信息

自动化不仅要操作元素,有时候还需要"看"页面:

PYTHON# 获取文本内容title = page.locator("h1").inner_text()# 获取元素属性href = page.get_by_role("link").get_attribute("href")# 获取输入框的值value = page.locator("#search").input_value()# 判断元素状态is_visible = page.get_by_role("button").is_visible()is_enabled = page.get_by_role("button").is_enabled()is_checked = page.get_by_role("checkbox").is_checked()

这些方法返回的都是 Python 原生类型,可以直接用在断言里。

 04. 

四、Playwright 高级功能

4.1 网络拦截与 Mock

这是 Playwright 最强大的功能之一,也是我做爬虫时的杀手锏。

你可以拦截浏览器的所有网络请求,修改请求头、伪造响应、阻止某些资源加载:

PYTHON# 拦截图片请求,加快页面加载page.route("**/*.{png,jpg,jpeg,gif}", lambda route: route.abort())# 拦截特定 API,返回假数据def handle_api(route):    route.fulfill(        status=200,        content_type="application/json",        body='{"code": 0, "data": {"name": "测试数据"}}'    )page.route("**/api/user/info", handle_api)# 修改请求头def modify_headers(route):    headers = route.request.headers    headers["X-Custom-Header"] = "custom-value"    route.continue_(headers=headers)page.route("**/*", modify_headers)

测试场景里,这个功能可以用来 Mock 后端接口。前端页面还没接后端?没关系,自己构造假数据,照样跑测试。我之前做一个数据看板项目,后端接口延期了两周,我就是靠 Mock 数据把前端测试跑完了,一点没耽误进度。

爬虫场景里,这个功能更香。有些网站的接口返回 JSON 数据,比解析 HTML 方便 10 倍不止。我爬某电商平台的商品评论,就是直接拦截接口请求,拿到的全是结构化的 JSON,解析效率比 XPath 快了 5 倍。

4.2 等待机制详解

虽然 Playwright 做了自动等待,但有些场景你还是需要手动控制:

PYTHON# 等待元素出现page.wait_for_selector("#result", timeout=5000)# 等待元素可见page.wait_for_selector("#result", state="visible")# 等待元素消失(加载完成)page.wait_for_selector("#loading", state="hidden")# 等待网络空闲page.wait_for_load_state("networkidle")# 等待函数返回 Truepage.wait_for_function("() => document.querySelector('.count').textContent === '100'")# 固定等待(不推荐,但调试时可以用)page.wait_for_timeout(2000)

记住一个原则:能用自动等待解决的问题,千万别用固定等待time.sleep() 在 Playwright 里是禁止使用的,它会阻塞事件循环,导致不可预期的行为。如果必须等待,用 wait_for_timeout,它内部是异步实现,不会出问题。

4.3 截图与录屏

PYTHON# 页面截图page.screenshot(path="screenshot.png")# 整页截图page.screenshot(path="full_page.png", full_page=True)# 元素截图page.locator(".chart").screenshot(path="chart.png")# 录屏(需要开启上下文录制)context = browser.new_context(record_video_dir="videos/")page = context.new_page()# ... 操作 ...context.close()  # 关闭时自动保存视频

我经常用截图功能做测试报告。每个失败的用例自动截一张图,测试报告里直接展示问题页面,比纯文字描述直观多了。

4.4 处理弹窗与新窗口

PYTHON# 处理 alert/confirm/promptdef handle_dialog(dialog):    print(f"弹窗类型: {dialog.type}, 内容: {dialog.message}")    dialog.accept()  # 确认    # dialog.dismiss()  # 取消page.on("dialog", handle_dialog)page.click("#show-alert")# 处理新窗口/标签页with page.context.expect_page() as new_page_info:    page.click("a[target='_blank']")new_page = new_page_info.valueawait new_page.wait_for_load_state()print(await new_page.title())

这个功能做自动化测试时特别有用。点一个链接跳转到新页面,直接拿到新页面的引用,继续操作,比 Selenium 的 switch_to.window() 简洁多了。

4.5 移动端模拟

Playwright 原生支持移动端模拟,不用额外安装什么:

PYTHON# 模拟 iPhone 12context = browser.new_context(    viewport={"width": 390, "height": 844},    user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) ...",    locale="zh-CN",    geolocation={"longitude": 116.28, "latitude": 40.05},  # 经纬度    permissions=["geolocation"]  # 权限)# 或者直接用内置的设备预设from playwright.sync_api import devicescontext = browser.new_context(**devices["iPhone 12"])

这个功能做响应式测试特别方便。同一个测试用例,分别用桌面端和移动端跑一遍,验证不同设备上的表现。

 05. 

五、实战案例:端到端测试

接下来看一个完整的测试案例,演示如何用 Playwright 编写可靠的端到端测试。

场景:测试一个用户登录流程,包含正确登录、错误密码、账户锁定三种情况。

PYTHONfrom playwright.sync_api import sync_playwright, expectdef test_login_success():    """正确登录场景"""    with sync_playwright() as p:        browser = p.chromium.launch()        context = browser.new_context()        page = context.new_page()        # 访问登录页        page.goto("https://demo.playwright.test/login")        # 填写表单        page.get_by_label("用户名").fill("testuser")        page.get_by_label("密码").fill("correct_password")        # 点击登录        page.get_by_role("button", name="登录").click()        # 验证跳转到首页        expect(page).to_have_url("https://demo.playwright.test/dashboard")        # 验证用户名显示        expect(page.get_by_text("欢迎,testuser")).to_be_visible()        context.close()def test_login_wrong_password():    """密码错误场景"""    with sync_playwright() as p:        browser = p.chromium.launch()        context = browser.new_context()        page = context.new_page()        page.goto("https://demo.playwright.test/login")        page.get_by_label("用户名").fill("testuser")        page.get_by_label("密码").fill("wrong_password")        page.get_by_role("button", name="登录").click()        # 验证错误提示出现        expect(page.get_by_text("用户名或密码错误")).to_be_visible()        # 验证还在登录页        expect(page).to_have_url("https://demo.playwright.test/login")        context.close()def test_account_locked():    """账户锁定场景"""    with sync_playwright() as p:        browser = p.chromium.launch()        context = browser.new_context()        page = context.new_page()        page.goto("https://demo.playwright.test/login")        # 模拟连续 5 次输错密码        for _ in range(5):            page.get_by_label("用户名").fill("locked_user")            page.get_by_label("密码").fill("wrong_password")            page.get_by_role("button", name="登录").click()            page.wait_for_timeout(500)  # 短暂等待        # 第 6 次输入正确密码        page.get_by_label("用户名").fill("locked_user")        page.get_by_label("密码").fill("correct_password")        page.get_by_role("button", name="登录").click()        # 验证账户锁定提示        expect(page.get_by_text("账户已被锁定")).to_be_visible()        context.close()

这个测试用例有几个亮点:

使用 expect 断言库,内置自动重试机制。断言会等待条件满足再验证,不用担心时序问题。

使用语义化定位器。get_by_label("用户名") 比 CSS 选择器 .form-control input:nth-child(1) 好维护多了。

测试隔离。每个测试都是全新的 Context,完全独立运行,互不干扰。

断言明确。expect(page).to_have_url(...) 比 assert "dashboard" in page.url 可读性好很多,失败时也更容易定位问题。

 06. 

六、实战案例:爬虫开发

说完测试,再聊一个 Playwright 的另一个主战场——爬虫。

场景:爬取某电商平台的商品列表,提取商品名称、价格、销量。

传统做法是用 requests 库请求页面,然后解析 HTML。但现代网站大量使用 JavaScript 渲染,requests 拿到的可能是一个空壳 Div。Selenium 虽然能执行 JS,但速度慢、占用高、反爬能力弱。

Playwright 兼顾了两边的优点:原生支持 JS 渲染,速度比 Selenium 快 2-3 倍,反爬能力更强。

PYTHONfrom playwright.sync_api import sync_playwrightimport jsondef scrape_products(keyword, max_pages=5):    """爬取电商平台商品列表"""    results = []    with sync_playwright() as p:        browser = p.chromium.launch(headless=True)  # 无头模式        context = browser.new_context(            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"        )        # 拦截 API 请求,直接拿 JSON 数据        api_data = []        def handle_response(response):            if "/api/product/list" in response.url and response.status == 200:                try:                    data = response.json()                    api_data.append(data)                except:                    pass        page = context.new_page()        page.on("response", handle_response)        for page_num in range(1, max_pages + 1):            print(f"正在爬取第 {page_num} 页...")            # 访问搜索页            page.goto(                f"https://search.example.com/?keyword={keyword}&page={page_num}",                wait_until="networkidle"            )            # 等待商品列表加载            page.wait_for_selector(".product-item", timeout=10000)            # 如果拦截到了 API 数据,直接解析 JSON            if api_data:                for item in api_data[-1].get("data", {}).get("list", []):                    results.append({                        "name": item.get("title"),                        "price": item.get("price"),                        "sales": item.get("sales"),                    })                api_data.clear()  # 清空,准备接收下一页数据            else:                # 否则用传统方式解析 DOM                products = page.locator(".product-item").all()                for product in products:                    name = product.locator(".product-title").inner_text()                    price = product.locator(".product-price").inner_text()                    sales = product.locator(".sales-count").inner_text()                    results.append({                        "name": name,                        "price": price,                        "sales": sales,                    })        context.close()    return results# 运行爬虫if __name__ == "__main__":    products = scrape_products("Python书籍", max_pages=3)    with open("products.json", "w", encoding="utf-8") as f:        json.dump(products, f, ensure_ascii=False, indent=2)    print(f"共爬取 {len(products)} 条商品数据")

这个爬虫有两个技巧值得说说:

拦截 API 请求。现代网站大量使用 AJAX 加载数据,直接拦截接口拿 JSON,比解析 HTML 省事多了。数据规整,字段清晰,解析效率还高。

无头模式 + User-Agent 伪装。headless=True 让浏览器在后台运行,不弹出窗口。配合自定义 User-Agent,伪装成正常浏览器访问。

说实话,我之前用 Selenium 爬数据,速度慢不说,还动不动就被反爬检测拦了。换 Playwright 后,同样的网站,检测拦截率降低了 70%。不是说 Playwright 不会被检测,而是它的底层通信机制更接近真实浏览器,给反爬系统制造了更大的识别难度。

 07. 

七、调试与工具链

Playwright 自带了一套强大的调试工具,这是我用过最贴心的设计。

7.1 Playwright Inspector

启动命令:

BASHplaywright open https://example.com

这会打开一个可视化窗口,你可以:

检查页面元素,生成定位器

逐步执行测试代码

查看每个操作的实际参数

设置断点,单步调试

我刚学 Playwright 时,Inspector 是我的主要学习工具。看到它生成的代码,再对照自己的写法,进步很快。

7.2 Trace Viewer

Trace Viewer 是 Playwright 最强大的调试功能。当测试失败时,Playwright 可以生成一个追踪文件,包含测试执行的全过程:

每个操作的屏幕截图

每个时刻的 DOM 快照

所有网络请求和响应

控制台日志

你可以离线回放整个测试过程,精准定位问题出在哪一步。

PYTHONfrom playwright.sync_api import sync_playwrightwith sync_playwright() as p:    browser = p.chromium.launch()    context = browser.new_context()    # 开始录制    context.tracing.start(screenshots=True, snapshots=True)    page = context.new_page()    # ... 测试代码 ...    # 停止录制,保存到文件    context.tracing.stop(path="trace.zip")# 打开追踪文件查看# playwright show-trace trace.zip

7.3 代码生成器

如果你不知道某个操作的代码怎么写,可以用代码生成器:

BASHplaywright codegen https://example.com

运行命令后会自动打开浏览器,你手动操作一遍,Playwright 会实时生成对应的 Python 代码。生成的代码质量很高,用的是语义化定位器,有合理的等待逻辑。生成完后直接复制到你的项目里,稍作调整就能用。

我之前让实习生用这个工具生成基础脚本,他第一天就写出了 100 多行可用的自动化代码,第二天就开始理解框架设计了。这个工具对新手真的太友好了。

 08. 

八、实战:价格监控系统

最后看一个综合案例,用 Playwright 做个实用的价格监控系统。

场景:监控某电商平台的商品价格,价格低于阈值时发送通知。

PYTHONfrom playwright.sync_api import sync_playwrightimport jsonimport timeimport requestsCONFIG_FILE = "config.json"def load_config():    """加载配置文件"""    try:        with open(CONFIG_FILE, "r") as f:            return json.load(f)    except FileNotFoundError:        return {"products": []}def save_config(config):    """保存配置文件"""    with open(CONFIG_FILE, "w") as f:        json.dump(config, f, ensure_ascii=False, indent=2)def get_current_price(page, url):    """获取商品当前价格"""    page.goto(url, wait_until="networkidle")    # 有些价格可能在弹窗里,需要关闭    try:        page.get_by_role("button", name="关闭").click(timeout=2000)    except:        pass    # 等待价格元素出现    page.wait_for_selector(".price", timeout=10000)    # 获取价格文本并清洗    price_text = page.locator(".price").inner_text()    price = float(price_text.replace("¥", "").replace(",", ""))    return pricedef send_notification(title, message):    """发送通知(这里用 PushPlus 示例)"""    token = "your_token_here"    url = f"http://www.pushplus.plus/send?token={token}&title={title}&content={message}"    requests.get(url)def monitor_products():    """监控商品价格"""    config = load_config()    products = config.get("products", [])    if not products:        print("没有配置监控商品")        return    with sync_playwright() as p:        browser = p.chromium.launch(headless=True)        context = browser.new_context()        for product in products:            name = product["name"]            url = product["url"]            threshold = product["threshold"]            last_price = product.get("last_price")            print(f"正在检查: {name}")            try:                page = context.new_page()                current_price = get_current_price(page, url)                print(f"  当前价格: ¥{current_price}, 阈值: ¥{threshold}")                # 价格低于阈值,发送通知                if current_price <= threshold:                    msg = f"{name} 当前价格 ¥{current_price},低于阈值 ¥{threshold}!"                    print(f"  🚨 {msg}")                    send_notification("价格监控提醒", msg)                # 更新最后价格                product["last_price"] = current_price                product["last_check"] = time.strftime("%Y-%m-%d %H:%M:%S")                page.close()            except Exception as e:                print(f"  ❌ 检查失败: {str(e)}")        context.close()    save_config(config)# 初始化配置def init_config():    """初始化配置文件"""    config = {        "products": [            {                "name": "iPhone 15 Pro",                "url": "https://item.example.com/iphone15",                "threshold": 7000,                "last_price": None,                "last_check": None            },            {                "name": "MacBook Air M3",                "url": "https://item.example.com/macbook-air",                "threshold": 8000,                "last_price": None,                "last_check": None            }        ]    }    save_config(config)    print("配置文件已创建")if __name__ == "__main__":    import sys    if len(sys.argv) > 1 and sys.argv[1] == "init":        init_config()    else:        monitor_products()

运行方式:

BASH# 首次运行,初始化配置python price_monitor.py init# 定期执行监控python price_monitor.py

配合系统的定时任务(Windows 任务计划程序、Linux cron),就能实现自动价格监控。我自己跑这个脚本三个月,抓到过两次 iPhone 降价通知,省了将近 1000 块。

 09. 

九、总结

用了两年多 Playwright,我的感受是:这是一个让自动化测试回归本质的工具。

它的自动等待机制消灭了 90% 的时序问题,语义化定位器让代码好维护了,统一的 API 让跨浏览器测试不再是噩梦。配套的调试工具(Inspector、Trace Viewer)又让问题定位变得异常简单。

对于测试工程师来说,Playwright 绝对是 2026 年最值得投资的新技能。市场数据已经说明一切:75% 的新项目开始采用 Playwright,GitHub Stars 突破 7.8 万,周下载量 1350 万次。这些数字背后,是整个行业对 Playwright 技术路线的认可。

对于爬虫开发者来说,Playwright 的 JS 渲染能力和低检测率,让它在现代网站爬取场景里几乎是必选方案。同样的数据,用 requests 可能拿不到,用 Playwright 一把梭。

如果你是 Selenium 老用户,我的建议是:不要急着迁移旧项目,先在新项目里用起来。等对框架熟悉了,再评估迁移成本。如果你是自动化测试的新手,直接从 Playwright 开始,少走三年弯路。

工具在进化,理念也在进步。Playwright 代表的,是"让测试更简单、让自动化更可靠"这个方向的正确探索。

📌 更多Python技术干货,关注"Python与AI智能研习社"~

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-04 13:56:22 HTTP/2.0 GET : https://f.mffb.com.cn/a/487639.html
  2. 运行时间 : 0.139358s [ 吞吐率:7.18req/s ] 内存消耗:5,827.33kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=41f7251116a4082a0d62db4a119a1c61
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000647s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001070s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.002563s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000540s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001235s ]
  6. SELECT * FROM `set` [ RunTime:0.000512s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001247s ]
  8. SELECT * FROM `article` WHERE `id` = 487639 LIMIT 1 [ RunTime:0.001037s ]
  9. UPDATE `article` SET `lasttime` = 1783144582 WHERE `id` = 487639 [ RunTime:0.008389s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000501s ]
  11. SELECT * FROM `article` WHERE `id` < 487639 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001705s ]
  12. SELECT * FROM `article` WHERE `id` > 487639 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000801s ]
  13. SELECT * FROM `article` WHERE `id` < 487639 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.013780s ]
  14. SELECT * FROM `article` WHERE `id` < 487639 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.012098s ]
  15. SELECT * FROM `article` WHERE `id` < 487639 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.008374s ]
0.141624s