Python爬虫实战:构建“下载-去重-入库”的图片采集流水线(附SQLite持久化存储)!

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~

㊙️本期爬虫难度指数:⭐⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(⚠️ 必写)](#3️⃣ 合规与注意事项(⚠️ 必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:请求与下载层(Fetcher)](#6️⃣ 核心实现:请求与下载层(Fetcher))
      • [7️⃣ 核心实现:解析与去重层(Parser & Deduplication)](#7️⃣ 核心实现:解析与去重层(Parser & Deduplication))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(强烈建议写)](#🔟 常见问题与排错(强烈建议写))
      • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

💕订阅后更新会优先推送,按目录学习更高效💯~

1️⃣ 摘要(Abstract)

🎯 目标 :编写一个 Python 爬虫,批量下载高清壁纸,并引入感知哈希(pHash)算法 进行图像去重,最后将图片的元数据(来源、Hash值、本地路径)存入 SQLite 数据库。
🛠 工具requests (下载) + BeautifulSoup4 (解析) + Pillow & ImageHash (图像处理与指纹) + SQLite (元数据管理)。
💡 读完收获

  1. 学会如何优雅地处理二进制文件(图片)下载与流式写入。
  2. 核心技能:掌握 pHash 算法,识别出"内容相同但文件名/大小不同"的图片。
  3. 搭建一个最小化的"图片资产管理系统"雏形。

2️⃣ 背景与需求(Why)

🤔 为什么要爬?

做深度学习训练集、搭建个人素材库,或者单纯是想把喜欢的画师作品一次性拖回本地。

🚩 痛点

普通爬虫只看 URL 是否相同。但互联网上充满了重复转载 ,同一张图可能是 img_01.jpg,在另一个站叫 wallpaper_2024.png。如果只靠文件名去重,你的硬盘会被同样的图塞满。我们需要基于内容的去重。

🎯 目标站点 :在此以 https://pic.netbian.com/4kdongman/ (彼岸图网 4K 动漫区)为例,这是一个经典的静态壁纸站,结构简单,非常适合练手。

📋 目标字段

  • Origin URL (原始链接)
  • Local Path (本地存储路径)
  • pHash (图像指纹,用于去重)
  • Resolution (分辨率,如 1920x1080)

3️⃣ 合规与注意事项(⚠️ 必写)

图片站的流量成本很高,请务必做一个有素质的爬虫!

  1. Referer 防盗链 :很多图片服务器会检查 Referer,如果你不告诉它你是从网页点进来的,它会返给你一张"禁止盗链"的碎图。
  2. 频率控制(关键) :下载图片属于 IO 密集且占带宽的操作。必须设置较长的 time.sleep(建议 1-2秒),不要把人家服务器带宽跑满了。
  3. 版权意识:爬下来的图自己欣赏或做研究可以,千万别打包去卖,或者搭建镜像站非法牟利。

4️⃣ 技术选型与整体流程(What/How)

🎨 流程图
获取列表页解析详情页下载图片到临时区计算 pHash数据库查重(若不重复) 移动到正式库 & 写入 SQLite(若重复) 删除临时文件

🛠 为什么选 pHash (Perceptual Hash)?

  • MD5:哪怕图片改了一个像素,MD5 都会变,无法识别"看起来一样"的图。
  • pHash :它关注图片的低频结构信息。图片压缩、调色、微调尺寸后,pHash 值依然非常接近。这才是我们想要的"去重"。

5️⃣ 环境准备与依赖安装(可复现)

Python 3.8+。除了常规的爬虫库,这次需要加上图像处理库。

bash 复制代码
pip install requests beautifulsoup4 ImageHash pillow
  • Pillow (PIL):Python 的图像处理标准库。
  • ImageHash:专门算图片哈希值的神器。

📂 项目结构

text 复制代码
img_spider/
├── images/             # 最终存图目录
├── temp/               # 下载临时目录(用于算Hash)
├── metadata.db         # SQLite 数据库
└── main.py             # 主程序

6️⃣ 核心实现:请求与下载层(Fetcher)

下载大文件(图片)时,建议使用 stream=True,防止一张图把内存撑爆。

python 复制代码
import requests
import os

def download_image(img_url, temp_dir='temp'):
    """
    流式下载图片到临时目录
    """
    headers = {
        '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',
        'Referer': 'https://pic.netbian.com/' # 必带!防盗链
    }
    
    filename = img_url.split('/')[-1]
    temp_path = os.path.join(temp_dir, filename)
    os.makedirs(temp_dir, exist_ok=True)
    
    try:
        with requests.get(img_url, headers=headers, stream=True, timeout=15) as r:
            r.raise_for_status()
            # 只有当 Content-Type 是图片时才保存
            if 'image' in r.headers.get('Content-Type', ''):
                with open(temp_path, 'wb') as f:
                    for chunk in r.iter_content(chunk_size=8192):
                        f.write(chunk)
                return temp_path
            else:
                print(f"⚠️ 跳过非图片链接: {img_url}")
                return None
    except Exception as e:
        print(f"❌ 下载失败 {img_url}: {e}")
        return None

7️⃣ 核心实现:解析与去重层(Parser & Deduplication)

这是本次的重头戏!我们要把"计算 pHash"和"数据库比对"结合起来。

python 复制代码
import imagehash
from PIL import Image
import sqlite3
import shutil

# 初始化数据库
def init_db(db_name='metadata.db'):
    conn = sqlite3.connect(db_name)
    c = conn.cursor()
    # 创建表:url 唯一,但 phash 允许重复(为了容错,但逻辑上我们要控制)
    c.execute('''CREATE TABLE IF NOT EXISTS images
                 (id INTEGER PRIMARY KEY, url TEXT, path TEXT, phash TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
    conn.commit()
    return conn

def process_image(temp_path, original_url, conn, final_dir='images'):
    """
    核心逻辑:计算Hash -> 查重 -> 入库
    """
    try:
        # 1. 打开图片计算 pHash
        img = Image.open(temp_path)
        img_hash = str(imagehash.phash(img))
        
        c = conn.cursor()
        
        # 2. 查重逻辑
        # 简单比对:直接看数据库里有没有完全一样的 hash 字符串
        # 进阶比对:计算汉明距离(Hamming Distance),这里为了演示用简单字符串相等
        c.execute("SELECT id FROM images WHERE phash = ?", (img_hash,))
        row = c.fetchone()
        
        if row:
            print(f"♻️ 检测到重复图片 (Hash: {img_hash}),跳过入库。")
            img.close()
            os.remove(temp_path) # 删掉临时文件
            return False
        else:
            # 3. 不重复,移动到正式目录并入库
            os.makedirs(final_dir, exist_ok=True)
            filename = os.path.basename(temp_path)
            final_path = os.path.join(final_dir, filename)
            
            img.close() # 必须先关闭文件句柄才能移动
            shutil.move(temp_path, final_path)
            
            c.execute("INSERT INTO images (url, path, phash) VALUES (?, ?, ?)", 
                      (original_url, final_path, img_hash))
            conn.commit()
            print(f"✅ 入库成功: {filename} (Hash: {img_hash})")
            return True
            
    except Exception as e:
        print(f"⚠️ 图片处理出错: {e}")
        if os.path.exists(temp_path):
            os.remove(temp_path)
        return False

8️⃣ 数据存储与导出(Storage)

上面已经在 process_image 里集成了 SQLite 的写入。相比 CSV,SQLite 更适合这种需要频繁"查询是否存在"的场景。

字段映射

字段名 类型 说明
id INTEGER 自增主键
url TEXT 原始来源,方便回溯
path TEXT 本地存储位置
phash TEXT 核心指纹,例如 d41d8cd98f00b204

去重策略补充:如果你想做得更极致,可以计算新 Hash 与库中所有 Hash 的汉明距离,若距离 < 5 则视为相似图片。但对于几十万张图的库,这需要 VP-Tree 等算法加速,今天先按"完全相等"处理。

9️⃣ 运行方式与结果展示(必写)

python 复制代码
# main.py 组装
def main():
    base_url = "https://pic.netbian.com/4kdongman/index_{}.html"
    conn = init_db()
    
    # 演示:只爬取前 2 页
    # 注意:该网站第一页是 index.html,第二页是 index_2.html,逻辑需微调,这里简化
    urls = ["https://pic.netbian.com/4kdongman/"] + [base_url.format(i) for i in range(2, 3)]
    
    for url in urls:
        print(f"\n🔍 正在扫描页面: {url}")
        resp = requests.get(url, headers={'User-Agent': 'Mozilla/5.0...'})
        resp.encoding = 'gbk' # 彼岸图网是 GBK 编码!
        
        soup = BeautifulSoup(resp.text, 'html.parser')
        # 提取缩略图列表里的详情页链接
        links = soup.select('.slist ul li a')
        
        for link in links:
            detail_url = "https://pic.netbian.com" + link['href']
            # 这里简化逻辑:直接假设详情页里有张大图,实际需再发请求去详情页解析
            # 为演示代码简洁,我们假设抓取到了 img_src
            # 真实场景需增加一个 parse_detail(detail_url) 函数
            print(f"   -> 发现详情页: {detail_url}")
            
            # 模拟:假设我们解析出了大图 URL
            # ⚠️ 实际代码需要你去详情页抓取 'src'
            
    conn.close()

if __name__ == "__main__":
    # 这里放一段测试用的伪代码逻辑,证明跑通流程
    conn = init_db()
    
    # 测试:下载一张图
    print("🧪 开始测试单图流程...")
    test_img_url = "https://pic.netbian.com/uploads/allimg/240228/001428-1709050468ea80.jpg" 
    temp_file = download_image(test_img_url)
    
    if temp_file:
        process_image(temp_file, test_img_url, conn)
        
    conn.close()
    print("🎉 测试结束,查看 images 目录和 metadata.db")

📊 运行结果示例

text 复制代码
🧪 开始测试单图流程...
✅ 入库成功: 001428-1709050468ea80.jpg (Hash: ffe0c0f0f0e1c3c0)
🎉 测试结束...

(再次运行同一代码)

text 复制代码
🧪 开始测试单图流程...
♻️ 检测到重复图片 (Hash: ffe0c0f0f0e1c3c0),跳过入库。

🔟 常见问题与排错(强烈建议写)

  1. 图片打不开/损坏

    • 原因:下载未完成连接就断了,或者对方开启了防盗链返回了假图。
    • 解决:用 PIL.Image.open() 校验一下,如果报错 UnidentifiedImageError,说明图是坏的,直接删掉。
  2. 编码报错 UnicodeDecodeError

    • 国内老牌图片站很多还在用 GBK 。记得 response.encoding = 'gbk',否则解析 HTML 全是乱码。
  3. Hash 误判

    • pHash 过于强大,有时两张图只有一点点色差也被判为相同。如果对画质要求极高(如区分 4K 和 1080P),请在去重逻辑里加入"分辨率比对":Hash 相同且分辨率更低才算重复。

1️⃣1️⃣ 进阶优化(可选但加分)

  • 异步并发 (Aiohttp) :图片下载是纯 I/O 操作。用 asyncio + aiohttp,下载速度能翻 10 倍!但记得限制并发数(Semaphore),不然 IP 必死。
  • 汉明距离查重 :不要用 SQL 的 =,而是把 Hash 转为二进制,计算异或值。SQLite 本身不支持这个,可以在 Python 内存里加载所有 Hash 进行比对(量大时用 Redis 或 Faiss)。
  • 缩略图生成:入库时顺便生成一张 200x200 的缩略图,方便以后做前端展示。

1️⃣2️⃣ 总结与延伸阅读

🎉 复盘

今天我们不仅写了个爬虫,还涉及了计算机视觉的基础------感知哈希。我们实现了一个能自动把关、拒绝重复内容的智能图片库。

👣 下一步

  1. 人脸识别 :使用 face_recognition 库,只下载包含"人脸"的图片。
  2. NSFW 过滤:为了身心健康(和硬盘安全),可以接一个简单的 AI 模型过滤掉不适宜的图片。
  3. 分布式存储:图片多了硬盘装不下?学习如何把图片存到 AWS S3 或 阿里云 OSS 上。

这次的坑比较深(涉及二进制处理),如果代码跑不通或者遇到防盗链 403,记得检查 Headers 里的 Referer 哦!加油!

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
爱内卷的学霸一枚2 小时前
Python并发编程与性能优化实战指南
开发语言·python·性能优化
Blurpath住宅代理2 小时前
如何在Python爬虫中使用代理IP?从配置到轮换的完整指南
网络·爬虫·python·住宅ip·住宅代理·动态住宅代理
DeniuHe2 小时前
Pytorch中统计学相关的函数
pytorch·python·深度学习
newbiai2 小时前
电商直播AI视频生成工具哪个方便快捷?
人工智能·python·音视频
ID_180079054732 小时前
Python结合淘宝关键词API进行商品数据挖掘与
开发语言·python·数据挖掘
梦因you而美2 小时前
Python win32com操作Excel:彻底禁用链接更新及各类弹窗(实测有效)
python·excel·win32com·禁用链接更新·excel弹框
一个没有本领的人2 小时前
vscode选择了正确的解释器,但终端显示运行的依然为原来的python版本
ide·vscode·python
云和数据.ChenGuang2 小时前
python 面向对象基础入门
开发语言·前端·python·django·flask
啊阿狸不会拉杆2 小时前
《机器学习导论》第 2 章-监督学习
数据结构·人工智能·python·学习·算法·机器学习·监督学习