安装

python3 -m pip install pyppeteer

最好是py3.5+

手动安装

你懂的,天朝网络环境很复杂,如果要用pyppeteer自己绑定的chromium,半天都下载不下来,所以我们要手动安装,然后在程序里面指定executablePath

下载地址

模块介绍

启动pyppeteer.launch

launch 浏览器,可以传入一个字典来配置几个options,比如:

browser = await pyppeteer.launch({
'headless': False, # 关闭无头模式
'devtools': True, # 打开 chromium 的 devtools
// 'executablePath': '你下载的Chromium.app/Contents/MacOS/Chromiu', //# 浏览器的存放地址
'args': [
'--disable-extensions',
'--hide-scrollbars',
'--disable-bundled-ppapi-flash',
'--mute-audio',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
],
'dumpio': True,
})
browser = await launch({'headless': False,'dumpio':True, 'autoClose':False,'args': ['--no-sandbox', '--window-size=1366,850']})
await page.setViewport({'width':1366,'height':768})

注入脚本page.evaluate

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

我们会看到这一步非常关键,因为puppeteer出于政策考虑(这个词用的不是很好,就是那个意思)会设置window.navigator.webdrivertrue,告诉网站我是一个 webdriver 驱动的浏览器。有些网站比较聪明(反爬措施做得比较好),就会通过这个来判断对方是不是爬虫程序。

截获response和request

await page.setRequestInterception(True)
page.on('request', intercept_request)
page.on('response', intercept_response)

intercept_requestintercept_response相当于是注册的两个回调函数,在浏览器发出请求和获取到请求之前指向这两个函数。

比如可以这样禁止获取图片、多媒体资源和发起 websocket 请求:

async def intercept_request(req):
"""请求过滤"""
if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
await req.abort()
else:
await req.continue_()

然后每次获取到请求之后将内容打印出来(这里只打印了fetchxhr类型response 的内容):

async def intercept_response(res):
resourceType = res.request.resourceType
if resourceType in ['xhr', 'fetch']:
resp = await res.text()
print(resp)

一共有哪些resourceType,pyppeteer文档里面有:

页面自动下拉的代码

健壮的代码

请求钩子

async def request_check(req):
'''请求过滤'''
if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
await req.abort()
else:
await req.continue_() await page.setRequestInterception(True)
page.on('request', request_check)

判断加载是否完成

async def goto(page, url):
while True:
try:
await page.goto(url, {
'timeout': 0,
'waitUntil': 'networkidle0'
})
break
except (pyppeteer.errors.NetworkError,
pyppeteer.errors.PageError) as ex:
# 无网络 'net::ERR_INTERNET_DISCONNECTED','net::ERR_TUNNEL_CONNECTION_FAILED'
if 'net::' in str(ex):
await asyncio.sleep(10)
else:
rais

注入JS文件

from pathlib import Path

CURDIR = Path(__file__).parent
JS_AJAX_HOOK_LIB = str(CURDIR / 'static' / 'ajaxhook.min.js') await page.addScriptTag(path=JS_AJAX_HOOK_LIB)

这样注入的js文件不能有中文,因为 pyppeteer 里面打开文件用的是默认编码,可以 hook 住

open 函数来解决。

# 因为 pyppeteer 库里面 addScriptTag 用的是系统默认编码,导致js文件里面不能有中文
pyppeteer.frame_manager.open = lambda file: open(file, encoding='utf8')

完整的跑一个小项目

import asyncio
from pyppeteer import launch
import time async def main():exepath = 'C:/Users/tester02/AppData/Local/Google/Chrome/Application/chrome.exe'
browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})
page = await browser.newPage()
await page.setViewport({'width': 1366, 'height': 768})
await page.goto('http://192.168.2.66') // # 请求网址
await page.type("#Login_Name_Input", "test02") # 第一个参数是选择器,选中某个元素,第二个参数是要输入的内容
await page.type("#Login_Password_Input", "12345678", )
await page.waitFor(1000) # 停顿
await page.click("#Login_Login_Btn")
await page.waitFor(3000)
await browser.close()# 关闭浏览器 asyncio.get_event_loop().run_until_complete(main()) # 执行main函数

登陆谷歌

