未来索引
开启左侧

渲染神器-Puppeteer

[复制链接]
yiniuyun 发表于 2020-8-28 17:25:47 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我们经常在采集数据的时候都会遇到一些动态的网页,而且内容比较有难度。对于一些大型网站来说我们是很有必要进行接口的分析的,可是对于很多的小网站来说这样分析接口就显得太浪费时间了,这时我们就可以借助一些渲染器,这样就容易的多了。接下来给大家推介一个渲染神器那就是Puppeteer,它是由 Google 官方推出的,经过一段时间的学习和实践个人感觉还是很稳定和好用的。下面分享下实践的例子
我们需要新建项目中middlewares.py文件(./项目名/middlewares.py)
import websockETSfrom scrapy.http import HtmlResponsefrom logging import getLoggerimport asyncioimport pyppeteerimport loggingfrom concurrent.futures._base import TimeoutErrorimport base64import sysimport randompyppeteer_level = logging.WARNINGlogging.getLogger('websockets.protocol').setLevel(pyppeteer_level)logging.getLogger('pyppeteer').setLevel(pyppeteer_level)PY3 = sys.version_info[0] >= 3def base64ify(bytes_or_str):    if PY3 and isinstance(bytes_or_str, str):        input_bytes = bytes_or_str.encode('utf8')    else:        input_bytes = bytes_or_str    output_bytes = base64.urlsafe_b64encode(input_bytes)    if PY3:        return output_bytes.decode('ascii')    else:        return output_bytesclass ProxyMiddleware(object):    USER_AGENT = open('useragents.txt').readlines()    def process_request(self, request, spider):        # 代理服务器        proxyHost = "t.16yun.cn"        proxyPort = "31111"        # 代理隧道验证信息        proxyUser = "username"        proxyPass = "password"        request.meta['proxy'] = "http://{0}:{1}".format(proxyHost, proxyPort)        # 添加验证头        encoded_user_pass = base64ify(proxyUser + ":" + proxyPass)        request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass        # 设置IP切换头(根据需求)        tunnel = random.randint(1, 10000)        request.headers['Proxy-Tunnel'] = str(tunnel)        request.headers['User-Agent'] = random.choice(self.USER_AGENT)class PyppeteerMiddleware(object):    def __init__(self, **args):        """        init logger, loop, browser        :param args:        """        self.logger = getLogger(__name__)        self.loop = asyncio.get_event_loop()        self.browser = self.loop.run_until_complete(            pyppeteer.launch(headless=True))        self.args = args    def __del__(self):        """        close loop        :return:        """        self.loop.close()    def render(self, url, retries=1, script=None, wait=0.3, scrolldown=False, sleep=0,               timeout=8.0, keep_page=False):        """        render page with pyppeteer        :param url: page url        :param retries: max retry times        :param script: js script to evaluate        :param wait: number of seconds to wait before loading the page, preventing timeouts        :param scrolldown: how many times to page down        :param sleep: how many long to sleep after initial render        :param timeout: the longest wait time, otherwise raise timeout error        :param keep_page: keep page not to be closed, browser object needed        :param browser: pyppetter browser object        :param with_result: return with js evaluation result        :return: content, [result]        """        # define async render        async def async_render(url, script, scrolldown, sleep, wait, timeout, keep_page):            try:                # basic render                page = await self.browser.newPage()                await asyncio.sleep(wait)                response = await page.goto(url, options={'timeout': int(timeout * 1000)})                if response.status != 200:                    return None, None, response.status                result = None                # evaluate with script                if script:                    result = await page.evaluate(script)                # scroll down for {scrolldown} times                if scrolldown:                    for _ in range(scrolldown):                        await page._keyboard.down('PageDown')                        await asyncio.sleep(sleep)                else:                    await asyncio.sleep(sleep)                if scrolldown:                    await page._keyboard.up('PageDown')                # get html of page                content = await page.content()                return content, result, response.status            except TimeoutError:                return None, None, 500            finally:                # if keep page, do not close it                if not keep_page:                    await page.close()        content, result, status = [None] * 3        # retry for {retries} times        for i in range(retries):            if not content:                content, result, status = self.loop.run_until_complete(                    async_render(url=url, script=script, sleep=sleep, wait=wait,                                 scrolldown=scrolldown, timeout=timeout, keep_page=keep_page))            else:                break        # if need to return js evaluation result        return content, result, status    def process_request(self, request, spider):        """        :param request: request object        :param spider: spider object        :return: HtmlResponse        """        if request.meta.get('render'):            try:                self.logger.debug('rendering %s', request.url)                html, result, status = self.render(request.url)                return HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8',                                    status=status)            except websockets.exceptions.ConnectionClosed:                pass    @classmethod    def from_crawler(cls, crawler):        return cls(**crawler.settings.get('PYPPETEER_ARGS', {}))


然后修改项目配置文件 (./项目名/settings.py)
DOWNLOADER_MIDDLEWARES = {
        'scrapypyppeteer.middlewares.PyppeteerMiddleware': 543,
        'scrapypyppeteer.middlewares.ProxyMiddleware': 100,        
    }
然后我们运行程序
1589263259438-dc052a4e-d968-4fda-bd6a-ee435e65e06c.png
对于浏览器的渲染还有很多地方可以学习,希望多多分享呀。

智能技术共享平台 - 未来论 http://www.mywll.com/
高级模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题
推荐阅读 更多
阅读排行 更多
广告位
!jz_fbzt! !jz_sgzt! !jz_xgzt! 快速回复 !jz_sctz! !jz_fhlb! 搜索

智能技术共享平台 - 未来论

关注服务号

进入小程序

全国服务中心:

运维中心:天津

未来之家:天津 青岛 济南 郑州 石家庄

                商务邮箱:xy@mywll.com

Copyright © 2012-2021 未来派 未来论 (津ICP备16000236号-5)