【问题标题】:Build scraper REST API server with pyppeteer or selenium使用 pyppeteer 或 selenium 构建爬虫 REST API 服务器
【发布时间】:2021-05-19 05:27:20
【问题描述】:

我需要创建一个服务器,通过从指定站点获取抓取的数据,我可以向其发出 REST 请求。

例如这样的网址:

http://myip/scraper?url=www.exampe.com&token=0

我必须抓取一个内置 javascript 的网站,该网站可以识别它是由真实浏览器还是无头浏览器打开的。

唯一的选择是 selenium 或 pyppeteer 和一个 virtualDisplay。

我目前使用 selenium 和 FastAPI,但它不是一个有很多请求的可用解决方案。对于每个请求 chrome 的打开和关闭,这都会大大延迟响应并使用大量资源。

使用 pyppeteer async,您可以在同一个浏览器实例上同时打开多个选项卡,从而减少响应时间。但这可能会在多个选项卡之后导致其他问题。

我正在考虑创建一个浏览器实例池,在该池上将各种请求划分为puppeteer-cluster

但到目前为止我还没有弄清楚。

我目前正在为浏览器尝试此代码:

import json
from pyppeteer import launch
from strings import keepa_storage


class Browser:
    async def __aenter__(self):
        self._session = await launch(headless=False, args=['--no-sandbox', "--disable-gpu", '--lang=it',
                                                     '--disable-blink-features=AutomationControlled'], autoClose=False)
        return self

    async def __aexit__(self, *err):
        self._session = None

    async def fetch(self, url):
        page = await self._session.newPage()
        page_source = None
        try:
            await page.goto("https://example.com/404")

            for key in keepa_storage:
                await page.evaluate(
                    "window.localStorage.setItem('{}',{})".format(key, json.dumps(example_local_storage.get(key))))

            await page.goto(url)
            await page.waitForSelector('#tableElement')
            page_source = await page.content()
            
        except TimeoutError as e:
            print(f'Timeout for: {url}')
        finally:
            await page.close()
            return page_source

这个请求的代码:

async with Browser() as http:
    source = await asyncio.gather(
        http.fetch('https://example.com')
    )

但我不知道如何为多个服务器请求重用相同的浏览器会话

【问题讨论】:

  • 就在几天前,我遇到了类似的问题。我们通过使用 Manager-Worker 形成来解决它。经理维护工人队列的地方。每个工作人员基本上都是无头模式下的 chromedriver 实例。一旦经理收到请求,它就会派出一名工作人员来消费该项目。成功完成后,它将工作人员放回队列,否则重新启动工作人员并将其放回队列。
  • 这个想法理论上还不错,但总是为每个chromedriver实例只打开一个链接。我想尝试为每个浏览器实例打开更多的 url。你有你工作的例子吗?
  • 我们可以在每个 Chromedriver 中打开多个标签页。我不能分享整个代码。对此感到抱歉。过段时间我会分享这两个类的整体界面。
  • 谢谢,同时我试着看看怎么做。

标签: python selenium fastapi pyppeteer


【解决方案1】:

在初始化服务器时,创建一个Manager 对象。根据实现manager 自动产生所有需要的Worker。在 API 实现方法中调用manager.assign(item)。这应该得到一个空闲的工作人员并将项目分配给它。如果此时没有工作人员空闲,由于manager._AVAILABLE_WORKERQueue 性质,它应该等到工作人员可用。在不同的线程上创建一个无限循环并调用manager.heartbeat() 以确保工人没有懈怠。

我已经在评论部分提到了每种方法的目的和它应该做什么。这应该足以让你开始。如果需要进一步说明,请随时告诉我。

import Queue

class Worker:
    ###
    # class to define behavior and parameters of workers
    ###

    def __init__(self, base_url):
        ###
        # Initialises a worker
        # STEP 1. Create one worker with given inputs
        # STEP 2. Mark the worker busy
        # STEP 3. Get ready for item consumption with initialisation/login process done
        # STEP 4. Mark the worker available and active
        ###
        raise NotImplementedError()

    def process_item(self, **item):
        ###
        # Worker processes the given item and returns data to manager
        # Step 1. worker marks himself busy
        # Step 2. worker processes the item. Handle Errors here
        # Step 3. worker marks himself available
        # Step 4. Return the data scraped
        ###
        raise NotImplementedError()

class Manager:
    ###
    # class for manager who supervises all the workers and assigns work to them
    ###

    def __init__(self):
        self._WORKERS = set()  # set container to hold all the workers details
        self._AVAILABLE_WORKERS = Queue(maxsize=10)  # queue container to hold available workers
        # create all the worker we want and add them to self._WORKERS and self._AVAILABLE_WORKERS

    def assign(self, item):
        ###
        # Assigns an item to a worker to be processed and once processed returns data to the server
        # STEP 1. remove worker from available pool
        # STEP 2. assign item to worker
        # STEP 3A. if item is successfully processed, put the worker back to available pool
        # STEP 3B. if error occurred during item processing, try to reset the worker and put the worker back to
        # available pool
        ###
        raise NotImplementedError()

    def heartbeat(self):
        ###
        # process to check if all the workers are active and accounted for at particular interval.
        # if the worker is available but not in the pool add it to the pool after checking if it's not busy
        # if the worker is not active then reset the worker and add it to the pool
        ###
        raise NotImplementedError()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多