返回

Playwright 爬虫使用

官方文档:Getting started | Playwright Python

参考链接:强大易用!新一代爬虫利器 Playwright 的介绍

安装

Playwrigth 会安装 Chromium, Firefox and WebKit 浏览器并配置一些驱动,我们不必关心中间配置的过程,Playwright 会为我们配置好。

pip install playwright
 
# 安装完后初始化
playwright install

基本使用

打开浏览器,跳转到百度网页,打印标题;设置了 headless 参数为 False,代表显示浏览器界面。

from playwright.sync_api import Playwright, sync_playwright
 
with sync_playwright() as p:
    rowser = playwright.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto('https://www.baidu.com')
    print(page.title())
    browser.close()

代码生成

可以录制我们在浏览器中的操作并将代码自动生成出来,在爬虫中对于一些点击,跳转,鼠标移动等自动化操作,可以使用。

方便在一些步骤不知道怎么写时,可以自动生成代码参考。

在指定路径下输入命令,会弹出对应窗口,就可以开始人工操作,会生成代码,但对于有点复杂的操作好像自动生成不成功。

playwright codegen -o script.py 

启动一个谷歌浏览器,然后将操作结果输出到 script.py 文件,下面生成一个代码看看。

下面这段代码,打开茶语网,点击【茶评】,会打开一个新窗口跳转过去。

from playwright.sync_api import Playwright, sync_playwright
 
 
def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
 
    # Open new page
    page = context.new_page()
 
    # Go to https://www.chayu.com/
    page.goto("https://www.chayu.com/")
 
    # Click #sub-nav >> text=茶评
    # 由于弹出新窗口,需要等待,这里就是自动等待页面加载
    # 点击茶评,等待页面加载
    with page.expect_popup() as popup_info:
        # 就页面的操作都可以在这里面继续加
        page.click("#sub-nav >> text=茶评")
    page1 = popup_info.value # 赋值新窗口对象
 
    # Close page
    page.close()
 
    # Close page
    page1.close()
 
    # ---------------------
    context.close()
    browser.close()
 
 
with sync_playwright() as playwright:
    run(playwright)

AJAX 动态加载数据获取

对于获取动态加载的数据,需要及其注意的一点,需要浏览器把动态加载的页面显示在上面,才能提取,例如:https://chaping.chayu.com/tea/3266,获取评论数据,ajax 加载的,打开页面:

 如果打开页面直接提取评论数据,是提取不到的,需要吧数据滑动到页面上后,再提取(坑死了)

事件监听

Page 对象提供了一个 on 方法,它可以用来监听页面中发生的各个事件,比如 close、console、load、request、response 等等。

可以监听 response 事件,response 事件可以在每次网络请求得到响应的时候触发,我们可以设置对应的回调方法。

可以结合 ajax 获取数据使用 ,截获 Ajax 请求,输出对应的 JSON 结果。

from playwright.sync_api import sync_playwright
 
def on_response(response):
    if '/api/movie/' in response.url and response.status == 200:
        print(response.json())
 
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.on('response', on_response)
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    browser.close()

不加载图片

调用了 route 方法,第一个参数通过正则表达式传入了匹配的 URL 路径,这里代表的是任何包含 .png 或 .jpg  的链接,遇到这样的请求,会回调 cancel_request 方法处理,cancel_request 方法可以接收两个参数,一个是 route,代表一个 CallableRoute 对象,另外一个是 request,代表 Request 对象。这里我们直接调用了 route 的 abort 方法,取消了这次请求,所以最终导致的结果就是图片的加载全部取消了

from playwright.sync_api import sync_playwright
import re
 
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    
    # 不加载图片
    def cancel_request(route, request):
        route.abort()
    page.route(re.compile(r"(\.png)|(\.jpg)"), cancel_request)
 
    page.goto("https://spa6.scrape.center/")
    page.wait_for_load_state('networkidle')
    page.screenshot(path='no_picture.png')
    browser.close()

CSS 选择器、文本选择器、click()

click 方法里面接选择器表达式,提取后点击,可设置 timeout 超时时间,默认 30秒,设置以毫秒为单位,如等待 5 秒未点击成功,这报错 timeout=5000

