本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在Python编程中,网络爬虫是获取网络数据的重要手段,尤其在图像数据采集方面具有广泛应用。本文详细讲解如何使用Python实现百度图片的自动爬取,涵盖网络爬虫基础原理、requests库发送HTTP请求、BeautifulSoup解析HTML内容、图片链接提取与下载、多线程加速以及图片处理技术(如PIL库)。通过本教程,读者可掌握完整的图片爬虫开发流程,并为后续数据挖掘、图像分析等任务打下坚实基础。
百度图片爬取_爬取_爬取图片_爬虫_python爬_python_

1. 百度图片爬虫的核心概念与应用场景

1.1 网络爬虫的基本定义

网络爬虫(Web Crawler)是一种自动获取网页内容的程序,它通过模拟浏览器行为,向目标网站发送请求并解析返回的数据,从而提取所需信息。爬虫广泛应用于搜索引擎、数据挖掘、信息监控等领域。

1.2 图片爬虫的特殊性

与通用网页爬虫不同,图片爬虫专注于提取图像资源。其难点在于图片数据通常不直接嵌入页面,而是通过延迟加载(Lazy Load)或Ajax接口动态获取。此外,图片的存储格式、大小、分辨率等因素也增加了爬取与处理的复杂性。

1.3 百度图片搜索引擎的结构特点

百度图片搜索引擎通过关键词匹配返回相关图片结果。其搜索结果页通常包含缩略图链接,而高清图链接往往隐藏在JavaScript渲染后的DOM结构或独立接口中。因此,爬取百度图片需要深入分析URL构造规则与页面渲染机制。

1.4 百度图片爬取的合法合规原则

在进行图片爬取时,必须遵守《网络安全法》和网站的Robots协议,尊重数据版权与网站负载限制。建议设置合理的请求间隔,避免对目标服务器造成压力,同时明确数据用途,仅用于合法合规的场景。

1.5 实际应用场景举例

百度图片爬虫技术广泛应用于多个领域:

  • 电商行业 :用于商品图像采集与竞品分析;
  • 人工智能训练 :构建图像识别训练集;
  • 内容创作辅助 :收集灵感图、素材图;
  • 学术研究 :图像语义分析、图像风格迁移等任务的数据准备。

通过本章学习,读者将对图片爬虫技术建立整体认知,为后续实战打下坚实基础。

2. Python爬虫基础与requests库深度实践

在进入图片爬虫实战之前,必须对Python爬虫的基本构建模块进行系统性掌握。本章将从网络请求的基础理论入手,逐步深入到使用 requests 库进行高效网络数据抓取的实践环节。本章不仅会讲解HTTP协议的基本原理、状态码的含义、请求头的配置等基础知识,还会深入剖析 requests 库的核心功能,包括GET/POST请求的发送、参数传递、Session管理等。最后,我们还将构建一套完善的异常处理机制,确保爬虫程序具备良好的健壮性与可维护性。

2.1 网络请求的基本流程

在Python中实现网络爬虫,首先要理解网络请求的基本流程。这一流程涵盖了从客户端发起请求,到服务器响应并返回数据的全过程。理解这一过程对于构建高效、稳定的爬虫系统至关重要。

2.1.1 HTTP协议基础与请求/响应模型

HTTP(HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,用于从Web服务器传输超文本到本地浏览器的传输协议。它是一种无状态、基于请求/响应模型的协议。

一个完整的HTTP请求通常包括以下组成部分:

  • 请求行(Request Line) :包括请求方法(如GET、POST)、请求的URL和HTTP版本。
  • 请求头(Headers) :包含客户端发送的元信息,如User-Agent、Accept、Content-Type等。
  • 请求体(Body) :仅在POST、PUT等方法中出现,用于传递数据。

服务器接收到请求后,返回响应,响应结构如下:

  • 状态行(Status Line) :包括HTTP版本、状态码和状态消息。
  • 响应头(Response Headers) :提供服务器的信息,如Content-Type、Content-Length等。
  • 响应体(Response Body) :实际返回的数据内容,如HTML文档、JSON数据等。

例如,一个简单的GET请求如下:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 ...

服务器响应示例:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234

<html>...</html>

逻辑分析
- 请求行指明了请求方法为GET,请求路径为 /index.html ,使用HTTP 1.1协议。
- 请求头中的 Host 字段指明了目标主机, User-Agent 用于标识客户端类型。
- 响应状态码200表示请求成功, Content-Type 说明返回内容是HTML文档。

2.1.2 常见状态码与含义

HTTP状态码是服务器对客户端请求的响应状态标识,由三位数字组成。以下是常见的状态码及其含义:

状态码 含义描述
200 请求成功
301 永久重定向
302 临时重定向
400 客户端请求有误
403 禁止访问
404 资源未找到
500 服务器内部错误

这些状态码可以帮助我们判断请求是否成功,或者是否需要进行重试、重定向等处理。

逻辑分析
- 200系列表示成功;
- 300系列表示需要进一步操作(如重定向);
- 400系列表示客户端错误;
- 500系列表示服务器错误。

2.1.3 请求头与用户代理配置

请求头是HTTP请求的重要组成部分,常用于传递客户端的附加信息。其中, User-Agent 字段尤为重要,它标识了客户端浏览器的类型和操作系统,用于服务器进行适配。

在使用 requests 库时,我们可以自定义请求头,以模拟浏览器访问,避免被网站反爬机制识别为机器人。

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'Connection': 'keep-alive'
}

