Scrapy第十五篇:后起之秀-Playwright

目录

1.安装

2.初始化

3.基本使用

4.代码生成

5.选择器

6.事件监听 

6.正则表达式很多时候,点击的url后面会附带参数,这个参数是变动的,这是后需要用正则去匹配最典型的情况是等待一个页面加载:page.wait_for_url(http://xxxxx?id=123456)从源码可以看出我们是可以传入正则表达式的:​编辑

7.滑动翻页

8.并发

9.实例

10.使用模板

11.使用经验

12.饶过检测

13.定位iframe嵌套

14.元素减法与排除

15.获取元素的父级节点、祖先节点。

16.多个定位表达式进行或操作

17.多属性条件表达式


1.安装

pip install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple

2.初始化

playwright install

3.基本使用

Playwright 支持两种模式:同步、异步。

同步模式:

import time

from playwright.sync_api import sync_playwright

if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = p.chromium.launch(headless=False)
        # 新建页面
        page = driver.new_page()
        # 访问网址
        page.goto('https://www.baidu.com')
        print(page.title())
        page.fill('xpath=//input[@id="kw"]', '杜兰特')
        page.click('xpath=//input[@id="su"]')
        # 关闭驱动
        time.sleep(5)
        driver.close()

异步模式:

import asyncio

from playwright.async_api import async_playwright


async def main():
    # 浏览器上下文管理器
    async with async_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = await p.chromium.launch(headless=False)
        # 新建页面
        page = await driver.new_page()
        # 访问网址
        await page.goto('https://www.baidu.com')
        title = await page.title()
        print(title)
        # 关闭驱动
        await driver.close()


asyncio.run(main())

注意:with 用于上下文对象的管理,它可以返回一个上下文管理器,也就对应一个 PlaywrightContextManager 对象,无论运行期间是否抛出异常,它能够帮助我们自动分配并且释放 Playwright 的资源。

4.代码生成

Terminal执行:

playwright codegen -o script.py

操作浏览器后,右边会生产相应的代码预览

关闭浏览器后,会在同级目录生成script.py代码 ,可以直接执行。

5.选择器

import time

from playwright.sync_api import sync_playwright

if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = p.chromium.launch(headless=False)
        # 新建页面
        page = driver.new_page()
        # 访问网址
        page.goto('https://www.baidu.com')
        print(page.title())
        # xpath选择器(推荐,不同的模拟器有不同的写法,但是xpath都是相通的,一招鲜吃遍天)
        page.fill('xpath=//input[@id="kw"]', '杜兰特')
        # ID选择器
        page.click('#su')
        time.sleep(2)
        # class选择器
        page.fill('.s_ipt', '欧文')
        # 文本选择器
        page.click('text=百度一下')
        # 关闭驱动
        time.sleep(2)
        driver.close()

6.事件监听 

Page 对象提供了一个 on 方法,它可以用来监听页面中发生的各个事件,比如 close、console、load、request、response 等等。一个典型的用法就是用来监听页面的ajax请求响应:

import time
import cchardet
from playwright.sync_api import sync_playwright, Response


# 监听Response回调函数
def handle_response(response: Response):
    detect = cchardet.detect(response.body())
    if detect['encoding']:
        print(response.body().decode(detect['encoding']))


if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = p.chromium.launch(headless=False)
        # 新建页面
        page = driver.new_page()
        # 访问网址
        page.goto('https://spa6.scrape.center/')
        print(page.title())
        # 监听网络请求的Response
        page.on('response', handle_response)
        # 等待网络请求加载完毕
        page.wait_for_load_state('networkidle')
        time.sleep(2)
        driver.close()

6.正则表达式
很多时候,点击的url后面会附带参数,这个参数是变动的,这是后需要用正则去匹配
最典型的情况是等待一个页面加载:page.wait_for_url(http://xxxxx?id=123456)
从源码可以看出我们是可以传入正则表达式的:

 使用示例:

page.wait_for_url(re.compile("https://www.abcd.xyz/web/index[\s\S]*"))

7.滑动翻页

page.locator("body").press("PageDown")

8.并发

import asyncio
import time

from playwright.async_api import async_playwright


async def run(url):
    async with async_playwright() as playwright:
        # create a chromium browser instance
        chromium = playwright.chromium
        # browser = await chromium.launch()
        # create a bowser instance which headless is true
        browser = await chromium.launch(headless=False)
        # create two isolated browser contexts
        user_context = await browser.new_context()
        page = await user_context.new_page()
        await page.goto(url)
        await page.fill('xpath=//input[@id="kw"]', '杜兰特')
        await page.click('xpath=//input[@id="su"]')
        time.sleep(10)
        # await page2.goto('https://www.baidu.com')


async def main():
    tasks = []
    urls = ['https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com']
    for url in urls:
        task = asyncio.ensure_future(run(url))
        tasks.append(task)
    await asyncio.gather(*tasks)


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

9.实例

"""
同步模式
"""
import time

from playwright.sync_api import sync_playwright, Page, Locator


def handle_business(page: Page, url):
    """
    逻辑处理
    :param page: 页面实例
    :param url: 访问地址
    :return:
    """
    page.goto(url)
    # 定位输入框
    """
    定位!
    1.xpath定位:【搜索框】locator_input = page.locator('xpath=//input[@id="kw"]')
    2.元素属性定位:【搜索框】locator_input = page.locator('[name="wd"]')后者locator_input = page.locator('[class="s_ipt"]')
    3.本文定位:【搜索按钮】locator_submit_button = page.locator('text="百度一下"')
    4.id定位:【搜索按钮】locator_submit_button = page.locator('#su')
    5.CSS定位:略
    6.伪类①:is【如果定位不到text="百度一下",则定位#su】locator_submit_button = page.locator(':is([text="百度一下"]),#su')
    7.伪类②:has
    8.伪类③:hans-text
    """
    locator_input = page.locator('[class="s_ipt"]', ).filter()
    """
    页面操作!
    1.page.goto("https://example.com") # 前往页面
    2.page.go_back(**kwargs) #前进
    3.page.go_forward(**kwargs) # 后退
    4.page.screenshot(path="screenshot.png") 截图
    5.print(page.title()) # 打印当前页标题
    6.print(page.url()) # 打印当前页URL
    7.page.reload(**kwargs) # 重新加载当前页面
    """
    # 填写
    locator_input.fill('SpringBoot')
    # 定位提交按钮
    locator_submit_button: Locator = page.locator(':is([text="百度一下"]),#su')
    # 鼠标单击
    locator_submit_button.click()
    """
    等待元素!
    1.等待元素:page.wait_for_selector(selector="[class='page-item_M4MDr pc']", strict=False, state="visible", timeout=5000)
         state:
         - `attached` - 等待元素出现在DOM中
         - `detached` - 等待元素消失在DOM中
         - `visible` - 等待元素可访问
         - `hidden` - 等待元素隐藏
         strict:断言选择器元素唯一。
    2.等待固定时间:page.wait_for_timeout(1000)
    3.等待状态:page.wait_for_load_state(state="networkidle")
        state:
        - `domcontentloaded` - 等待DOM加载完毕
        - `load` - 等待加载事件完毕
        - `networkidle` - 等待网络闲置至少500ms(即网络加载完毕)
    """
    page.wait_for_load_state(state="networkidle")
    """
    元素操作!
    1.文本输入:locator_input.fill('SpringBoot')
    2.获取节点属性:attribute = page.get_attribute("#su", "type")
    3.点击:locator_submit_button.click()或者page.click("#su")
    """

    time.sleep(50)


if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 创建浏览器驱动
        browser = p.chromium.launch(headless=False)
        # 创建浏览器实例
        context = browser.new_context()
        # 创建页面实例
        page = context.new_page()
        handle_business(page, 'https://www.baidu.com/')

10.使用模板

import time

from playwright.sync_api import sync_playwright


if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = p.chromium.launch(headless=False, args=["--start-maximized"])
        # 全屏
        context = driver.new_context(no_viewport=True)
        # 新建页面
        page = context.new_page()

        # 这里使用生成器生成!!!!TODO

        # 关闭驱动
        time.sleep(10)
        context.close()
        driver.close()

11.使用经验

①最好不要去手写,太影响效率,最好是使用生成器,然后复制进模板进行修改。

②生成器生成的代码,往往无法直接使用,因为很多定位器是动态的,无法使用,我们需要把动态的定位器换成固定的定位器。

③有些元素只有悬停才会出现,我们可以使用hover()

page1.get_by_role("link", name="系统维护").hover()

④有些元素找不到、定位不到,可能是加载太慢了,我们可以先用xpath定位,等待元素出现

# 先等待元素
page1.wait_for_selector(
                    selector='//div[@class="am-modal-dialog" and @style="width: 540px; height: 150px;"]//span[@class="am-modal-btn"]')
# 再获取元素
                selector = page1.query_selector(
                    selector='//div[@class="am-modal-dialog" and @style="width: 540px; height: 150px;"]//span[@class="am-modal-btn"]')
# 最后操作元素
                selector.click()

⑤在循环里面做好异常捕获,并在捕获中将界面打回新一轮的起点。

⑥有时候跑得太快,下拉框没有选择进去,可以多执行几次。

page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
                    "select[name=\"orgCategory\"]").select_option(
                    f"{number}")
page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
                    "select[name=\"orgCategory\"]").select_option(
                    f"{number}")
