Pyppeteer原理介绍和入门尝试

news2024/9/19 10:58:18

pyppeteer仓库地址:https://github.com/miyakogi/pyppeteer

puppeteer仓库地址:https://github.com/search?q=puppeteer&type=repositories

因为有些网页是可以检测到是否是使用了selenium。并且selenium所谓的保护机制不允许跨域cookies保存以及登录的时候必须先打开网页然后后加载cookies再刷新的方式很不友好。所以采用谷歌chrome官方无头框架puppeteer的python版本pyppeteer。

Pyppeteer 简介

1.Chrome 浏览器和 Chromium 浏览器

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

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

Pyppeteer 就是依赖于 Chromium 这个浏览器来运行的。那么有了 Pyppeteer 之后,我们就可以免去那些繁琐的环境配置等问题。如果第一次运行的时候,Chromium 浏览器没有安装,那么程序会帮我们自动安装和配置,就免去了繁琐的环境配置等工作。另外 Pyppeteer 是基于 Python 的新特性 async 实现的,所以它的一些执行也支持异步操作,效率相对于 Selenium 来说也提高了。

注意:本来chrome就问题多多,puppeteer也是各种坑,加上pyppeteer是基于前者的改编python版本,也就是产生了只要前两个有一个有bug,那么pyppeteer就会原封不动的继承下来,本来这没什么,但是现在遇到的问题就是pyppeteer这个项目从18年9月份之后就没更新过了,前两者都在不断的更新迭代,而pyppeteer一直不更新,导致很多bug根本没人修复。

2.asyncio

asyncio是Python的一个异步协程库,自3.4版本引入的标准库,直接内置了对异步IO的支持,号称是Python最有野心的库,官网上有非常详细的介绍:

Pyppeteer快速上手

1.安装

在第一次使用pyppeteer的时候也会自动下载并安装chromium浏览器,效果是一样的。总的来说,pyppeteer比起selenium省去了driver配置的环节。

当然,出于某种原因,也可能会出现chromium自动安装无法顺利完成的情况,这时可以考虑手动安装:首先,从下列网址中找到自己系统的对应版本,下载chromium压缩包;

'linux': 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/575458/chrome-linux.zip'
'mac': 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/575458/chrome-mac.zip'
'win32': 'https://storage.googleapis.com/chromium-browser-snapshots/Win/575458/chrome-win32.zip'
'win64': 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/575458/chrome-win32.zip'

2.2 初始化设置

import asyncio, time
from pyppeteer import launch

async def main():
    browser = await launch(headless=False, dumpio=True, autoClose=False,
                           args=['--no-sandbox', '--window-size=1920,1080', '--disable-infobars'])   # 进入有头模式
    page = await browser.newPage()           # 打开新的标签页
    await page.setViewport({'width': 1920, 'height': 1080})      # 页面大小一致
    await page.goto('https://www.baidu.com/?tn=99669880_hao_pg') # 访问主页

    # evaluate()是执行js的方法,js逆向时如果需要在浏览器环境下执行js代码的话可以利用这个方法
    # js为设置webdriver的值,防止网站检测
    await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
    # await page.screenshot({'path': './1.jpg'})   # 截图保存路径

    page_text = await page.content()   # 获取网页源码
    print(page_text)
    time.sleep(1)
asyncio.get_event_loop().run_until_complete(main()) #调用

参数参考:Pyppeteer:比selenium更高效的爬虫界的新神器

launch可接收的参数非常多,其中

ignoreHTTPSErrors(bool):是否忽略 HTTPS 错误。默认为 False
headless指定浏览器是否以无头模式运行,默认是True。
args 指定给浏览器实例传递的参数,
--disable-infobars 代表关闭浏览上方的“Chrome 正受到自动测试软件的控制”,
--window-size=1920,1080是设置浏览器的显示大小,
--no-sandbox 是 在 docker 里使用时需要加入的参数。
关闭提示条:”Chrome 正受到自动测试软件的控制”,这个提示条有点烦,那咋关闭呢?这时候就需要用到 args 参数了,禁用操作如下:browser = await launch(headless=False, args=['--disable-infobars'])

其他很多参数可以参考puppeteer的文档:https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer&version=v2.1.1&show=api-class-puppetee

绕过 webdriver 检测

检测地址:https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html

import asyncio
from pyppeteer import launch

