scrapy 执行 shell 命令 - 并行

我想利用 scrapy 的 parse 执行不同的 bash 命令,这个命令是并行的

在 Scrapy 中,parse 方法是用来解析网页内容的,通常不直接用于执行外部命令。如果你想在 Scrapy 中执行并行的 bash 命令,可以考虑以下几种方法:

方法 1: 使用 Python 的 subprocess 模块

你可以在 parse 方法中使用 Python 的 subprocess 模块来执行 bash 命令。为了实现并行执行,可以使用 concurrent.futures 模块中的 ThreadPoolExecutorProcessPoolExecutor

import scrapy
import subprocess
from concurrent.futures import ThreadPoolExecutor

class MySpider(scrapy.Spider):
    name = 'my_spider'
    start_urls = ['http://example.com']

    def parse(self, response):
        commands = [
            'echo Command 1',
            'echo Command 2',
            'echo Command 3',
        ]

        with ThreadPoolExecutor(max_workers=5) as executor:
            futures = [executor.submit(self.run_command, cmd) for cmd in commands]
            for future in futures:
                future.result()  # 等待所有命令执行完成

    def run_command(self, command):
        result = subprocess.run(command, shell=True, capture_output=True, text=True)
        self.logger.info(f"Command: {command}, Output: {result.stdout}")

方法 2: 使用 Scrapy 的 Item PipelineTwisted 的异步特性

Scrapy 是基于 Twisted 的异步框架,你可以利用 Twisted 的异步特性来执行并行的 bash 命令。

import scrapy
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
import subprocess

class MySpider(scrapy.Spider):
    name = 'my_spider'
    start_urls = ['http://example.com']

    def parse(self, response):
        commands = [
            'echo Command 1',
            'echo Command 2',
            'echo Command 3',
        ]

        deferreds = [self.run_command(cmd) for cmd in commands]
        dl = defer.DeferredList(deferreds)
        dl.addCallback(self.all_commands_done)
        return dl

    def run_command(self, command):
        return deferToThread(subprocess.run, command, shell=True, capture_output=True, text=True)

    def all_commands_done(self, results):
        for success, result in results:
            if success:
                self.logger.info(f"Command: {result.args}, Output: {result.stdout}")

方法 3: 使用 Scrapy 的 Item Pipelineasyncio

如果你使用的是 Python 3.7+,你可以使用 asyncio 来执行并行的 bash 命令。

import scrapy
import asyncio
import subprocess

class MySpider(scrapy.Spider):
    name = 'my_spider'
    start_urls = ['http://example.com']

    async def parse(self, response):
        commands = [
            'echo Command 1',
            'echo Command 2',
            'echo Command 3',
        ]

        tasks = [self.run_command(cmd) for cmd in commands]
        await asyncio.gather(*tasks)

    async def run_command(self, command):
        process = await asyncio.create_subprocess_shell(
            command,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        stdout, stderr = await process.communicate()
        self.logger.info(f"Command: {command}, Output: {stdout.decode().strip()}")

总结

  • 方法 1 使用 ThreadPoolExecutor 来并行执行命令,适合简单的并行任务。
  • 方法 2 利用 Scrapy 的异步特性,适合在 Scrapy 的异步环境中执行并行任务。
  • 方法 3 使用 asyncio,适合 Python 3.7+ 的环境,并且可以更好地与 Scrapy 的异步框架集成。

根据你的需求和环境选择合适的方案。