爬虫的新模块pyppeteer的使用
安装
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.webdriver
为true
,告诉网站我是一个 webdriver 驱动的浏览器。有些网站比较聪明(反爬措施做得比较好),就会通过这个来判断对方是不是爬虫程序。
截获response和request
await page.setRequestInterception(True)
page.on('request', intercept_request)
page.on('response', intercept_response)
intercept_request
和intercept_response
相当于是注册的两个回调函数,在浏览器发出请求和获取到请求之前指向这两个函数。
比如可以这样禁止获取图片、多媒体资源和发起 websocket 请求:
async def intercept_request(req):
"""请求过滤"""
if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
await req.abort()
else:
await req.continue_()
然后每次获取到请求之后将内容打印出来(这里只打印了fetch
和xhr
类型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的不同点
- Pyppeteer支持字典和关键字传参,Puppeteer只支持字典传参
# Puppeteer只支持字典传参
browser = await launch({'headless': True})
# Pyppeteer支持字典和关键字传参
browser = await launch({'headless': True})
browser = await launch(headless=True)
- 元素选择器方法名 $变为querySelector
# Puppeteer使用$符
Page.$()/Page.$$()/Page.$x()
# Pyppeteer使用Python风格的函数名
Page.querySelector()/Page.querySelectorAll()/Page.xpath()
# 简写方式为:
Page.J(), Page.JJ(), and Page.Jx()
- 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池
必须多个账号,那么如何注册多个淘宝账号呢。。
- 可以通过第三方提供手机号验证码服务商,通过pyppeteer注册账号,保存账号信息
- 登录账号并保存在redis
- 开线程检查账号是否已过期,若过期重新登录即可
最新文章
- webuploader 断点续传
- bug:C#线程间操作无效: 从不是创建控件"; XX"; 的线程访问它
- appium 启动失败解决方案
- Spring MVC 下index.jsp访问
- 使用Ajax以及Jquery.form异步上传图片
- ACE编译运行错误解决
- dell 去鼠标版功能widnows
- ITU-T Technical Paper: IP网络测量模型
- Rabbit MQ 消息确认和持久化机制
- combination sum(I, II, III, IV)
- js时间戳转化成日期格式
- nginx 502错误 upstream sent too big header while reading response header from upstream
- keras tensorboard的使用
- jianx vtritualbox 虚拟镜像的体积
- How to ignore files and directories in subversion?
- Windows Server 2016 Essentials试用
- 深入研究嵌入式web服务器的视频监控应用
- sysbench 参数
- C#委托(匿名函数)的各种变形写法
- 【OCP|052】OCP最新题库解析系列-3
热门文章
- pcntl_waitpid函数解释
- jQuery插件—获取URL参数
- 给 textbox TextMode=";password"; 赋值后显示出来
- 【转载】Globelmposter勒索病毒最新变种预警
- leetcode903 Valid Permutations for DI Sequence
- 【CUDA开发】论CUDA和LAV解码器是否真的实用
- 【单元测试框架unittest】
- 从零开始学游戏开发(一):下载与安装UE4游戏引擎
- jira使用一:如何给项目分组、sprint并行、禁止发送消息给atlassian
- Java中String做为synchronized同步锁