安装
python3 -m pip install pyppeteer
最好是py3.5+javascript
手动安装java
你懂的,天朝网络环境很复杂,若是要用pyppeteer
本身绑定的chromium
,半天都下载不下来,因此咱们要手动安装,而后在程序里面指定executablePath
。node
下载地址python
模块介绍
启动pyppeteer.launch
launch 浏览器,能够传入一个字典来配置几个options,好比:web
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 驱动的浏览器。有些网站比较聪明(反爬措施作得比较好),就会经过这个来判断对方是否是爬虫程序。ajax
截获response和request
await page.setRequestInterception(True) page.on('request', intercept_request) page.on('response', intercept_response)
intercept_request
和intercept_response
至关因而注册的两个回调函数,在浏览器发出请求和获取到请求以前指向这两个函数。redis
好比能够这样禁止获取图片、多媒体资源和发起 websocket 请求:chrome
async def intercept_request(req): """请求过滤""" if req.resourceType in ['image', 'media', 'eventsource', 'websocket']: await req.abort() else: await req.continue_()
而后每次获取到请求以后将内容打印出来(这里只打印了fetch
和xhr
类型response 的内容):json
async def intercept_response(res): resourceType = res.request.resourceType if resourceType in ['xhr', 'fetch']: resp = await res.text() print(resp)
一共有哪些resourceType,pyppeteer文档里面有:api
页面自动下拉的代码
健壮的代码
请求钩子
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
- 开线程检查帐号是否已过时,若过时从新登陆便可