🐸 青蛙编程网
首页 / Python / 爬虫 / 4. 数据存储与去重

4. 数据存储与去重

这是什么:数据解析出来了,只 print 一下等于白抓——程序一结束就没了。这一章解决「抓到的数据怎么留、别重复抓」两个问题:三种落盘格式(CSV / JSON / SQLite)加一个去重集合。

为什么重要:爬虫是长期跑的活儿,今天抓一页、明天抓全站,格式选错返工、URL 不去重就死循环。把存储和去重做成固定套路,后面的框架学习才不慌。

如何用:按数据用途三选一:给人看的表格用 CSV、程序之间传递用 JSON、要查询要汇总用 SQLite;然后给爬虫装上「记性」(去重)和「教养」(限速)。

CSV:给 Excel 和 Pandas 看

CSV 就是用逗号分列的纯文本,Excel、Pandas 都能直接打开:

import csv

rows = [
    ["名言", "作者", "标签"],                                  # 表头
    ["“The world as we have created it…”", "Albert Einstein", "change,deep-thoughts"],
    ["“It is our choices…”", "J.K. Rowling", "abilities,choices"],
]

# utf-8-sig:带 BOM 头,Excel 双击打开中文不乱码;newline="" 是 csv 模块的铁规矩
with open("quotes.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerows(rows)
参数 不写的后果
newline="" Windows 下每行之间多一个空行
encoding="utf-8-sig" Excel 打开中文变乱码(记事本正常,Excel 不认)

JSON:给程序看的格式

下一章开始会大量遇到 JSON 接口,存取也用它——和 Python 的字典/列表无缝互转:

import json

data = [{"text": "It is our choices…", "author": "J.K. Rowling", "tags": ["choices"]}]

with open("quotes.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)   # 中文原样、带缩进可读

with open("quotes.json", encoding="utf-8") as f:
    same = json.load(f)                                # 读回来还是列表套字典
print(same[0]["author"])

ensure_ascii=False 别忘——否则中文全变 \uXXXX 转义。

SQLite:要查询、要汇总的场合

数据量大、要筛选统计时,直接进数据库。Python 标准库自带 SQLite(零安装,详见 SQLite 教程):

import sqlite3

conn = sqlite3.connect("quotes.db")
conn.execute(
    "CREATE TABLE IF NOT EXISTS quote ("
    " id INTEGER PRIMARY KEY, text TEXT, author TEXT, tags TEXT)"
)

rows = [
    ("“The world as we have created it…”", "Albert Einstein", "change"),
    ("“It is our choices…”", "J.K. Rowling", "choices"),
]
conn.executemany("INSERT INTO quote (text, author, tags) VALUES (?, ?, ?)", rows)
conn.commit()

for author, text in conn.execute("SELECT author, text FROM quote WHERE author LIKE ?", ("J%",)):
    print(author, text)
conn.close()

选型一句话:CSV 给人看,JSON 给程序传,SQLite 给要查询的数据住——三者都比 print 强一亿倍。

去重:给爬虫装上「记性」

爬虫顺链接爬,同一个 URL 会反复遇到(首页链接到列表页、列表页又链回首页),不去重就是死循环。用一个 set 记住爬过谁:

import requests
from bs4 import BeautifulSoup

headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0"}
seen = set()                       # 已抓过的 URL
queue = ["https://quotes.toscrape.com/"]   # 待抓队列

while queue:
    url = queue.pop(0)             # 取队首
    if url in seen:                # 抓过就跳过
        continue
    seen.add(url)
    print(f"抓取 {url}(已抓 {len(seen)} 页)")

    r = requests.get(url, headers=headers, timeout=10)
    soup = BeautifulSoup(r.text, "html.parser")
    for a in soup.select("li.next a"):           # 找「下一页」链接
        full = requests.compat.urljoin(url, a["href"])   # 相对地址 → 绝对地址
        if full not in seen:
            queue.append(full)

你会看到:程序自己从第 1 页顺着「下一页」爬到第 10 页然后自己停下——队列 + 去重,就是爬虫「爬满全站」的全部秘密。urljoin 负责把 /page/2/ 这种相对地址拼成完整 URL。

礼貌爬取:管住爬虫的手

全站抓取对服务器是有压力的,两行代码做个有教养的爬虫:

import random
import time

time.sleep(random.uniform(1, 3))   # 每次请求之间随机停 1~3 秒
规矩 怎么做
请求有间隔 每次抓完 time.sleep(1~3),别一秒几十连发
避开业务高峰 大批量采集放深夜,别在人家峰值凑热闹
遇 429 立刻退 降速翻倍,甚至当天别再爬
声明身份 User-Agent 里可以带上邮箱/项目名,方便站长联系你

常见翻车现场

症状 原因 修复
Excel 打开 CSV 乱码 编码没带 BOM encoding="utf-8-sig"
CSV 隔行有空行 少了 newline="" open(..., newline="")
JSON 打开是 \uXXXX 默认转义中文 ensure_ascii=False
爬虫停不下来 URL 没去重,队列越转越多 set 记录 + 判断(本章套路)
相对链接拼错 直接拿 /page/2/ 当完整 URL 请求 urljoin(当前页, href)

动手改

  1. 给上面的全站爬虫加存储:每页解析出的名言(用第 3 章的代码)写进 quotes_all.csv
  2. 把 queue.pop(0) 换成 queue.pop() 再跑——变成了什么遍历顺序?(提示:深度优先)
  3. 在爬虫里加 time.sleep(random.uniform(1, 3)),感受一下「有教养」和「没教养」的速度差

上一章:解析 HTML · 下一章:Scrapy 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。