page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
                    "select[name=\"orgCategory\"]").select_option(
                    f"{number}")
page1.get_by_text("确定").click()

12.饶过检测

正常使用如下:

import time

from playwright.sync_api import sync_playwright

if __name__ == '__main__':
    # 浏览器上下文管理器
    with sync_playwright() as p:
        # 启动谷歌浏览器(关闭无头模式)
        driver = p.chromium.launch(headless=False)
        # 新建页面
        page = driver.new_page()
        # 访问网址
        page.goto('https://bot.sannysoft.com/')
        # 关闭驱动
        time.sleep(60)
        # driver.close()

可以看到明显的报红

 使用内核与使用浏览器始终会有点区别。为了解决这个问题,可以在启动之前执行一下JS文件。

如果有梯子则直接执行下边的代码,如果没有梯子,则要去下载到本地加载执行。

import time

import requests
from playwright.sync_api import sync_playwright


def getBypassJs():
    # 最好是在线获取,因为是实时更新的。但是前提是要有梯子。否则在线可能获取不到该文件。
    response = requests.get("https://raw.githubusercontent.com/requireCool/stealth.min.js/main/stealth.min.js")
    js = response.content.decode('utf-8')

    # 如果没有梯子,则去下载下来,从本地读取。
    # with open('./stealth.min.js', 'r') as f:
    #     js = f.read()

    return js


