首页/新闻资讯/正文详情

Python图片自动化处理实战:requests抓取、re提取与cv2/PIL合成

发布时间:2026/9/23 18:25:19 来源:云帆数科 栏目:资讯中心
Python图片自动化处理实战:requests抓取、re提取与cv2/PIL合成
1. 从一条深夜消息说起这个项目的真实需求是什么事情的起点很简单。女朋友那阵子迷上了在聊天软件里发各种搞怪表情包和诱惑图——有时候是美食特写有时候是可爱小动物有时候是故意拍得很夸张的自拍。每天晚上准时来一张配文想我了吗。一开始我还挺受用后来发现这玩意儿是有套路的她发一张我回一句想然后她就截图存证第二天拿出来当证据说我每天都想她。作为一个写代码的人我的第一反应不是怎么回她而是怎么用技术手段反击。具体来说我想做一个自动化的小工具能批量抓取、生成、处理图片然后在她发图的时候用一套更离谱、更夸张、更搞笑的图片回敬过去。这个项目的核心不是爬虫本身而是图片的获取、处理与自动化输出这条完整链路。关键词里出现的python、requests、re、cv2、PIL正好对应了这条链路的几个关键环节requests负责网络请求拿图re负责从网页里提取图片链接cv2和PIL负责图片的读取、裁剪、加字、合成。整个项目本质上是一个图片素材自动化处理流水线只不过披了一层反击女朋友的娱乐外衣。适合谁来参考如果你刚学完 Python 基础语法想找一个有真实动机、能跑出结果、还能发朋友圈炫耀的小项目练手这个方向非常合适。它不涉及复杂的算法但对requests的异常处理、re的提取逻辑、PIL的中文字体渲染这些新手必踩的坑覆盖得很全。下面我按实际搭建顺序把每个环节拆开讲。2. 素材从哪来requests 抓图与 re 提取链接的配合逻辑2.1 为什么选 requests 而不是别的库Python 里发 HTTP 请求的库不少urllib是标准库自带的httpx支持异步aiohttp性能更强。但对于这个项目requests是最优解理由很实际同步模型足够用。我一次只需要抓几十到几百张图不是高并发场景异步带来的复杂度不值得。API 设计直观。requests.get(url)一行就能拿到响应response.content直接是二进制图片数据省去编码转换的麻烦。异常体系清晰。requests.exceptions下有Timeout、ConnectionError、HTTPError等细分异常方便做针对性重试。热词里出现了exceeded retry limit, last status: 429 too many requests这其实是很多新手在抓图时最容易撞上的墙——请求频率太高被目标站点限流。429状态码的含义是请求过多不是请求错误。遇到它的时候正确的做法不是加大重试次数而是降低频率 加随机延迟。我后面会专门讲这个。2.2 用 re 从 HTML 里抠出图片链接拿到网页 HTML 之后下一步是把图片地址提取出来。这里用re而不是BeautifulSoup是因为目标页面结构往往不规整图片链接可能散落在src、>import re def extract_img_urls(html, base_url): # 匹配 src />import requests import time import random import os 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 } def download_image(url, save_dir, idx): try: resp requests.get(url, headersHEADERS, timeout10) if resp.status_code 429: print(f[限流] {url}等待后重试) time.sleep(random.uniform(5, 10)) return False resp.raise_for_status() ext url.split(.)[-1].split(?)[0] if ext.lower() not in (jpg, jpeg, png, gif, webp): ext jpg path os.path.join(save_dir, f{idx:04d}.{ext}) with open(path, wb) as f: f.write(resp.content) return True except requests.exceptions.Timeout: print(f[超时] {url}) except requests.exceptions.ConnectionError: print(f[连接失败] {url}) except Exception as e: print(f[未知错误] {url} - {e}) return False关键点在于每次请求之间加time.sleep(random.uniform(0.5, 1.5))。固定间隔容易被识别为自动化行为随机间隔更接近真人浏览节奏。另外timeout10必须设否则遇到慢响应会一直卡住。3. 图片处理的核心cv2 与 PIL 的分工与配合3.1 两个库到底该用哪个很多人纠结cv2和PIL选哪个。我的经验是读图、缩放、裁剪、画几何图形用 cv2加文字、合成、格式转换、保存用 PIL。原因是 cv2 基于 NumPy 数组做像素级操作快PIL 的ImageDraw在文字渲染和图层合成上更成熟尤其是中文字体支持。热词里出现了python下载cv2和pil 中文字体说明这两个点确实是新手卡壳的地方。cv2 的安装包名是opencv-python不是cv2pip install opencv-python pillow requests而 PIL 的中文字体问题是下面要重点讲的。3.2 cv2 读图与尺寸统一抓下来的图片尺寸五花八门直接合成会很难看。我习惯先统一到一个基准尺寸比如宽度固定 800高度按比例缩放import cv2 import numpy as np def resize_keep_ratio(img, target_w800): h, w img.shape[:2] if w target_w: return img scale target_w / w new_h int(h * scale) return cv2.resize(img, (target_w, new_h), interpolationcv2.INTER_AREA) def load_image(path): # cv2 默认读 BGR中文路径会失败用 np.fromfile 绕过 img cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR) return img这里有个新手必踩的坑cv2.imread()遇到中文路径会返回None不报错但读不到图。解决办法就是用np.fromfile读成字节流再cv2.imdecode解码。同理保存时用cv2.imencode加tofile。3.3 PIL 加中文文字的完整方案给图片加文字是这个项目的灵魂——比如在图片上写收到已阅或者就这。但 PIL 默认字体不支持中文直接draw.text会显示成一堆方框。必须显式指定中文字体文件from PIL import Image, ImageDraw, ImageFont def add_text_cn(img_path, text, out_path, font_pathC:/Windows/Fonts/msyh.ttc): img Image.open(img_path).convert(RGB) draw ImageDraw.Draw(img) w, h img.size font_size max(24, w // 20) font ImageFont.truetype(font_path, font_size) # 计算文字位置底部居中 bbox draw.textbbox((0, 0), text, fontfont) tw, th bbox[2] - bbox[0], bbox[3] - bbox[1] x (w - tw) // 2 y h - th - 30 # 加描边避免文字和背景同色看不清 draw.text((x, y), text, fontfont, fillwhite, stroke_width2, stroke_fillblack) img.save(out_path)字体路径在不同系统下不一样我整理了一个对照表系统常见中文字体路径WindowsC:/Windows/Fonts/msyh.ttc微软雅黑WindowsC:/Windows/Fonts/simhei.ttf黑体macOS/System/Library/Fonts/PingFang.ttcLinux/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc注意stroke_width和stroke_fill是 PIL 9.2.0 之后才支持的参数。如果你的版本太老需要手动画两次文字来模拟描边。升级命令pip install --upgrade pillow。3.4 批量处理的流水线设计单张处理没意义这个项目的价值在于批量。我的做法是把整个流程拆成三个阶段每个阶段独立可重跑采集阶段抓图存到raw/目录文件名用序号。处理阶段遍历raw/统一尺寸、加文字、加滤镜输出到processed/。输出阶段从processed/里随机抽取或者按规则排序生成最终结果。这样设计的好处是如果处理逻辑改了不用重新抓图如果抓图失败了也不影响已经处理好的部分。每个阶段用独立的脚本通过目录传递数据比写一个大函数清晰得多。4. 那些让我熬夜的坑从 429 到字体乱码的排查实录4.1 429 限流的完整排查链路第一次跑批量下载跑到第 30 张左右开始疯狂报429 Too Many Requests。我当时的反应是加个重试不就行了结果重试也全是 429最后触发了exceeded retry limit。排查过程是这样的第一步确认是不是 IP 被限。换了个网络环境测试发现能正常请求说明是频率问题不是封禁。第二步看响应头。resp.headers里有Retry-After字段值是60意思是60 秒后再来。这个信息非常关键但很多人不看。第三步调整策略。把固定sleep(1)改成sleep(random.uniform(2, 5))并且在收到 429 时读取Retry-After动态等待。改进后的逻辑def safe_get(url, max_retry3): for i in range(max_retry): resp requests.get(url, headersHEADERS, timeout10) if resp.status_code 429: wait int(resp.headers.get(Retry-After, 30)) print(f被限流等待 {wait} 秒) time.sleep(wait) continue return resp return None实测下来加了动态等待之后连续抓 200 张图没有再触发限流。核心经验就一条尊重目标站点的节奏别把它当自己的服务器用。4.2 中文乱码与字体加载失败第二个大坑是字体。代码里写了ImageFont.truetype(msyh.ttc, 40)结果报OSError: cannot open resource。原因是我只写了文件名没写完整路径。PIL 不会自动去系统字体目录找必须给绝对路径。还有一个更隐蔽的问题字体文件存在但加载后中文还是显示成方框。这种情况通常是字体文件本身不含中文字形比如某些精简版的英文字体。解决办法就是换用明确支持中文的字体上面表格里那几个都是验证过的。4.3 cv2 与 PIL 混用时的颜色通道陷阱cv2 读进来是 BGRPIL 用的是 RGB。如果直接把 cv2 的数组丢给Image.fromarray()颜色会红蓝颠倒。正确做法是转换img_bgr cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR) img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) pil_img Image.fromarray(img_rgb)反过来PIL 转 cv2 也要转回去。这个坑我在项目里踩了两次第一次是图片整体偏蓝第二次是加了文字之后背景色变了都是通道顺序搞错。4.4 文件命名与去重的细节抓图时如果直接用 URL 的最后一段做文件名很容易重名或者文件名里带特殊字符导致保存失败。我的做法是用序号命名同时在内存里维护一个 URL 的set做去重。另外下载前先检查文件是否已存在避免重复劳动if os.path.exists(path): return True这个判断看起来简单但在断点续传场景下非常有用——脚本中断后重跑已下载的会自动跳过。5. 让工具真正好用自动化触发与结果输出5.1 从手动跑脚本到定时自动跑工具能跑通之后我把它改成了定时任务。Windows 下用任务计划程序Linux/macOS 下用cron。比如每天早上 8 点自动抓一批新图、处理、输出到指定目录# crontab -e 添加 0 8 * * * /usr/bin/python3 /path/to/pipeline.py /path/to/log.txt 21把输出重定向到日志文件很重要否则出错了你根本不知道。日志里记录每次抓了多少张、成功多少、失败多少方便回溯。5.2 结果输出的几种玩法处理完的图片怎么用决定了这个工具的最终形态。我试过几种随机抽取每次从processed/里随机拿一张配合一个随机文案生成回复图。九宫格拼图用 PIL 把 9 张图拼成一张大图适合一次性反击。GIF 动图用imageio或 PIL 的save方法把多张图合成 GIF效果更炸裂。九宫格拼图的代码不复杂核心是计算每张图的粘贴位置def make_grid(img_paths, out_path, size300): grid Image.new(RGB, (size * 3, size * 3), white) for i, p in enumerate(img_paths[:9]): im Image.open(p).convert(RGB).resize((size, size)) x (i % 3) * size y (i // 3) * size grid.paste(im, (x, y)) grid.save(out_path)5.3 性能优化什么时候该考虑并发单线程抓 100 张图按每张 1 秒算要 100 秒。如果图多确实慢。但我不建议一上来就上多线程原因有两个一是并发更容易触发限流二是新手调试并发代码的成本很高。我的建议是先用单线程跑通全流程确认逻辑没问题再考虑用concurrent.futures.ThreadPoolExecutor做小规模并发比如 3-5 个线程。而且并发下载时sleep要放在每个线程内部不能放在主线程否则等于没并发。from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers3) as executor: futures [executor.submit(download_image, url, save_dir, i) for i, url in enumerate(urls)] results [f.result() for f in futures]max_workers3是我实测比较稳的值再高就容易触发限流了。6. 几个只有踩过才知道的实操心得第一个心得关于目录结构。项目刚开始我把所有东西堆在一个文件夹里抓的图、处理的图、脚本、日志混在一起找东西非常痛苦。后来改成raw/、processed/、output/、logs/四个目录每个脚本只操作自己负责的目录清爽很多。这个习惯建议从第一个项目就养成。第二个心得关于配置外置。字体路径、目标尺寸、请求间隔这些参数不要硬编码在代码里。我一开始写死了msyh.ttc换到 macOS 上跑直接报错。后来改成从config.json读取跨平台切换只需要改配置文件{ font_path: /System/Library/Fonts/PingFang.ttc, target_width: 800, request_delay: [2, 5], max_workers: 3 }第三个心得关于日志。print在调试时够用但脚本跑在后台时看不到输出。用logging模块写到文件出问题能查。我现在的习惯是每个脚本开头配置好 logger关键节点都记一条比如开始抓取共 N 个链接第 X 张下载成功处理完成输出 M 张。第四个心得关于版本兼容。PIL的textbbox是 8.0 之后才有的之前用的是textsize。cv2的imdecode参数在不同版本也有细微差别。建议在项目里固定依赖版本用requirements.txt管理requests2.31.0 opencv-python4.9.0.80 pillow10.2.0这样换机器部署时不会因为版本差异出问题。最后说个真实的体会这个项目最大的价值不在于反击这个噱头而在于它把requests抓取、re提取、cv2处理、PIL合成这条链路完整串了一遍。跑通之后你会发现同样的思路可以迁移到很多场景——批量处理商品图、自动生成带水印的素材、给照片批量加日期戳底层逻辑是一模一样的。工具本身是死的把链路理解透了换个需求就是换个壳子的事。

