鱼C论坛

 找回密码
 立即注册
查看: 1356|回复: 0

[技术交流] Scrapy框架介绍之Puppeteer渲染

[复制链接]
发表于 2020-5-27 17:35:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1、Scrapy框架
Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。
框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。
Scrapy 使用了 Twisted'twɪstɪd异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。

                               
登录/注册后可看大图

Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。
Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,
Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器),
Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.
Downloader Middlewares(下载中间件):你可以当作是一个可以自定义扩展下载功能的组件。
Spider Middlewares(Spider中间件):你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)
2、Puppeteer渲染
Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,用来模拟 Chrome 浏览器的运行。
为了爬取js渲染的html页面,我们需要用浏览器来解析js后生成html。在scrapy中可以利用pyppeteer来实现对应功能。
完整代码 📎scrapy-pyppeteer.zip
我们需要新建项目中middlewares.py文件(./项目名/middlewares.py)
  1. import websockets
  2. from scrapy.http import HtmlResponse
  3. from logging import getLogger
  4. import asyncio
  5. import pyppeteer
  6. import logging
  7. from concurrent.futures._base import TimeoutError
  8. import base64
  9. import sys
  10. import random

  11. pyppeteer_level = logging.WARNING
  12. logging.getLogger('websockets.protocol').setLevel(pyppeteer_level)
  13. logging.getLogger('pyppeteer').setLevel(pyppeteer_level)

  14. PY3 = sys.version_info[0] >= 3


  15. def base64ify(bytes_or_str):
  16.     if PY3 and isinstance(bytes_or_str, str):
  17.         input_bytes = bytes_or_str.encode('utf8')
  18.     else:
  19.         input_bytes = bytes_or_str

  20.     output_bytes = base64.urlsafe_b64encode(input_bytes)
  21.     if PY3:
  22.         return output_bytes.decode('ascii')
  23.     else:
  24.         return output_bytes


  25. class ProxyMiddleware(object):
  26.     USER_AGENT = open('useragents.txt').readlines()

  27.     def process_request(self, request, spider):
  28.         # 代理服务器
  29.         proxyHost = "t.16yun.cn"
  30.         proxyPort = "31111"
  31.         # 代理隧道验证信息
  32.         proxyUser = "username"
  33.         proxyPass = "password"

  34.         request.meta['proxy'] = "http://{0}:{1}".format(proxyHost, proxyPort)

  35.         # 添加验证头
  36.         encoded_user_pass = base64ify(proxyUser + ":" + proxyPass)
  37.         request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass

  38.         # 设置IP切换头(根据需求)
  39.         tunnel = random.randint(1, 10000)
  40.         request.headers['Proxy-Tunnel'] = str(tunnel)
  41.         request.headers['User-Agent'] = random.choice(self.USER_AGENT)


  42. class PyppeteerMiddleware(object):
  43.     def __init__(self, **args):
  44.         """
  45.         init logger, loop, browser
  46.         :param args:
  47.         """
  48.         self.logger = getLogger(__name__)
  49.         self.loop = asyncio.get_event_loop()
  50.         self.browser = self.loop.run_until_complete(
  51.             pyppeteer.launch(headless=True))
  52.         self.args = args

  53.     def __del__(self):
  54.         """
  55.         close loop
  56.         :return:
  57.         """
  58.         self.loop.close()

  59.     def render(self, url, retries=1, script=None, wait=0.3, scrolldown=False, sleep=0,
  60.                timeout=8.0, keep_page=False):
  61.         """
  62.         render page with pyppeteer
  63.         :param url: page url
  64.         :param retries: max retry times
  65.         :param script: js script to evaluate
  66.         :param wait: number of seconds to wait before loading the page, preventing timeouts
  67.         :param scrolldown: how many times to page down
  68.         :param sleep: how many long to sleep after initial render
  69.         :param timeout: the longest wait time, otherwise raise timeout error
  70.         :param keep_page: keep page not to be closed, browser object needed
  71.         :param browser: pyppetter browser object
  72.         :param with_result: return with js evaluation result
  73.         :return: content, [result]
  74.         """

  75.         # define async render
  76.         async def async_render(url, script, scrolldown, sleep, wait, timeout, keep_page):
  77.             try:
  78.                 # basic render
  79.                 page = await self.browser.newPage()
  80.                 await asyncio.sleep(wait)
  81.                 response = await page.goto(url, options={'timeout': int(timeout * 1000)})
  82.                 if response.status != 200:
  83.                     return None, None, response.status
  84.                 result = None
  85.                 # evaluate with script
  86.                 if script:
  87.                     result = await page.evaluate(script)

  88.                 # scroll down for {scrolldown} times
  89.                 if scrolldown:
  90.                     for _ in range(scrolldown):
  91.                         await page._keyboard.down('PageDown')
  92.                         await asyncio.sleep(sleep)
  93.                 else:
  94.                     await asyncio.sleep(sleep)
  95.                 if scrolldown:
  96.                     await page._keyboard.up('PageDown')

  97.                 # get html of page
  98.                 content = await page.content()

  99.                 return content, result, response.status
  100.             except TimeoutError:
  101.                 return None, None, 500
  102.             finally:
  103.                 # if keep page, do not close it
  104.                 if not keep_page:
  105.                     await page.close()

  106.         content, result, status = [None] * 3

  107.         # retry for {retries} times
  108.         for i in range(retries):
  109.             if not content:
  110.                 content, result, status = self.loop.run_until_complete(
  111.                     async_render(url=url, script=script, sleep=sleep, wait=wait,
  112.                                  scrolldown=scrolldown, timeout=timeout, keep_page=keep_page))
  113.             else:
  114.                 break

  115.         # if need to return js evaluation result
  116.         return content, result, status

  117.     def process_request(self, request, spider):
  118.         """
  119.         :param request: request object
  120.         :param spider: spider object
  121.         :return: HtmlResponse
  122.         """
  123.         if request.meta.get('render'):
  124.             try:
  125.                 self.logger.debug('rendering %s', request.url)
  126.                 html, result, status = self.render(request.url)
  127.                 return HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8',
  128.                                     status=status)
  129.             except websockets.exceptions.ConnectionClosed:
  130.                 pass

  131.     @classmethod
  132.     def from_crawler(cls, crawler):
  133.         return cls(**crawler.settings.get('PYPPETEER_ARGS', {}))
复制代码

然后修改项目配置文件 (./项目名/settings.py)
  1. DOWNLOADER_MIDDLEWARES = {
  2.         'scrapypyppeteer.middlewares.PyppeteerMiddleware': 543,

  3.         'scrapypyppeteer.middlewares.ProxyMiddleware': 100,        
  4.     }
复制代码

然后我们运行程序

                               
登录/注册后可看大图

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-20 13:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表