解决Scrapy2.8 open-spider不支持异步起动

问题:

我在scrapy2.8中的pipelines使用了异步定义了open_spider,在里面如果再次使用await 等待执行回调返回,会出现报错,但是如果我不使用await 就没产生报错信息。
但是,如果我仔细排查,发现我在使用异步定义open_spider的方法中是没有输出,不会执行的,如下:

async def open_spider(self, spider):
    print('Into the async open spider.....')

分析:

Scrapy2.5以上,已经全面支持异步的调用了,但是在某些模块的使用上,还是只能支持同步的启动,如果使用了异步的定义编写方式,这模块就默认跳过,或者不会在scrapy的启动调用列表中。就例如在pipelines中的open_spider中,如果我想在启动的时候,定义接入数据库或者介入某些异步动作,我使用如下定义

async def open_spider(self, spider):

那么scrapy 框架就因为不支持使用异步启动的方式而把open_spider调整为默认的模块执行,而你在自己编写的open_spider模块就会使用默认的open_spider来替代了。

解决方案:

既然scrapy 2.8框架不主动出发我的异步函数,我唯有自己主动的调用。
按照scrapy的每个模块执行顺序的不一,我只使用了pipeline来做延时

class RabbitMQPipeline:
    def __init__(self):
        self.connection = None
        self.channel = None

    async def open_spider(self, spider):
        # 初始化连接和通道
        if not self.connection or self.connection.is_closed:
            self.connection = await aio_pika.connect_robust("amqp://guest:guest@localhost/")
        self.channel = await self.connection.channel()
        await self.channel.declare_queue(queue_name='your_queue_name')

    async def close_spider(self, spider):
        # 关闭通道和连接
        if self.channel and not self.channel.is_closed:
            await self.channel.close()
        if self.connection and not self.connection.is_closed:
            await self.connection.close()

    async def process_item(self, item, spider):
        message = f"Your item data: {item['data']}"
        await self.channel.default_exchange.publish(
            aio_pika.Message(body=message.encode()),
            routing_key='your_queue_name'
        )
        return item

    @classmethod
    def from_crawler(cls, crawler):
        pipeline = cls()
        # 注册信号量
        crawler.signals.connect(pipeline.open_spider, signal=signals.spider_opened)
        crawler.signals.connect(pipeline.close_spider, signal=signals.spider_closed)
        return pipeline

    async def start_requests(self, spider):
        # 在这里调用异步的 open_spider 方法
        await self.open_spider(spider)
        # 返回空的请求列表,因为实际的请求将通过其他方式生成
        return []       
         ```
         
Scrapy中,虽然原生API并不直接支持在爬虫运行过程中动态更新`settings.py`文件,但可以通过一些技巧间接实现。最常见的方式是利用信号系统(Signal System),配合中间件(Middleware)或者异步任务(如Celery)来实现部分设置的动态更新。 一种做法是创建一个中间件,在这个中间件中,你可以监听某个特定的信号(比如`spider_opened`),并在接收到信号时读取或生成新的设置,然后将它们替换到现有的设置对象中。下面是一个简化的示例: ```python import os from scrapy import signals from scrapy.settings import Settings class DynamicSettingsMiddleware: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.new_settings = {} # 这里可以存储临时的新设置 @classmethod def from_crawler(cls, crawler): instance = cls() crawler.signals.connect(instance.spider_opened, signal=signals.spider_opened) return instance def spider_opened(self, spider): new_settings_path = 'path/to/new/settings.json' # 新设置文件路径 if os.path.exists(new_settings_path): with open(new_settings_path) as f: self.new_settings.update(json.load(f)) # 加载新设置 # 将新设置合并到现有设置中 crawler.settings.set(**self.new_settings) # 将中间件添加到设置中 DOWNLOADER_MIDDLEWARES = { 'your_module.DynamicSettingsMiddleware': 500, } ``` 在这个例子中,假设`new_settings.json`包含了一个新的`settings.py`结构,中间件会在每次打开新的Spider时,尝试从该文件加载新的设置并合并到原有设置中。 然而,这仅限于能够通过JSON或其他易于解析的数据格式更新的部分配置。对于复杂或依赖于环境变量的设置,可能需要结合其他方法或第三方工具。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ViniJack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值