response = requests.get('https://www.example.com', headers=headers)
print(response.status_code)

逐行分析
- 第1行导入requests库;
- 第3-7行定义自定义请求头,模拟Chrome浏览器;
- 第9行发送GET请求,并传入自定义headers;
- 第10行输出状态码,用于判断请求是否成功。

参数说明
- User-Agent :用于伪装成浏览器;
- Accept-Language :告知服务器可接受的语言类型;
- Accept-Encoding :指定客户端接受的压缩格式;
- Connection :控制是否保持连接。

2.2 requests库的核心使用方法

requests 是Python中最常用的HTTP客户端库,它简化了HTTP请求的发送和响应处理过程。本节将深入讲解其核心功能,包括GET/POST请求的发送、参数传递方式、以及Session对象的使用。

2.2.1 发送GET与POST请求

GET请求通常用于获取数据,参数直接拼接在URL中;POST请求用于提交数据,参数通常放在请求体中。

发送GET请求示例

import requests

response = requests.get('https://httpbin.org/get', params={'key1': 'value1', 'key2': 'value2'})
print(response.url)

逻辑分析
- 使用 params 参数传递查询参数;
- 最终请求URL为: https://httpbin.org/get?key1=value1&key2=value2

发送POST请求示例

response = requests.post('https://httpbin.org/post', data={'key1': 'value1'})
print(response.text)

逻辑分析
- 使用 data 参数提交表单数据;
- 数据将作为请求体发送给服务器;
- response.text 返回服务器返回的文本内容。

2.2.2 参数传递与URL编码

在GET请求中,参数通常通过URL查询字符串传递,此时需要进行URL编码,确保特殊字符正确传输。

import requests

params = {'q': 'Python requests', 'page': 1}
response = requests.get('https://search.example.com', params=params)
print(response.url)

逻辑分析
- params 字典将自动进行URL编码;
- 输出的URL为: https://search.example.com?q=Python+requests&page=1
- 其中空格被编码为 + ,参数之间用 & 连接。

对于POST请求,若需传递JSON数据,可使用 json 参数:

response = requests.post('https://api.example.com/data', json={'name': 'John', 'age': 30})

参数说明
- json 参数会自动设置 Content-Type: application/json
- 数据将被序列化为JSON格式发送。

2.2.3 Session对象与持久连接

当需要多次请求同一网站时,使用 Session 对象可以保持连接状态,提升效率。

import requests

session = requests.Session()
session.headers.update({'User-Agent': 'MyApp/1.0'})

response1 = session.get('https://httpbin.org/get')
response2 = session.post('https://httpbin.org/post', data={'key': 'value'})

print(response1.status_code, response2.status_code)

逻辑分析
- 创建一个Session对象;
- 设置默认请求头;
- 多次使用该Session对象发送请求,复用底层TCP连接;
- 提升请求效率,避免重复建立连接。

参数说明
- Session.headers :设置默认请求头;
- 后续请求会自动继承该头信息。

2.3 爬虫异常处理机制

网络请求过程中,可能遇到各种异常,如连接超时、服务器错误、IP被封等。构建健壮的爬虫程序必须具备完善的异常处理机制。

2.3.1 请求超时设置与异常捕获

为了避免程序长时间卡住,应设置合理的超时时间。 requests 支持设置连接和读取超时。

import requests

try:
    response = requests.get('https://httpbin.org/delay/5', timeout=3)
except requests.exceptions.Timeout:
    print("请求超时,请检查网络或重试")