import asyncio
import time
from pyppeteer import launch async def gmailLogin(username, password, url):
#'headless': False如果想要浏览器隐藏更改False为True
# 127.0.0.1:1080为代理ip和端口,这个根据自己的本地代理进行更改,如果是vps里或者全局模式可以删除掉'--proxy-server=127.0.0.1:1080'
browser = await launch({'headless': False, 'args': ['--no-sandbox', '--proxy-server=127.0.0.1:1080']})
page = await browser.newPage()
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36') await page.goto(url) # 输入Gmail
await page.type('#identifierId', username)
# 点击下一步
await page.click('#identifierNext > content')
page.mouse # 模拟真实点击
time.sleep(10)
# 输入password
await page.type('#password input', password)
# 点击下一步
await page.click('#passwordNext > content > span')
page.mouse # 模拟真实点击
time.sleep(10)
# 点击安全检测页面的DONE
# await page.click('div > content > span')#如果本机之前登录过,并且page.setUserAgent设置为之前登录成功的浏览器user-agent了,
# 就不会出现安全检测页面,这里如果有需要的自己根据需求进行更改,但是还是推荐先用常用浏览器登录成功后再用python程序进行登录。 # 登录成功截图
await page.screenshot({'path': './gmail-login.png', 'quality': 100, 'fullPage': True})
#打开谷歌全家桶跳转,以Youtube为例
await page.goto('https://www.youtube.com')
time.sleep(10) if __name__ == '__main__':
username = '你的gmail包含@gmail.com'
password = r'你的gmail密码'
url = 'https://gmail.com'
loop = asyncio.get_event_loop()
loop.run_until_complete(gmailLogin(username, password, url))
# 代码由三分醉编写,网址www.sanfenzui.com,参考如下文章:
# https://blog.csdn.net/Chen_chong__/article/details/82950968

登陆淘宝

async def taobao_login(username, password, url):
"""
淘宝登录主程序
:param username: 用户名
:param password: 密码
:param url: 登录网址
:return: 登录cookies
"""
###
await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
page.mouse
time.sleep(1)
# 输入用户名,密码
await page.type('#TPL_username_1', username, {'delay': input_time_random() - 50}) # delay是限制输入的时间
await page.type('#TPL_password_1', password, {'delay': input_time_random()})
time.sleep(2)
# 检测页面是否有滑块。原理是检测页面元素。
slider = await page.Jeval('#nocaptcha', 'node => node.style') # 是否有滑块 if slider:
print('当前页面出现滑块')
# await page.screenshot({'path': './headless-login-slide.png'}) # 截图测试
flag, page = await mouse_slide(page=page) # js拉动滑块过去。
if flag:
await page.keyboard.press('Enter') # 确保内容输入完毕,少数页面会自动完成按钮点击
print("print enter", flag)
await page.evaluate('''document.getElementById("J_SubmitStatic").click()''') # 如果无法通过回车键完成点击,就调用js模拟点击登录按钮。
time.sleep(2)
cookies_list = await page.cookies()
print(cookies_list)
return await get_cookie(page) # 导出cookie 完成登陆后就可以拿着cookie玩各种各样的事情了。
else:
print("")
await page.keyboard.press('Enter')
print("print enter")
await page.evaluate('''document.getElementById("J_SubmitStatic").click()''')
await page.waitFor(20)
await page.waitForNavigation() try:
global error # 检测是否是账号密码错误
print("error_1:", error)
error = await page.Jeval('.error', 'node => node.textContent')
print("error_2:", error)
except Exception as e:
error = None
finally:
if error:
print('确保账户安全重新入输入')
# 程序退出。
loop.close()
else:
print(page.url)
return await get_cookie(page)

今日头条文章

import asyncio
from pyppeteer import launch async def main():
# headless参数设为False,则变成有头模式
browser = await launch(
# headless=False
) page = await browser.newPage() # 设置页面视图大小
await page.setViewport(viewport={'width':1280, 'height':800}) # 是否启用JS,enabled设为False,则无渲染效果
await page.setJavaScriptEnabled(enabled=True) await page.goto('https://www.toutiao.com/') # 打印页面cookies
print(await page.cookies()) # 打印页面文本
print(await page.content()) # 打印当前页标题
print(await page.title()) # 抓取新闻标题
title_elements = await page.xpath('//div[@class="title-box"]/a')
for item in title_elements:
# 获取文本
title_str = await (await item.getProperty('textContent')).jsonValue()
print(await item.getProperty('textContent'))
# 获取链接
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())

