Pyppeteer原理介绍和入门尝试

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 launchasync 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 launchasync 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'})# 打印页面cookiesprint(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)# 获取所有文本内容  执行 jscontent = 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 = 0height = 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 += 500else: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 launchasync 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.mzph.cn/news/859967.shtml

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

相关文章

测试的基础知识大全【测试概念、分类、模型、流程、测试用例书写、用例设计、Bug、基础功能测试实战】

测试基础笔记 Day01阶段⽬标⼀、测试介绍⼆、测试常⽤分类2.1 阶段划分单元测试集成测试系统测试验收测试 2.2 代码可⻅度划分⿊盒测试&#xff1a;主要针对功能&#xff08;阶段划分->系统测试&#xff09;灰盒测试&#xff1a;针对接⼝测试&#xff08;阶段划分->集成测…

【UEFI实战】HttpBoot

环境配置 首先下载tftpd工具&#xff0c;可以在phjounin / tftpd64 / Downloads — Bitbucket下载到&#xff0c;建议不要安装到C盘&#xff0c;因为可能无法修改其配置。配置tftpd工具的DHCP服务&#xff1a; 注意这里的IP地址需要跟实际网卡IP匹配。 下载Apache&#xff0c…

【TensorRT】TensorRT C# API 项目更新 (2):优化安装方式和代码

1. 项目介绍 NVIDIA TensorRT™ 是一款用于高性能深度学习推理的 SDK&#xff0c;包括深度学习推理优化器和运行时&#xff0c;可为推理应用程序提供低延迟和高吞吐量。基于 NVIDIA TensorRT 的应用程序在推理过程中的执行速度比纯 CPU 平台快 36 倍&#xff0c;使您能够优化在…

扣子/coze智能体开发的经验与避坑指南

近期&#xff0c;我计划几场关于分享智能体应用开发的活动。因此&#xff0c;我顺便总结了我在创建智能体过程中遇到的问题和解决方案&#xff0c;帮助大家避免类似的陷阱&#xff0c;提高智能体的性能和用户体验。以下是我总结的几点关键经验。 1. 人设与回复逻辑的提示词 在…

《C++ Primer》导学系列:第 8 章 - IO库

8.1 IO类 C标准库提供了一套丰富的输入输出&#xff08;IO&#xff09;类&#xff0c;用于处理数据的输入输出操作。这些类位于<iostream>头文件中&#xff0c;包括处理标准输入输出的istream和ostream类&#xff0c;处理文件输入输出的ifstream和ofstream类&#xff0c…

索引的分类和回表查询——Java全栈知识(29)

索引的分类和回表查询 Mysql 的索引按照类型可以分为以下几类&#xff0c;但是我们使用的 InnoDB 只支持主键索引&#xff0c;唯一索引&#xff0c;普通索引&#xff0c;并不支持全文索引。 1、聚集索引和二级索引 InnoDB 可以将索引分为两类分别是聚集索引和二级索引&…

编译原理大题自解(活前缀DFA、LR(0)分析表)

目录 4. (简答题) &#xff08;1&#xff09;给出识别活前缀的DFA &#xff08;2&#xff09;设计此文法的 LR(0)分析表 第一种解法 第二种解放 首先声明这是作者的写法&#xff08;不保证正确&#xff01;&#xff09;仅供参考。本题因为可能存在冲突的原因&#xff0c;所…

SpringCloud分布式微服务链路追踪方案:Zipkin

创作博客的目的是希望将自己掌握的知识系统地整理一下&#xff0c;并以博客的形式记录下来。这不仅是为了帮助其他有需要的人查阅相关内容&#xff0c;也是为了自己能够更好地巩固和加深对这些知识的理解。创作的时候也是对自己所学的一次复盘和总结&#xff0c;在创作的过程中…

【例子】webpack配合babel实现 es6 语法转 es5 案例 [通俗易懂]

首先来说一下实现 es6 转 es5 的一个简单步骤 1、新建一个项目&#xff0c;并且在命令行中初始化项目 npm init -y2、安装对应版本的 webpack webpack-cli(命令行工具) "webpack""webpack-cli"3、安装 Babel 核心库和相关的 loader "babel-core&qu…

PasteSpiderFile文件同步管理端使用说明(V24.6.21.1)

