Scrapy爬取JSON数据的完整指南
在数据抓取领域,Scrapy凭借其高效、灵活的特性,已成为Python生态中最流行的爬虫框架之一,而JSON(JavaScript Object Notation)作为轻量级的数据交换格式,因其结构清晰、易于解析,被广泛应用于API响应和网页数据传递中,本文将详细介绍如何使用Scrapy爬取JSON数据,从环境准备到代码实现,再到常见问题解决,助你快速这一实用技能。
Scrapy爬取JSON数据的核心思路
Scrapy爬取JSON数据的核心流程与常规爬取类似,但针对JSON格式的特殊性,需重点关注请求参数设置、响应解析和数据提取三个环节,具体思路如下:
- 发送请求:通过
scrapy.Request
向目标URL发起请求,若需传递参数(如API的查询参数),可通过meta
或url
拼接实现; - 获取响应:服务器返回JSON格式的响应数据,Scrapy的
Response
对象会自动解析为text
(文本格式)或json
(已解析的字典/列表,需设置response.json()
); - 解析数据:利用Python字典或列表操作,从JSON结构中提取目标字段;
- 存储数据:通过Scrapy的Item Pipeline将提取的数据清洗、处理后存入文件(如JSON、CSV)或数据库。
准备工作:安装Scrapy与创建项目
安装Scrapy
若尚未安装Scrapy,可通过pip快速安装:
pip install scrapy
创建Scrapy项目
使用命令行创建项目框架:
scrapy startproject json_spider cd json_spider
创建后,项目目录结构如下:
json_spider/
├── scrapy.cfg # 部署配置
└── json_spider/ # 项目核心模块
├── __init__.py
├── items.py # 定义数据结构
├── middlewares.py # 中间件
├── pipelines.py # 数据处理管道
├── settings.py # 爬虫配置
└── spiders/ # 爬虫文件目录
└── __init__.py
实战:爬取JSON API数据
以一个公开的JSON API(如https://jsonplaceholder.typicode.com/posts
,返回模拟文章数据)为例,演示完整爬取流程。
定义数据结构(items.py)
首先在items.py
中定义要提取的字段,明确数据规范:
# json_spider/items.py import scrapy class JsonSpiderItem(scrapy.Item): id = scrapy.Field() # 文章ID= scrapy.Field() # 文章标题 body = scrapy.Field() # 文章内容 userId = scrapy.Field() # 作者ID
创建爬虫文件(spiders/)
在spiders
目录下创建爬虫文件(如json_api_spider.py
):
scrapy genspider json_api_spider jsonplaceholder.typicode.com ``` 如下,需修改`allowed_domains`和`start_urls`: ```python # json_spider/spiders/json_api_spider.py import scrapy from json_spider.items import JsonSpiderItem class JsonApiSpiderSpider(scrapy.Spider): name = "json_api_spider" allowed_domains = ["jsonplaceholder.typicode.com"] start_urls = ["https://jsonplaceholder.typicode.com/posts"] def parse(self, response): # 将响应解析为JSON(若响应已是JSON格式) data = response.json() # 遍历JSON数据(假设返回的是列表) for item in data: # 创建Item对象并赋值 json_item = JsonSpiderItem() json_item["id"] = item.get("id") json_item["title"] = item.get("title") json_item["body"] = item.get("body") json_item["userId"] = item.get("userId") # 返回Item,交给Pipeline处理 yield json_item
配置爬虫(settings.py)
为避免被反爬,可在settings.py
中添加基础配置:
# json_spider/settings.py BOT_NAME = "json_spider" SPIDER_MODULES = ["json_spider.spiders"] NEWSPIDER_MODULE = "json_spider.spiders" # 禁用robots.txt(仅测试用,生产环境建议遵守) ROBOTSTXT_OBEY = False # 设置User-Agent(模拟浏览器访问) USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # 开启Pipeline(需在pipelines.py中实现) ITEM_PIPELINES = { "json_spider.pipelines.JsonSpiderPipeline": 300, }
实现数据存储(pipelines.py)
在pipelines.py
中编写数据处理逻辑,如将数据存为JSON文件:
# json_spider/pipelines.py import json class JsonSpiderPipeline: def open_spider(self, spider): """爬虫启动时调用,初始化文件""" self.file = open("output.json", "w", encoding="utf-8") self.file.write("[\n") # JSON列表格式 def close_spider(self, spider): """爬虫结束时调用,关闭文件""" self.file.write("]\n") self.file.close() def process_item(self, item, spider): """处理每个Item,写入文件""" line = json.dumps(dict(item), ensure_ascii=False) + ",\n" self.file.write(line) return item
运行爬虫
在项目根目录下执行命令:
scrapy crawl json_api_spider
运行后,当前目录会生成output.json
如下(示例):
[ {"id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", "userId": 1}, {"id": 2, "title": "qui est esse", "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", "userId": 1} ]
进阶:处理复杂JSON与动态参数
爬取嵌套JSON数据
若JSON数据存在嵌套结构(如"user": {"name": "Tom", "age": 25}
),需通过多层提取获取字段:
def parse(self, response): data = response.json() for item in data: json_item = JsonSpiderItem() json_item["id"] = item.get("id") json_item["userName"] = item.get("user", {}).get("name") # 提取嵌套字段 yield json_item
传递动态参数(如分页、查询条件)
若API需传递动态参数(如分页page=1
),可通过meta
或url
拼接实现:
# 方法1:拼接URL start_urls = ["https://jsonplaceholder.typicode.com/posts?_page=1&_limit=5"] # 方法2:使用scrapy.Request动态传递参数 def start_requests(self): for page in range(1, 3): # 爬取前2页 yield scrapy.Request( url=f"https://jsonplaceholder.typicode.com/posts?_page={page}&_limit=5", callback=self.parse )
处理非JSON响应或错误
若响应可能不是JSON格式(如404错误),需添加异常处理:
def parse(self, response): try: data = response.json() # 若解析失败会抛出JSONDecodeError for item in data: yield JsonSpiderItem( id=item.get("id"), title=item.get("title") ) except json.JSONDecodeError: self.logger.error(f"响应非JSON格式: {response.text}")
常见问题与解决方案
响应解析失败:AttributeError: 'NoneType' object has no attribute 'json'
原因:服务器返回非JSON响应(如HTML错误页面)。
解决:检查response.headers["Content-Type"]
,确保为application/json
;添加异常处理(
还没有评论,来说两句吧...