位置: 文档库 > Python > python爬虫[一] 批量下载妹子图

python爬虫[一] 批量下载妹子图

基辛格 上传于 2020-06-27 00:24

《Python爬虫[一] 批量下载妹子图》

在互联网数据采集领域,Python爬虫因其简洁的语法和强大的库支持成为首选工具。本文将通过实战案例,详细讲解如何使用Python实现批量下载特定类型图片(以"妹子图"为例),涵盖爬虫基础原理、核心代码实现、反爬策略应对及完整项目流程。

一、爬虫基础原理

网络爬虫本质是通过程序模拟浏览器行为,自动访问网页并提取所需数据。其核心流程包括:

  1. 发送HTTP请求获取网页内容

  2. 解析HTML/XML文档提取目标数据

  3. 存储或处理获取的数据

针对图片下载场景,需重点关注:

  • 图片URL的定位与提取

  • 多线程/异步下载优化

  • 反爬机制应对(User-Agent、IP代理等)

二、环境准备与工具选择

推荐开发环境:

Python 3.8+
PyCharm/VSCode 编辑器
Chrome浏览器(用于分析网页结构)

核心依赖库:

requests      # HTTP请求库
beautifulsoup4 # HTML解析
parsel        # 更强大的CSS选择器支持
concurrent.futures # 多线程
os, time      # 系统操作与延时控制

安装命令:

pip install requests beautifulsoup4 parsel

三、完整代码实现

1. 基础版本(单线程)

import requests
from bs4 import BeautifulSoup
import os
import time

def download_image(url, save_path):
    """下载单张图片"""
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }
        response = requests.get(url, headers=headers, timeout=10)
        if response.status_code == 200:
            with open(save_path, 'wb') as f:
                f.write(response.content)
            print(f"下载成功: {save_path}")
        else:
            print(f"下载失败,状态码: {response.status_code}")
    except Exception as e:
        print(f"下载出错: {e}")

def parse_page(html):
    """解析网页提取图片URL"""
    soup = BeautifulSoup(html, 'html.parser')
    img_tags = soup.find_all('img', class_='lazy')  # 根据实际网站调整选择器
    urls = []
    for img in img_tags:
        src = img.get('data-original') or img.get('src')
        if src and 'http' in src:
            urls.append(src)
    return urls

def main():
    base_url = 'https://www.example.com/meizi'  # 替换为实际目标网站
    save_dir = './meizi_images'
    os.makedirs(save_dir, exist_ok=True)
    
    try:
        response = requests.get(base_url)
        if response.status_code == 200:
            img_urls = parse_page(response.text)
            for i, url in enumerate(img_urls[:10]):  # 限制下载数量
                ext = url.split('.')[-1] if '.' in url else 'jpg'
                filename = f"{save_dir}/img_{i}.{ext}"
                download_image(url, filename)
                time.sleep(1)  # 礼貌性延迟
    except Exception as e:
        print(f"请求出错: {e}")

2. 进阶版本(多线程+异常处理)

import requests
from parsel import Selector
import os
from concurrent.futures import ThreadPoolExecutor
import random

class MeiziDownloader:
    def __init__(self, max_workers=5):
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
        self.max_workers = max_workers
        self.save_dir = './high_quality_meizi'
        os.makedirs(self.save_dir, exist_ok=True)
    
    def get_page(self, url):
        """带重试机制的页面获取"""
        for _ in range(3):
            try:
                response = self.session.get(url, timeout=15)
                if response.status_code == 200:
                    return response.text
            except Exception as e:
                print(f"请求失败,重试中... {e}")
                time.sleep(random.uniform(1, 3))
        return None
    
    def extract_images(self, html):
        """使用parsel提取图片URL"""
        sel = Selector(text=html)
        return [img.get('src') for img in sel.css('.meizi-img::attr(src)').getall()]
    
    def download_single(self, url, index):
        """单图片下载(线程内执行)"""
        try:
            ext = url.split('.')[-1].split('?')[0] if '.' in url else 'jpg'
            filename = f"{self.save_dir}/meizi_{index}.{ext}"
            
            for _ in range(2):
                try:
                    img_data = self.session.get(url, stream=True).content
                    with open(filename, 'wb') as f:
                        f.write(img_data)
                    print(f"成功下载: {filename}")
                    return True
                except Exception as e:
                    print(f"下载失败,重试... {e}")
                    time.sleep(2)
            return False
        except Exception as e:
            print(f"处理出错: {e}")
            return False
    
    def run(self, start_url):
        """主执行流程"""
        html = self.get_page(start_url)
        if not html:
            print("无法获取页面内容")
            return
        
        img_urls = self.extract_images(html)
        print(f"共发现 {len(img_urls)} 张图片")
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            futures = [
                executor.submit(self.download_single, url, i)
                for i, url in enumerate(img_urls[:20])  # 限制数量
            ]
            for future in futures:
                future.result()  # 等待所有线程完成