# 选择文本是 Log in 的节点,并点击
page.click("text=Log in",timeout=5000)
 
# 选择 id 为 nav-bar 子孙节点 class 属性值为 contact-us-item,并点击
page.click("#nav-bar .contact-us-item")
 
# 选择文本中包含 Playwright 的 article 节点
page.click("article:has-text('Playwright')")
 
# 选择 id 为 nav-bar 节点中文本值等于 Contact us 的节点
page.click("#nav-bar :text('Contact us')")
 
# 选择 class 为 item-description 的节点,且该节点还要包含 class 为 item-promo-banner 的子节点
page.click(".item-description:has(.item-promo-banner)")
 
# 择的就是一个 input 节点,并且该 input 节点要位于文本值为 Username 的节点的右侧
page.click("input:right-of(:text('Username'))")


# 通过 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 选择器

需要在开头指定 xpath= 字符串,代表后面是一个 XPath 表达式

page.click("xpath=//button")

获取网页源代码

这里获取的网页源代码,不管网页是 ajax 加载的,都是获取最终的 html。

凡是需要对 html 中的元素进行操作的 page.wait_for_load_state(’networkidle’) 必写,用于等待 html 加载。

page.wait_for_load_state('networkidle')
html = page.content()

文本输入

一般文本输入会先清空原来的,但 playwright 没有像 selenium 一样的方法,只能先定位到文本框,然后填充空字符串后再输入

在 input 标签 name 属性为 wd 的输入 nba

page.fill("input[name=\"wd\"]", "")
page.fill("input[name=\"wd\"]", "nba")

选择提取标签

提取所有 div 标签 class 属性为 list 的 

elements = page.query_selector_all('div.list')

提取 span 标签 class 属性为 score 的,如匹配到多个,则取第一个

score = element.query_selector('span.score')

提取文本

提取文本为【品牌:】的标签,在提取标签下的所有文本

brand = element.query_selector('text=品牌:').text_content()

提取标签中的属性

提取 h5 标签下的 a 标签,获取 a 标签中的 href 属性取值

 link = element.query_selector('h5 a').get_attribute('href')

鼠标滚动

向右滚动 0,向下滚动 7000,可用于下拉滚动条功能

page1.mouse.wheel(0,7000)

下拉滚动条

执行 js 代码,下拉滚动条,15000 可以根据情况设置

page1.evaluate("var q=document.documentElement.scrollTop=15000")

参考:crawler – 逸飞的技术日志

登录获得保存 cookie

from playwright.sync_api import Playwright, sync_playwright
import json
 
 
def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
 
    # Open new page
    page = context.new_page()
 
    # Go to http://www.glidedsky.com/login
    page.goto("http://www.glidedsky.com/login")
 
    # Click input[name="email"]
    page.click("input[name=\"email\"]")
 
    # Fill input[name="email"]
    page.fill("input[name=\"email\"]", "账号")
 
    # Click input[name="password"]
    page.click("input[name=\"password\"]")
 
    # Fill input[name="password"]
    page.fill("input[name=\"password\"]", "密码")
 
    # Click button:has-text("Login")
    page.click("button:has-text(\"Login\")")
    # assert page.url == "http://www.glidedsky.com/"
 
    # 获得登录后 cookie
    storage = context.storage_state()
    with open("state.json", "w") as f:
        f.write(json.dumps(storage))
 
    # Close page
    page.close()
 
    # ---------------------
    context.close()
    browser.close()
 
 
with sync_playwright() as playwright:
    run(playwright)

使用 cookie 进行登录

from playwright.sync_api import Playwright, sync_playwright
import json
 
 
def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
 
    # 加载状态
    with open("state.json") as f:
        storage_state = json.loads(f.read())
    context = browser.new_context(storage_state=storage_state)
 
    # Open new page
    page = context.new_page()
 
    # Go to http://www.glidedsky.com/login
    page.goto("http://www.glidedsky.com/")
 
 
    # Close page
    page.close()
 
    # ---------------------
    context.close()
    browser.close()
 
 
with sync_playwright() as playwright:
    run(playwright)
Built with Hugo
Theme Stack designed by Jimmy