news 2026/5/22 9:26:23

Python爬虫实战:从零编写一个健壮的静态页面抓取器!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python爬虫实战:从零编写一个健壮的静态页面抓取器!

㊗️本期内容已收录至专栏《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 的requestsBeautifulSoup4库,带你完整抓取一个名言警句网站,并最终将数据导出为结构化的 CSV 文件。
读完这篇硬核干货,你将获得:

  1. 掌握针对静态网页的“请求-解析-存储”全链路标准开发范式。
  2. 学会如何编写具备基础反爬应对能力(如随机 UA、异常重试)的健壮代码。
  3. 拥有一份随时可复用到其他静态站点抓取任务中的工程化代码模板。

1️⃣ 摘要(Abstract)

本文以抓取经典沙盒站点 Quotes to Scrape 为例,详细拆解了使用 Python + Requests + BS4 工具栈实现数据采集的全过程,最终产出规范的 CSV 数据集。
通过阅读本文,你不仅能收获一份拿来即用的高质量源码,还能深刻理解爬虫背后的容错机制设计与合规边界原则,助你从爬虫“脚本小子”进阶为“数据工程师”。

2️⃣ 背景与需求(Why)

为什么要爬?
在日常工作中,我们经常需要进行信息聚合与自动化数据分析。在这个案例中,假设我们正在训练一个情感分析模型,或者需要为一个“每日一句”的 Bot 积累语料库,手动复制粘贴显然不够 Geek。自动化采集不仅解放双手,还能保证数据的时效性与完整性。

目标站点与字段清单:

  • 目标站点:http://quotes.toscrape.com/(包含多页结构)

  • 目标字段:

    • Quote_Text(名言内容)
    • Author(作者名字)
    • Tags(关联标签,多个标签以逗号拼接)

3️⃣ 合规与注意事项(必写)

说实话,写爬虫最关键的不是技术多牛,而是要知道“边界”在哪。进去喝茶可不是闹着玩的。

  • 关于 robots.txt:动手前永远记得看一眼domain.com/robots.txt。这是一种君子协定,如果对方明确Disallow了特定目录,请保持尊重,不要硬闯。
  • 频率控制与礼貌并发:不要把爬虫写成 DDoS 攻击器!在请求之间加入time.sleep(),模拟人类正常的浏览频率。这不仅是保护目标服务器,也是保护你自己的 IP 不被秒封。
  • 底线原则:绝不采集涉及个人隐私的敏感信息(如手机号、身份证、私密聊天);在遇到明确的登录墙或付费墙时,不要尝试使用黑客手段绕过获取未授权数据。咱们技术人,要用中立、克制的态度对待数据。

4️⃣ 技术选型与整体流程(What/How)

我们这次面对的是一个纯静态网页——这意味着所有的数据都直接写在了页面的 HTML 源码里,不需要执行 JavaScript,也不需要去抓 XHR 接口。

整体流程:
目标URL发现➡️采集(Fetcher)发起网络请求➡️解析(Parser)提取DOM节点➡️清洗(Cleaner)数据去空去重➡️存储(Storage)落盘CSV

为什么选 requests + BS4?
对于静态页面,requests的简单易用加上BeautifulSoup的链式调用,是快速验证需求的最优解。杀鸡焉用牛刀,在这个量级下如果上 Scrapy 或 Playwright 反而显得臃肿。我们的目标是:用最少的代码,干最漂亮的事。

5️⃣ 环境准备与依赖安装(可复现)

开始码代码前,先把环境拾掇利索。

  • Python 版本:建议使用 Python 3.8 或以上版本。

  • 依赖安装:打开你的终端,敲下这行命令:

    pipinstallrequests beautifulsoup4
  • 项目结构推荐:好的代码从目录开始。

    quote_spider/ ├── spider.py # 主程序入口 ├── user_agents.py # 存放UA池(可选) └── data/ # 存放最终产出的CSV文件

6️⃣ 核心实现:请求层(Fetcher)

网络请求是爬虫的“先头部队”,这部分代码必须够稳!
为了防止被防爬机制误杀,我加入了随机User-Agent,并设置了timeout防止程序假死。踩过坑的都知道,不加重试机制的请求都是耍流氓,这里演示一个简单的退避重试思路。

