Scrapy外部脚本调用时无JSON输出的问题排查
Scrapy外部脚本调用无JSON输出问题修复方案
问题背景
- 已有可正常运行的Scrapy爬虫用于抓取目标数据,配套Jupyter Notebook处理爬虫生成的JSON格式数据
- 需求为通过外部脚本直接调用爬虫,爬取完成后自动触发后续数据处理流程,避免使用人员手动进入目录执行命令行操作
- 异常表现:通过命令行启动爬虫时可正常生成JSON输出;从外部脚本调用时,Spider可正常启动、终端可正常打印爬取日志,但爬取完成后无预期JSON文件生成
已尝试的无效方案
- 在parse方法中手动实现JSON文件写入逻辑,该逻辑仅偶尔生效,代码如下:
def parse(self, response): resp_dict = json.loads(response.body) f = open(file_name, 'w') json.dump(resp_dict, f, indent=4) f.close()
- 手动添加Scrapy Feed导出配置,怀疑配置放置位置有误:
settings = get_project_settings() settings.set('FEED_FORMAT', 'json') settings.set('FEED_URI', 'result.json')
- 初始化CrawlerProcess时直接传入FEEDS配置,仍未解决问题:
process = CrawlerProcess(settings={ "FEEDS": { "items.json": {"format": "json"}, } }) process.crawl(HoggleSpider) process.start()
核心问题根因
- parse方法内手动写文件的逻辑本身不可靠:Scrapy基于Twisted异步框架运行,parse方法会被异步调度执行,使用
w模式重复打开文件会反复覆盖已有内容;若进程退出时IO操作未完成,会直接出现文件缺失、内容为空的问题,该写法完全脱离Scrapy的生命周期管理,没有稳定性保障。 - Feed配置不生效通常由三类问题导致:
- 调用
get_project_settings()时,脚本运行的工作目录不是Scrapy项目根目录(即存放scrapy.cfg的目录),Scrapy无法加载项目基础配置,后续手动设置的参数也不会被Crawler实例正确读取。 - 手动修改settings对象后未将其传入
CrawlerProcess初始化参数,配置修改仅停留在局部变量层面,不会产生实际效果。 - parse方法未通过
yield返回待导出的Item/字典对象:Scrapy的Feed导出管道只会处理引擎接收到的yield数据,若数据仅在parse方法内做局部处理、未提交给引擎,导出逻辑拿不到任何数据,自然不会生成输出文件。
- 调用
可直接落地的修复方案
- 首先修改Spider的parse方法,移除手动写文件的逻辑,将爬取到的数据通过yield提交给Scrapy引擎:
关键提示:必须将需要导出的数据提交给引擎,否则所有Feed导出配置都不会生效
import json from scrapy import Spider class HoggleSpider(Spider): name = "hoggle" # 保留原有Spider的start_urls、自定义配置等内容 def parse(self, response): resp_dict = json.loads(response.body) # 返回需要导出的数据给Scrapy引擎 yield resp_dict
- 调整外部脚本逻辑,正确处理工作目录、加载配置,保证Feed导出正常生效,爬取完成后可直接衔接后续数据处理逻辑:
import os from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings # 导入自定义的Spider类 from your_spider_module import HoggleSpider # 强制将脚本所在目录设置为工作目录,避免任意位置运行脚本时Scrapy找不到项目配置 # 若脚本放在Scrapy项目的子目录,可按需调整os.chdir的路径参数 os.chdir(os.path.dirname(os.path.abspath(__file__))) # 加载Scrapy项目基础配置 settings = get_project_settings() # 配置Feed导出规则,priority参数保证自定义配置覆盖默认配置 settings.set("FEEDS", { # 可填写绝对路径进一步避免路径问题,例如r"C:\project\result.json" "result.json": { "format": "json", "indent": 4, # 匹配原有json.dump的缩进格式 "overwrite": True # 每次爬取覆盖旧文件,避免追加内容导致JSON格式错误 } }, priority="project") if __name__ == "__main__": # 传入加载完成的配置初始化爬虫进程 process = CrawlerProcess(settings) process.crawl(HoggleSpider) # start()方法会阻塞直到爬取全流程完成 process.start() # 后续数据处理逻辑直接写在start()之后即可,爬取完成后会自动执行 # 原有Jupyter Notebook中的数据处理代码可直接迁移到此处
注意事项
- 不要在同一个Python进程中多次调用
process.start(),Scrapy依赖的Twisted事件循环启动一次后无法重复启动,若需要多次运行爬虫,可改用CrawlerRunner搭配回调实现逻辑。 - 若使用相对路径配置输出文件,可在脚本中打印
os.getcwd()确认当前工作路径,避免文件实际生成到了其他未注意到的目录,误以为没有生成;直接填写输出文件的绝对路径是最稳妥的方案。
内容的提问来源于stack exchange,提问作者Dennis Waites
相关产品推荐
相关产品推荐

