【源码解读】如何充分发挥 Scrapy 的异步能力

作为一个易上手的高性能爬虫框架,Scrapy 使用 Twisted 异步网络框架处理并发请求。

但是,在日常工作和面试过程中,经常发现有些同学会笃定地认为 Scrapy 采用的是多线程并发模型。实际上,虽然 Twisted 框架提供了线程池支持,但是其核心网络部分处理逻辑依赖的是「单线程 IO 多路复用」技术,在 Linux 平台上,是围绕 epoll() 系统调用实现的 Reactor 模式。

为了利用好 Scrapy 的异步任务能力,避免写出 “使用 urllib 和 requests 库完成 HTTP 请求” 这样的错误代码,本文将 Scrapy 各个组件的异步能力及可以使用什么样的异步技术进行一些总结。

可扩展组件:

Spider Middleware - 它是处于 EngineSpider 之间的组件,可以用于处理 Spider 的输入 (response)和输出(itemrequest)。它一般可以用于:处理 Spider 回调函数的输出,可以用于修改、增加和删除 request 或者 item;处理 Spider.start_requests() 函数生成的 request;捕捉 Spider 回调函数抛出的异常等等。用户自己实现的 Spider Middleware 可以定义一个或多个如下方法:

  • process_spider_input(response, spider) - 每个响应 repsonse 进入 Spider 回调函数之前可由该 方法处理。

  • process_spider_output(response, result, spider) - Spider 处理完响应 response 产生的结果 result 可经该方法处理。

  • process_spider_exception(response, exception, spider) - Spider 回调函数、其它 Spider Middlewareprocess_spider_input 方法抛出的异常可由该方法处理。

  • process_start_requests(start_requests, spider) - Spider 启动后由 start_requests() 方法产生 的 Request 可经方法处理。

Downloader Middleware - 它是处于 EngineDownloader 之间的组件,可以用于处理从 Engine 传递 给 Downloaderrequest 和从 Downloader 传递给 Engineresponse。它一般可用于:处理即将发到网络上的请求;修改传递即将给 Spider 的响应数据;丢掉响应数据,然后生成一个新的请求;根据请求凭空构造一个响 应(并不发出实际的请求);丢弃某些请求等等。用户自己实现的 Downloader Middleware 可以定义一个或多个如下 方法:

  • process_request(request, spider) - 这个方法可以处理每一个经过该中件间的 request。它可以返回NoneResponse 实例、Request 实例或者抛出 IgnoreRequest 异常。

  • process_responsee(response, spider) -这个方法可以处理每一个经过该中件间的 response。它可以返回Response 实例、Request 实例或者抛出 IgnoreRequest 异常。

  • process_exception(request, exception, spider) - 这个方法可以处理下载器或者 Downloader Middlewareprocess_request 抛出的包括 IgnoreRequest 在内的所有异常。它可以返回 NoneResponse 实例 或者 Request 实例。

Item Pipeline - 它用于处理 Spider 生成的 item,对其进行清理、验证、持久化等处理。用户自己实现的Item Pipeline 可以定义一个或多个如下方法:

  • process_item(item, spider) - 它用来处理 Spider 生成的 item。它可以返回字段类型的数据、Item 实例、Deferred 实例或者抛出 DropItem 异常。

  • open_spider(spider) - Spider 打开时调用。

  • close_spider(spider) - Spider 关闭时调用。

  • from_crawler(cls, crawler)

Scheduler - Scheduler接收来自engine的请求,并在engine请求它们时将它们排入队列以便稍后(也引导到engine)。

Extension - 提供了向 Scrapy 中插入自定义功能的机制。Extension 是普通的类,它们在 Scrapy 启动时实例化。 通常,Extension 实现向 Scrapy 注册信号处理函数,由信号触发完成相应工作。

Spider - Spiders是由Scrapy用户编写的自定义类,用于解析响应并从中提取items(也称为下载的items)或其他要跟进的requests。

异步手段

Twisted Deferred

我们本节主要汇总一下 Scrapy 中哪些可扩展组件支持返回 Deferred 对象。

Item Pipeline

对于 Item Pipeline,我们从文档中已经得知,用户自定义 Item Pipelineprocess_item 可以返回 Deferred 实例。Itempipeline 的处理本身就是由 Deferred 驱动的,作为其回调函数使用的 process_item 返回的 Deferred便会插入到原始 Deferred 的处理流程中。

# scrapy.core.scraper.Scraper
    def _process_spidermw_output(self, output, request, response, spider):
        """Process each Request/Item (given in the output parameter) returned
        from the given spider
        """
        if isinstance(output, Request):
            self.crawler.engine.crawl(request=output, spider=spider)
        elif isinstance(output, (BaseItem, dict)):
            self.slot.itemproc_size += 1
            dfd = self.itemproc.process_item(output, spider)
            dfd.addBoth(self._itemproc_finished, output, response, spider)
            return dfd
        elif output is None:
            pass
        else:
            ###
Spider Middlewa
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Scrapy提供了异步的Item Pipeline机制,可以方便地将数据存储到MySQL数据库中。具体实现步骤如下: 1. 安装异步MySQL库aiomysql:`pip install aiomysql` 2. 在settings.py中配置MySQL数据库信息: ``` MYSQL_HOST = 'localhost' MYSQL_PORT = 3306 MYSQL_USER = 'root' MYSQL_PASSWORD = 'password' MYSQL_DBNAME = 'database_name' ``` 3. 创建一个异步的MySQL连接池: ``` import aiomysql class MySQLPipeline(object): def __init__(self, mysql_host, mysql_port, mysql_user, mysql_password, mysql_dbname): self.mysql_host = mysql_host self.mysql_port = mysql_port self.mysql_user = mysql_user self.mysql_password = mysql_password self.mysql_dbname = mysql_dbname self.pool = None @classmethod async def from_crawler(cls, crawler): mysql_host = crawler.settings.get('MYSQL_HOST', 'localhost') mysql_port = crawler.settings.get('MYSQL_PORT', 3306) mysql_user = crawler.settings.get('MYSQL_USER', 'root') mysql_password = crawler.settings.get('MYSQL_PASSWORD', 'password') mysql_dbname = crawler.settings.get('MYSQL_DBNAME', 'database_name') obj = cls(mysql_host, mysql_port, mysql_user, mysql_password, mysql_dbname) obj.pool = await aiomysql.create_pool( host=obj.mysql_host, port=obj.mysql_port, user=obj.mysql_user, password=obj.mysql_password, db=obj.mysql_dbname, charset='utf8mb4', autocommit=True, maxsize=10, minsize=1 ) return obj async def process_item(self, item, spider): async with self.pool.acquire() as conn: async with conn.cursor() as cur: sql = "INSERT INTO table_name (field1, field2) VALUES (%s, %s)" await cur.execute(sql, (item['field1'], item['field2'])) return item async def close_spider(self, spider): self.pool.close() await self.pool.wait_closed() ``` 4. 在settings.py中启用MySQLPipeline: ``` ITEM_PIPELINES = { 'myproject.pipelines.MySQLPipeline': 300, } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值