# 测试检测webdriver
async def main():
    browser = await launch(headless=False, args=['--disable-infobars'])
    page = await browser.newPage()
    await page.setUserAgent("Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5")
    await page.setViewport(viewport={'width': 1536, 'height': 768})
    await page.goto('https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html')
    await asyncio.sleep(25)
    await browser.close()
asyncio.get_event_loop().run_until_complete(main())

Pyppeteer 开启 Chromium 照样还是能被检测到 WebDriver 的存在:

无论是 selenium 的 execute_script() 方法,还是 pyppeteer 的 evaluate() 方法执行下面代码都能临时修改浏览器属性中的 webdriver 属性,当页面刷新或者跳转之后该值就会原形毕露。

await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')

但是 pyppeteer 的最底层是封装的puppeteer,是 js 库,是和网站源码交互最深的方式。

在 pyppeteer 中提供了一个方法:evaluateOnNewDocument(),该方法是将一段 js 代码加载到页面文档中,当发生页面导航、页面内嵌框架导航的时候加载的 js 代码会自动执行,那么当页面刷新的时候该 js 也会执行,这样就保证了修改网站的属性持久化的目的:

await page.evaluateOnNewDocument('() =>{ Object.defineProperties(navigator,'
                                     '{ webdriver:{ get: () => false } }) }') 

基本使用,支持的选择器有
# 在页面内执行 document.querySelector。如果没有元素匹配指定选择器,返回值是 None
J = querySelector
# 在页面内执行 document.querySelector,然后把匹配到的元素作为第一个参数传给 pageFunction
Jeval = querySelectorEval
# 在页面内执行 document.querySelectorAll。如果没有元素匹配指定选择器,返回值是 []
JJ = querySelectorAll
# 在页面内执行 Array.from(document.querySelectorAll(selector)),然后把匹配到的元素数组作为第一个参数传给 pageFunction
JJeval = querySelectorAllEval
# XPath表达式
Jx = xpath

快速入门


import asyncio
from pyppeteer import launch


async def main():
    # headless参数设为False,则变成有头模式
    # Pyppeteer支持字典和关键字传参,Puppeteer只支持字典传参
    
    # 指定引擎路径
    # exepath = r'C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer\local-chromium\575458\chrome-win32/chrome.exe'
    # browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})
    
    browser = await launch(
        # headless=False,
        {'headless': False}
    )

    page = await browser.newPage()

    # 设置页面视图大小
    await page.setViewport(viewport={'width': 1280, 'height': 800})

    # 是否启用JS,enabled设为False,则无渲染效果
    await page.setJavaScriptEnabled(enabled=True)
    # 超时间见 1000 毫秒
    res = await page.goto('https://www.toutiao.com/', options={'timeout': 1000})
    resp_headers = res.headers  # 响应头
    resp_status = res.status  # 响应状态
    
    # 等待
    await asyncio.sleep(2)
    # 第二种方法,在while循环里强行查询某元素进行等待
    while not await page.querySelector('.t'):
        pass
    # 滚动到页面底部
    await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')

    await asyncio.sleep(2)
    # 截图 保存图片
    await page.screenshot({'path': 'toutiao.png'})

    # 打印页面cookies
    print(await page.cookies())

    """  打印页面文本 """
    # 获取所有 html 内容
    print(await page.content())

    # 在网页上执行js 脚本
    dimensions = await page.evaluate(pageFunction='''() => {
            return {
                width: document.documentElement.clientWidth,  // 页面宽度
                height: document.documentElement.clientHeight,  // 页面高度
                deviceScaleFactor: window.devicePixelRatio,  // 像素比 1.0000000149011612
            }
        }''', force_expr=False)  # force_expr=False  执行的是函数
    print(dimensions)

    #  只获取文本  执行 js 脚本  force_expr  为 True 则执行的是表达式
    content = await page.evaluate(pageFunction='document.body.textContent', force_expr=True)
    print(content)

    # 打印当前页标题
    print(await page.title())

    # 抓取新闻内容  可以使用 xpath 表达式
    """
    # Pyppeteer 三种解析方式
    Page.querySelector()  # 选择器
    Page.querySelectorAll()
    Page.xpath()  # xpath  表达式
    # 简写方式为:
    Page.J(), Page.JJ(), and Page.Jx()
    """
    element = await page.querySelector(".feed-infinite-wrapper > ul>li")  # 纸抓取一个
    print(element)
    # 获取所有文本内容  执行 js
    content = await page.evaluate('(element) => element.textContent', element)
    print(content)

    # elements = await page.xpath('//div[@class="title-box"]/a')
    elements = await page.querySelectorAll(".title-box a")
    for item in elements:
        print(await item.getProperty('textContent'))
        # <pyppeteer.execution_context.JSHandle object at 0x000002220E7FE518>

        # 获取文本
        title_str = await (await item.getProperty('textContent')).jsonValue()

        # 获取链接
        title_link = await (await item.getProperty('href')).jsonValue()
        print(title_str)
        print(title_link)

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


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


