㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐ (入门级)
🉐福利:一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。
全文目录:
- 🌟 开篇语
- 0️⃣ 前言(Preface)
- 1️⃣ 摘要(Abstract)
- 2️⃣ 背景与需求(Why)
- 3️⃣ 合规与注意事项(必写)⚠️
- 4️⃣ 技术选型与整体流程(What/How)
- 5️⃣ 环境准备与依赖安装(可复现)💻
- 6️⃣ 核心实现:请求层(Fetcher)
- 7️⃣ 核心实现:解析层(Parser)
- 8️⃣ 数据存储与导出(Storage)
- 9️⃣ 运行方式与结果展示(必写)🚀
- 🔟 常见问题与排错(强烈建议写)🔧
- 1️⃣1️⃣ 进阶优化(可选但加分)🌟
- 1️⃣2️⃣ 总结与延伸阅读
- 🌟 文末
- ✅ 专栏持续更新中|建议收藏 + 订阅
- ✅ 互动征集
- ✅ 免责声明
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注Python 爬虫工程化实战,主理专栏 《Python爬虫实战》:从采集策略到反爬对抗,从数据清洗到分布式调度,持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”,让数据价值真正做到——抓得到、洗得净、用得上。
📌专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣专栏推广时间:如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
0️⃣ 前言(Preface)
项目核心:本篇我们将使用 Python 的
requests+BeautifulSoup4工具,抓取经典标靶网站的哲理名言,最终产出一份结构化的英文命名数据集文件。读完本文你能获得什么:
- 掌握静态网页爬虫的“黄金四步法”(请求、解析、清洗、存储)。
- 学会编写具备基础容错和重试机制的工业级网络请求代码。
- 获得一份可以直接运行并产出数据的实战源码,为后续进阶打下坚实基础。
1️⃣ 摘要(Abstract)
项目核心:本文旨在通过轻量级库
requests与bs4解析目标静态 HTML 页面,提取文本与属性,并将清洗后的数据持久化至本地 CSV 文件中。读完本文你能获得什么:
- 理解合法合规采集数据的重要原则(如遵循
robots.txt)。 - 精通基于 CSS 选择器的 DOM 树解析与异常字段捕获技巧。
- 了解爬虫开发中常见的 403/429 错误排查与进阶优化思路。
- 理解合法合规采集数据的重要原则(如遵循
2️⃣ 背景与需求(Why)
为什么要爬?
说实话,很多人学爬虫一开始只是觉得“酷”,但在实际业务中,爬虫是数据分析的源泉与信息聚合的核心驱动力。假设我们需要训练一个自然语言处理(NLP)的“每日一言”大模型,或者只是想给自己的博客做一个自动更新的“名言警句”侧边栏,手动复制粘贴显然不够极客。我们需要通过自动化手段,快速构建属于自己的知识图谱。
目标站点与字段:
目标站点:
http://quotes.toscrape.com/(官方合法的爬虫测试沙盒)目标字段清单:
Quote_Text(名言内容)Author(作者姓名)Tags(名言相关标签,由于可能有多个,我们将以逗号分隔)
3️⃣ 合规与注意事项(必写)⚠️
作为一名资深爱好者,我必须提醒你,技术无罪,但使用技术要有底线。
- 遵循 robots.txt:在爬取任何网站前,请习惯性地访问
域名/robots.txt,查看站长允许或禁止抓取的目录。 - 频率控制(不要攻击式并发):不要像发起 DDoS 攻击一样去请求别人的服务器。加上
time.sleep()延时,温柔一点,细水长流。 - 不采集敏感信息:坚决不碰涉及个人隐私、国家安全的数据。
- 不绕过付费/登录限制:尊重内容创作者的知识产权,我们只获取公开的互联网公共信息。
4️⃣ 技术选型与整体流程(What/How)
技术定调:静态网页爬取
目标网站服务端直接通过 HTML 返回了完整的数据内容,没有使用复杂的 Ajax 动态加载或加密混淆。因此,这属于最经典的静态网页爬虫。
为什么选requests+bs4?
俗话说“杀鸡焉用牛刀”,面对这种结构清晰的静态网站,上Scrapy框架有点重,用Playwright渲染引擎又太浪费系统资源。requests负责高效建立 HTTP 通信,BeautifulSoup的 CSS 选择器写起来直观且优雅,是本场景的最佳组合。
整体流程图如下(English visualization as requested):
5️⃣ 环境准备与依赖安装(可复现)💻
无论你是小白还是老鸟,拥有一个干净的虚拟环境都是好习惯。加油,把这几行命令敲起来!
Python 版本:推荐 Python 3.8 或以上版本。
依赖安装:
pipinstallrequests beautifulsoup4 pandas推荐项目结构:
quote_spider/ ├── main.py # 入口启动文件 ├── fetcher.py # 网络请求模块 ├── parser.py # 页面解析模块 ├── storage.py # 数据存储模块 └── data/ # 存放输出文件的目录(为了方便你直接复制运行,接下来的代码我会整合在一个优雅的脚本中)
6️⃣ 核心实现:请求层(Fetcher)
这一层是爬虫的“敲门砖”。不带Headers的请求就像没穿衣服在街上跑,很容易被服务器封禁。
importrequestsimporttimeimportlogging logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s')deffetch_page(url,retries=3):# 必须说明:模拟真实浏览器的 Headersheaders={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Referer':'http://quotes.toscrape.com/'# 告诉服务器我从哪里来}forattemptinrange(retries):try:# 必须说明:加上 timeout 防止死锁挂起response=requests.get(url,headers=headers,timeout=10)response.raise_for_status()# 遇到 4xx 或 5xx 会抛出异常returnresponse.textexceptrequests.exceptions.RequestExceptionase:logging.warning(f"Fetch failed on{url}. Attempt{attempt+1}of{retries}. Error:{e}")time.sleep(2**attempt)# 指数退避策略:失败后等待 1s, 2s, 4slogging.error(f"Max retries reached for{url}")returnNone7️⃣ 核心实现:解析层(Parser)
拿到 HTML 源码后,我们要精准地“摘取”数据。这里采用bs4的 CSS 选择器(.select())。
frombs4importBeautifulSoupdefparse_quotes(html_content):ifnothtml_content:return[],Nonesoup=BeautifulSoup(html_content,'html.parser')quotes_data=[]# 获取列表页中的所有名言块quote_blocks=soup.select('div.quote')forblockinquote_blocks:# 提取字段并做容错处理(缺失字段怎么办?用 .get_text() 前先判断)text_element=block.select_one('span.text')quote_text=text_element.get_text(strip=True)iftext_elementelse"Unknown Quote"author_element=block.select_one('small.author')author=author_element.get_text(strip=True)ifauthor_elementelse"Unknown Author"tags_elements=block.select('div.tags a.tag')tags=[tag.get_text(strip=True)fortagintags_elements]tags_str=", ".join(tags)iftagselse"No Tags"quotes_data.append({'Quote_Text':quote_text,'Author':author,'Tags':tags_str})# 获取下一页的链接next_btn=soup.select_one('li.next a')next_url=f"http://quotes.toscrape.com{next_btn['href']}"ifnext_btnelseNonereturnquotes_data,next_url8️⃣ 数据存储与导出(Storage)
这里我们选择极其通用且易于数据分析的CSV格式。我们使用 URL 或内容的 Hash 来去重这里不需要,因为目标网站分页明确无重复,但为了严谨,我们基于Quote_Text做简单的去重校验。
importcsvimportosdefsave_to_csv(data_list,filename="quotes_dataset.csv"):ifnotdata_list:return# 去重策略:按名言内容去重seen=set()unique_data=[]foritemindata_list:ifitem['Quote_Text']notinseen:seen.add(item['Quote_Text'])unique_data.append(item)# 字段映射与存储keys=['Quote_Text','Author','Tags']file_exists=os.path.isfile(filename)withopen(filename,'a',newline='',encoding='utf-8')asoutput_file:dict_writer=csv.DictWriter(output_file,fieldnames=keys)ifnotfile_exists:dict_writer.writeheader()dict_writer.writerows(unique_data)logging.info(f"Successfully saved{len(unique_data)}unique records to{filename}.")9️⃣ 运行方式与结果展示(必写)🚀
将上面的代码拼接在一起(你可以自行加一个main()函数,并在其中加上time.sleep(1)翻页逻辑)。
启动命令:打开你的终端,运行:
python main.py输出位置:代码将在当前目录生成一个名为
quotes_dataset.csv的文件。示例结果(前3行):
| Quote_Text | Author | Tags |
|---|---|---|
| “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.” | Albert Einstein | change, deep-thoughts, thinking, world |
| “It is our choices, Harry, that show what we truly are, far more than our abilities.” | J.K. Rowling | abilities, choices |
| “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.” | Albert Einstein | inspirational, life, live, miracle |
🔟 常见问题与排错(强烈建议写)🔧
爬虫总会遇到各种玄学问题,别气馁,排错过程才是长知识的黄金时刻!
- 遇上 403 或 429 怎么办?:403 通常是因为你的 Headers 太假了,尤其是缺失了真实的
User-Agent或者Referer。429 说明你的访问频率触碰了站点的警报线。解法:加大time.sleep()延迟,或者建立代理 IP 池轮换 IP。 - 抓下来的 HTML 全是 JS 脚本的空壳?:恭喜你,遇到了 SPA(单页应用,如 Vue/React 开发的网站)。静态请求拿不到数据了。解法:按 F12 打开开发者工具,看
Network->XHR/Fetch,直接去抓取背后的 JSON 数据接口 API。 - 解析报错(
NoneType' object has no attribute 'get_text')?:这是因为网站结构变化或某个元素本来就不存在。解法:在.get_text()前一定要做非空判断(参考本文第7节的容错写法)。 - 中文乱码怎么破?:
requests有时会猜错网页编码,直接在解析前强制声明:response.encoding = 'utf-8'。
1️⃣1️⃣ 进阶优化(可选但加分)🌟
如果你已经玩透了基础的,想要挑战更强大的架构:
- 并发加速:把简单的
for循环换成concurrent.futures.ThreadPoolExecutor,实现多线程抓取,速度起飞!但记住,线程池越大,越容易被封。 - 断点续跑:如果网站有 10000 页,爬到 5000 页断网了怎么办?可以引入 Redis 集合存储已经爬取过的 URL,每次开跑前做一次差集比对。
- 定时调度:结合服务器的
crontab或者更高级的Apache Airflow,每天早上 8 点自动抓取更新数据。
1️⃣2️⃣ 总结与延伸阅读
🎉 恭喜你读到了这里!复盘一下,我们今天用 Python 完成了一套完整的爬虫闭环:从设置带重试机制的网络请求,到 CSS 结构化解析,最后稳稳地把干干净净的数据存入了CSV中。
下一步的打怪升级方向:
当你遇到需要滑动验证码、需要执行 JavaScript 或者点击翻页的复杂网站时,这套基础方案就不够用了。接下来你可以去了解Playwright(无头浏览器自动化控制)以及工业级的Scrapy爬虫框架。
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持!❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣想系统提升的小伙伴:强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~
✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 “谁使用,谁负责” 。如不同意,请立即停止使用并删除本项目。!!!