Pyppeteer自动测试框架

Pyppeteer的文档地址: https://miyakogi.github.io/pyppeteer/#

安装

首先就是安装问题了,由于 Pyppeteer 采用了 Python 的 async 机制,所以其运行要求的 Python 版本为 3.5 及以上。
​
安装方式非常简单:
​
pip3 install pyppeteer
好了,安装完成之后我们命令行下测试下:
​
import pyppeteer
如果没有报错,那么就证明安装成功了。
 
 
python -c 'import pyppeteer; pyppeteer.chromium_downloader.download_chromium()'   直接下载pyppeteer

快速上手

import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
​
async def main():
    browser = await launch()
    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()
​
asyncio.get_event_loop().run_until_complete(main())
那么这里面的过程发生了什么?
​
实际上,Pyppeteer 整个流程就完成了浏览器的开启、新建页面、页面加载等操作。另外 Pyppeteer 里面进行了异步操作,所以需要配合 async/await 关键词来实现。
​
首先, launch 方法会新建一个 Browser 对象,然后赋值给 browser,然后调用 newPage  方法相当于浏览器中新建了一个选项卡,同时新建了一个 Page 对象。然后 Page 对象调用了 goto 方法就相当于在浏览器中输入了这个 URL,浏览器跳转到了对应的页面进行加载,加载完成之后再调用 content 方法,返回当前浏览器页面的源代码。然后进一步地,我们用 pyquery 进行同样地解析,就可以得到 JavaScript 渲染的结果了。
​
另外其他的一些方法如调用 asyncio 的 get_event_loop 等方法的相关操作则属于 Python 异步 async 相关的内容了,大家如果不熟悉可以了解下 Python 的 async/await 的相关知识。
​
好,通过上面的代码,我们就可以完成 JavaScript 渲染页面的爬取了。

网站主要通过 window.navigator.webdriver 来对 webdriver 进行检测,所以我们只需要使用 JavaScript 将它设置为 false 即可,代码如下:


class GetJsEncryptPage():def __init__(self):
        self.loop = asyncio.get_event_loop()
        self.log = ICrawlerLog('spider').save
​
    async def main(self, url, ):  # 定义main协程函数,
        # 以下使用await 可以针对耗时的操作进行挂起
        browser = await launch({'headless': True, 'args': ['--no-sandbox', '--disable-infobars',
                                                           # '--proxy-server={}'.format(get_ip()),
                                                           ],})  # 启动pyppeteer 属于内存中实现交互的模拟器
        page = await browser.newPage()  # 启动个新的浏览器页面标签
        await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
        cookies = {}
        try:
            await page.goto(url)  # 访问页面
            # 始终让window.navigator.webdriver=false
            # navigator是windiw对象的一个属性,同时修改plugins,languages,navigator 且让
            # await page.setJavaScriptEnabled(enabled=True)  # 使用 JS 渲染
            await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')  # 以下为插入中间js,将淘宝会为了检测浏览器而调用的js修改其结果。
            await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
            await page.goto(url)  # 访问页面
            # content = await page.content()  # 获取页面内容
            await asyncio.sleep(2)
        except:
            await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
            await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
            await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
            # await page.evaluate('window.open("{}");'.format(url))
            await page.evaluate('window.location="{}";'.format(url))
            # await page.goto(url)  # 访问登录页面
        try:
            cookies = await self.get_cookie(page)
        except Exception as e:
            await browser.close()
        finally:
            await browser.close()return cookies
​
    async def get_cookie(self, page):
        # res = await page.content()
        cookies_list = await page.cookies()
        cookies = {}
        for cookie in cookies_list:
            cookies[cookie.get('name')] =  cookie.get('value')
        return cookies
​
    def retry_if_result_none(self, result):
        return result is Nonedef input_time_random(self, ):
        return random.randint(100, 151)def work(self, url):
        passdef run(self, url, func):
        result = {}
        try:
            # task = asyncio.wait([])
            result = self.loop.run_until_complete(func(url))  # 将协程注册到事件循环,并启动事件循环
        except Exception as e:
            self.log.info('协程被动结束, chrome关闭')
            for task in asyncio.Task.all_tasks():
                task.cancel()
                self.loop.stop()
                self.loop.run_forever()
        # self.loop.close()
        return result

需求一: 请求网页时, 遇到到网络异常 --> 自定义page.goto请求

async def goto(self, 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:
                    raise

需求二: Pyppeteer执行JS 获取返回值

修改浏览器属性
async def change_status(self, page):  
        await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
        await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {},  }; }''')
        await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
        await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
        
----------------------------------------   
执行JS
await page.evaluate('''() =>{ return 'python' } }) }''')

需求三: page.on监听请求与响应, 并对请求和响应进行修改和过滤

page.on(event, function) ,指定监听事件, 与处理函数
例如: page.on(‘request’, intercept_response)

请求处理函数

async def request_check(req):
        '''请求过滤'''
        if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
            await req.abort()
        else:
            await req.continue_()# 响应处理函数
async def intercept_response(res):
        resourceType = res.request.resourceType
        if resourceType in ['image', 'media']:
            resp = await res.text()
            print(resp)

需求四: 进行网页截图

# 依据Xpath 进行网页截图
async def get_picture(page, xpath):
        # 进行截图
        picture = ''
        try:
            for _ in range(6):
                tdContent = await page.xpath(xpath)
                clip = await tdContent[0].boundingBox()
                picture = base64.b64encode(await page.screenshot({
                    'path': './dashboard_shot.png',                  # 图片路径, 不指定就不保存
                    'clip': clip,                                     # 指定图片位置,大小
                    # 'encoding': 'base64',                           #  返回的图片格式, 默认二进制
                }))
                if picture != '':
                    break
        except Exception as e:
            self.log.info('截图获取失败')
        return picture

需求五: 初始化, 设置代理IP, 设置浏览器UA, 设置浏览器大小, 设置无头浏览

browser = await launch({'headless': True, 'timeout': 500, 'args': ['--disable-extensions',
                                                                           '--hide-scrollbars',
                                                                           '--disable-bundled-ppapi-flash',
                                                                           '--mute-audio',
                                                                           '--no-sandbox',  
                                                                           '--disable-setuid-sandbox',
                                                                           '--disable-gpu',  
                                                                           '--proxy-server={}'.format(get_ip()),
                                                                           ], })
page = await browser.newPage()
await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
await page.setViewport({'width': 1000,'height': 3480,})

需求六: 获取浏览器依据加载的图片内容, Selenium与Pyppeteer相同

执行JS, 返回图片的二进制的Base64编码, 参照: https://www.w3ctech.com/topic/767
'''
() => {
var img = document.getElementById("%s");
var canvas = document.createElement("canvas");
canvas.width = %s;
canvas.height = %s;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");}''' % (id, width, height)

需求七: 切换浏览器的tag

在Pyppeteer中每一个标签页就是一个page对象, 切换page对象就是切换标签页
for _page in await browser.pages() :
   if _page != page:
      await _page.close()
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值