Python中如何在前端通过API同时调用多个Scrapy编写的爬虫?

最近在学习爬虫,使用了 scrapy。 一个需要是,用户在浏览器输入关键词,然后返回爬取的 url 给用户。 调研发现了 scrapyd 和 scrapyrt 但是发现 scrapyd 没有办法返回自定义的 response. 于是又使用了 scrapyrt,但是发现无法同时处理多个用户的请求。 所以来求助各位 orz 总结一下,我的问题是:

  • 如果使用 scrapyd,除了返回 response 给前端的方式,还有什么办法可以将爬到的 url 给前端?以及,scrapyd 是否能够支持同时处理多个 request?
  • 如果使用 scrapyrt,如何支持同时处理多个 request ?
  • 如果不使用 scrapyd/scrapyrt,是否有一些 python web 的 framework 可以实现我这个需要。
  • 目前已经将爬虫部署在了 docker 中,用户不会很多,如果上述方案都比较麻烦难以实现,我启动多个 docker 来处理,这个方案是否现实?

Python中如何在前端通过API同时调用多个Scrapy编写的爬虫?

3 回复

我理解你的需求是想在前端通过一个API接口同时触发多个Scrapy爬虫执行。这通常需要将Scrapy爬虫包装成可通过HTTP调用的服务。以下是完整的解决方案:

核心架构:

  1. 使用Scrapyd(官方推荐)或ScrapyRT将爬虫部署为HTTP服务
  2. 在前端通过AJAX/Fetch同时调用多个爬虫API
  3. 使用异步处理避免阻塞

完整代码示例:

# 1. 首先安装必要库
# pip install scrapy scrapyd scrapyd-client

# 2. 部署Scrapy爬虫到Scrapyd服务器
# 在scrapy项目目录执行:
# scrapyd-deploy target_name -p project_name

# 3. 创建API服务层(Flask示例)
from flask import Flask, jsonify, request
import requests
import concurrent.futures
import json

app = Flask(__name__)

# Scrapyd服务器配置
SCRAPYD_SERVERS = {
    'spider1': 'http://localhost:6800',
    'spider2': 'http://localhost:6801'
}

def trigger_spider(server_url, spider_name, params=None):
    """触发单个爬虫"""
    url = f"{server_url}/schedule.json"
    data = {
        'project': 'your_project_name',
        'spider': spider_name
    }
    if params:
        data.update(params)
    
    response = requests.post(url, data=data)
    return response.json()

@app.route('/api/run-spiders', methods=['POST'])
def run_multiple_spiders():
    """同时运行多个爬虫的API端点"""
    spiders_config = request.json.get('spiders', [])
    results = []
    
    # 使用线程池并发调用
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future_to_spider = {}
        
        for config in spiders_config:
            spider_name = config['name']
            server_url = SCRAPYD_SERVERS.get(spider_name)
            params = config.get('params', {})
            
            future = executor.submit(
                trigger_spider, 
                server_url, 
                spider_name, 
                params
            )
            future_to_spider[future] = spider_name
        
        # 收集所有结果
        for future in concurrent.futures.as_completed(future_to_spider):
            spider_name = future_to_spider[future]
            try:
                result = future.result()
                results.append({
                    'spider': spider_name,
                    'status': 'success',
                    'job_id': result.get('jobid')
                })
            except Exception as e:
                results.append({
                    'spider': spider_name,
                    'status': 'error',
                    'error': str(e)
                })
    
    return jsonify({'results': results})

# 4. 前端调用示例(JavaScript)
"""
// 前端调用代码
async function runAllSpiders() {
    const response = await fetch('/api/run-spiders', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            spiders: [
                {name: 'spider1', params: {start_url: '...'}},
                {name: 'spider2', params: {category: '...'}}
            ]
        })
    });
    const result = await response.json();
    console.log('所有爬虫触发结果:', result);
}
"""

if __name__ == '__main__':
    app.run(port=5000, debug=True)

关键点说明:

  1. Scrapyd部署:每个Scrapy项目需要部署到Scrapyd服务器,它会提供REST API
  2. 并发处理:使用ThreadPoolExecutor实现真正的并行调用
  3. 异步响应:API立即返回job_id,爬虫在后台执行
  4. 状态监控:可以通过/listjobs.json接口查询爬虫执行状态

替代方案:

  • 使用ScrapyRT:更轻量,直接运行爬虫并返回结果
  • 使用Celery:适合需要任务队列和重试机制的复杂场景
  • 使用FastAPI:获得更好的异步性能和自动API文档

一句话建议:用Scrapyd+Flask线程池实现并行调用最直接。


挽尊
这个需求不太适合直接使用 scrapy 来做,可以用 web 框架+http 请求库,把爬虫做成接口的形式。比如使用 flask+requests,简单粗暴就能实现;或者是用 tornado 这种自带 server 和 client 的,又不需要特殊处理性能问题、又能在不依赖其他 http 请求库的情况下发出 http 请求。
部署方面多容器做负载均衡是可以的,但是如果是在同一台机器下启动多个的话其实意义不大。

感谢回复. 确实发现直接使用 scrapy 不太合适了. 不过当时需求比较紧急,就暂时使用了启动多个 docker 实例来同时处理请求的解决办法,前端用循环队列每次发 request 到一个不同的端口.可能是由于需要同时用的人不会很多,所以效果还挺好的.不过这肯定不是长久之计,现在已经按照你的思路,使用了 django + scrapyd,做成了接口的形式.

回到顶部