逻辑分析
- 设置 timeout=3 ,表示3秒内未收到响应则抛出Timeout异常;
- 捕获异常后打印提示信息;
- 防止程序因网络延迟而卡死。

参数说明
- timeout :可接受单个数值(同时限制连接和读取)或元组(连接时间, 读取时间)。

2.3.2 重试策略与连接池管理

在遇到临时性网络问题时,合理设置重试策略可以提高爬虫成功率。

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

session = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
session.mount('https://', HTTPAdapter(max_retries=retries))

response = session.get('https://httpbin.org/status/500')

逻辑分析
- 使用 Retry 设置最大重试次数为5次;
- 遇到5xx错误自动重试;
- backoff_factor 控制重试间隔时间;
- HTTPAdapter 绑定到Session对象,统一管理连接池。

参数说明
- total :最大重试次数;
- backoff_factor :重试间隔的退避因子;
- status_forcelist :触发重试的状态码列表。

2.3.3 反爬应对与请求频率控制

为了避免被网站封禁IP,应合理控制请求频率,模拟浏览器行为。

import requests
import time

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}

for i in range(5):
    response = requests.get('https://httpbin.org/get', headers=headers)
    print(f"第{i+1}次请求,状态码:{response.status_code}")
    time.sleep(2)  # 每次请求间隔2秒

逻辑分析
- 循环发送5次GET请求;
- 每次请求间隔2秒,避免触发反爬机制;
- 模拟真实用户行为,降低被封IP风险。

参数说明
- time.sleep(2) :控制请求频率;
- 适当使用代理IP、更换User-Agent等策略可进一步增强反反爬能力。

本章通过由浅入深的方式,从HTTP协议基础讲起,逐步过渡到 requests 库的使用与异常处理机制的构建。掌握本章内容,将为后续构建百度图片爬虫打下坚实的基础。在下一章中,我们将深入探讨如何构造百度图片搜索URL,并解析返回的HTML结构,提取目标图片链接。

3. 百度图片搜索URL构造与HTML解析

在掌握了Python网络请求与爬虫基础后,本章将聚焦于百度图片搜索接口的分析与HTML文档的解析。这一阶段是构建百度图片爬虫项目的核心环节之一。通过深入解析URL构造逻辑、理解HTML结构,并掌握高效的解析方法,我们能够精准提取出所需的图片链接信息。

本章将从百度图片搜索接口的URL构造入手,逐步展开对HTML文档的结构解析,并最终实现图片链接的提取策略。本章内容将为后续的图片下载与存储打下坚实的技术基础。

3.1 百度图片搜索接口分析

要从百度图片中获取数据,首先需要了解其搜索接口的工作机制。通过构造特定格式的URL,可以模拟用户输入关键词进行搜索的行为,从而获取对应的图片结果页面。

3.1.1 搜索关键词参数构造

百度图片搜索的URL通常如下格式:

https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&sf=1&fmq=1719571200000&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=关键词

其中, word 参数表示搜索关键词。我们可以使用Python的 urllib.parse.quote 函数对关键词进行URL编码,以确保中文或特殊字符能被正确传递。

代码示例:构造搜索关键词URL

import urllib.parse

def build_search_url(keyword):
    base_url = "https://image.baidu.com/search/index?"
    params = {
        'tn': 'baiduimage',
        'ipn': 'r',
        'ct': '201326592',
        'cl': '2',
        'lm': '-1',
        'st': '-1',
        'sf': '1',
        'fmq': '1719571200000',
        'pv': '',
        'ic': '0',
        'nc': '1',
        'z': '',
        'se': '1',
        'showtab': '0',
        'fb': '0',
        'width': '',
        'height': '',
        'face': '0',
        'istype': '2',
        'ie': 'utf-8',
        'word': keyword
    }
    encoded_params = urllib.parse.urlencode(params)
    return base_url + encoded_params

# 示例:搜索“猫”
search_url = build_search_url("猫")
print(search_url)

逻辑分析:

  • urllib.parse.urlencode 将字典参数转换为URL编码的查询字符串。
  • base_url 是百度图片搜索的固定前缀。
  • params 字典中包含多个请求参数,其中 word 是搜索关键词。
  • 构造完成的URL可以用于发起GET请求,获取百度图片搜索结果页面。

参数说明:

参数名 说明
tn 百度图片的搜索标识
ipn 请求来源标识
ct 内容类型编码
cl 颜色参数
word 搜索关键词(必须)

⚠️ 注意:部分参数在实际请求中可能不需要修改,但保留这些参数有助于更接近真实用户行为,避免被反爬虫机制拦截。

