《Python爬虫[一] 批量下载妹子图》
在互联网数据采集领域,Python爬虫因其简洁的语法和强大的库支持成为首选工具。本文将通过实战案例,详细讲解如何使用Python实现批量下载特定类型图片(以"妹子图"为例),涵盖爬虫基础原理、核心代码实现、反爬策略应对及完整项目流程。
一、爬虫基础原理
网络爬虫本质是通过程序模拟浏览器行为,自动访问网页并提取所需数据。其核心流程包括:
发送HTTP请求获取网页内容
解析HTML/XML文档提取目标数据
存储或处理获取的数据
针对图片下载场景,需重点关注:
图片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()
五、法律与道德规范
实施网络爬虫必须遵守的准则:
检查目标网站的robots.txt文件
控制请求频率,避免对服务器造成压力
仅下载允许公开获取的内容
尊重版权,不得用于商业用途
典型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初学者和中级开发者学习网络数据采集技术。