Pyppeteer和Puppeteer的不同点

  1. Pyppeteer支持字典和关键字传参,Puppeteer只支持字典传参
# Puppeteer只支持字典传参
browser = await launch({'headless': True})
# Pyppeteer支持字典和关键字传参
browser = await launch({'headless': True})
browser = await launch(headless=True)
  1. 元素选择器方法名 $变为querySelector
# Puppeteer使用$符
Page.$()/Page.$$()/Page.$x()
# Pyppeteer使用Python风格的函数名
Page.querySelector()/Page.querySelectorAll()/Page.xpath()
# 简写方式为:
Page.J(), Page.JJ(), and Page.Jx()
  1. Page.evaluate() 和 Page.querySelectorEval()的参数

Puppeteer的evaluate()方法使用JavaScript原生函数或JavaScript表达式字符串。Pyppeteer的evaluate()方法只使用JavaScript字符串,该字符串可以是函数也可以是表达式,Pyppeteer会进行自动判断。但有时会判断错误,如果字符串被判断成了函数,并且报错,可以添加选项force_expr=True,强制Pyppeteer作为表达式处理。

获取页面内容:

content = await page.evaluate('document.body.textContent', force_expr=True)

获取元素的内部文字:

element = await page.querySelector('h1')
title = await page.evaluate('(element) => element.textContent', element)

爬虫例子

# -*- coding: utf-8 -*-

import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq # 最好指定一下自己浏览器的位置,如果不指定会自动下载,太慢了...
executable_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" # 示例一: 渲染页面
async def crawl_page():
# 打开浏览器
browser = await launch(executablePath=executable_path) # 打开tab
page = await browser.newPage() # 输入网址回车
await page.goto('http://quotes.toscrape.com/js/') # 获取内容并解析
doc = pq(await page.content())
print('Quotes:', doc('.quote').length) # 关闭浏览器
await browser.close() # 示例二:截图,保存pdf,执行js
async def save_pdf():
browser = await launch(executablePath=executable_path)
page = await browser.newPage()
await page.goto('http://quotes.toscrape.com/js/') # 网页截图保存
await page.screenshot(path='example.png') # 网页导出 PDF 保存
await page.pdf(path='example.pdf') # 执行 JavaScript
dimensions = await page.evaluate('''() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}''') print(dimensions) await browser.close() if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(crawl_page())
# asyncio.get_event_loop().run_until_complete(save_pdf(

如何建立一个cookie池

必须多个账号,那么如何注册多个淘宝账号呢。。

  1. 可以通过第三方提供手机号验证码服务商,通过pyppeteer注册账号,保存账号信息
  2. 登录账号并保存在redis
  3. 开线程检查账号是否已过期,若过期重新登录即可

最新文章

  1. webuploader 断点续传
  2. bug:C#线程间操作无效: 从不是创建控件" XX" 的线程访问它
  3. appium 启动失败解决方案
  4. Spring MVC 下index.jsp访问
  5. 使用Ajax以及Jquery.form异步上传图片
  6. ACE编译运行错误解决
  7. dell 去鼠标版功能widnows
  8. ITU-T Technical Paper: IP网络测量模型
  9. Rabbit MQ 消息确认和持久化机制
  10. combination sum(I, II, III, IV)
  11. js时间戳转化成日期格式
  12. nginx 502错误 upstream sent too big header while reading response header from upstream
  13. keras tensorboard的使用
  14. jianx vtritualbox 虚拟镜像的体积
  15. How to ignore files and directories in subversion?
  16. Windows Server 2016 Essentials试用
  17. 深入研究嵌入式web服务器的视频监控应用
  18. sysbench 参数
  19. C#委托(匿名函数)的各种变形写法
  20. 【OCP|052】OCP最新题库解析系列-3

热门文章

  1. pcntl_waitpid函数解释
  2. jQuery插件—获取URL参数
  3. 给 textbox TextMode="password" 赋值后显示出来
  4. 【转载】Globelmposter勒索病毒最新变种预警
  5. leetcode903 Valid Permutations for DI Sequence
  6. 【CUDA开发】论CUDA和LAV解码器是否真的实用
  7. 【单元测试框架unittest】
  8. 从零开始学游戏开发(一):下载与安装UE4游戏引擎
  9. jira使用一:如何给项目分组、sprint并行、禁止发送消息给atlassian
  10. Java中String做为synchronized同步锁