3.1.2 分页机制与URL拼接技巧

百度图片搜索结果支持分页加载,每页显示固定数量的图片。通过分析URL结构可以发现,分页主要通过 pn 参数控制,表示偏移量(即从第几条结果开始显示)。

例如:

pn=0  # 第一页
pn=30 # 第二页(假设每页30条)
pn=60 # 第三页

代码示例:实现分页功能

def build_paginated_url(keyword, page=1, per_page=30):
    base_url = "https://image.baidu.com/search/index?"
    offset = (page - 1) * per_page
    params = {
        'tn': 'baiduimage',
        'ipn': 'r',
        'ct': '201326592',
        'cl': '2',
        'lm': '-1',
        'st': '-1',
        'sf': '1',
        'fmq': '1719571200000',
        'pv': '',
        'ic': '0',
        'nc': '1',
        'z': '',
        'se': '1',
        'showtab': '0',
        'fb': '0',
        'width': '',
        'height': '',
        'face': '0',
        'istype': '2',
        'ie': 'utf-8',
        'word': keyword,
        'pn': offset
    }
    encoded_params = urllib.parse.urlencode(params)
    return base_url + encoded_params

# 示例:获取“猫”关键词的第2页结果
search_url = build_paginated_url("猫", page=2)
print(search_url)

逻辑分析:

  • page 参数控制页码, per_page 表示每页数量(默认30条)。
  • offset = (page - 1) * per_page 计算出起始偏移量。
  • params['pn'] = offset 设置分页参数。
  • 构造出的URL可以实现分页访问。

表格:分页参数对照表

页码 pn值 每页数量
第1页 0 30
第2页 30 30
第3页 60 30
第4页 90 30

3.1.3 Ajax加载与数据接口识别

百度图片搜索结果采用Ajax动态加载机制,这意味着当用户滚动页面时,新的图片会通过Ajax请求从服务器加载。为了更高效地获取数据,我们可以直接访问Ajax请求的数据接口,而不是解析HTML页面。

通过浏览器开发者工具(F12)的Network面板,可以捕获到Ajax请求的URL,通常返回的是JSON格式的数据,包含图片的真实地址。

示例Ajax请求URL:

https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=猫&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=猫&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1719571200000=

特点:

  • 返回JSON数据,便于解析。
  • 参数结构与普通搜索页面类似。
  • pn 控制偏移量, rn 控制每页数量。

代码示例:获取Ajax接口数据

import requests
import json

def fetch_ajax_images(keyword, page=1, per_page=30):
    base_url = "https://image.baidu.com/search/acjson?"
    offset = (page - 1) * per_page
    params = {
        'tn': 'resultjson_com',
        'ipn': 'rj',
        'ct': '201326592',
        'cl': '2',
        'lm': '-1',
        'ie': 'utf-8',
        'oe': 'utf-8',
        'adpicid': '',
        'st': '-1',
        'z': '',
        'ic': '0',
        'word': keyword,
        's': '',
        'se': '',
        'tab': '',
        'width': '',
        'height': '',
        'face': '0',
        'istype': '2',
        'qc': '',
        'nc': '1',
        'fr': '',
        'expermode': '',
        'force': '',
        'pn': offset,
        'rn': per_page,
        'gsm': hex(offset)[2:],  # 百度部分参数为16进制
        '1719571200000': ''  # 时间戳参数(可选)
    }

    headers = {
        'User-Agent': 'Mozilla/5.0'
    }

    response = requests.get(base_url, params=params, headers=headers)
    if response.status_code == 200:
        return response.json()
    else:
        print(f"请求失败,状态码:{response.status_code}")
        return None

# 示例:获取“猫”的第一页Ajax数据
data = fetch_ajax_images("猫", page=1)
print(json.dumps(data, indent=2))

逻辑分析:

  • 使用 requests.get 发送带有参数的GET请求。
  • 设置 headers 中的 User-Agent 模拟浏览器行为。
  • 若请求成功,返回JSON格式的数据。
  • 可以提取JSON中的 thumbURL middleURL objURL 等字段获取图片地址。

流程图:Ajax请求获取图片数据流程

graph TD
    A[构造Ajax请求URL] --> B[发送GET请求]
    B --> C{响应状态码200?}
    C -->|是| D[解析JSON数据]
    C -->|否| E[输出错误信息]
    D --> F[提取图片URL列表]
    E --> G[重试或退出]

3.2 HTML文档结构与BeautifulSoup解析

