在 Scrapy 爬虫开发中,固定写死start_urls和爬虫配置往往无法满足灵活的爬取需求(比如批量爬取不同站点、按需调整爬取延迟 / 请求头)。Spider 参数化 是解决这一问题的核心方案,能够实现start_urls的动态传入和自定义设置的灵活配置,大幅提升爬虫的可复用性和扩展性。本文将从核心实现原理、具体操作步骤、完整示例到高级用法,全面讲解 Scrapy Spider 参数化的落地技巧。
一、核心前置知识:Spider 参数的传入入口
Scrapy 提供了两种核心方式向 Spider 传入参数,支撑后续start_urls和自定义设置的动态化:
- 命令行运行时传入(最常用) :通过
scrapy crawl命令后追加-a参数实现,格式为-a 键1=值1 -a 键2=值2,支持多个参数同时传入。 - 项目代码内实例化传入:在代码中直接创建 Spider 实例时,通过关键字参数传入,适用于需要程序化调用 Spider 的场景。
无论哪种传入方式,参数都会最终传递到 Spider 类的__init__方法中,我们可以在__init__方法中接收并处理这些参数,进而实现start_urls动态赋值和自定义设置生效。
二、第一部分:动态传入 start_urls(3 种实现方案)
start_urls是 Scrapy Spider 的核心属性,用于定义爬虫的初始爬取链接,默认是一个固定的列表。通过参数化改造,可实现按需传入爬取链接,以下是 3 种实用方案,从简单到灵活依次展开。
方案 1:基础方案 ------ 命令行传入单个 URL,直接赋值
适用于仅需爬取单个初始链接的场景,实现最简单,零复杂逻辑。
实现步骤
- 编写 Spider 类,在
__init__方法中接收url参数(参数名可自定义)。 - 将接收的
url封装为列表,赋值给self.start_urls(Scrapy 要求start_urls为可迭代对象,列表是最优选择)。 - 命令行传入
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 的局限。
实现步骤
- 命令行传入以逗号(或其他分隔符)拼接的多个 URL 字符串。
- 在 Spider 的
__init__方法中接收urls参数,通过split()方法分割字符串,得到 URL 列表。 - 将分割后的列表赋值给
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 数量极多(数十、数百个)的场景,避免命令行参数过长,提升可维护性。
实现步骤
- 准备 URL 文件(每行一个 URL,格式为
.txt)。 - 命令行传入文件路径参数
url_file。 - 在 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 参数化的核心价值在于提升爬虫的灵活性和可复用性,通过本文的讲解,我们掌握了:
- 两种核心的 Spider 参数传入方式(命令行
-a、代码实例化)。 - 3 种动态传入
start_urls的方案(单个 URL、多个 URL、文件读取),满足不同场景的爬取需求。 - 利用
custom_settings动态覆盖爬虫配置,实现爬取延迟、请求头、输出格式的自定义。 - 参数化的最佳实践与注意事项,规避常见坑点。
掌握这些技巧后,你可以摆脱固定配置的束缚,开发出更具通用性、更易维护的 Scrapy 爬虫,高效应对各种复杂的爬取场景。
如果你也对爬虫感兴趣,欢迎你和我沟通交流~