Scrapy Spider 参数化:动态传入 start_urls 和自定义设置

在 Scrapy 爬虫开发中,固定写死start_urls和爬虫配置往往无法满足灵活的爬取需求(比如批量爬取不同站点、按需调整爬取延迟 / 请求头)。Spider 参数化 是解决这一问题的核心方案,能够实现start_urls的动态传入和自定义设置的灵活配置,大幅提升爬虫的可复用性和扩展性。本文将从核心实现原理、具体操作步骤、完整示例到高级用法,全面讲解 Scrapy Spider 参数化的落地技巧。

一、核心前置知识:Spider 参数的传入入口

Scrapy 提供了两种核心方式向 Spider 传入参数,支撑后续start_urls和自定义设置的动态化:

  1. 命令行运行时传入(最常用) :通过scrapy crawl命令后追加-a参数实现,格式为-a 键1=值1 -a 键2=值2,支持多个参数同时传入。
  2. 项目代码内实例化传入:在代码中直接创建 Spider 实例时,通过关键字参数传入,适用于需要程序化调用 Spider 的场景。

无论哪种传入方式,参数都会最终传递到 Spider 类的__init__方法中,我们可以在__init__方法中接收并处理这些参数,进而实现start_urls动态赋值和自定义设置生效。

二、第一部分:动态传入 start_urls(3 种实现方案)

start_urls是 Scrapy Spider 的核心属性,用于定义爬虫的初始爬取链接,默认是一个固定的列表。通过参数化改造,可实现按需传入爬取链接,以下是 3 种实用方案,从简单到灵活依次展开。

方案 1:基础方案 ------ 命令行传入单个 URL,直接赋值

适用于仅需爬取单个初始链接的场景,实现最简单,零复杂逻辑。

实现步骤
  1. 编写 Spider 类,在__init__方法中接收url参数(参数名可自定义)。
  2. 将接收的url封装为列表,赋值给self.start_urls(Scrapy 要求start_urls为可迭代对象,列表是最优选择)。
  3. 命令行传入url参数,启动爬虫。
完整代码示例

python

运行

复制代码
# 文件名:single_url_spider.py
import scrapy

class SingleUrlSpider(scrapy.Spider):
    name = "single_url_spider"  # 爬虫名称,用于命令行启动

    def __init__(self, url=None, *args, **kwargs):
        super(SingleUrlSpider, self).__init__(*args, **kwargs)
        # 接收命令行传入的url参数,动态赋值给start_urls
        if url:
            self.start_urls = [url]  # 必须封装为列表,满足Scrapy的可迭代要求
        else:
            # 提供默认值,避免无参数传入时报错
            self.start_urls = ["https://www.example.com"]

    def parse(self, response):
        # 简单的解析逻辑,提取页面标题
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url
        }
启动命令(命令行传入参数)

bash

运行

复制代码
scrapy crawl single_url_spider -a url=https://www.baidu.com

方案 2:进阶方案 ------ 命令行传入多个 URL(逗号分隔)

适用于需要爬取多个初始链接的场景,解决基础方案仅支持单个 URL 的局限。

实现步骤
  1. 命令行传入以逗号(或其他分隔符)拼接的多个 URL 字符串。
  2. 在 Spider 的__init__方法中接收urls参数,通过split()方法分割字符串,得到 URL 列表。
  3. 将分割后的列表赋值给self.start_urls,完成多 URL 动态传入。
完整代码示例

python

运行

复制代码
# 文件名:multi_url_spider.py
import scrapy

class MultiUrlSpider(scrapy.Spider):
    name = "multi_url_spider"

    def __init__(self, urls=None, *args, **kwargs):
        super(MultiUrlSpider, self).__init__(*args, **kwargs)
        if urls:
            # 分割逗号分隔的URL字符串,转为列表
            self.start_urls = urls.split(",")
        else:
            self.start_urls = ["https://www.example.com", "https://www.example.org"]

    def parse(self, response):
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url,
            "status_code": response.status
        }
启动命令(传入多个 URL,逗号分隔)

bash

运行

复制代码
scrapy crawl multi_url_spider -a urls=https://www.baidu.com,https://www.sogou.com,https://www.qq.com

方案 3:灵活方案 ------ 从文件 / 配置中读取 URL(参数指定文件路径)

适用于 URL 数量极多(数十、数百个)的场景,避免命令行参数过长,提升可维护性。

实现步骤
  1. 准备 URL 文件(每行一个 URL,格式为.txt)。
  2. 命令行传入文件路径参数url_file
  3. 在 Spider 的__init__方法中,读取指定文件的内容,提取所有 URL,赋值给self.start_urls
完整代码示例

python

运行

复制代码
# 文件名:file_url_spider.py
import scrapy

