存下来:csv、json 与 sqlite3
3 道练习题·预计 55 分钟·做对一题解锁下一段
前言
学到上一章,书店的 120 本书已经能完整地取下来了。可是这些数据,只活在程序运行的那一会儿。程序一结束,它们就全没了,下次想用,还得重新抓一遍。
重新抓一遍有什么不好呢?它不光浪费自己的时间,也在浪费别人网站的资源。所以,爬虫的最后一步是存储:把取到的数据存下来,以后要用,直接读文件就行。
那存成什么呢?实战 5 已经学过 csv 和 json,这一章就把它们用在抓下来的数据上。另外,还要再认识一位新朋友:sqlite3,它是 Python 自带的一个小数据库。数据一多,要按条件筛选、要分组统计的时候,它比 csv 文件好用得多。
很多童鞋一听「数据库」,就觉得很高深。其实 sqlite3 就是一个文件,不用安装,也不用启动什么服务。真正要学的,是一种叫 SQL 的查询语言。不过别担心,这一章只用到其中最常用的四五句,多看几遍就熟了。
学这一章之前,需要先学完 爬虫课第 7 章 ,会从接口取回整个书店的数据。另外,还要熟悉 实战 5 里 csv 和 json 的读写。
目录
这一章会学到:
- 一、为什么要存下来——别让数据跟着程序一起消失
- 二、存成 csv 和 json——各有各的用处
- 三、存进数据库——sqlite3 和建表、插入
- 四、用 SQL 查数据——筛选、排序、分组统计
- 五、常见坑
- 六、自己查一次文档——用字典直接插入
- 七、小结与回看
学完这一章,你能把抓到的数据存成 csv、json 和 sqlite3 数据库,并用 SQL 从数据库里查出想要的结果。
一、为什么要存下来
要存数据,先得有数据。咱们先用上一章的办法,从接口取回全部图书。后面几节,都从这一步开始:
import time
from urllib.parse import urljoin
import requests
url = 'https://learn-py.org/lab/bookstore/api/page-1.json'
books = []
while url:
data = requests.get(url).json()
books.extend(data['books'])
url = urljoin(url, data['next']) if data['next'] else None
time.sleep(0.2)
print(len(books))
print(list(books[0].keys()))输出的结果:
120
['id', 'title', 'author', 'category', 'price', 'rating', 'reviews', 'stock', 'pages', 'year']一共 120 本书,每本有 10 个字段。数据拿到了,为什么还要特意存下来呢?存下来有这么几个好处:
- 不用重复抓:分析数据的时候,代码可能要改十几遍。要是每改一次,都重新请求一遍网站,既慢,又打扰别人。
- 留下证据:网站的数据会变,今天的价格,明天可能就改了。存下来的,就是当时的样子。
- 交给别的程序用:存成 csv,Excel 就能打开。到了数据分析课,pandas 一行就能把它读进来。
那么,存成什么格式好呢?这要看用途:
| 格式 | 适合 |
|---|---|
| csv | 一行一条、每条字段都一样的表格数据;要给 Excel、pandas 用 |
| json | 有嵌套的数据,比如一本书带着好几条评论;要原样保留数字、None |
| sqlite3 数据库 | 数据多,经常要按条件筛选、排序、分组统计;要一点一点往里加 |
下一节,先从学过的 csv 和 json 开始。
二、存成 csv 和 json
先说 csv。每本书的字段都一样,排起来正好是一张表,所以用 csv.DictWriter 存最方便。表头的字段名,直接从第一本书的键里拿。存完以后,再读回来看看:
import csv
import requests
books = requests.get('https://learn-py.org/lab/bookstore/api/page-1.json').json()['books']
with open('my_books.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=list(books[0].keys()))
writer.writeheader()
writer.writerows(books)
with open('my_books.csv', encoding='utf-8', newline='') as f:
rows = list(csv.DictReader(f))
print(len(rows))
print(rows[2])输出的结果:
20
{'id': '3', 'title': '早起养花', 'author': '林子墨', 'category': '生活', 'price': '56.2', 'rating': '', 'reviews': '0', 'stock': '47', 'pages': '310', 'year': '2014'}这里只存了第一页的 20 本,好让例子短一点。读回来以后,有两件事要留意,实战 5 都说过:
- 所有的值都变成了字符串,比如
'56.2'、'47',用的时候要自己转换。 - 没有评分的
None,存进 csv 以后,变成了空字符串''。
这两件事,换成 json 就都不是问题了。同样存完再读回来看看:
import json
import requests
books = requests.get('https://learn-py.org/lab/bookstore/api/page-1.json').json()['books']
with open('my_books.json', 'w', encoding='utf-8') as f:
json.dump(books, f, ensure_ascii=False, indent=2)
with open('my_books.json', encoding='utf-8') as f:
loaded = json.load(f)
print(loaded[2])输出的结果:
{'id': 3, 'title': '早起养花', 'author': '林子墨', 'category': '生活', 'price': 56.2, 'rating': None, 'reviews': 0, 'stock': 47, 'pages': 310, 'year': 2014}和 csv 读回来的那一行对比一下:这回数字还是数字,None 也还是 None 。
还有两点要说明。在网页里运行时,这些文件存在浏览器临时准备的地方,刷新页面就没了。在自己电脑上运行,它们就存在代码文件的旁边。另外,要是 csv 是给 Excel 打开的,记得把编码换成 'utf-8-sig' 。
下面先做一道练习,把一页书存进 csv,再读回来数一数。
取回接口 https://learn-py.org/lab/bookstore/api/page-2.json 里的 20 本书,每本只留 id 、title 、price 、stock 四个字段,用 csv.DictWriter 存进 page2.csv 。再把文件读回来,打印一共几行、缺货(stock 为 0)的有几本。
注意:读回来的值都是字符串。
运行后的输出应该是:
20 行,缺货 2 本推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai