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;添加异常处理(



还没有评论,来说两句吧...