class FileUrlSpider(scrapy.Spider):
    name = "file_url_spider"

    def __init__(self, url_file=None, *args, **kwargs):
        super(FileUrlSpider, self).__init__(*args, **kwargs)
        self.start_urls = []
        # 若传入文件路径,读取文件中的URL
        if url_file:
            try:
                with open(url_file, "r", encoding="utf-8") as f:
                    # 读取每行,去除空白字符和换行符,过滤空行
                    self.start_urls = [line.strip() for line in f if line.strip()]
            except FileNotFoundError:
                self.logger.error(f"URL文件不存在:{url_file}")
            except Exception as e:
                self.logger.error(f"读取URL文件失败:{str(e)}")
        # 若文件读取失败或未传入文件,使用默认URL
        if not self.start_urls:
            self.start_urls = ["https://www.example.com"]

    def parse(self, response):
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url,
            "page_length": len(response.text)
        }
配套 URL 文件示例(urls.txt

txt

复制代码
https://www.baidu.com
https://www.sogou.com
https://www.163.com
https://www.taobao.com
https://www.jd.com
启动命令(传入 URL 文件路径)

bash

运行

复制代码
# 注意:文件路径可写绝对路径(如D:/scrapy_project/urls.txt)或相对路径
scrapy crawl file_url_spider -a url_file=urls.txt

三、第二部分:动态传入自定义设置(覆盖默认配置)

Scrapy 的爬虫配置(如爬取延迟、请求头、并发数、输出格式等)默认在settings.py中配置,通过参数化可实现按需覆盖单个爬虫的配置,而不影响全局配置,提升爬虫的灵活性。

核心原理:利用 Spider 的custom_settings属性

Scrapy Spider 类提供了custom_settings属性(字典类型),用于定义当前爬虫的专属配置,其优先级高于项目全局的settings.py。我们可以通过动态传入参数,修改custom_settings中的值,实现自定义设置的动态化。

实现方案(3 种常用自定义配置动态化)

方案 1:动态调整爬取延迟(DOWNLOAD_DELAY)和并发数(CONCURRENT_REQUESTS

适用于需要根据目标站点反爬强度,灵活调整爬取频率的场景。

python

运行

复制代码
# 文件名:custom_delay_spider.py
import scrapy

class CustomDelaySpider(scrapy.Spider):
    name = "custom_delay_spider"
    # 基础自定义配置(可被参数覆盖)
    custom_settings = {
        "CONCURRENT_REQUESTS": 4,  # 默认并发数
        "DOWNLOAD_DELAY": 1,       # 默认爬取延迟(秒)
        "ROBOTSTXT_OBEY": False    # 不遵循robots协议
    }

    def __init__(self, url=None, download_delay=None, concurrent_requests=None, *args, **kwargs):
        super(CustomDelaySpider, self).__init__(*args, **kwargs)
        # 动态赋值start_urls
        self.start_urls = [url] if url else ["https://www.example.com"]

        # 动态覆盖爬取延迟配置
        if download_delay and download_delay.isdigit():
            self.custom_settings["DOWNLOAD_DELAY"] = int(download_delay)

        # 动态覆盖并发数配置
        if concurrent_requests and concurrent_requests.isdigit():
            self.custom_settings["CONCURRENT_REQUESTS"] = int(concurrent_requests)

    def parse(self, response):
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url,
            "download_delay": self.custom_settings["DOWNLOAD_DELAY"],
            "concurrent_requests": self.custom_settings["CONCURRENT_REQUESTS"]
        }
启动命令(传入自定义延迟和并发数)

bash

运行

复制代码
scrapy crawl custom_delay_spider -a url=https://www.baidu.com -a download_delay=3 -a concurrent_requests=2
方案 2:动态设置请求头(DEFAULT_REQUEST_HEADERS

适用于需要模拟不同浏览器、不同用户代理(User-Agent)的爬取场景,提升反爬规避能力。

python

运行

复制代码
# 文件名:custom_headers_spider.py
import scrapy

class CustomHeadersSpider(scrapy.Spider):
    name = "custom_headers_spider"
    custom_settings = {
        "DEFAULT_REQUEST_HEADERS": {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh;q=0.9",
        }
    }

    def __init__(self, url=None, user_agent=None, referer=None, *args, **kwargs):
        super(CustomHeadersSpider, self).__init__(*args, **kwargs)
        self.start_urls = [url] if url else ["https://www.example.com"]

        # 动态覆盖User-Agent
        if user_agent:
            self.custom_settings["DEFAULT_REQUEST_HEADERS"]["User-Agent"] = user_agent

        # 动态覆盖Referer
        if referer:
            self.custom_settings["DEFAULT_REQUEST_HEADERS"]["Referer"] = referer

    def parse(self, response):
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url,
            "user_agent": response.request.headers.get("User-Agent", b"").decode("utf-8"),
            "referer": response.request.headers.get("Referer", b"").decode("utf-8")
        }
启动命令(传入自定义 User-Agent 和 Referer)

bash

运行

复制代码
scrapy crawl custom_headers_spider -a url=https://www.baidu.com -a user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" -a referer="https://www.google.com"
方案 3:动态指定输出文件路径和格式(FEEDS

适用于需要将爬取结果保存到不同文件、不同格式(JSON、CSV、JSONL)的场景,避免手动修改settings.py

python

运行

复制代码
# 文件名:custom_feed_spider.py
import scrapy

class CustomFeedSpider(scrapy.Spider):
    name = "custom_feed_spider"
    custom_settings = {
        "FEEDS": {},  # 初始化输出配置字典
        "ROBOTSTXT_OBEY": False
    }

    def __init__(self, urls=None, output_file=None, output_format=None, *args, **kwargs):
        super(CustomFeedSpider, self).__init__(*args, **kwargs)
        # 动态处理start_urls
        self.start_urls = urls.split(",") if urls else ["https://www.example.com"]

        # 动态配置输出文件(默认格式为JSON,默认路径为result.json)
        output_path = output_file if output_file else "result.json"
        output_type = output_format if output_format else "json"

        self.custom_settings["FEEDS"] = {
            output_path: {
                "format": output_type,
                "encoding": "utf-8",
                "overwrite": True  # 覆盖已存在的文件
            }
        }

    def parse(self, response):
        yield {
            "page_title": response.xpath('//title/text()').extract_first(),
            "url": response.url,
            "crawl_time": scrapy.utils.datetime.now_datetime().strftime("%Y-%m-%d %H:%M:%S")
        }
启动命令(传入输出文件路径和格式)

bash

运行

复制代码
# 保存为CSV格式,文件名为baidu_qq.csv
scrapy crawl custom_feed_spider -a urls=https://www.baidu.com,https://www.qq.com -a output_file=baidu_qq.csv -a output_format=csv

四、第三部分:高级技巧 ------ 参数化的最佳实践与注意事项

1. 最佳实践

  • 参数校验与默认值:所有传入的参数都应添加校验逻辑(如是否为数字、是否为有效路径),并设置合理默认值,避免爬虫因参数缺失或非法而崩溃。
  • 日志记录参数 :在 Spider 启动时,通过self.logger.info()记录传入的关键参数,方便后续调试和日志追溯。
  • 分离配置与业务 :对于复杂的自定义配置,可将配置存入json/yaml文件,通过参数传入配置文件路径,在 Spider 中读取并加载配置,进一步提升可维护性。
  • 使用kwargs接收额外参数 :保留*args, **kwargs并传递给父类构造方法,避免屏蔽 Scrapy 内置参数的功能。

2. 注意事项

  • start_urls必须是可迭代对象 :Scrapy 会迭代start_urls生成初始请求,因此必须将参数转换为列表、元组等可迭代类型,不可直接赋值为字符串。
  • custom_settings的优先级custom_settings仅对当前 Spider 生效,优先级高于settings.py,低于命令行-s参数(如scrapy crawl spider -s DOWNLOAD_DELAY=5)。
  • 参数类型转换:命令行传入的所有参数默认都是字符串类型,若需要数字(如延迟、并发数)或布尔值,需手动进行类型转换。
  • 文件读取权限与编码 :从文件读取 URL 时,需处理文件不存在、权限不足等异常,同时指定encoding="utf-8"避免中文或特殊字符乱码。

五、总结

Scrapy Spider 参数化的核心价值在于提升爬虫的灵活性和可复用性,通过本文的讲解,我们掌握了:

  1. 两种核心的 Spider 参数传入方式(命令行-a、代码实例化)。
  2. 3 种动态传入start_urls的方案(单个 URL、多个 URL、文件读取),满足不同场景的爬取需求。
  3. 利用custom_settings动态覆盖爬虫配置,实现爬取延迟、请求头、输出格式的自定义。
  4. 参数化的最佳实践与注意事项,规避常见坑点。

掌握这些技巧后,你可以摆脱固定配置的束缚,开发出更具通用性、更易维护的 Scrapy 爬虫,高效应对各种复杂的爬取场景。

如果你也对爬虫感兴趣,欢迎你和我沟通交流~

相关推荐
上海云盾-高防顾问2 小时前
防CC攻击不止限速:智能指纹识别如何精准抵御恶意爬虫
爬虫·安全·web安全
特行独立的猫2 小时前
python+Proxifier+mitmproxy实现监听本地网路所有的http请求
开发语言·爬虫·python·http
CCPC不拿奖不改名2 小时前
基于FastAPI的API开发(爬虫的工作原理):从设计到部署详解+面试习题
爬虫·python·网络协议·tcp/ip·http·postman·fastapi
小白学大数据2 小时前
某程旅行小程序爬虫技术解析与实战案例
爬虫·小程序
程序员agions2 小时前
Node.js 爬虫实战指南(四):反反爬策略大全,和网站斗智斗勇
爬虫·node.js
___波子 Pro Max.2 小时前
Python中Optional类型的作用解析
python
没学上了2 小时前
Pycharm修改环境
ide·python·pycharm
就这个丶调调2 小时前
Python中使用OpenAI实现AI问答:流式返回、记忆存储与工具调用详解
python·openai·流式响应·工具调用·ai问答·记忆存储
2301_764441332 小时前
python实现罗斯勒吸引子(Rössler Attractor)
开发语言·数据结构·python·算法·信息可视化