ascyncio 同步与异步执行 Pyppeteer

1.同步

基本思路是新建一个browser浏览器和一个页面page,依次访问每个基金的净值数据页面并爬取数据。核心代码如下:

get_data()函数 用于净值数据页面解析和数据的转化,
get_all_codes()函数 用于获取全部开放式基金的基金代码(共6000余个)。
虽然程序也使用了async/await的结构,但是对多个基金的净值数据获取都是在callurl_and_getdata()函数中顺序执行的,之所以这样写是因为pyppeteer中的方法都是coroutine对象,必须以这种形式构建程序。

为了排除打开浏览器的耗时干扰,我们仅统计访问页面和数据抓取的用时,其结果为:12.08秒。

2. 异步

主要是把对fundlist的循环运行改装成async的task对象

3. 获取标签的文本、值
# 获取a标签
title_elements = await page.Jx('//*[@class="result c-container "]/h3/a')
 
for item in title_elements:
    # 获取文本:方法一,通过getProperty方法获取
    title_str1 = await (await item.getProperty('textContent')).jsonValue()
    print(title_str1)
 
    # 获取文本:方法二,通过evaluate方法获取
    title_str2 = await page.evaluate('item => item.textContent', item)
    print(title_str2)
 
    # 获取链接:通过getProperty方法获取
    title_link = await (await item.getProperty('href')).jsonValue()

常见的bug

1. pyppeteer.errors.NetworkError: Protocol error Network.getCookies: Target close

方法1:控制访问指定url之后await page.goto(url),会遇到上面的错误,如果这时候使用了sleep之类的延时也会出现这个错误或者类似的time out。
这个问题是puppeteer的bug,但是对方已经修复了,而pyppeteer迟迟没更新,就只能靠自己了,搜了很多人的文章,例如:https://github.com/miyakogi/pyppeteer/issues/171 ,但是我按照这个并没有成功。
也有人增加一个函数,但调用这个参数依然没解决问题。

async def scroll_page(page):
    cur_dist = 0
    height = await page.evaluate("() => document.body.scrollHeight")
    while True:
        if cur_dist < height:
            await page.evaluate("window.scrollBy(0, 500);")
            await asyncio.sleep(0.1)
            cur_dist += 500
        else:
            break

方法2:可以把python第三方库websockets版本7.0改为6.0就可以了,亲测可用。

pip uninstall websockets #卸载websockets
pip install websockets==6.0 #指定安装6.0版本
2. chromium浏览器多开页面卡死问题

方法:解决这个问题的方法就是浏览器初始化的时候添加’dumpio’:True

3. 浏览器窗口很大,内容显示很小

上面的问题是需要设置浏览器显示大小,默认就是无法正常显示。可以看到页面左侧右侧都是空白,网站内容并没有完整铺满chrome.

browser = await launch({'headless': False,'dumpio':True, 'autoClose':False,'args': ['--no-sandbox', '--window-size=1366,850']})
await page.setViewport({'width':1366,'height':768})

方法:通过上面设置Windows-size和Viewport大小来实现网页完整显示。

但是对于那种向下无限加载的长网页这种情况如果浏览器是可见状态会显示不全,针对这种情况的解决方法就是复制当前网页新开一个标签页粘贴进去就正常了

4. Execution context was destroyed, most likely because of a navigation.

因为页面发生了跳转导致 page 丢失
方法:

// 在登录页跳转之后添加
await page.waitForNavigation(); // 等待页面跳转
5.登录出现 滑块 和cookies获取
import asyncio
from pyppeteer import launch

