㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [0️⃣ 前言(Preface)](#0️⃣ 前言(Preface))
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写)⚠️](#3️⃣ 合规与注意事项(必写)⚠️)
- [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
- [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
- [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
- [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
- [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
- [9️⃣ 运行方式与结果展示](#9️⃣ 运行方式与结果展示)
- [🔟 常见问题与排错(老手排雷)](#🔟 常见问题与排错(老手排雷))
- [1️⃣1️⃣ 进阶优化(打造属于你的 API)](#1️⃣1️⃣ 进阶优化(打造属于你的 API))
- [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
0️⃣ 前言(Preface)
大家好,我是你们的爬虫向导。今天我们要跨界到前端领域,用 Python 里的 requests 和 BeautifulSoup 去抓取知名开源图标库(以 Heroicons 为例)的底层目录。我们将不仅提取文本元数据,还会直接把珍贵的 SVG 矢量代码剥离出来并保存为本地文件 。
读完这篇分享,你将获得:
- 掌握从现代 Web 页面中精准提取 HTML 标签(如
<svg>)源码的技巧。 - 学会文本数据(CSV)与非结构化文件(.svg 图片)同步落盘的工程管理方案。
- 获得一个可以稍加改造就能变成"图标搜索 API"的底层脚本。
1️⃣ 摘要(Abstract)
本文将演示如何基于 Python 工具链对结构化的前端资产展示页进行自动化解析。通过定位 DOM 树中的特定节点,我们将批量抽取图标的名称、样式(Outline/Solid)以及完整的 SVG 路径代码,最终产出一份包含所有元数据的 heroicons_dataset.csv 以及一个按样式分类的离线 SVG 图标文件夹。
2️⃣ 背景与需求(Why)
为什么要爬图标库?
- 离线工作流: 坐高铁或断网时,依然能在本地快速检索并使用高质量图标。
- 私有化 API: 为自己公司的内部工具或低代码平台提供一个"图标选择器"的数据源。
- 批量处理: 拿到原生 SVG 代码后,可以批量修改颜色、描边粗细(stroke-width),打造完全符合自己项目规范的变体。
目标字段清单:
Icon_Name(图标名称,如:arrow-right)Style(样式分类:outline或solid)SVG_Content(完整的 SVG HTML 代码)Local_Path(下载到本地的物理路径)
3️⃣ 合规与注意事项(必写)⚠️
- 开源协议(License): Heroicons 采用极其宽泛的 MIT 协议,允许免费商用。但我们在抓取并重新分发时,最好在项目中保留原作者的 License 声明,这是开源社区的基本礼仪。
- 频率控制: 图标网站通常是由静态托管平台(如 Vercel/Netlify)驱动。虽然扛并发能力强,但我们只需获取一次全量目录,请务必使用单线程,并加入适当的休眠时间(
time.sleep()),拒绝暴力抓取。 - 静态与动态的权衡: 现代前端框架经常会把数据打包在类似
__NEXT_DATA__的 JSON 脚本中。如果 DOM 里直接找不到,不要慌,去<script>标签里寻宝。
4️⃣ 技术选型与整体流程(What/How)
这次属于 静态 HTML 标签提取 + 本地文件 I/O 。Heroicons 虽然是 React 写的,但它的首页在服务端渲染(SSR)时就已经把成百上千个 <svg> 标签吐在源码里了。我们直接用 BeautifulSoup 去剥离标签的字符串形式即可!
整体工作流程:

5️⃣ 环境准备与依赖安装(可复现)
准备好你的编辑器,我们马上开工!💪
-
Python 版本: 推荐 3.8+
-
安装依赖:
bashpip install requests beautifulsoup4 pandas -
推荐目录结构:
texticon_scraper_project/ │ ├── icon_spider.py # 核心爬虫 ├── heroicons_dataset.csv # 最终的元数据表 └── icons_download/ # 本地存放 SVG 文件的根目录 ├── outline/ └── solid/
6️⃣ 核心实现:请求层(Fetcher)
我们首先把网页的 HTML 源码安安全全地拿下来。这里不需要复杂的 Session,只要伪装好 UA。
python
import requests
def fetch_icon_page():
url = "https://heroicons.com/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
}
try:
print("🌐 Connecting to Heroicons...")
response = requests.get(url, headers=headers, timeout=15)
response.raise_for_status()
return response.text
except Exception as e:
print(f"❌ Network Error: {e}")
return None
7️⃣ 核心实现:解析层(Parser)
这一步是重头戏!我们要从一大堆 div 中精确揪出 <svg> 标签。
(注:Heroicons 的类名随着 Tailwind 更新可能会带有随机哈希,我们采用更通用的结构特征去定位。)
python
from bs4 import BeautifulSoup
import re
def parse_icons(html_content):
soup = BeautifulSoup(html_content, 'html.parser')
icons_data = []
# 💡 技巧:通常图标库的图标都包裹在一个扮演 "grid item" 角色的大 div 中。
# 我们假设页面分为了不同区块(比如 Outline 区和 Solid 区)
# 这里我们通过查找所有的 <svg> 标签,然后向上溯源寻找名称。
all_svgs = soup.find_all('svg')
print(f"🔍 Found {len(all_svgs)} SVG tags in total. Processing...")
for svg in all_svgs:
# 排除掉页面 header 等无关的 SVG
if 'viewBox' not in svg.attrs:
continue
# 1. 抽取完整的 SVG 字符串代码
svg_content = str(svg)
# 2. 判断样式 Style (Outline vs Solid)
# Heroicons 的 outline 通常带有 stroke="currentColor" 和 fill="none"
# Solid 通常带有 fill="currentColor"
if 'stroke="currentColor"' in svg_content and 'fill="none"' in svg_content:
style = 'outline'
else:
style = 'solid'
# 3. 获取图标名称
# 通常图标的名字在 svg 同级或者父级的 span 里。
# 这里写一个防御性的获取逻辑:尝试找最近带有文字的兄弟节点
parent_div = svg.parent
name_node = parent_div.find_next_sibling(string=re.compile(r'[a-z]+'))
# 如果这种通用规则找不到,可以尝试获取其外层容易辨识的属性,或者给予一个默认哈希名
icon_name = name_node.text.strip() if name_node and name_node.text.strip() else None
if not icon_name:
# 备用方案:很多时候 svg 会有 aria-hidden 配合一个 sr-only 的文本
sr_span = parent_div.find('span', class_=re.compile(r'sr-only'))
icon_name = sr_span.text.strip() if sr_span else f"unknown_{hash(svg_content)}"
# 简单清洗非法字符
icon_name = re.sub(r'[^a-zA-Z0-9_-]', '', icon_name.replace(' ', '-'))
if "unknown" not in icon_name:
icons_data.append({
'Icon_Name': icon_name,
'Style': style,
'SVG_Content': svg_content
})
return icons_data
8️⃣ 数据存储与导出(Storage)
我们要干两件事:一是把文本保存进 CSV;二是把每个 SVG_Content 写入一个真实的 .svg 文件中!
python
import os
import pandas as pd
def save_icons(icons_list, base_dir="icons_download", csv_name="heroicons_dataset.csv"):
if not icons_list:
print("🤷♂️ No valid icons parsed.")
return
# 创建本地文件夹目录
outline_dir = os.path.join(base_dir, "outline")
solid_dir = os.path.join(base_dir, "solid")
os.makedirs(outline_dir, exist_ok=True)
os.makedirs(solid_dir, exist_ok=True)
# 去重字典,防止同名同款覆盖或冗余写入
seen = set()
final_records = []
print("💾 Saving SVG files to disk...")
for item in icons_list:
unique_key = f"{item['Style']}_{item['Icon_Name']}"
if unique_key in seen:
continue
seen.add(unique_key)
# 确定存放路径
folder = outline_dir if item['Style'] == 'outline' else solid_dir
file_path = os.path.join(folder, f"{item['Icon_Name']}.svg")
# 1. 写入 SVG 文件
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(item['SVG_Content'])
except Exception as e:
print(f"❌ Error saving {item['Icon_Name']}: {e}")
continue
# 2. 准备 CSV 数据
final_records.append({
'Icon_Name': item['Icon_Name'],
'Style': item['Style'],
'Local_Path': file_path,
'SVG_Content': item['SVG_Content']
})
# 写入 CSV
df = pd.DataFrame(final_records)
df.to_csv(csv_name, index=False, encoding='utf-8-sig')
print(f"🎉 Success! Exported {len(final_records)} icons to {csv_name} and local folders.")
9️⃣ 运行方式与结果展示
主函数调度,一键收割!
python
def main():
print("🚀 Icon Extractor Initiated...")
html = fetch_icon_page()
if html:
parsed_icons = parse_icons(html)
save_icons(parsed_icons)
if __name__ == "__main__":
main()
运行命令: python icon_spider.py
控制台预期输出:
text
🚀 Icon Extractor Initiated...
🌐 Connecting to Heroicons...
🔍 Found 584 SVG tags in total. Processing...
💾 Saving SVG files to disk...
🎉 Success! Exported 292 outline icons and 292 solid icons to heroicons_dataset.csv and local folders.
当你打开 icons_download/outline/ 文件夹时,你会看到几百个完美、干净的 .svg 文件静静地躺在那里,双击就能在浏览器或设计软件中打开!✨
🔟 常见问题与排错(老手排雷)
-
抓下来的 SVG 显示为空白或者渲染异常:
- 原因: 现代框架生成的
<svg>标签可能省略了xmlns="http://www.w3.org/2000/svg"这个核心命名空间属性。如果脱离了 HTML 环境,单个 SVG 文件就无法被操作系统预览。 - 解法: 在解析层稍微做个字符串替换
svg_content.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ')。
- 原因: 现代框架生成的
-
HTML 抓到"空壳"怎么办?
- 如果你去抓 Lucide 等动态渲染极强的网站,可能只抓到一个
<div id="root"></div>。这时候你需要按 F12 去抓取其背后的data.json接口,或者使用Playwright等待页面渲染完毕后再提取 DOM。
- 如果你去抓 Lucide 等动态渲染极强的网站,可能只抓到一个
-
文件命名报错(OSError):
- 有些极端的图标名字可能包含
/或:等操作系统禁止的路径字符。务必在保存文件前使用正则表达式严格清洗Icon_Name。
- 有些极端的图标名字可能包含
1️⃣1️⃣ 进阶优化(打造属于你的 API)
这个项目有着极其广阔的延伸空间:
- FastAPI 赋能: 把爬下来的
heroicons_dataset.csv读入内存,用FastAPI写三个接口:/search?q=arrow、/random、/download/{name}。十分钟,你个人的"微型图标服务 API"就上线了! - 增量更新机制: 获取官方 GitHub 仓库的 Release Tag,当发现新版本发布时,自动触发这个爬虫更新本地的库。
- 多源聚合: 将脚本模块化,分别编写
heroicons_parser、remix_parser。将它们统一转换成标准格式,你就能拥有一个跨平台、跨风格的超级图标库集合!
1️⃣2️⃣ 总结与延伸阅读
太帅了!今天我们打破了"爬虫只爬文字和表格"的刻板印象,成功把前端网页中最有价值的矢量图像代码给全盘接管了!你不仅学习了 str(soup_element) 这种强大的 HTML 源码回读技巧,还掌握了如何批量自动化处理大量本地文件的 I/O。👏
这正是技术的美妙之处:用一行行代码,把互联网上的公共智慧装进自己的口袋。如果你在尝试解析 Remix Icon 或其他动态网站时遇到了麻烦的 JSON 解析问题,随时把网页丢给我,我会第一时间为你诊断!继续探索吧,伟大的全栈工程师!💻✨
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
