
㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐ (入门级)福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错强烈建议写1️⃣1️⃣ 进阶优化可选但加分1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface大家好这里是你们的 Python 爬虫老司机。今天咱们要搞点实用的——用requestsBeautifulSoup4这套经典组合去抓取测试站点 Books to Scrape 的全站图书数据并最终把它们导成一份干干净净的 CSV 文件。看完这篇长文你不仅能带走一套工业级的静态网页爬虫模板还能顺便掌握请求重试与基础的反爬应对姿势。面对不规则 HTML 标签时的容错处理思维。完整的数据流转闭环采集 → 清洗 → 存储。1️⃣ 摘要Abstract本文以 Books to Scrape 为例详述了基于 Python 静态请求工具链requestsbs4实现全站数据采集的完整过程最终产出包含书名、价格、评分等字段的结构化 CSV 数据集。阅读本文你将获得一套高复用性的请求层Fetcher与解析层Parser代码解耦方案。常见网络异常如超时、空响应的兜底策略。告别乱码和数据错位的清洗入库实战经验。2️⃣ 背景与需求Why为什么要做这个项目在日常的数据分析、竞品监控或是单纯的信息聚合需求中拿到结构化数据是第一步。我见过太多新手一上来就乱写正则最后代码维护得像一坨乱麻。通过这个项目咱们要练就的是工程化的思维。我们的目标站点是http://books.toscrape.com/我们需要提取的目标字段清单如下book_url图书详情页唯一链接用于去重title图书完整名称price价格需剔除货币符号rating星级评分需转化为纯数字stock_status库存状态3️⃣ 合规与注意事项必写身为爬虫爱好者咱们得讲究“盗亦有道” ♂️关于 robots.txt目标网站的/robots.txt允许抓取本教程完全合规。大家在爬取真实商业网站前务必先看一眼这个文件。频率控制永远不要做“赛博蝗虫”本文代码中加入了time.sleep()退避机制。不要为了图快去使用无限制的攻击式并发这会给目标服务器带来沉重负担。数据边界本案例仅采集公开的商品基础信息绝对不触碰任何涉及用户隐私的敏感数据且不涉及绕过任何付费或强制登录的鉴权限制。4️⃣ 技术选型与整体流程What/How因为目标站点是纯静态服务端渲染SSR的页面没有复杂的 JavaScript 动态加载属于典型的静态抓取场景。所以我们不杀鸡用牛刀放弃厚重的 Playwright/Selenium也不用相对复杂的 Scrapy 框架而是采用最轻快敏捷的方案requestsBeautifulSoup4。整体的数据流转过程如下[ 发起请求 (Fetcher) ] ➡️ [ 获取 HTML 源码 ] ➡️ [ CSS 选择器解析 (Parser) ] ➡️ [ 字典组装与字段清洗 ] ➡️ [ 落盘为 CSV (Storage) ]5️⃣ 环境准备与依赖安装可复现动手前先搭环境。本教程基于Python 3.8请确保你的环境没问题。打开终端一把梭安装以下依赖pipinstallrequests beautifulsoup4 pandas tenacity注tenacity是一个非常优雅的重试库后面你会爱上它的。推荐的项目结构book_scraper/ ├── config.py # 配置项如请求头、目标URL ├── fetcher.py # 核心请求层 ├── parser.py # 核心解析层 ├── storage.py # 存储逻辑 ├── main.py # 启动入口 └── data/ # 存放输出的CSV文件6️⃣ 核心实现请求层Fetcher网络请求是爬虫的排头兵最怕的就是裸奔。Headers伪装是第一步带上常见的User-Agent有必要的话带上Referer。Session推荐使用requests.Session()它能帮我们在多次请求中自动保持连接池稍微提升一点抓取速度。Timeout永远记得加timeout10不然你的程序可能会在某个死链接上挂起一整晚别问我怎么知道的全是血泪教训 。失败重试网络波动太正常了我们引入tenacity进行优雅重试。# fetcher.pyimportrequestsfromtenacityimportretry,stop_after_attempt,wait_fixedimportlogging logging.basicConfig(levellogging.INFO,format%(asctime)s - %(levelname)s - %(message)s)classBookFetcher:def__init__(self):self.sessionrequests.Session()self.session.headers.update({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-Language:en-US,en;q0.9,})retry(stopstop_after_attempt(3),waitwait_fixed(2))defget_html(self,url):带重试机制的请求获取try:respself.session.get(url,timeout10)resp.raise_for_status()# 遇到 4xx/5xx 直接抛出异常触发重试resp.encodingutf-8returnresp.textexceptrequests.RequestExceptionase:logging.warning(f请求失败准备重试:{url}| 报错:{e})raise7️⃣ 核心实现解析层Parser拿到 HTML 后我们要像剥洋葱一样把数据抽出来。我个人偏爱 BeautifulSoup 的 CSS 选择器select/select_one语法跟写网页样式一样顺滑。列表页抽链接找到所有article.product_pod h3 a拿到详情页 URL。详情页抽字段进入详情页抓取标题和价格。容错机制网页结构随时会变某个字段找不到时千万别让NoneType object has no attribute text毁了你跑了半小时的任务务必写一个安全的提取辅助函数。# parser.pyfrombs4importBeautifulSoupimportreclassBookParser:defparse_list_page(self,html,base_urlhttp://books.toscrape.com/catalogue/):提取列表页中的图书详情链接soupBeautifulSoup(html,lxml)# lxml 解析器比 html.parser 更快links[]fora_taginsoup.select(article.product_pod h3 a):# 处理相对路径hrefa_tag.get(href,).replace(../../../,)links.append(base_urlhref)returnlinksdefparse_detail_page(self,html,url):提取详情页关键字段soupBeautifulSoup(html,lxml)# 安全提取函数defsafe_extract(selector,attrtext,defaultN/A):elementsoup.select_one(selector)ifnotelement:returndefaultifattrtext:returnelement.get_text(stripTrue)returnelement.get(attr,default)titlesafe_extract(div.product_main h1)raw_pricesafe_extract(div.product_main p.price_color)# 数据清洗正则去掉英镑符号保留数字pricere.sub(r[^\d\.],,raw_price)ifraw_price!N/Aelse0.00# 评分转换机制英文类名转数字rating_dict{One:1,Two:2,Three:3,Four:4,Five:5}raw_ratingsafe_extract(div.product_main p.star-rating,attrclass)rating_class[cforcinraw_ratingifcinrating_dict.keys()]ratingrating_dict.get(rating_class[0],0)ifrating_classelse0stocksafe_extract(div.product_main p.instock)return{book_url:url,title:title,price:float(price),rating:rating,stock_status:stock}8️⃣ 数据存储与导出Storage数据落盘咱们这次选 Python 数据科学界的“扛把子”——pandas。它不仅导出 CSV 极其方便后续还能直接用于数据分析。关于去重由于每次抓取可能因为网络中断导致重复运行我们以图书唯一标识book_url作为去重主键。字段映射表字段名 (Field)类型 (Type)示例值 (Example)book_urlstringhttp://books.toscrape.com/…/index.htmltitlestringA Light in the Atticpricefloat51.77ratinginteger3stock_statusstringIn stock (22 available)# storage.pyimportpandasaspdimportosclassCSVStorage:def__init__(self,filenamebook_data.csv):self.filenamefilename self.data_buffer[]defadd_data(self,item_dict):self.data_buffer.append(item_dict)defsave(self):ifnotself.data_buffer:returndfpd.DataFrame(self.data_buffer)# 如果文件存在则追加如果不存在则带表头写入ifos.path.exists(self.filename):existing_dfpd.read_csv(self.filename)dfpd.concat([existing_df,df])# 基于 URL 字段进行去重df.drop_duplicates(subset[book_url],keeplast,inplaceTrue)df.to_csv(self.filename,indexFalse,encodingutf-8-sig)print(f✅ 成功保存{len(self.data_buffer)}条数据至{self.filename})self.data_buffer[]# 清空缓冲池9️⃣ 运行方式与结果展示必写万事俱备编写我们的入口文件main.py将三者串联起来。# main.pyfromfetcherimportBookFetcherfromparserimportBookParserfromstorageimportCSVStorageimporttimeimportrandomdefmain():fetcherBookFetcher()parserBookParser()storageCSVStorage(filenamebook_data.csv)# 演示只抓取第1页的列表list_urlhttp://books.toscrape.com/catalogue/page-1.htmlprint(f 开始抓取列表页:{list_url})list_htmlfetcher.get_html(list_url)detail_linksparser.parse_list_page(list_html)fori,linkinenumerate(detail_links[:5]):# 为演示速度仅取前5本print(f正在抓取第{i1}本:{link})detail_htmlfetcher.get_html(link)book_dataparser.parse_detail_page(detail_html,link)storage.add_data(book_data)# 乖巧休眠防止被封 IPtime.sleep(random.uniform(1,2))storage.save()print( 任务全部完成)if__name____main__:main()如何启动在终端运行python main.py。输出结果展示结果会保存在当前目录的book_data.csv中大概长这样book_url,title,price,rating,stock_status http://books.../a-light.../index.html,A Light in the Attic,51.77,3,In stock (22 available) http://books.../tipping.../index.html,Tipping the Velvet,53.74,1,In stock (20 available) http://books.../soumission.../index.html,Soumission,50.10,1,In stock (20 available) 常见问题与排错强烈建议写在真实的爬虫战场上不可能永远顺风顺水。碰到 bug 别慌看看下面这几个常见坑点HTTP 403 权限拒绝 或 429 Too Many Requests排错手段说明你抓得太猛或者特征太明显被反爬了。增加time.sleep()的时间加入随机User-Agent池或者购买合法的代理 IP 进行轮询。HTML 抓到“空壳”怎么办症状浏览器里能看到数据但requests打印出来的源码里没有。原因数据是通过 AJAX 接口异步加载或者前端框架Vue/React渲染的。解法按 F12 打开开发者工具去 Network 面板抓XHR/Fetch类型的 JSON 接口或者直接上 Playwright 进行真实浏览器渲染抓取。解析报错AttributeError原因目标站点的 HTML 结构不稳定某些页面缺失某个标签。解法参考本文第7节的safe_extract()函数利用try-except或if element:进行严格的容错处理。乱码与编码问题解法在拿到 response 后手动指定编码response.encoding response.apparent_encoding或者直接写死response.encoding utf-8。存 CSV 时记得加encodingutf-8-sig防止 Excel 打开时出现中文乱码。1️⃣1️⃣ 进阶优化可选但加分现在的脚本虽然稳但如果面对 10 万级的数据量单线程就显得太慢了。后续可以从以下几点优化并发加速将requests替换为aiohttp配合 Python 的asyncio协程速度能提升数倍。断点续跑遇到异常中断时最让人崩溃。可以通过把抓取过的 URL 存入 Redis 的 Set 集合中每次抓取前做一下比对实现增量抓取。日志与监控在生产环境中引入logging模块并将报错推送至钉钉/飞书机器人最怕半夜跑崩了还要爬起来看服务器日志。1️⃣2️⃣ 总结与延伸阅读好啦复盘一下今天咱们完成了什么我们用极其规范的代码结构实现了一个抗压性强、解析安全的静态网页爬虫并顺利将数据结构化落盘。这是一次工程化思维的实践✨下一步可以做什么如果你觉得requests的这套组合拳写起来还是有很多重复性劳动我强烈建议你去了解一下Scrapy框架——它是 Python 爬虫界的重型武器自带高并发、管道流和强大的中间件生态。如果是对付动态网站那么微软开源的Playwright绝对是你必须掌握的神器。 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。