if __name__ == '__main__':
    downloader = MeiziDownloader()
    downloader.run('https://www.meizitu.com/a/5200.html')  # 示例URL

四、关键技术点解析

1. 反爬策略应对

常见反爬机制及解决方案:

  • User-Agent检测:随机切换UA

  • IP限制:使用代理IP池

  • 频率限制:设置随机延迟(time.sleep(random.uniform(1,3)))

  • JavaScript渲染:使用Selenium或Playwright

示例代理设置:

proxies = {
    'http': 'http://123.123.123.123:8080',
    'https': 'https://123.123.123.123:8080'
}
response = requests.get(url, proxies=proxies)

2. 性能优化技巧

多线程下载实现:

from concurrent.futures import ThreadPoolExecutor

def download_task(url):
    # 下载逻辑
    pass

urls = [...]  # 图片URL列表
with ThreadPoolExecutor(max_workers=10) as executor:
    executor.map(download_task, urls)

连接池优化:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retries = Retry(total=3, backoff_factor=1)
session.mount('http://', HTTPAdapter(max_retries=retries))
session.mount('https://', HTTPAdapter(max_retries=retries))

3. 数据存储方案

推荐存储方式对比:

方式 优点 缺点
本地文件 简单直接 不易管理
数据库 便于查询 需要配置
云存储 可扩展 需要API密钥

SQLite存储示例:

import sqlite3

conn = sqlite3.connect('images.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS images
             (id INTEGER PRIMARY KEY, url TEXT, path TEXT)''')

def save_to_db(url, path):
    c.execute("INSERT INTO images (url, path) VALUES (?, ?)", (url, path))
    conn.commit()

五、法律与道德规范

实施网络爬虫必须遵守的准则:

  1. 检查目标网站的robots.txt文件

  2. 控制请求频率,避免对服务器造成压力

  3. 仅下载允许公开获取的内容

  4. 尊重版权,不得用于商业用途

典型robots.txt示例分析:

User-agent: *
Disallow: /admin/
Allow: /public/
Crawl-delay: 10

六、项目扩展方向

可改进的维度:

  • 增加图形界面(PyQt/Tkinter)

  • 实现断点续传功能

  • 添加图片分类标签

  • 开发浏览器插件形式

分布式爬虫架构示例:

主节点(Master):
- 任务分发
- 结果汇总

工作节点(Worker):
- 执行具体下载任务
- 返回结果

通信方式:
- Redis队列
- RabbitMQ消息队列

七、常见问题解决方案

Q1:遇到403 Forbidden错误怎么办?

A:检查是否缺少必要请求头,尝试添加:

headers = {
    'Referer': 'https://www.example.com/',
    'Accept-Language': 'zh-CN,zh;q=0.9'
}

Q2:如何处理动态加载的图片?

A:使用Selenium模拟浏览器操作:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://www.example.com')
# 等待JS加载完成
time.sleep(3)
# 获取渲染后的HTML
html = driver.page_source
driver.quit()

Q3:如何提高大文件下载稳定性?

A:使用流式下载和分块保存:

response = requests.get(url, stream=True)
with open('large_file.jpg', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

关键词:Python爬虫、图片下载多线程、反爬策略、requests库BeautifulSoup、数据采集、网络编程异步下载道德规范

简介:本文通过实战案例系统讲解Python爬虫实现批量下载图片的全过程,涵盖基础原理、核心代码实现、反爬策略应对、性能优化技巧及法律规范说明,提供从单线程到多线程的完整解决方案,适合Python初学者和中级开发者学习网络数据采集技术。