PasteSpider作为一款适合开发人员的部署管理工具&#xff0c;特意针对开发人员的日常情况做了一个PasteSpiderFile客户端&#xff0c;用于windows上的开发人员迅速的更新发布自己的最新代码到服务器上&#xff01; 虽然PasteSpider也支持svn/git的源码拉取&#xff0c;自动编译…

【自然语言处理系列】安装nltk_data和punkt库(亲测有效)

目录 一、下载nltk_data-gh-pages.zip数据文件 二、将nltk_data文件夹移到对应的目录 三、测试 四、成功调用punkt库 问题&#xff1a; 解决方案&#xff1a; 在使用自然语言处理库nltk时&#xff0c;许多初学者会遇到“nltk.download(punkt)”无法正常下载的问题。本…

Android Media Framework(七)MediaCodecService

Android引入Treble架构后&#xff0c;OpenMAX框架以HIDL Service的形式为System分区提供服务&#xff0c;本文将探讨该服务是如何启动&#xff0c;服务提供了什么内容&#xff0c;以及服务是如何被应用层所使用的。 1 概述 在Android的Treble架构中&#xff0c;为了确保系统的…

面试经典150题

打家劫舍 class Solution { public:int rob(vector<int>& nums) {int n nums.size();if(n 1){return nums[0];}vector<int> dp(n, 0);dp[0] nums[0];//有一间房可以偷//有两间房可以偷if(nums[1] > nums[0]){dp[1] nums[1];}else{dp[1] nums[0];}for …

react18 实现具名插槽

效果预览 技术要点 当父组件给子组件传递的 JSX 超过一个标签时&#xff0c;子组件接收到的 children 是一个数组&#xff0c;通过解析数组中各 JSX 的属性 slot &#xff0c;即可实现具名插槽的分发&#xff01; 代码实现 Father.jsx import Child from "./Child";…

【D3.js in Action 3 精译】第一部分 D3.js 基础知识

第一部分 D3.js 基础知识 欢迎来到 D3.js 的世界&#xff01;可能您已经迫不及待想要构建令人惊叹的数据可视化项目了。我们保证&#xff0c;这一目标很快就能达成&#xff01;但首先&#xff0c;我们必须确保您已经掌握了 D3.js 的基础知识。这一部分提到的概念将会在您后续的…

探秘神经网络激活函数:Sigmoid、Tanh和ReLU,解析非线性激活函数的神奇之处

引言 在神经网络中&#xff0c;激活函数扮演着至关重要的角色。它们赋予神经网络非线性的能力&#xff0c;使得网络具备学习和表示复杂函数关系的能力。本文将详细解析三种常见的激活函数&#xff1a;Sigmoid、Tanh和ReLU&#xff0c;揭开它们在神经网络中的奥秘。无论你是初学…

【十一】【QT开发应用】模拟腾讯会议登录界面设计UI

ui 加入会议的样式表 QPushButton { /* 前景色 */ color:#0054E6; /* 背景色 */ background-color:rgb(255,255,255); /* 边框风格 */ border-style:outset; /* 边框宽度 */ border-width:0.5px; /* 边框颜色 */ border-color:gray; /* 边框倒角 */ border-radius…

日常-----最爱的人

今日话题 大家好嗷&#xff0c;今天聊的技术可比之前的重要的多啊&#xff0c;哼哼&#xff0c;也不是今天&#xff0c;大家像我看齐嗷&#xff0c;我宣布个事情&#xff01;&#xff01;&#xff01; 于2024年6月21日晚上&#xff0c;本人遇到了这一生最爱的人 嘿嘿 这种事…

微信小程序 引入MiniProgram Design失败

这tm MiniProgramDesign 是我用过最垃圾的框架没有之一 我按照官网的指示安装居然能安装不成功,牛! 这里说明我是用js开发的 到以上步骤没有报错什么都没有,然后在引入组件的时候报错 Component is not found in path “./miniprogram _npm/vant/weapp/button/index” (using…

CSS阴影优化气泡框样式

<body> <div class"pop">气泡框</div> </body>body{display: flex;justify-content: center;align-items: center;height: 100% } .pop{display: flex;justify-content: center;align-items: center;background: #409eff;width: 150px;heigh…