楼层: 首页/ 软件技术/ Python 爬虫技术/ 数据存储:CSV / JSON / MySQL
4

数据存储:CSV / JSON / MySQL

Storing Scraped Data

爬下来的数据总得存。小数据存 CSV/JSON 用 Excel 打开就行,大数据进数据库。这章把常用存储方式过一遍。

CSV 存储

import csv # csv.writer:按行写列表 with open("data.csv", "w", newline="", encoding="utf-8-sig") as f: w = csv.writer(f) w.writerow(["名字", "年龄"]) # 表头 w.writerow(["小明", 18]) w.writerow(["小红", 20]) # utf-8-sig:Excel 打开不乱码 # Pandas 一把梭:df.to_csv("data.csv", index=False)

JSON 存储

import json data = [{"title": "Python", "views": 100}, {"title": "爬虫", "views": 200}] with open("data.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) # ensure_ascii=False:中文不转义;indent=2:缩进好看

数据库存储:MySQL

pip install pymysql import pymysql conn = pymysql.connect(host="localhost", user="root", password="123", database="spider", charset="utf8mb4") cur = conn.cursor() # 建表 cur.execute("""CREATE TABLE IF NOT EXISTS articles ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200), url VARCHAR(500), UNIQUE KEY uk_url (url) )""") # 插入(去重:用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE) sql = "INSERT IGNORE INTO articles (title, url) VALUES (%s, %s)" cur.execute(sql, ("Python入门", "https://a.com/1")) conn.commit() cur.close() conn.close()

完整案例:数据存入 MySQL

# crawl_to_mysql.py # 1. 建表 SQL CREATE_TABLE_SQL = """ CREATE TABLE IF NOT EXISTS articles ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) NOT NULL, url VARCHAR(500) UNIQUE, author VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 """ # 2. 插入(INSERT IGNORE 去重) INSERT_SQL = "INSERT IGNORE INTO articles (title, url, author) VALUES (%s, %s, %s)" # 3. 爬虫主流程 import pymysql, requests from bs4 import BeautifulSoup conn = pymysql.connect(host="localhost", user="root", password="123", database="spider", charset="utf8mb4") cur = conn.cursor() cur.execute(CREATE_TABLE_SQL) resp = requests.get("https://example.com/news") soup = BeautifulSoup(resp.text, "lxml") for item in soup.select(".item"): title = item.select_one(".title").text url = item.select_one("a")["href"] cur.execute(INSERT_SQL, (title, url, "")) conn.commit() print(f"插入 {cur.rowcount} 行")
SQL 注入与参数化

永远用 %s 占位符,别自己拼 SQL 字符串。f"INSERT ... VALUES ('{title}')"——title 里要是带个引号,SQL 就坏了,还可能被注入。cur.execute(sql, (title, url)) 让库帮你转义,安全又省心。

MongoDB:存"形状不固定"的数据

爬来的数据经常字段不统一:这条有"评论数"那条没有,这条多俩"标签"。硬塞进 MySQL 表结构反而别扭。MongoDB 是文档型数据库,每条记录就是一个 JSON 文档,字段随便加,特别适合存爬虫结果。

论大白话:MySQL 是 Excel,MongoDB 是一堆 dict

MySQL 要求先定表头(建表),每行列数固定;MongoDB 像一个大 list,里面每个元素是个 dict,各 dict 的 key 可以不一样。pymongo 就是它的 Python 客户端。

用 pymongo 插入和查询

pip install pymongo # 以官网最新稳定版为准;MongoDB 服务要先起来 from pymongo import MongoClient client = MongoClient("mongodb://localhost:27017/") db = client["spider"] # 选库,不存在会自动建 col = db["movies"] # 选集合(相当于表) # 插入一条(就是个 dict) col.insert_one({"title": "霸王别姬", "score": 9.6, "tags": ["经典", "爱情"]}) # 批量插入 col.insert_many([ {"title": "活着", "score": 9.4}, {"title": "无间道", "score": 9.3, "region": "香港"}, # 多个 region 字段,MySQL 会报错,MongoDB 随便 ]) # 查询:score 大于 9.3 的,按分数倒序 for m in col.find({"score": {"$gt": 9.3}}).sort("score", -1): print(m["title"], m["score"]) # 霸王别姬 9.6 # 活着 9.4
防坑:去重靠唯一索引

爬虫重跑会重复插入。先给 title 建唯一索引:col.create_index("title", unique=True),之后重复 insert_one 会报错,配合 update_one(..., upsert=True) 就能"有则更新、无则插入",天然支持增量爬取。

练习:选库小测(点开对答案)

问:爬商品评论,每条评论字段经常变、数量千万级,用 MySQL 还是 MongoDB?
答:MongoDB。字段不固定、文档型、水平扩容方便,正好吃这场景。但如果你后续要按复杂条件做多表 join 统计报表,关系型的 MySQL 反而更顺手。

其他存储一览

存储什么时候用
CSV小数据、给 Excel 看
JSON嵌套结构、给程序读
SQLite单文件数据库,零配置,小项目首选
MySQL生产环境、多人查
MongoDB非结构化数据、文档型
Excelopenpyxl / Pandas to_excel