python 自动化学习(四) pyppeteer 浏览器操作自动化

背景

之前我在工作中涉及到了很多地方都是重复性的页面点点点工作,又因为安全保密原则不开放接口和数据库,只有一个页面来提供点击进行操作,就想着用前面学的自动化来实现,但发现前面学的模拟操作对浏览器来说并没有那么友好,而后改用“selenium”,但是存在一个问题,我这里并不能直接访问外网,好不容易找到selenium的库文件,发现又需要相对应版本的浏览器引擎,导致我无法使用,在此期间我发现了另一个不需要浏览器引擎的库pyppeteer 成功实现了一部分功能,这里做一下笔记

 介绍

1、selenium   //跨浏览器,官方维护的比较好,资料也多,各个版本比较稳定,源码读起来舒服
              //缺点是配置时需要留心程序语言的版本和驱动版本以及浏览器版本,还有就是本身不支持步
              //需要重写源码或者利用grid分布式来实现异步

2、pyppeteer  //是基于chrome官方为chromium定制的自动化测试框架puppeteer而
              //实现的一个python包装的非官方版本框架,最后一次更新是在2018年
              //优点就是速度比selenium快,支持异步,常被拿来做爬虫,
              //缺点就是兼容性很差,而且它没有跟随chromium以及puppeteer的迭代而更新
              //使用时会有很多问题。

安装

pip install pyppeteer==1.0.2

入门案例

我们打开浏览器、输入、点击按钮什么的都是是耗时的操作,我们下面通过使用异步关键字 async 和 await,定义了一个异步函数 main。通过在异步函数中使用 await 关键字,可以将耗时的操作转化为非阻塞的异步调用

import asyncio                          # 导入 asyncio 模块,用于编写异步代码
from pyppeteer import launch           # 导入 pyppeteer 的 launch 函数,用于启动浏览器

async def main():                      # 定义一个异步函数 main

asyncio.get_event_loop().run_until_complete(main())    # 运行 main 函数

一、定义浏览器并打开页面

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
    page = await browser.newPage()
    await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
    await page.goto('https://www.baidu.com')

    await page.waitFor(10000)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

参数说明

async def main():
    browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
    #launch 定义一个浏览器实例
    #executablePath   本地谷歌浏览器路径
    #headless=False   有界面的浏览器
    #args=['--start-maximized']浏览器窗口最大化
    


    //在浏览器上创建一个新页面
    page = await browser.newPage()   

    //width 和height 自动匹配浏览器大小
    //deviiceScaleFactor 将页面的视口设置为浏览器的默认大小,并将设备像素比设置为 1
    await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
       
    //打开浏览器并跳转到指定地址   
    await page.goto('https://www.google.com')  


    //上面任务结束后等待10s
    await page.waitFor(10000)

    //关闭浏览器
    await browser.close()

我们这里能打开浏览器并且跳转了,下面我们正常情况下需要做的就是模拟鼠标键盘的一些操作,我们这里以码云的登录注册平台来做测试

#注册地址,可能有变动直接百度搜索
https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E5%25B9%25BF%25E5%2591%258A%25E8%2583%258C%25E6%2599%25AF%25E5%259B%25BE%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E5%25B9%25BF%25E5%2591%258A%25E8%2583%258C%25E6%2599%25AF%25E5%259B%25BE%26_channel_track_key%3Du1BDg7fB%26link_version%3D1%26wl_src%3Dbaidu

二、寻找页面元素信息

浏览器页面根据我们分辨率大小和窗口大小,跳转浏览器的位置都会导致我们无法直接通过之前的方法获取坐标,这里我们依赖的是直接获取web页面的元素信息(span a dir id class等等)通过他们定位具体的元素坐标

登录页面查看元素

我们登录到注册页面按F12 进入开发者模式,点击查看栏能看到html的信息,点击左边的箭头,选择我们要查看的页面元素,下面图中是选择了第一个输入框的位置

 

 得到html信息

<input class="session-register__name" required="required" placeholder="姓名" maxlength="60" size="60" type="text" name="user[name]" id="user_name">

三、常见的几种获取元素坐标方法

各个场景的html编写的不相同,同一种方法切换场景后很可能就不好使了,这里放几种我常用的方法

1、通过元素class或id获取坐标

#基于class名称获取坐标
async def click_radio(page,selector):
    await page.waitForSelector(selector)  #等待元素出现
    element = await page.querySelector(selector)  #查找指定的元素信息
    if element:
        box = await  element.boundingBox()  #获取元素坐标和尺寸
        x = box['x']
        y = box['y']
        widht = box['width']
        height = box['height']

        await page.mouse.move(x + widht / 2,  y + height / 2)

        await page.mouse.down()  #模拟鼠标点击一次
        await page.mouse.up()
    else:
        print("element not found")



调用函数

async def main():
...
    #修改地址为码云
    await page.goto('https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E8%25BF%259B%25E5%2585%25A5%25E5%25AE%2598%25E7%25BD%2591%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E4%25B8%25BB%25E6%258C%2589%25E9%2592%25AE1%26_channel_track_key%3Dsee7zmAJ%26link_version%3D1%26wl_src%3Dbaidu')

    #调用自定义函数,传参page  class名称前面要加点"."
    await click_radio(page,".session-register__name")

    #模拟输入文本信息
    await page.keyboard.type("12345678")
...
asyncio.get_event_loop().run_until_complete(main())

注意

上面案例中使用的是class获取的坐标,如果没有定义class或者有多个相同的class时可以通过id获取,区别在于class传参是 "." 加class名称   而id传参是  "#" 加id名称

小知识