相关推荐

蓝牙协议官方PDF合集:HCI命令、GATT UUID、广播字段精准查证指南
蓝牙协议官方PDF合集:HCI命令、GATT UUID、广播字段精准查证指南

简介:本资源是面向嵌入式开发工程师、无线通信学习者及物联网系统设计人员的蓝牙协议深度学习资料包,聚焦蓝牙协议栈原理与BLE低功耗应用开发。合集涵盖从蓝牙1.x到5.2全版本核心规范、LE物理层与链路层机制、GATT服务模型、安全加密流程及典型Profile&a… · 2026/9/23 18:25:19

超材料CST仿真S参数反演:MATLAB源码实现与避坑指南
超材料CST仿真S参数反演:MATLAB源码实现与避坑指南

简介:这份源码资源面向从事超材料设计、微波射频仿真及电磁逆问题研究的学习者与工程人员,核心解决如何从CST仿真得到的S参数出发,反演超材料单元几何参数的问题。包内共1个文件,为MATLAB脚本格式(.m)&… · 2026/9/23 18:25:19

C#实现Windows 7虚拟桌面管理器:轻量级本地工作区调度方案
C#实现Windows 7虚拟桌面管理器:轻量级本地工作区调度方案

简介:VDesk_虚拟桌面_是一款面向Windows 7用户的轻量级虚拟桌面工具,基于C#开发,旨在弥补旧版系统缺乏多工作区支持的短板,帮助开发者、办公用户及多任务处理者在不升级操作系统的前提下,实现类似Windows 10的多桌面切… · 2026/9/23 18:25:13

