
1. 项目概述从零开始的图片爬取初体验刚接触Python爬虫那会儿我最想干的事儿就是把网上好看的图片一股脑儿全扒拉下来做个自己的图库。这想法听起来简单但真动手时才发现从打开浏览器到图片安安稳稳存进硬盘中间每一步都有门道。今天咱们就抛开那些复杂的框架和反爬机制聊聊最基础、最核心的图片爬取流程。无论你是想收集设计素材、备份网络相册还是单纯想体验一下“数据触手可及”的乐趣这篇内容都能给你一套清晰、可落地的操作指南。咱们的目标很明确用最少的代码理解最本质的原理完成一次成功的图片抓取。2. 核心思路与工具选型为什么是RequestsBeautifulSoup在开始写代码之前得先想明白两件事怎么拿到网页内容以及怎么从这一大堆HTML代码里把图片链接“抠”出来。市面上工具很多但入门阶段我强烈推荐Requests搭配BeautifulSoup这个黄金组合。这不是随便选的背后有它的道理。Requests库的作用是模拟浏览器向目标网站发送一个HTTP请求然后把服务器返回的网页HTML文本内容完整地拿到我们手里。它比Python自带的urllib库更简洁、更人性化几行代码就能搞定网络通信。而BeautifulSoup库则是一个强大的HTML/XML解析器。你可以把它想象成一个智能的“网页结构分析仪”。它能把Requests获取到的、杂乱无章的HTML字符串解析成一棵结构清晰的树。在这棵树上你可以轻松地找到img标签并提取出其中的src属性也就是图片的网址。为什么不直接用正则表达式去匹配图片链接呢我早期也试过写出来的正则表达式又长又脆网页结构稍微一变比如标签里多了一个空格或者换行匹配就失效了。BeautifulSoup帮我们屏蔽了这些琐碎的格式差异直接按标签名、属性名来查找稳定性和可读性都高出一大截。这个组合能让你把精力集中在爬虫的逻辑上而不是和字符串格式搏斗。注意在动手写任何爬虫之前请务必花几分钟阅读目标网站的robots.txt文件通常在网站根目录如https://example.com/robots.txt。这个文件规定了网络爬虫哪些页面可以访问哪些不可以。尊重robots.txt是基本的网络礼仪和法律风险规避手段。对于明确禁止爬取的目录请停止你的爬虫程序。2.1 环境准备与安装理论清楚了咱们先把“厨房”收拾好。你需要一个Python环境建议3.6及以上版本然后通过pip安装我们需要的两个库。打开你的命令行终端Windows上是CMD或PowerShellMac/Linux上是Terminal输入以下命令pip install requests beautifulsoup4这里有个细节库的名字叫beautifulsoup4但在代码里导入时我们写的是from bs4 import BeautifulSoup。安装成功后你可以创建一个新的Python文件比如命名为simple_image_crawler.py然后开始我们的代码之旅。3. 实战第一步获取网页源代码万事俱备只欠目标。我们得先找一个合适的练习网站。这里必须强调法律与道德边界请仅对明确允许爬取、或用于个人学习研究的公开网站进行操作。绝对不要对涉及个人隐私、受版权严格保护或明确声明禁止爬取的网站下手。为了演示我们可以找一个免费的、无版权风险的图片素材网站或者干脆自己写一个简单的本地HTML页面来模拟。假设我们的目标页面URL是http://example.com/gallery。第一步就是用Requests去“敲门”。import requests from bs4 import BeautifulSoup # 目标网页的URL url http://example.com/gallery # 设置请求头模拟真实浏览器访问这是一个非常重要的好习惯 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 } try: # 发送GET请求获取响应 response requests.get(url, headersheaders) # 检查请求是否成功HTTP状态码为200表示成功 response.raise_for_status() # 将响应的内容HTML文本以UTF-8编码解码 html_content response.content.decode(utf-8) print(网页源代码获取成功) except requests.exceptions.RequestException as e: print(f请求网页时出错: {e}) exit() # 如果请求失败退出程序这段代码有几个关键点设置请求头Headers尤其是User-Agent。服务器通过这个字段来判断访问者是谁。如果不设置默认的User-Agent会是python-requests/2.x.x这等于直接告诉对方“我是爬虫”很可能被拒绝访问或返回不同的页面。把自己伪装成一个普通的Chrome浏览器是绕过基础反爬的第一步。异常处理Try-Except网络请求充满了不确定性服务器可能宕机、链接可能超时。用try-except包裹请求代码并在出错时给出友好提示并退出能让你的程序更健壮而不是直接崩溃。编码解码response.content返回的是字节流bytes我们需要用正确的编码通常是utf-8将其解码成字符串str。有些网站可能是gbk编码如果解码出错可以查看response.encoding属性或者用response.apparent_encoding让Requests自动判断。3.1 解析HTML与定位图片标签拿到HTML字符串后就该BeautifulSoup上场了。它的任务是把字符串变成我们可以遍历和搜索的“树”。# 使用BeautifulSoup解析HTML指定解析器为‘html.parser’Python内置无需额外安装 soup BeautifulSoup(html_content, html.parser) # 查找所有的图片标签。HTML中图片通常由img标签定义。 img_tags soup.find_all(img) print(f在页面中共找到 {len(img_tags)} 个图片标签。)soup.find_all(img)这行代码会返回一个列表包含了当前页面中所有的img标签对象。现在我们需要从这些标签对象里提取出真正的图片网址也就是src属性的值。4. 核心环节提取链接与下载图片找到了标签提取链接看似简单但坑就藏在这里。图片的src属性值并不总是完整的网址绝对URL它可能是相对路径。比如它可能是/images/photo.jpg也可能是./assets/pic.png甚至是//cdn.example.com/img.png协议相对URL。如果我们直接把这样的字符串拿去下载肯定会失败因为Python不知道相对于谁。因此我们需要一个步骤将相对URL补全为绝对URL。Python的urllib.parse模块里的urljoin函数就是干这个的。from urllib.parse import urljoin # 准备一个列表来存储所有处理好的、完整的图片URL image_urls [] # 遍历每一个找到的img标签 for img in img_tags: # 获取src属性的值 img_src img.get(src) # 有些img标签可能没有src属性或者src为空需要跳过 if not img_src: continue # 使用urljoin以原始页面url为基准将src补全为绝对URL full_url urljoin(url, img_src) image_urls.append(full_url) print(f发现图片: {full_url})现在image_urls列表里存放的就是可以直接用于下载的、完整的图片地址了。接下来就是下载并保存。4.1 实现图片下载与本地存储下载的本质是再次使用Requests向图片的URL发起请求但这次我们不需要解析HTML而是直接获取返回的二进制内容图片本身就是二进制文件然后写入本地硬盘。import os # 创建一个目录来存放下载的图片避免文件散乱 save_dir downloaded_images if not os.path.exists(save_dir): os.makedirs(save_dir) # 遍历我们整理好的图片URL列表 for i, img_url in enumerate(image_urls): try: # 再次发送GET请求获取图片数据。注意这次请求最好也带上headers。 img_response requests.get(img_url, headersheaders) img_response.raise_for_status() # 确保图片请求成功 # 从URL中提取图片文件名。如果URL中没有明确文件名我们就自己构造一个。 # 例如从 ‘http://example.com/path/to/image.jpg?width200‘ 中提取 ‘image.jpg’ filename os.path.basename(img_url).split(?)[0] # 去掉URL参数 if not filename: # 如果提取不到就用索引命名 filename fimage_{i}.jpg # 构建完整的本地文件保存路径 filepath os.path.join(save_dir, filename) # 以二进制写入模式(‘wb’)打开文件并将图片的二进制内容写入 with open(filepath, wb) as f: f.write(img_response.content) print(f已保存: {filepath}) except requests.exceptions.RequestException as e: print(f下载图片失败 {img_url}: {e}) except IOError as e: print(f保存文件失败 {img_url}: {e})代码要点解析创建目录使用os.makedirs在保存前检查并创建目录这是一个好习惯。处理文件名os.path.basename()可以从URL路径中提取最后一段作为文件名。但有些URL可能带有查询参数如?width200我们用.split(?)[0]将其去掉只保留核心文件名部分。如果提取失败则用序号作为文件名。二进制写入图片、视频等文件必须用wb二进制写模式打开然后用response.content字节流写入。如果错误地使用了w文本模式或response.text文件将会损坏无法打开。嵌套异常处理下载和保存是两个可能出错的环节分别用try-except捕获RequestException网络错误和IOError文件读写错误确保一个图片下载失败不会导致整个程序中断。5. 常见问题与实战调试技巧按照上面的步骤一个基础的图片爬虫就成型了。但在实际运行中你几乎一定会遇到下面这些问题。我把它们和解决方法整理出来能帮你省下大量搜索的时间。5.1 问题一爬取到的图片链接是空列表或数量不对可能原因1图片是懒加载Lazy Load的。现象用浏览器打开页面能看到很多图但爬虫只抓到少数几个甚至只有一张占位图。原理现代网站为了性能图片不会一次性加载。初始的HTML中img标签的src属性可能是一个灰色的占位图真正的图片URL藏在另一个属性里比如>img_tags soup.find_all(img) for img in img_tags: # 优先尝试获取>img_headers { User-Agent: ..., Referer: url, # 将来源页设置为图片所在的页面URL } img_response requests.get(img_url, headersimg_headers)可能原因2文件名或保存路径包含非法字符。现象在Windows系统上如果文件名包含:,*,?,,,,|等字符会导致保存失败。解决在生成filename后对其进行清洗。import re def clean_filename(filename): # 替换掉Windows文件名中不允许的字符为下划线 return re.sub(r[:/\\|?*], _, filename) clean_name clean_filename(filename) filepath os.path.join(save_dir, clean_name)5.3 问题三程序运行速度慢或卡住可能原因同步请求的阻塞等待。现象图片一张一张下载网络慢的时候程序就像卡住了一样。原理requests.get()是同步操作必须等到一张图片完全下载完成后才会开始下一张的请求。优化方向这是基础爬虫的局限。后续进阶可以使用多线程threading、多进程multiprocessing或异步IOaiohttp asyncio来并发下载速度会有数量级的提升。但作为第一篇我们先保证功能的正确性。5.4 一个增强版的实战示例代码综合以上要点这里提供一个更健壮、考虑了懒加载和错误处理的完整示例代码框架import requests from bs4 import BeautifulSoup from urllib.parse import urljoin import os import re import time def download_images_from_page(page_url, save_folderdownloaded_images): 从指定页面下载所有图片的核心函数。 Args: page_url (str): 要爬取的网页地址。 save_folder (str): 本地保存图片的文件夹名称。 # 1. 创建保存目录 if not os.path.exists(save_folder): os.makedirs(save_folder) # 2. 设置请求头 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: page_url, } # 3. 获取并解析页面 try: resp requests.get(page_url, headersheaders, timeout10) # 设置超时 resp.raise_for_status() # 有些网站编码不是utf-8这里用apparent_encoding自动判断更稳妥 resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) except Exception as e: print(f访问页面失败: {e}) return # 4. 查找所有img标签 img_tags soup.find_all(img) print(f页面解析完成共找到 {len(img_tags)} 个img标签。) # 5. 遍历并下载 for idx, img in enumerate(img_tags): # 优先获取>