if __name__ == "__main__":
    js: str = getBypassJs()
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        page.add_init_script(js)
        page.goto("https://bot.sannysoft.com/")
        time.sleep(10)
        page.close()

13.定位iframe嵌套

page.goto("https://qzone.qq.com/")
        page.wait_for_load_state()
        # 获取第一层iframe元素页面
        iframe = page.frame_locator('#login_frame')
        # 获取所需元素
        selector = iframe.locator('xpath=//a[@id="switcher_plogin"]')
        selector.click()
        time.sleep(1)
        iframe.locator('input[id="u"]').fill("16234895@qq.com")
        time.sleep(1)
        iframe.locator('input[id="p"]').fill("1234567890")
        time.sleep(1)
        iframe.locator('input[id="login_button"]').click()
        time.sleep(5)
        # 定位第二次嵌套的iframe页面
        iframe_captcha = iframe.frame_locator('#tcaptcha_iframe_dy')
        # 获取所需元素
        condition = iframe_captcha.locator('span[id="instructionText"]').text_content()
        gb_div = iframe_captcha.locator('div[id="slideBg"]')

        # 获取元素的边界信息
        element_box = gb_div.bounding_box()

        ab_path_dir = os.path.join(os.getcwd(), f"screenshot{uuid.UUID}.png")
        print(f"截图存放路径{ab_path_dir}")
        # 指定截图区域
        screenshot = page.screenshot(
            path=ab_path_dir,  # 保存截图的文件路径
            clip={
                'x': element_box['x'],
                'y': element_box['y'],
                'width': element_box['width'],
                'height': element_box['height']
            }
        )

14.元素减法与排除

场景:无法准确定位到所需元素,但是能够定位到全部元素集合非需元素集合,需要将二者相减才能得到所需元素。相减原理:利用元素的坐标点坐标是否相同,来判断是不是同一个元素