importrequestsimporttimeimportrandom# 简易版 UA 池USER_AGENTS=["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/115.0"]deffetch_page(url,retries=3):headers={"User-Agent":random.choice(USER_AGENTS),"Referer":"http://quotes.toscrape.com/"}forattemptinrange(retries):try:# 必须设置 timeout,5秒连不上直接进异常response=requests.get(url,headers=headers,timeout=5)response.raise_for_status()# 状态码不是200就抛异常returnresponse.textexceptrequests.exceptions.RequestExceptionase:print(f"请求失败 [{url}], 错误:{e}. 正在进行第{attempt+1}次重试...")time.sleep(2**attempt)# 指数退避,越重试等越久print(f"放弃抓取该页面:{url}")returnNone

7️⃣ 核心实现:解析层(Parser)

拿到 HTML 源码后,轮到BeautifulSoup4上场了。这其实是一个找规律的游戏。
我们打开网页的开发者工具(F12),发现每一条名言都包裹在一个<div class="quote">里。

容错是解析的灵魂:页面结构随时可能微调,如果某个节点没找到,不要让整个程序崩溃,给个默认值即可。

frombs4importBeautifulSoupdefparse_page(html):ifnothtml:return[],Nonesoup=BeautifulSoup(html,'html.parser')quotes_data=[]# 提取当前页的所有名言块quote_blocks=soup.find_all('div',class_='quote')forblockinquote_blocks:# 容错处理:安全地抽取文本text_elem=block.find('span',class_='text')author_elem=block.find('small',class_='author')tags_elems=block.find_all('a',class_='tag')quote_text=text_elem.get_text(strip=True)iftext_elemelse"无名言"author=author_elem.get_text(strip=True)ifauthor_elemelse"佚名"tags=",".join([tag.get_text(strip=True)fortagintags_elems])iftags_elemselse"无标签"quotes_data.append({"Quote_Text":quote_text,"Author":author,"Tags":tags})# 获取下一页的链接逻辑next_li=soup.find('li',class_='next')next_url=Noneifnext_li:next_tag=next_li.find('a')ifnext_tagand'href'innext_tag.attrs:# 拼接完整的下一页URLnext_url="http://quotes.toscrape.com"+next_tag['href']returnquotes_data,next_url

8️⃣ 数据存储与导出(Storage)

辛辛苦苦抓下来的数据,总得有个家。对于中小型爬虫,CSV 格式拥有极佳的便携性和兼容性。
去重策略在这里就不上布隆过滤器了,对于这种小站,咱们可以基于Quote_Text在内存里做一个简单的Set去重。

importcsvimportosdefsave_to_csv(data_list,filename="data/quotes_result.csv"):# 确保目录存在os.makedirs(os.path.dirname(filename),exist_ok=True)# 字段映射表定义fieldnames=["Quote_Text","Author","Tags"]file_exists=os.path.isfile(filename)# newline='' 防止Windows下产生空行,encoding='utf-8-sig'防止Excel打开乱码withopen(filename,mode='a',newline='',encoding='utf-8-sig')asf:writer=csv.DictWriter(f,fieldnames=fieldnames)ifnotfile_exists:writer.writeheader()# 首次写入表头forrowindata_list:writer.writerow(row)print(f"成功保存{len(data_list)}条数据到{filename}")

9️⃣ 运行方式与结果展示(必写)

万事俱备,开始组装主引擎!

defmain():start_url="http://quotes.toscrape.com/page/1/"current_url=start_url total_scraped=0seen_quotes=set()# 简单内存去重集合print("🚀 爬虫启动,目标锁定:Quotes to Scrape")whilecurrent_url:print(f"正在抓取:{current_url}")html=fetch_page(current_url)data,next_url=parse_page(html)# 去重与清洗逻辑unique_data=[]foritemindata:ifitem['Quote_Text']notinseen_quotes:seen_quotes.add(item['Quote_Text'])unique_data.append(item)ifunique_data:save_to_csv(unique_data)total_scraped+=len(unique_data)current_url=next_url# 翻页跟进# 频率控制:做个文明的爬虫time.sleep(random.uniform(1.5,3.0))print(f"🎉 抓取任务圆满完成!共获取{total_scraped}条去重后的名言。")if__name__=="__main__":main()

如何启动:
在终端中进入项目根目录,输入python spider.py
最终输出的 CSV 示例结果(展示前3行):

Quote_TextAuthorTags
“The world as we have created it is a process of our thinking…”Albert Einsteinchange,deep-thoughts,thinking,world
“It is our choices, Harry, that show what we truly are…”J.K. Rowlingabilities,choices
“There are only two ways to live your life. One is as though…”Albert Einsteininspirational,life,live,miracle,miracles

🔟 常见问题与排错(强烈建议写)