虽然Ajax接口能更高效地获取图片数据,但在某些场景下(如接口不可用或需解析页面结构时),仍需使用HTML解析技术。BeautifulSoup 是Python中用于解析HTML文档的强大库,特别适合处理不规则或嵌套结构的HTML内容。

3.2.1 安装与基本用法

安装命令:

pip install beautifulsoup4

基本用法示例:

from bs4 import BeautifulSoup
import requests

url = build_search_url("猫")
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

# 输出HTML文档标题
print(soup.title.string)

逻辑分析:

  • requests.get 获取HTML页面内容。
  • BeautifulSoup 初始化解析器,指定解析器为 'html.parser'
  • soup.title.string 提取页面标题。

3.2.2 CSS选择器与标签匹配技巧

BeautifulSoup支持CSS选择器语法,可以快速定位HTML节点。

示例:查找所有img标签

images = soup.find_all('img')
for img in images[:5]:  # 打印前5个img标签
    print(img)

示例:使用CSS选择器查找class为”imgbox”的元素

img_boxes = soup.select('.imgbox')
for box in img_boxes:
    print(box)

表格:CSS选择器常用语法

语法 含义
tag 标签名选择器(如 img
.class class选择器(如 .imgbox
#id id选择器(如 #main
tag.class 组合选择器(如 div.content
tag[attr=value] 属性选择器(如 img[src]

3.2.3 多层嵌套结构的提取方法

百度图片搜索页面的HTML结构往往存在多层嵌套,需要通过逐层查找提取目标数据。

示例:提取嵌套结构中的img标签

# 假设结构为 div -> ul -> li -> span -> img
results = soup.find('div', {'class': 'imgbox'})
if results:
    ul = results.find('ul')
    if ul:
        for li in ul.find_all('li'):
            img = li.find('img')
            if img and 'src' in img.attrs:
                print(img['src'])

逻辑分析:

  • 先定位外层 div 容器。
  • 逐层进入 ul li img
  • 提取 img src 属性。

3.3 图片链接提取策略

在获取HTML内容或Ajax返回的JSON数据后,下一步是提取图片链接。由于网页可能使用懒加载或防盗链技术,需要识别真实图片地址。

3.3.1 img标签的定位与src属性提取

示例:提取HTML中的所有img标签src地址

img_tags = soup.find_all('img')
image_urls = [img.get('src') for img in img_tags if img.get('src')]
print(image_urls[:5])

3.3.2 数据懒加载与真实图片地址识别

懒加载通常使用 data-src srcset 属性代替 src ,需特别注意。

示例:识别懒加载图片的真实地址

lazy_images = soup.find_all('img', {'data-src': True})
real_urls = [img['data-src'] for img in lazy_images]
print(real_urls[:5])

3.3.3 去重机制与链接存储结构设计

为了避免重复下载图片,需对链接进行去重处理。可使用集合或数据库存储已提取的链接。

示例:使用集合去重

seen_urls = set()

for url in real_urls:
    if url not in seen_urls:
        seen_urls.add(url)
        print(f"新链接:{url}")

表格:链接存储结构设计建议

存储方式 优点 缺点
内存集合(set) 快速去重,适合小规模数据 内存占用高,程序重启后丢失
文件存储(txt/json) 持久化保存 读写速度慢
SQLite数据库 结构化存储,支持查询与扩展 需要学习SQL语法

本章详细讲解了百度图片搜索接口的URL构造方法、HTML解析技术以及图片链接的提取策略。通过本章的学习,读者将掌握如何构造搜索URL、解析页面结构、识别懒加载图片,并实现链接的去重与存储。这些技能是构建百度图片爬虫系统的核心能力,也为后续章节的图片下载与项目整合奠定了坚实基础。

4. 图片下载、处理与本地存储方案

在完成百度图片的搜索与链接提取之后,下一步就是将图片从网络下载到本地,并进行必要的处理与存储。本章将围绕“下载—处理—存储”这一完整流程展开,重点介绍如何使用 Python 的 requests PIL (现为 Pillow )库来完成图像的二进制下载、格式转换、尺寸调整、压缩等处理操作,并探讨几种可行的本地存储方案。

本章内容结构如下:

4.1 图片二进制数据的下载流程

在获取到图片的 URL 之后,需要通过 HTTP 请求将图片以二进制形式下载到本地。与普通文本内容不同,图片属于二进制数据,必须使用二进制模式进行读写。

4.1.1 使用requests获取图片流

requests 库支持以流式方式下载大文件,避免一次性加载到内存中。这对于图片下载尤其重要,因为图片文件可能较大。

import requests

def download_image(url, save_path):
    try:
        response = requests.get(url, stream=True, timeout=10)
        if response.status_code == 200:
            with open(save_path, 'wb') as f:
                for chunk in response.iter_content(1024):
                    f.write(chunk)
            print(f"Downloaded: {save_path}")
        else:
            print(f"Failed to download image from {url}, status code: {response.status_code}")
    except Exception as e:
        print(f"Error downloading image from {url}: {e}")
代码逻辑分析:
  • requests.get(url, stream=True) :启用流式下载,避免一次性读取整个图片到内存中。
  • response.iter_content(1024) :每次读取 1024 字节的数据块,逐块写入文件。
  • open(save_path, 'wb') :以二进制写入模式打开文件,确保数据正确写入。
  • 异常处理:捕获网络请求异常和文件写入错误,提高程序健壮性。
参数说明:
参数名 类型 描述
url str 要下载的图片地址
save_path str 本地保存路径(含文件名)

4.1.2 二进制文件的写入操作

写入二进制文件时需要注意文件的打开模式。Python 中, 'wb' 表示以二进制写入模式打开文件,如果文件不存在则创建,存在则覆盖。

with open('image.jpg', 'wb') as f:
    f.write(image_data)
逻辑分析:
  • with :确保文件在使用完后正确关闭,防止资源泄漏。
  • 'wb' :二进制写入模式。
  • image_data :从网络请求中获取的二进制内容。

4.1.3 文件命名策略与路径管理

为了避免图片命名重复,可以使用哈希值或时间戳进行命名。同时,为了方便管理,建议将图片按类别或时间分目录存储。

import os
import hashlib

def generate_safe_filename(url):
    hash_obj = hashlib.md5(url.encode())
    return f"{hash_obj.hexdigest()}.jpg"

def save_image_with_category(url, base_dir, category):
    filename = generate_safe_filename(url)
    category_dir = os.path.join(base_dir, category)
    os.makedirs(category_dir, exist_ok=True)
    save_path = os.path.join(category_dir, filename)
    download_image(url, save_path)
参数说明:
参数名 类型 描述
url str 图片链接
base_dir str 存储根目录
category str 分类名称,如“汽车”、“风景”等
文件结构示例:
images/
├── 汽车/
│   └── a1b2c3d4e5f67890.jpg
├── 风景/
│   └── 0f9e8d7c6b5a4321.jpg

4.2 图像处理与优化(PIL库)

下载的图片可能需要进行缩放、格式转换、压缩等处理操作。Python 中最常用的图像处理库是 Pillow (即 PIL 的现代分支)。

4.2.1 PIL库的安装与基本操作

安装 Pillow:

pip install pillow

基本使用示例:

from PIL import Image

img = Image.open('image.jpg')
img.show()
逻辑分析:
  • Image.open() :加载图片文件。
  • img.show() :使用系统默认图像查看器显示图片。

4.2.2 图像尺寸调整与格式转换

调整图像尺寸和转换格式是图像处理中的常见需求,比如将大图缩略为 800x600 像素,或将 .png 转换为 .jpg

def resize_image(input_path, output_path, size=(800, 600)):
    with Image.open(input_path) as img:
        resized_img = img.resize(size)
        resized_img.save(output_path)
逻辑分析:
  • img.resize(size) :按指定尺寸缩放图片。
  • resized_img.save(output_path) :保存处理后的图片。
参数说明:
参数名 类型 描述
input_path str 原始图片路径
output_path str 处理后图片保存路径
size tuple(int) 缩放尺寸 (width, height)

4.2.3 图像质量压缩与水印去除

对于存储空间有限的场景,可以对图片进行质量压缩;同时,部分图片可能带有水印,可以通过裁剪或模糊处理去除。

def compress_image(input_path, output_path, quality=85):
    with Image.open(input_path) as img:
        img.save(output_path, optimize=True, quality=quality)
逻辑分析:
  • optimize=True :启用图像优化。
  • quality=85 :设置压缩质量(0-100,数值越小越压缩,画质越差)。
参数说明:
参数名 类型 描述
input_path str 原始图片路径
output_path str 压缩后图片保存路径
quality int 压缩质量等级

4.3 存储方案与数据管理

在图片下载与处理完成后,需要选择合适的存储方式,并设计良好的数据管理机制,以提高系统的可维护性与扩展性。

4.3.1 本地文件系统存储

最简单的存储方式是将图片直接保存到本地磁盘。优点是实现简单,缺点是难以进行高效查询和管理。

graph TD
    A[图片URL] --> B[发起HTTP请求]
    B --> C[下载图片二进制流]
    C --> D[写入本地文件]
    D --> E[保存路径结构化]
推荐目录结构:
images/
├── 2024-10/
│   ├── cat/
│   └── dog/
├── 2024-11/
│   ├── car/
│   └── landscape/

4.3.2 SQLite数据库存储图片元数据

为了方便管理,可以使用 SQLite 数据库存储图片的元数据(如 URL、分类、下载时间、文件路径等),而图片本身仍保存在本地文件系统中。

CREATE TABLE images (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    url TEXT NOT NULL,
    category TEXT,
    file_path TEXT,
    download_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

插入记录示例:

import sqlite3

def save_metadata_to_db(url, category, file_path):
    conn = sqlite3.connect('images.db')
    cursor = conn.cursor()
    cursor.execute('''
        INSERT INTO images (url, category, file_path)
        VALUES (?, ?, ?)
    ''', (url, category, file_path))
    conn.commit()
    conn.close()
参数说明:
参数名 类型 描述
url str 图片原始链接
category str 图片分类标签
file_path str 本地存储路径

4.3.3 存储性能优化与目录结构设计

随着图片数量增加,单目录下文件过多会导致访问性能下降。可以采用以下优化策略:

  • 按时间分目录 :每天/每月建立子目录。
  • 按哈希值分目录 :将图片哈希值前两位作为目录名。
  • 使用缓存机制 :缓存最近访问的图片路径,减少磁盘查找。
def get_hashed_dir(base_dir, url):
    hash_val = hashlib.md5(url.encode()).hexdigest()
    dir_name = hash_val[:2]
    return os.path.join(base_dir, dir_name)
优化目录结构示例:
images/
├── a1/
│   └── a1b2c3d4e5f67890.jpg
├── 0f/
│   └── 0f9e8d7c6b5a4321.jpg
优点:
  • 提高文件系统查找效率。
  • 避免单目录文件过多导致的性能瓶颈。
  • 便于后续扩展为分布式存储结构。

总结回顾

本章从“图片下载—处理—存储”三个关键环节入手,系统性地介绍了如何使用 Python 实现图片爬取的后续流程。通过 requests 库实现了高效的二进制图片下载,借助 Pillow 进行了图像的尺寸调整、格式转换与压缩,最后设计了本地文件系统与 SQLite 数据库相结合的存储策略,并提出了性能优化方案。

下一章将在此基础上,介绍如何通过多线程并发提升爬虫效率,并整合所有模块构建一个完整的百度图片爬虫项目。

5. 多线程加速与完整爬虫项目实战

5.1 并发爬取的基本原理

在爬虫项目中,为了提高效率,我们通常会采用并发机制来加速数据的获取。单线程爬虫虽然结构简单,但效率低下,特别是在面对大量请求时。而多线程机制可以充分利用CPU资源,同时处理多个网络请求,显著缩短爬取时间。

5.1.1 单线程与多线程效率对比

特性 单线程 多线程
执行方式 顺序执行 并行/并发执行
资源利用率
适用场景 小规模任务 大规模、高并发任务
编程复杂度 简单 中等

在Python中,由于GIL(全局解释器锁)的存在,多线程在CPU密集型任务中并不能真正并行。但在I/O密集型任务中,如网络请求,多线程仍能显著提升效率。

5.1.2 Python中的并发模型(threading与asyncio)

Python中常用的并发模型包括:

  • threading :多线程模型,适用于I/O密集型任务。
  • asyncio :异步IO模型,基于事件循环和协程,适用于高并发场景。

以下是一个使用 threading 的简单示例:

import threading
import time

def fetch_url(url):
    print(f"开始请求 {url}")
    time.sleep(1)  # 模拟网络延迟
    print(f"完成请求 {url}")

urls = ['http://example.com/page1', 'http://example.com/page2', 'http://example.com/page3']

threads = []
for url in urls:
    thread = threading.Thread(target=fetch_url, args=(url,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

print("所有请求完成")

参数说明:

  • target :线程执行的目标函数。
  • args :传递给目标函数的参数,必须为元组。
  • start() :启动线程。
  • join() :等待所有线程执行完毕。

5.1.3 使用concurrent.futures实现并发控制

concurrent.futures 模块提供了一个高级接口来实现线程池或进程池的并发执行。

from concurrent.futures import ThreadPoolExecutor
import time

def fetch_url(url):
    print(f"开始请求 {url}")
    time.sleep(1)
    print(f"完成请求 {url}")
    return url

urls = ['http://example.com/page1', 'http://example.com/page2', 'http://example.com/page3']

with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(fetch_url, urls)

print("所有任务完成")

代码说明:

  • ThreadPoolExecutor :线程池执行器, max_workers 表示最大并发线程数。
  • executor.map() :将任务分配给线程池执行,按顺序返回结果。

5.2 百度图片爬虫项目整合

在完成基本的并发控制后,我们可以将前面章节中的功能整合成一个完整的百度图片爬虫项目。

5.2.1 项目结构划分与模块化设计

一个完整的项目结构如下:

baidu_image_crawler/
├── crawler/
│   ├── __init__.py
│   ├── request_handler.py
│   ├── parser.py
│   ├── downloader.py
│   └── scheduler.py
├── config/
│   └── settings.py
├── logs/
│   └── crawler.log
├── main.py
└── requirements.txt
  • request_handler.py :处理HTTP请求。
  • parser.py :解析HTML并提取图片链接。
  • downloader.py :图片下载与本地存储。
  • scheduler.py :任务调度与并发控制。
  • settings.py :配置参数。
  • main.py :程序入口。

5.2.2 关键函数与类的封装

downloader.py 为例,封装图片下载类:

import os
import requests
from urllib.parse import urlsplit

class ImageDownloader:
    def __init__(self, save_dir="images"):
        self.save_dir = save_dir
        os.makedirs(self.save_dir, exist_ok=True)

    def download_image(self, url):
        try:
            response = requests.get(url, timeout=10)
            filename = os.path.join(self.save_dir, os.path.basename(urlsplit(url).path))
            with open(filename, 'wb') as f:
                f.write(response.content)
            print(f"已保存图片: {filename}")
        except Exception as e:
            print(f"下载失败: {url}, 错误: {e}")

功能说明:

  • 自动创建图片存储目录。
  • 使用 urlsplit 提取URL路径生成文件名。
  • 捕获异常并输出错误信息。

5.2.3 日志记录与爬虫状态监控

可使用 logging 模块记录爬虫运行日志:

import logging

logging.basicConfig(
    filename='logs/crawler.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def log_info(message):
    logging.info(message)
    print(message)

这样可以在控制台和日志文件中同时记录爬虫状态,便于后期排查问题。

5.3 爬虫项目的部署与运行

5.3.1 配置文件与参数化运行

config/settings.py 示例:

SEARCH_KEYWORD = "猫咪"
MAX_PAGES = 5
IMAGES_PER_PAGE = 30
SAVE_DIR = "downloaded_images"
MAX_WORKERS = 10

main.py 中引用配置参数:

from config.settings import SEARCH_KEYWORD, MAX_PAGES, SAVE_DIR
from crawler.scheduler import run_crawler

if __name__ == "__main__":
    run_crawler(keyword=SEARCH_KEYWORD, max_pages=MAX_PAGES, save_dir=SAVE_DIR)

这样可灵活配置爬虫行为,无需修改代码即可调整运行参数。

5.3.2 定时任务与自动化执行

可以使用 cron (Linux)或任务计划器(Windows)定期运行爬虫。

Linux示例(每天凌晨1点执行):

0 1 * * * /usr/bin/python3 /path/to/main.py

或者使用 APScheduler 实现代码级定时任务:

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

def job():
    print(f"定时任务执行时间: {datetime.now()}")
    run_crawler()

scheduler = BlockingScheduler()
scheduler.add_job(job, 'cron', hour=1)
scheduler.start()

5.3.3 常见问题排查与性能调优

问题类型 表现 解决方案
请求被拒绝 返回403或429错误 增加User-Agent,设置请求间隔
图片链接失效 下载时出现404 增加重试机制,过滤无效链接
存储路径错误 写入失败 检查目录权限,使用绝对路径
内存占用过高 多线程下载时OOM 限制线程数,使用流式下载
性能瓶颈 下载速度慢 优化并发策略,使用代理服务器

通过日志分析、性能测试和参数调整,逐步优化爬虫项目的稳定性和效率。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在Python编程中,网络爬虫是获取网络数据的重要手段,尤其在图像数据采集方面具有广泛应用。本文详细讲解如何使用Python实现百度图片的自动爬取,涵盖网络爬虫基础原理、requests库发送HTTP请求、BeautifulSoup解析HTML内容、图片链接提取与下载、多线程加速以及图片处理技术(如PIL库)。通过本教程,读者可掌握完整的图片爬虫开发流程,并为后续数据挖掘、图像分析等任务打下坚实基础。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