async def main():
    browser = await launch({'headless': False, 'args': ['--disable-infobars', '--window-size=1920,1080']})
    page = await browser.newPage()
    await page.setViewport({'width': 1920, 'height': 1080})
    await page.goto('https://login.taobao.com/member/login.jhtml')
    await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
    await page.waitForSelector('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static', {'timeout': 3000})
    await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
    await page.type('#TPL_username_1', '')  # 账号
    await page.type('#TPL_password_1', '')  # 密码
    await asyncio.sleep(5)
    slider = await page.Jeval('#nocaptcha', 'node => node.style')  # 是否有滑块,ps:试了好多次都没出滑块
    if slider:
        print('出现滑块')
    await page.click('#J_SubmitStatic')
    await asyncio.sleep(5)
    cookie = await page.cookies()
    print(cookie)
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())
6. pyppeteer.errors.TimeoutError: Navigation Timeout Exceeded: 30000 ms exceeded

由于点击事件执行很快已跳转到新的页面,导致程序运行到导航等待的时候,一直处于新的页面等待触发,直到30秒超时报错,所以,正确的做法应该是把点击和导航等待视为一个整体进行操作,以下为两种正确的写法,了解协程并发的朋友应该知道,在此不做详细说明

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

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

相关文章

JavaScript的学习之事件的简介

目录 一、事件是什么 二、如何处理事件 一、事件是什么 定义&#xff1a;事件就是浏览器和用户之间的交互行为。 例如&#xff1a;点击按钮、鼠标移动、关闭窗口等。 二、如何处理事件 我们可以在对应的事件属性中设置一些JS行为&#xff0c;当事件触发的时候会将这些代码执行…

java的输入流FileInput Stream类

一、定义 使用InputStream类的FileInputStream子类实现文本文件内容的读取。 二、常用构造方法 三、使用FileInput Stream类按多字节读取数据 1.示例 2、分析 四、常见错误 今天的总结就到这里啦&#xff0c;拜拜&#xff01;

Windows操作系统安装mysql数据库(zip安装包)

MySQL是目前最为流行的开放源码的数据库&#xff0c;是完全网络化的跨平台的关系型数据库系统&#xff0c;它是由瑞典MySQLAB公司开发&#xff0c;目前属于Oracle公司。任何人都能从Internet下载MySQL软件&#xff0c;而无需支付任费用&#xff0c;并且“开放源码”意味着任何人…

活动预告|探索 LLM 大模型的小型化 —— 微软 Phi3在 NVIDIA Jetson 与 NIM 平台的最佳实践

在当前高速发展的人工智能领域&#xff0c;如何高效的部署和优化 SLM (小型的大模型) 成为关键。随着微软 Phi-3 系列模型的发布&#xff0c;让 SLM 在 NVIDIA Jetson 边缘计算平台上的部署成为可能。同时 Phi-3 系列模型已在 NVIDIA NIM 平台提供加速的推理服务。 NVIDIA NIM…

msvcp120.dll丢失怎么办,找不到msvcp120.dll的多种解决方法

最近&#xff0c;我在运行一个程序时遇到了一个错误&#xff0c;系统提示找不到msvcp120.dll文件&#xff0c;无法继续执行代码。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行这个程序。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#…

ONLYOFFICE 桌面编辑器 8.1重磅来袭:全新功能提升您的办公效率

文章目录 前言ONLYOFFICE 桌面编辑器8.1一、PDF编辑&#xff1a;告别“头痛”时刻二、幻灯片版式&#xff1a;秒变“设计大师”三、无缝切换&#xff1a;办公界的“快速通道”四、语言支持&#xff1a;全球通吃的“翻译官”五、 隐藏“连接到云”板块&#xff1a;摆脱“云”的束…

RocketMQ如何添加JVM监控

这里是小奏,觉得文章不错可以关注公众号小奏技术 JVM监控选型 本次JVM监控我们采用prometheus官方提供的jmx_exporter来实现 RocketMQJVM开发 整体目录 1. 新增agent目录 我们在distribution目录新增一个agent模块&#xff0c;然后添加两个文件 jmx_prometheus_javaagent-…

Mysql索引底层数据结构——Java全栈知识(28)

Mysql索引底层数据结构 1、什么是索引 索引在项目中还是比较常见的&#xff0c;它是帮助MySQL高效获取数据的数据结构&#xff0c;主要是用来提高数据检索的效率&#xff0c;降低数据库的IO成本&#xff0c;同时通过索引列对数据进行排序&#xff0c;降低数据排序的成本&…

低价可转债崩盘,发生了什么?

