Python爬虫构建高效图片采集与去重系统

金宇澄

1. 项目概述:构建高效图片采集流水线

在数据驱动的互联网时代,图片采集已成为许多业务场景的基础需求。无论是电商比价、内容聚合还是AI训练数据准备,都需要从海量网页中高效获取图片资源。传统手动下载方式不仅效率低下,还面临重复下载、存储混乱等问题。

这个Python爬虫项目实现了一个完整的"下载-去重-入库"自动化流水线,核心解决三个痛点:

  1. 自动化批量下载:通过爬虫技术解放双手
  2. 智能去重:基于内容特征避免重复存储
  3. 结构化存储:使用SQLite实现持久化管理

整套系统采用模块化设计,各环节可独立扩展。我曾用类似方案为一家电商公司搭建商品图片库,单机日采集量稳定在5万张以上,重复率控制在3%以内。

2. 核心组件与技术选型

2.1 爬虫框架选择:Requests+BeautifulSoup组合

对于图片采集这种相对简单的爬取任务,轻量级的Requests库配合BeautifulSoup解析器是最佳选择。相比Scrapy等全功能框架,这个组合有三大优势:

  • 学习曲线平缓,适合快速开发
  • 资源占用低,适合长时间运行
  • 灵活性强,可应对各种反爬策略

实测案例:在采集某图片网站时,使用Scrapy平均每秒处理5个页面,而Requests+多线程可达15页/秒。

2.2 去重算法:感知哈希(pHash)

传统MD5校验只能识别完全相同的文件,而感知哈希可以识别内容相似的图片。我们采用pHash算法,其工作原理是:

  1. 缩小图片至32x32像素
  2. 转换为灰度图
  3. 计算DCT变换
  4. 取左上角8x8的DCT系数
  5. 计算平均值并生成64位哈希值

关键参数说明:

  • 哈希距离阈值设为5(经验值)
  • 支持JPG/PNG/GIF等常见格式
  • 内存中维护最近1000个哈希值缓存

2.3 存储方案:SQLite数据库

相比文件系统直接存储,SQLite提供了三大核心价值:

  1. 结构化存储元数据(URL、尺寸、标签等)
  2. 支持复杂查询(按尺寸/时间/来源筛选)
  3. 事务保证数据一致性

数据库表设计:

sql复制CREATE TABLE images (
    id INTEGER PRIMARY KEY,
    url TEXT UNIQUE,
    filepath TEXT,
    phash TEXT,
    width INTEGER,
    height INTEGER,
    format TEXT,
    source TEXT,
    download_time DATETIME
);

3. 完整实现步骤

3.1 环境准备与依赖安装

推荐使用Python 3.8+环境,依赖包通过requirements.txt管理:

code复制requests==2.26.0
beautifulsoup4==4.10.0
Pillow==9.0.0
imagehash==4.2.1
tqdm==4.62.3

安装命令:

bash复制pip install -r requirements.txt

注意:建议使用虚拟环境避免依赖冲突,可通过python -m venv venv创建

3.2 爬虫核心代码实现

基础爬取流程分为四个步骤:

  1. 发送HTTP请求获取页面
python复制def fetch_page(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36'
    }
    try:
        resp = requests.get(url, headers=headers, timeout=10)
        resp.raise_for_status()
        return resp.content
    except Exception as e:
        print(f"Failed to fetch {url}: {str(e)}")
        return None
  1. 解析HTML提取图片链接
python复制def extract_image_links(html, base_url):
    soup = BeautifulSoup(html, 'html.parser')
    img_tags = soup.find_all('img')
    
    links = []
    for img in img_tags:
        src = img.get('src') or img.get('data-src')
        if not src:
            continue
            
        # 处理相对路径
        if src.startswith('//'):
            src = 'https:' + src
        elif src.startswith('/'):
            src = urljoin(base_url, src)
            
        links.append(src)
    return links
  1. 多线程下载优化
python复制from concurrent.futures import ThreadPoolExecutor

def batch_download(urls, max_workers=5):
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(download_image, url) for url in urls]
        for future in tqdm(as_completed(futures), total=len(urls)):
            future.result()

3.3 去重模块实现

基于ImageHash库的完整去重流程:

python复制from PIL import Image
import imagehash

def calculate_phash(image_path):
    try:
        with Image.open(image_path) as img:
            return str(imagehash.phash(img))
    except Exception as e:
        print(f"Error processing {image_path}: {str(e)}")
        return None

def is_duplicate(db_conn, phash, threshold=5):
    if not phash:
        return False
        
    cursor = db_conn.cursor()
    cursor.execute("SELECT phash FROM images WHERE phash IS NOT NULL")
    existing_hashes = [row[0] for row in cursor.fetchall()]
    
    for existing in existing_hashes:
        if imagehash.hex_to_hash(phash) - imagehash.hex_to_hash(existing) <= threshold:
            return True
    return False

3.4 SQLite存储实现

数据库操作封装类:

python复制import sqlite3
from datetime import datetime

class ImageDB:
    def __init__(self, db_path='images.db'):
        self.conn = sqlite3.connect(db_path)
        self._create_table()
    
    def _create_table(self):
        self.conn.execute('''CREATE TABLE IF NOT EXISTS images
            (id INTEGER PRIMARY KEY AUTOINCREMENT,
             url TEXT UNIQUE,
             filepath TEXT,
             phash TEXT,
             width INTEGER,
             height INTEGER,
             format TEXT,
             source TEXT,
             download_time DATETIME)''')
        self.conn.commit()
    
    def insert_image(self, image_data):
        try:
            self.conn.execute(
                '''INSERT INTO images 
                (url, filepath, phash, width, height, format, source, download_time)
                VALUES (?,?,?,?,?,?,?,?)''',
                image_data)
            self.conn.commit()
            return True
        except sqlite3.IntegrityError:
            return False  # URL已存在

4. 高级优化技巧

4.1 反爬策略应对方案

常见反爬手段及应对措施:

反爬类型 解决方案 实现示例
User-Agent检测 轮换UA池 准备20+常见UA随机选择
IP限制 使用代理IP 维护IP池,失败自动切换
请求频率限制 动态延迟 根据响应时间自动调整间隔
JS渲染 简单模拟执行 提取