yiniuyun 发表于 2020-8-28 17:25:47

渲染神器-Puppeteer


我们经常在采集数据的时候都会遇到一些动态的网页,而且内容比较有难度。对于一些大型网站来说我们是很有必要进行接口的分析的,可是对于很多的小网站来说这样分析接口就显得太浪费时间了,这时我们就可以借助一些渲染器,这样就容易的多了。接下来给大家推介一个渲染神器那就是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 >= 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,       """      # 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 = * 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,          } 然后我们运行程序对于浏览器的渲染还有很多地方可以学习,希望多多分享呀。
页: [1]
查看完整版本: 渲染神器-Puppeteer