在真实的爬虫战场上,往往是“写代码一小时,Debug一整天”。以下是几个高频天坑:

  • 遭遇 403 Forbidden 怎么办?

    • 绝大部分是因为你的 Headers 太“裸露”了。检查 UA 是否带上,偶尔还要补齐Accept-Language甚至特定网站需要的Cookie。实在不行,就得上代理池(Proxy Pool)换 IP 跑了。
  • 状态码 200,但 HTML 抓下来是个“空壳”?

    • 恭喜你遇到了动态渲染网站(Vue/React)。这时候 Requests 就歇菜了,你需要去 Network 面板抓XHR/Fetch真实的数据接口,或者换用Playwright这种浏览器自动化工具直接渲染页面再抓。
  • 解析频繁报错AttributeError: 'NoneType' object has no attribute 'get_text'

    • 这意味着你的选择器(CSS/XPath)失效了,目标节点结构不稳定或者不存在。务必养成使用if element is not None:的防御性编程习惯。
  • 数据落盘乱码?

    • 记住,写入 CSV 加上encoding='utf-8-sig',在 Windows 环境里直接用 Excel 打开绝对治好你的乱码焦虑症。

1️⃣1️⃣ 进阶优化(加分项)

当你把上面的流程跑通,你就已经及格了。想拿满分?试试这些思路:

  • 火力全开(并发):引入concurrent.futures.ThreadPoolExecutor或者直接用asyncio + aiohttp,速度能飙升十几倍。不过切记,并发越高,离“封IP”越近。
  • 断点续跑机制:如果抓了 10 万条突然断网了怎么办?把抓取过的 URL 存入 Redis 的 Set 里做持久化已抓集合,下次启动只抓未抓过的。
  • 脱离脚本,走向工程:尝试把这套逻辑迁移到Scrapy框架里,体验一下工业级爬虫流水线的美感。

1️⃣2️⃣ 总结与延伸阅读

呼~长舒一口气。在这篇文章中,我们从最基础的 HTTP 请求起手,到精细的 DOM 节点解析,再到结构化的数据落盘,完整地构建了一个“小而美”的静态页面爬虫引擎。
如果你觉得意犹未尽,下一步可以做什么?
我强烈建议你去了解一下Playwright,掌握了它,你就掌握了对付所有复杂动态网页的终极武器;如果你对大规模分布式爬虫感兴趣,Scrapy-Redis绝对是你的必修课。

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持!❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣想系统提升的小伙伴:强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 “谁使用,谁负责” 。如不同意,请立即停止使用并删除本项目。!!!
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/22 9:23:05

一个肖特基二极管的型号:B140HB

简 介&#xff1a; 本文介绍了利用AI查询肖特基二极管B140HB-13型号的过程。该二极管由每台半导体生产&#xff0c;耐压40V&#xff0c;导通电流1A&#xff0c;正向压降约0.53V。AI快速准确地识别了器件表面文字并匹配数据手册。文章还提到将使用该二极管设计电感耦合反向电源测…

作者头像 李华
网站建设 2026/5/22 9:16:27

CMS垃圾回收

CMS概念&#xff1a;CMS&#xff08;Concurrent Mark Sweep&#xff09;收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用&#xff0c;它是HotSpot虚拟机第一款真正意义上的并发收集器&#xff0c;它第一次实现了让垃圾收集线程与用户…

作者头像 李华
网站建设 2026/5/22 9:16:21

126、强化学习:在机器人运动控制中的实战

126、强化学习:在机器人运动控制中的实战 从一次“摔跤”说起 去年做四足机器人步态优化时,我遇到了一个让人抓狂的问题——传统MPC(模型预测控制)在粗糙地形上总是跑着跑着就侧翻。调了三个月的权重矩阵,换了四版动力学模型,结果还不如实验室新来的实习生用PPO算法训了…

作者头像 李华
网站建设 2026/5/22 9:15:40

BarrageGrab:企业级多平台直播弹幕一体化采集解决方案

BarrageGrab&#xff1a;企业级多平台直播弹幕一体化采集解决方案 【免费下载链接】BarrageGrab 抖音快手bilibili直播弹幕wss直连&#xff0c;非系统代理方式&#xff0c;无需多开浏览器窗口 项目地址: https://gitcode.com/gh_mirrors/ba/BarrageGrab 在直播电商、游戏…

作者头像 李华
网站建设 2026/5/22 9:15:05

终极AMD Ryzen硬件调试指南:SMUDebugTool完全免费开源工具

终极AMD Ryzen硬件调试指南&#xff1a;SMUDebugTool完全免费开源工具 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https:…

作者头像 李华