下跌不在于“出库”&#xff0c;甚至不在于“风险”。问题更多在于交易层面&#xff0c;何时能积聚更多的左侧资金并成功过渡至右侧。 低价券怎么了&#xff1f; 如果说6月初主要是小微盘品种的退市风险&#xff0c;后来是一些评级下调的品种&#xff0c;到本周&#xff0c;已…

【Godot4自学手册】第四十二节实现拖拽进行物品交换和数量叠加

这一节我们主要学习背包系统中的物品拖拽后&#xff0c;物品放到新的位置&#xff0c;或交换物品位置&#xff0c;如果两个物品属于同一物品则数量相加。具体效果如下&#xff1a; 一、修改item.tscn场景 给item.tscn场景的根节点Item添加Label子节点&#xff0c;命名为Numv…

【计算机视觉】人脸算法之图像处理基础知识(六)

图像直方图 图像直方图是描述图像中像素强度分布的一种统计图表&#xff0c;它是图像处理和计算机视觉领域中一个非常基础且重要的概念。图像直方图通常用于分析图像的亮度、对比度特性&#xff0c;以及在图像增强、阈值分割、特征提取等多种图像处理任务。 import cv2 impor…

HTTPS 代理的优点和缺点是什么?

HTTPS&#xff08;超文本安全传输协议&#xff09;作为一种基于HTTP加上SSL安全层的网络通信协议&#xff0c;已经成为互联网上广泛使用的IP协议之一。它在保证信息安全和隐私方面具有很多优势&#xff0c;但也存在一些缺点。接下来&#xff0c;我们就来探究一下HTTPS协议的优缺…

日牵物流装备受邀盛装亮相2024长三角快递物流供应链与技术装备展览会(杭州)

日牵物流装备受邀盛装亮相2024长三角快递物流供应链与技术装备展览会&#xff0c;为物流节省每一个铜板&#xff0c;3C馆A21与您相约&#xff01; 日牵物流装备始建于1995年&#xff0c;总部坐落于辽宁省大连市&#xff0c;是一家集科研开发、生产制造、销售服务于一体的现代化…

HTML5【新特性总结】

HTML5【新特性总结】 HTML5 的新增特性主要是针对于以前的不足&#xff0c;增加了一些新的标签、新的表单和新的表单属性等。 这些新特性都有兼容性问题&#xff0c;基本是 IE9 以上版本的浏览器才支持&#xff0c;如果不考虑兼容性问题&#xff0c;可以大量使用这些新特性。…

Bootstrap和Bagging算法以及衍生算法

1. Bootstrap算法 实际上就是一种针对小样本的无放回式的抽样方法&#xff0c;通过方差的估计可以构造置信区间。 其核心思想和基本步骤如下&#xff1a;   &#xff08;1&#xff09; 采用重抽样技术从原始样本中抽取一定数量&#xff08;自己给定&#xff09;的样本&#…

今日分享:中国石油年金系统交互、视觉设计和vue开发

金融系统交互及UI设计时&#xff0c;需注意简洁明了、色彩合理、字体统一、交互易用、安全感和用户控制。确保用户快速理解、安全操作并提升体验。

vue elementui 封装图片、视频集合组件 组件的基本应用

用红线框起来的地方理论上用的是同一套代码 那么就可以封装成组件 //父级页面的调用 <div class"title">活动缘起</div> <div class"grid-content bg-gray-light">...<pv-box v-if"form && form.originMaterials &…

汇聚荣电商实力好不好?

在数字化浪潮的推动下&#xff0c;电商平台如雨后春笋般涌现&#xff0c;而“汇聚荣”作为其中的一员&#xff0c;其综合实力自然成为业界与消费者关注的焦点。那么&#xff0c;汇聚荣电商的实力究竟如何呢?接下来&#xff0c;我们将从多个维度深入探讨这一问题。 一、品牌影响…

win10 修改远程桌面端口,在Win10上修改远程桌面端口的要怎么操作

在Windows 10上修改远程桌面端口是一个涉及系统配置的过程&#xff0c;这通常是为了增强安全性或满足特定网络环境的需要。 一、通过注册表编辑器修改远程桌面端口 1. 打开注册表编辑器&#xff1a; - 按下Win R组合键&#xff0c;打开“运行”对话框。 - 在“运行”对话框…

科技赋能冷链园区:可视化带来全新体验

应用图扑可视化技术&#xff0c;冷链园区能够更加直观地监控和管理资源&#xff0c;优化运作流程&#xff0c;提高运营效率与服务质量。