YOLO训练数据集三格式齐备:VOC/COCO/YOLO互转与可复现训练链路
YOLO训练数据集三格式齐备:VOC/COCO/YOLO互转与可复现训练链路

简介:本资源是面向计算机视觉初学者与YOLO目标检测实践者的高质量泄露目标数据集配套包,解决真实场景下小目标检测模型训练缺乏标注规范、格式兼容与工程化支持的痛点。资源包含5000张真实场景高清图片及完整标注,涵盖VOC(1986个X… · 2026/9/23 18:59:38

代码能跑=论文稳过?软件工程毕设AI隐形BUG,盲审一查一个准[特殊字符]
代码能跑=论文稳过?软件工程毕设AI隐形BUG,盲审一查一个准[特殊字符]

2026软件工程、计算机软件开发、物联网软件方向毕设盲审迎来最严核查年。和大家固有认知不同:软工毕设从来不是「代码能运行就及格」,导师和盲审专家重点看的是需求分析、架构设计、数据库逻辑、功能模块闭环、技术栈适配、测试用例完整性。 很多软工同… · 2026/9/23 18:59:38

部署中国云计算平台避坑指南:3个致命错误让代码跑不通
部署中国云计算平台避坑指南:3个致命错误让代码跑不通

部署中国云计算平台避坑指南:3个致命错误让代码跑不通 代码从网上复制下来,本地环境明明装好了,一运行却报错 ModuleNotFoundError 或者 ConnectionRefused… · 2026/9/23 18:59:32

舌头分割数据集实战:从2类标签到U-Net基线,避开医学图像分割的5个坑
舌头分割数据集实战:从2类标签到U-Net基线,避开医学图像分割的5个坑

简介:本资源面向计算机视觉学习者与图像分割开发者,提供一套完整的舌头分割数据集,适用于语义分割模型训练、医学图像预处理及算法验证等场景。数据图像分辨率统一为640640,原图为jpg格式,mask标签为png格式&#xff0… · 2026/9/23 18:59:31

3个狠招让btc区块链浏览器性能优化提速10倍
3个狠招让btc区块链浏览器性能优化提速10倍

3个狠招让btc区块链浏览器性能优化提速10倍 官方文档翻了三遍还是头大?别慌,我懂这种痛苦。BTC区块链浏览器看着简单,实则是个吞内存的怪兽。很多人卡在 性能优化 上,代码跑起来卡得像PPT。… · 2026/9/23 18:59:25

ECC 两大机制拆解:安全前置钩子与测试驱动执行流
ECC 两大机制拆解:安全前置钩子与测试驱动执行流

ECC 两大机制拆解:安全前置钩子与测试驱动执行流 资料来源:ECC 开源仓库(affaan-m/ECC)一手源码 skills/safety-guard/SKILL.mdskills/tdd-workflow/SKILL.mdhooks/hooks.json 架构(hooks/README.md) 一、安全做成前置钩子(safety-guard) 核心思想:利用 harness 的 PreToolUse… · 2026/9/23 18:59:13

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码