//其实在遇到不是特别复杂的情况下,可以不用上面的方法,比如输入账户密码之类的
await page.waitForSelector(#id名称/.类名)  

2、通过文本获取坐标

这个在申请某些东西的时候可能会经常用到,比如申请云服务,某某产品,自研平台等等,会有大量需要挨个点击的图标,用上面第一个的时候不好使了就用这个方法

获取按钮html

<button name="button" type="submit" id="btn-submit" class="ui orange fluid submit button register-btn-submit large" sa_evt="click_GiteeCommunity_signup_signup">立即注册</button>

 向上面有文本内容显示的就可以用,我们获取到他的文本内容"立即注册" 和元素名称button

async def click_center(page,selector,type):
    #定义检索元素格式
    test = "//" + type +"[text()=" + "\'" + selector + "\'" + "]"
    element = await page.waitForXPath(test)  #获取对应元素
    box = await element.boundingBox()   #获取坐标

    target_x = box['x'] + box['width'] // 2
    target_y = box['y'] + box['height'] // 2
    await page.mouse.click(target_x,target_y)

调用

async def main():
    
    browser = await launch(executablePath="C:\Program Files\Google\Chrome\Application\chrome.exe",headless=False,args=['--start-maximized'])
    page = await browser.newPage()
    await page.setViewport({'width':0,'height':0,'deviiceScaleFactor':1})
    
    await page.goto('https://gitee.com/signup?redirect_to_url=%2F%3Fchannel_utm_content%3D%25E8%25BF%259B%25E5%2585%25A5%25E5%25AE%2598%25E7%25BD%2591%26channel_utm_medium%3Dsem%26channel_link_type%3Dweb%26channel_utm_source%3D%25E7%2599%25BE%25E5%25BA%25A6%26sat_cf%3D2%26channel_utm_campaign%3D%25E5%2593%2581%25E4%25B8%2593%26channel_utm_term%3D%25E4%25B8%25BB%25E6%258C%2589%25E9%2592%25AE1%26_channel_track_key%3Dsee7zmAJ%26link_version%3D1%26wl_src%3Dbaidu')

    #调用函数,定义文本+元素并点击
    await click_center(page,"立即注册","button")


    await page.waitFor(10000)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

 测试通过上面俩方法能解决绝大部分问题,如果遇到的坐标有偏移,比如说我文本输入框在文本的右侧,我通常会用下面的方法

    target_x = box['x'] + box['width'] + 200
    target_y = box['y'] + box['height'] // 2

小知识

你遇到点击某个按钮后会跳转到下一步的时候,最好按钮按下后面的第一个步骤中添加 await page.waitForNavigation()  这个是等等页面加载完成

3、修改页面元素,然后基于页面元素输入

特殊情况下,上面的方法都不适用,比如说我遇到过一个常见,需要申请两个文件系统/app1 和/app2  一个50G  一个100G ,但他的输入框上只有一个class,并且每个class都是完全已有的,没有id什么其他的元素,特征就是初始值为一个输入框,可以新增一个输入框,我的想法我把第一个输入框的元素class进行修改,第二个元素出现时class类就和第一个元素不同了,然后基于这个修改后的class名称在做具体的操作

我这里通过密码那一栏获取到下面的代码
<input required="required" autocomplete="new-password" placeholder="密码不少于6位" data-password-regx="^(?=.*[0-9])(?=.*[a-zA-Z!@_#$%^&amp;*()\-+=,.?]).{6,32}$" type="password" name="user[password]" id="user_password">

可以看到,他有一个id,但是没有做class我们用这个做实验

 案例

    await page.evaluate('''() => {
        const elements = document.querySelectorAll('#user_password');
        elements.forEach(element => {
          element.classList.add("ddd");
        });
    }''')


    #我们需要等等元素出现后在进行下面的操作
    await page.waitForSelector(".ddd")
    ...
    #另外,单独说个事,如果切换页面后逻辑中存在多个等待页面加载完成,那么页面就不动了

可以看到上面图里他帮忙添加了一个class的名称,我们可以在后面去调用他,需要注意的是,如果你要添加的元素的id不唯一那么所有的元素都会去添加相同的class,如果碰到了多个class名称,如  app1 app2 app3    class的名称则需要设置为 .app1 .app2 .app3  如果是id则是 #app1  #app2 #app3

4、查找特定文本元素并点击

我又碰到一个特殊的案例,我没找到演示用的页面,当记录下笔记了,这个是应用于li的一个下拉选项的场景,和第二步类似

async def click_multiple(page,selector):
    elements = await page.JJ('li')
    for element in elements:
        text_content = await element.getProperty("textContent")
        text_content = await text_content.jsonValue()
        if selector in text_content:
            box = await  element.boundingBox()
            coordinates = {
                'x': box['x'],
                'y': box['y'],
                'width': box['width'],
                'height': box['height']
            }
            await element.click()

上面的函数是查找页面所有的 li 元素,并检查每个元素的文本内容中是否包含给定的选择器。如果找到了匹配的元素,则对该元素执行点击操作。

参数说明

async def click_multiple(page, selector):
    elements = await page.JJ('li')  # 查找页面中所有的 <li> 元素并保存在 elements 变量中
    for element in elements:  # 遍历 elements 中的每个元素
        text_content = await element.getProperty("textContent")  # 获取元素的文本内容
        text_content = await text_content.jsonValue()  # 将文本内容转换为 JSON 格式
        if selector in text_content:  # 检查文本内容是否包含给定的选择器
            box = await element.boundingBox()  # 获取元素的位置和大小
            coordinates = {
                'x': box['x'],  # 元素的 x 坐标
                'y': box['y'],  # 元素的 y 坐标
                'width': box['width'],  # 元素的宽度
                'height': box['height']  # 元素的高度
            }
            await element.click()  # 点击元素

四、读取excel表数据并使用

def open_xlsx():
    from openpyxl import load_workbook

    wb = load_workbook("111.xlsx")
    ws = wb["Sheet1"]
    data = []
    for row in ws.iter_rows(min_row=2): #从第二行开始算
        row_values = []
        for cell in row:
            row_values.append(cell.value)
        data.append(row_values)
    wb.close()

    return data

data = open_xlsx()

#这里的user_list取出来的是表中每一行的数据,下面的0-1-2-3是每一列的数据
for user_list in data:
    print(user_list[0])



说明

def open_xlsx():
    from openpyxl import load_workbook

    # 打开 Excel 文件
    wb = load_workbook("111.xlsx")
    
    # 选择要读取的工作表
    ws = wb["Sheet1"]
    
    # 创建一个空的列表用于存储读取的数据
    data = []
    
    # 从第二行开始遍历每一行
    for row in ws.iter_rows(min_row=2):
        row_values = []
        
        # 遍历当前行的每一个单元格
        for cell in row:
            # 将单元格的值添加到行值列表中
            row_values.append(cell.value)
        
        # 将该行的值列表添加到数据列表中
        data.append(row_values)
    
    # 关闭 Excel 文件
    wb.close()

    # 返回读取的数据
    return data


# 调用 open_xlsx 函数并获取数据
data = open_xlsx()

# 遍历数据列表的每一行,并打印出每行的第一列数据
for user_list in data:
    print(user_list[0])

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI项目二:基于mediapipe的虚拟鼠标控制

若该文为原创文章&#xff0c;转载请注明原文出处。 一、项目介绍 由于博主太懒&#xff0c;mediapipe如何实现鼠标控制的原理直接忽略&#xff0c;最初的想法是想控制摄像头识别手指控制鼠标&#xff0c;达到播放电影的效果。基本上效果也是可以的。简单的说是使用mediapipe检…

uniApp引入vant2

uniApp引入vant2 1、cnpm 下载&#xff1a;cnpm i vantlatest-v2 -S2、main.js文件引入 import Vant from ./node_modules/vant/lib/vant;Vue.use(Vant);3.app.vue中引入vant 样式文件 import /node_modules/vant/lib/index.css;

JVM——栈和堆概述,以及有什么区别?

方法栈 方法栈并不是某一个 JVM 的内存空间&#xff0c;而是我们描述方法被调用过程的一个逻辑概念。 在同一个线程内&#xff0c;T1()调用T2()&#xff1a; T1()先开始&#xff0c;T2()后开始&#xff1b;T2()先结束&#xff0c;T1()后结束。 堆和栈概述 从英文单词角度来…

代码随想录算法训练营第三十六天 | 435. 无重叠区间,763.划分字母区间,56. 合并区间

代码随想录算法训练营第三十六天 | 435. 无重叠区间&#xff0c;763.划分字母区间&#xff0c;56. 合并区间 435. 无重叠区间:eyes:题目总结:eyes: 763.划分字母区间:eyes:题目总结:eyes: 56. 合并区间:eyes:题目总结:eyes: 435. 无重叠区间 题目链接 视频讲解 给定一个区间的…

云原生 envoy xDS 动态配置 java控制平面开发 支持restful grpc实现 EDS 动态endpoint配置

envoy xDS 动态配置 java控制平面开发 支持restful grpc 动态endpoint配置 大纲 基础概念Envoy 动态配置API配置方式动静结合的配置方式纯动态配置方式实战 基础概念 Envoy 的强大功能之一是支持动态配置&#xff0c;当使用动态配置时&#xff0c;我们不需要重新启动 Envoy…

【uni-app报错】获取用户收货地址uni.chooseAddress()报错问题

chooseAddress:fail the api need to be declared in …e requiredPrivateInf 原因&#xff1a; 小程序配置 / 全局配置 (qq.com) 解决&#xff1a; 登录小程序后台申请接口 按照流程申请即可 在项目根目录中找到 manifest.json 文件&#xff0c;在左侧导航栏选择源码视图&a…

Springboot整合Mybatis调用Oracle存储过程

1、配置说明 Oracel11g+springboot2.7.14+mybatis3.5.13 目标:springboot整合mybatis访问oracle中的存储过程,存储过程返回游标信息。 mybatis调用oracle中的存储过程方式 2、工程结构 3、具体实现 3.1、在Oracle中创建测试数据库表 具体数据可自行添加 create table s…

SIP网络音频模块-sip网络对讲音频模块(提供POE受电模块接口)

SIP网络音频模块-sip网络对讲音频模块&#xff08;提供POE受电模块接口&#xff09; SIP网络音频模块SV-2401V网络对讲音频模块&#xff08;支持POE&#xff09; SV-2403V网络对讲音频模块_网络语音对讲模块 网络音频模块 双向对讲 SIP广播系统 SIP网络音频模块嵌入式网络对…

YOLOv8改进后效果

数据集 自建铁路障碍数据集-包含路障&#xff0c;人等少数标签。其中百分之八十作为训练集&#xff0c;百分之二十作为测试集 第一次部署 版本&#xff1a;YOLOv5 训练50epoch后精度可达0.94 mAP可达0.95.此时未包含任何改进操作 第二次部署 版本&#xff1a;YOLOv8改进版本 首…

Mongodb基础操作

一、简介 MongoDB是一个NoSQL型的数据库&#xff0c;基于分布式文档型储存数据库&#xff0c;由C语言编写&#xff0c;它的特点是开源、高性能、高可用、高扩展、易部署。支持 Golang、RUBY、PYTHON、JAVA、C、PHP等多种开发语言。 二、应用场景 MongoDB适用于高并发读写、数据…

创新零售,京东重新答题?

继新一轮组织架构调整后&#xff0c;京东从低价到下沉动作不断。 新成立的创新零售部在京东老将闫小兵的带领下悄然完成了整合。近日&#xff0c;京喜拼拼已改名为京东拼拼&#xff0c;与七鲜、前置仓等业务共同承载起京东线上线下加速融合的梦想。 同时&#xff0c;拼拼的更…

FPGA: RS译码仿真过程

FPGA: RS译码仿真过程 在上一篇中记录了在FPGA中利用RS编码IP核完成信道编码的仿真过程&#xff0c;这篇记录利用译码IP核进行RS解码的仿真过程&#xff0c;带有程序和结果。 1. 开始准备 在进行解码的过程时&#xff0c;同时利用上一篇中的MATLAB仿真程序和编码过程&#x…

微信小程序|自定义弹窗组件

目录 引言小程序的流行和重要性自定义弹出组件作为提升用户体验和界面交互的有效方式什么是自定义弹出组件自定义弹出组件的概念弹出层组件在小程序中的作用和优势为什么需要自定义弹出组件现有的标准弹窗组件的局限性自定义弹出组件在解决这些问题上的优势最佳实践和注意事

日常BUG——Java使用Bigdecimal类型报错

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 直接上代码&#xff1a; Test public void test22() throws ParseException {System.out.p…

Linux怎样处理网络请求——彻底理解IO多路复用

常见的网络IO模型 网络 IO 模型分为四种&#xff1a;同步阻塞 IO、同步非阻塞IO、IO 多路复用、异步非阻塞 IO(Async IO, AIO)&#xff0c;其中AIO为异步IO&#xff0c;其他都是同步IO 同步阻塞IO 同步阻塞IO&#xff1a;在线程处理过程中&#xff0c;如果涉及到IO操作&…

这场大学生竞赛中,上百支队伍与合合信息用AI共克难题

目录 0 校企联合共克难题1 北京林业大学&#xff1a;文档格式转换2 浙江中医药大学&#xff1a;个性化题库3 中南林业科技大学&#xff1a;交互场景挖掘4 重庆邮电大学&#xff1a;大模型赋能智能文档5 总结 0 校企联合共克难题 近日&#xff0c;中国大学生服务外包创新创业大…

web前端开发基础入门html5+css3+js学习笔记(一)

目录 1.第一个前端程序2.前端工具的选择与安装3.VSCode开发者工具快捷键4.HTML5简介与基础骨架4.1 HTML5的DOCTYPE声明4.2 HTML5基本骨架4.2.1 html标签4.2.2 head标签4.2.3 body标签4.2.4 title标签4.2.5 meta标签 5.标签之标题5.1 快捷键5.1 标题标签位置摆放 6.标签之段落、…

GDP药品供应管理规范确保冷链运输合规性

药品运输面临许多挑战&#xff0c;包括产品可能因暴露在不利条件下导致降解。药品供应管理规范 (GDP) 运输指南在确保整个运输链的冷链合规性方面发挥着关键作用。 药品的分销与生产和制造生产线一样精细和敏感。自全球物流公司成立以来&#xff0c;配送过程中对受控环境的需求…

AI Chat 设计模式:14. 适配器模式

本文是该系列的第十四篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 关于适配器模式&#xff0c;如果由浅入深的来考察&#xff0c;你会依次提出什么问题…

【腾讯云 Cloud Studio 实战训练营】Hexo 框架 Butterfly 主题搭建个人博客

什么是Cloud Studio Cloud Studio 是基于浏览器的集成式开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装&#xff0c;随时随地打开浏览器就能在线编程。 ​ Hexo 博客成品展示 本人博客如下&…