# 获取元素下所有按钮
buttons_all = choice_locator.query_selector_all('xpath=//button[@type="button"]')
# 获取元素下所有干扰按钮
buttons_Interference = choice_locator.query_selector_all('xpath=//div[string-length(@data-id) = 11 and translate(@data-id, "0123456789", "") = ""]//button[@type="button"]')
# 获取所有干扰元素的坐标点
interference_point = [(b.bounding_box()['x'], b.bounding_box()['y']) for b in
                                                          buttons_Interference]

 判断根据坐标点是否相同来排除干扰元素。

# 真正元素下的按钮
buttons = []
for button in buttons_all:
    # 对比每个按钮的坐标点位置来判断
    # 获取元素的边界信息
    element_box = button.bounding_box()
    x = element_box['x']
    y = element_box['y']
    if (x, y) not in interference_point:
        buttons.append(button)

15.获取元素的父级节点、祖先节点。

element_handle与locator对象的相互转化。

# 从 query_selector 到 locator
    element_handle = page.query_selector('h1')
    if element_handle:
        locator = page.locator('h1')
        text = locator.text_content()
        print(f"Text from ElementHandle to Locator: {text}")

    # 从 locator 到 query_selector
    locator = page.locator('h1')
    element_handle = locator.element_handle()
    if element_handle:
        text = element_handle.text_content()
        print(f"Text from Locator to ElementHandle: {text}")

获取父级、祖级元素

i:Locator
i.element_handle().query_selector("xpath=..").text_content() # 父级
i.element_handle().query_selector("xpath=../..").text_content() # 父父级 
i.element_handle().query_selector("xpath=../../..").text_content() # 父父父级
i:ElementHandle
i.query_selector("xpath=../../..").text_content()

16.多个定位表达式进行或操作

selector_all = self.page.query_selector_all(
                    'button[type="button"]:has-text("去完成"), button[type="button"]:has-text("已完成")')

17.多属性条件表达式

page.query_selector_all('div[itemprop="author"][itemtype="http://schema.org/Person"].AuthorInfo')

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Scrapy-Playwright是一个用于Scrapy框架的插件,它允许您使用Playwright库来爬取JavaScript动态渲染的网站。下面是使用Scrapy-Playwright进行Web Scraping的简单教程: 1. 安装Scrapy-Playwright 您可以使用pip命令来安装Scrapy-Playwright。在命令提示符或终端中运行以下命令: ``` pip install scrapy-playwright ``` 2. 配置Scrapy-Playwright 要使用Scrapy-Playwright,您需要在Scrapy项目的settings.py文件中进行配置。添加以下行: ``` DOWNLOADER_MIDDLEWARES = { 'scrapy_playwright.PlaywrightMiddleware': 543, } PLAYWRIGHT_LAUNCH_OPTIONS = { 'headless': True, } ``` 这将启用Playwright中间件,并将Playwright设置为在无头模式下运行。 3. 创建Spider 创建一个新的Spider并导入PlaywrightRequest和PlaywrightResponse类。这些类类似于Scrapy的Request和Response类,但它们使用Playwright库来处理JavaScript渲染。 ``` from scrapy_playwright import PlaywrightRequest, PlaywrightResponse from scrapy.spiders import Spider class MySpider(Spider): name = 'myspider' start_urls = ['https://www.example.com'] def start_requests(self): for url in self.start_urls: yield PlaywrightRequest(url) def parse(self, response: PlaywrightResponse): # 处理响应 ``` 4. 处理响应 在parse方法中,您可以像处理Scrapy Response对象一样处理PlaywrightResponse对象。但是,PlaywrightResponse对象包含了一个page属性,它是由Playwright库返回的Page对象,您可以使用它来处理JavaScript渲染的内容。 ``` def parse(self, response: PlaywrightResponse): # 获取HTML和JavaScript渲染的内容 html = response.text js_rendered_html = response.page.content() ``` 5. 运行Spider 最后,您可以像运行任何其他Scrapy Spider一样运行您的Spider。 ``` scrapy crawl myspider ``` 希望这个简单的教程能够帮助您开始使用Scrapy-Playwright进行Web Scraping。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值