豆瓣图书爬虫实战:用requests与pandas将书单自动导出Excel

发布时间:2026/10/18 12:57:44

豆瓣图书爬虫实战:用requests与pandas将书单自动导出Excel 简介《豆瓣图书爬虫以及图书数据xlsx》是一份面向Python爬虫与数据分析初学者的实操型资源包围绕豆瓣图书信息抓取与整理展开。压缩包内共12个文件包含1个爬虫主程序与11个按主题拆分的xlsx表格覆盖数学、编程、金融、心理、计算机、机器学习等数十个图书类目整体体积约12.34MB。爬虫代码完整演示了使用requests请求网页、借助BeautifulSoup或PyQuery解析HTML、定位分类书名作者出版社评分等字段的采集流程xlsx数据文件则能用来练习pandas的数据清洗、统计聚合和Excel读写进而在推荐系统、市场研究或个人收藏管理中进行应用。目前已有2051人浏览学习。对于希望理解真实站点页面结构、掌握爬虫到存储全流程或直接使用现成豆瓣图书分类数据进行训练与分析的读者这套资源可提供清晰的一站式参考。1. 从“想看书”到“拿到xlsx”这个豆瓣图书爬虫到底解决了什么拆过不少爬虫项目但像这个豆瓣图书爬虫一样把“数据采集”和“数据清洗”直接做到一个文件里交付的确实不多见。它解决的场景很实在你对照着想读列表想整理一份评分、作者、简介都全的电子表格手动复制粘贴能搞到凌晨。这个项目直接输入一个豆瓣图书列表页的URL就能自动抓取页面上的书名、作者、评分、评价人数并且调用pandas组件清洗后导出为一份结构清晰的xlsx文件。对于需要给部门同事做数据报表的朋友或者平时爱做书单管理的工具党来说它是个不错的参考想改哪里改哪里。2. 技术选型与目录结构一次“够用就好”的请求封装这个项目虽然叫爬虫但它没有用Scrapy这类重框架而是老老实实选了requests BeautifulSoup pandas的轻量组合。这里面的取舍逻辑值得新入行的开发者琢磨一下。2.1 为什么是 requests BeautifulSoup 而不是 ScrapyScrapy 确实强大自带并发调度、报文去重、中间件扩展但它的学习曲线和工程复杂度有点陡。这个项目的目标是抓取豆瓣图书列表页并导出xlsx核心动作就是“请求 → 解析 → 落盘”。用Scrapy等于给一台自行车装了涡轮增压器不仅跑不快反而会因为框架本身的异步机制引入一堆配置上的麻烦。requests的长处在于直观一个get方法把响应拿回来状态码、headers、编码一目了然出了问题走调试器就行。BeautifulSoup配合lxml解析器在静态页面上的表现很稳定定位div、span比写正则表达式要省心得多。我一般会在项目里先写一个最小可运行的版本跑通后再考虑是不是要上框架。2.2 项目目录结构与依赖清单拿到手的时候整个项目非常清爽核心文件就四个新手上手零压力。目录结构大概是这样的douban_book_crawler/ ├── main.py ├── spider.py ├── parser.py ├── exporter.py └── requirements.txtmain.py是项目入口负责串联三个模块先调用spider.py里的get_html(url)方法拿HTML源码再把源码交给parser.py解析出书目的结构化字典列表最后通过exporter.py保存成xlsx文件。requirements.txt里锁定了依赖库的版本范围避免因为第三方库升级导致翻车。requirements.txt内容如下requests2.25.1 beautifulsoup44.9.3 lxml4.6.3 pandas1.2.0 openpyxl3.0.7注意这里的openpyxl它是pandas写出xlsx文件的后端引擎。如果不装它调用df.to_excel(path, indexFalse)会直接报ModuleNotFoundError这种坑新手很容易踩到。3. 核心模块拆解从请求到数据落盘的完整链路整个项目最关键的部分在于数据从URL到xlsx的流转过程。这一章我们按实际执行顺序把三段核心代码逐一剖开看它每一步做了什么以及为什么这样写。3.1 请求模块睡眠、重试与请求头伪装spider.py模块的职责很纯粹就是输入一个URL输出HTML字符串或者输出None。第一版代码可能只是简单requests.get(url)但实际跑起来会被封得怀疑人生。# spider.py import time import random import requests def get_html(url, retries3): # 构造完整的请求头模拟真实浏览器访问 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.8,zh-TW;q0.6,en-US;q0.4,en;q0.2, Connection: keep-alive } # 利用 Session 保持连接后续请求会携带同样的 cookies session requests.Session() session.headers.update(headers) for attempt in range(retries): try: # 添加随机延时降低请求频率 time.sleep(random.uniform(1.0, 3.0)) resp session.get(url, timeout10) if resp.status_code 200: resp.encoding utf-8 return resp.text elif resp.status_code 403: print(f请求被拒绝403第 {attempt 1} 次重试...) else: print(f返回状态码{resp.status_code}无法处理) except requests.RequestException as e: print(f网络异常{e}第 {attempt 1} 次重试...) time.sleep(2) return None关键参数是random.uniform(1.0, 3.0)这行代码让每次请求之间间隔1到3秒随机浮动。若不加这行连续高频请求会在几分钟内换来一个403封禁回执。headers里的User-Agent是伪装成Chrome浏览器的标识有些服务器会拒绝没有UA的脚本请求。session requests.Session()之所以有效是因为豆瓣的页面交互依赖cookie维持会话状态用Session对象能自动存储服务端下发的cookie并在后续的每一次请求中带上。这也解释了为什么很多人用requests.get单发请求会频繁触礁。3.2 解析模块BeautifulSoup 精准定位书名与评分拿到HTML源码之后下一步就是解析。我在parser.py里用到了BeautifulSoup的CSS选择器功能因为列表页的结构相对稳定用select_one和select可以很高效地定位目标元素。# parser.py from bs4 import BeautifulSoup def parse_book_list(html): soup BeautifulSoup(html, lxml) books [] # 豆瓣列表页的每个书目块都包含在 classitem 的 li 标签里 items soup.select(li.item) if not items: # 如果列表为空可能页面结构有变化或者请求到了验证页面 print(没有解析到任何内容请检查HTML结构) return books for item in items: # 书名在第一个 classtitle 的 a 标签内 title_tag item.select_one(.title a) title title_tag.text.strip() if title_tag else 无标题 # 作者信息在 classauthor 的 span 标签内 author_tag item.select_one(.author) author author_tag.text.strip() if author_tag else 未知作者 # 评分在 classrating_nums 的 span 标签内 rating_tag item.select_one(.rating_nums) rating float(rating_tag.text.strip()) if rating_tag else 0.0 # 评价人数在 classpl 的 span 标签内但通常和出版社信息混在一起 rating_count_tag item.select_one(.star .pl) rating_count rating_count_tag.text.strip() if rating_count_tag else 0人评价 # 摘要描述在 classquote 或 classinq 的 span 标签内 summary_tag item.select_one(.quote .inq) summary summary_tag.text.strip() if summary_tag else books.append({ 书名: title, 作者: author, 评分: rating, 评价人数: rating_count, 简介: summary }) return books上面的代码里item.select_one(.title a)代表在每一个classitem的节点下再查找匹配CSS样式.title a的子元素。text.strip()是去除文本前后的空白字符避免后期写入Excel时出现莫名缩进。rating_nums的文本内容是一个数字字符串这里直接转成float方便后续数据排序和比较。值得注意的是评价人数的解析这个字段的HTML结构经常和出版社、出版日期混在同一个span标签里。实际处理时通常还需要额外的正则表达式把“xxx人评价”提取出来这个项目直接保留原文为的是避免过度处理导致信息丢失。3.3 数据存储模块pandas 一键生成 xlsx数据解析完成之后exporter.py负责把结构化的list[dict]转换为Excel表格。这里用pandas的核心原因在于它的DataFrame对象天然支持从字典列表初始化并且自带了强大的清洗功能。# exporter.py import pandas as pd def save_to_excel(books, output_pathbooks.xlsx): if not books: print(没有可保存的数据请检查网络或解析逻辑) return # 将字典列表初始化为 DataFrame 对象 df pd.DataFrame(books) # 数据清洗丢弃全空的列避免写入无用的表头 df.dropna(axis1, howall, inplaceTrue) # 将空字符串或 NaN 替换为默认值保证Excel读取时不会出现空单元格 df.fillna(未知, inplaceTrue) # 重新设置索引并从 1 开始计数 df.index df.index 1 # 使用 openpyxl 引擎导出 xlsx try: df.to_excel(output_path, indexTrue, engineopenpyxl) print(f成功保存 {len(df)} 条数据到 {output_path}) except Exception as e: print(f保存文件失败{e})df.dropna(axis1, howall, inplaceTrue)这一行是我比较欣赏的细节。它遍历所有列如果某一列全部都是空值就直接丢弃。这防止了某些列表页因为个别书籍缺少简介字段导致整列导出后都是“未知”的情况。df.to_excel中的engineopenpyxl是明确指定xlsx写入引擎。如果不指定pandas可能会调用xlwt来写xls文件而旧版xlwt不支持xlsx格式会直接报错。每次跑完代码看到控制台输出“成功保存 42 条数据”心里那块石头才算落地。4. 运行与验证三步跑起来并检查数据对不对代码拆完读者最关心的还是怎么跑起来。这个项目的运行门槛很低三个步骤能走完前提是你机器上装好了Python 3.8以上的环境。4.1 环境配置与一键启动首先在项目根目录打开终端创建虚拟环境是个好习惯可以避免污染系统全局Python环境。在Windows下执行python -m venv venv venv\Scripts\activate pip install -r requirements.txt在macOS或Linux下激活命令是source venv/bin/activate。安装完依赖后项目主入口main.py会请求固定配置的URL。默认通常抓取某一标签下的热销榜例如收集“小说”类当前评分前20的书籍信息。python main.py运行中控制台会打印每一轮的请求状态。正常情况下能看到类似“解析到 20 本书已保存到 books.xlsx”的日志。如果这里出现了403或者Timeout多半是你当前的网络出口IP已经被目标站点标记可以尝试更换网络或者检查下一章的避坑指南。4.2 验证 xlsx 输出与字段完整性跑完之后别急着关终端打开生成的books.xlsx核对一下表头和数据。正常情况下第一行是序号接着是书名、作者、评分、评价人数、简介五列。我一般会用pandas回读一下文件确认数据完整性import pandas as pd # 回读 xlsx 文件 df_check pd.read_excel(books.xlsx, index_col0) print(df_check.columns.tolist()) print(df_check.shape) print(df_check.head())若输出的字段列表符合预期且shape不为空说明整个链路已经打通。如果书名字段出现了大量“未知”大概率是解析逻辑错位需要回到parser.py检查选择器是否命中了当前页面结构。4.3 调试技巧抓包与 print 的妙用很多新手在全流程跑不通时喜欢直接给requests.get加上verifyFalse这是治标不治本的做法。更高效的排查手段是先把HTML源码保存到本地做离线解析。# 快速保存一份 HTML 快照方便定位问题 html_text get_html(https://目标列表页URL) if html_text: with open(debug.html, w, encodingutf-8) as f: f.write(html_text)保存后用浏览器直接打开debug.html查看当前页面是否包含了预期要抓取的书目元素。这是踩坑经验里最万能的一招它能直接区分“请求没拿到数据”和“请求拿到了但解析规则不对”这两种完全不同的错误路径。5. 实战避坑那些年我踩过的豆瓣爬虫的坑单独开一章写坑是因为在这个项目上翻车的次数实在太多。整理出四个最典型的踩坑记录按“现象 → 原因 → 解决”的方式写出来希望能让后来者少走点弯路。5.1 403 Forbidden 专项不是你的代码错了是请求头没伪装好现象代码运行后get_html函数返回了None控制台打印“请求被拒绝403第 1 次重试...”重试多次仍是403。原因requests.get默认携带的User-Agent是python-requests/2.x.x这种标识太过明显目标服务器可以直接识别为自动化程序并拒绝访问。解决必须在每次请求时显式构造headers并且在Session级别统一更新。这个项目最初版本只对get调用传了headers但后续封禁率很高。改成session.headers.update(headers)之后成功率直线上升。记住一点凡是遇到403优先检查请求头别急着调延时。5.2 数据解析丢失当网页改版选择器就失灵了现象某一天运行脚本发现生成的xlsx文件中“简介”字段全是默认值“未知”但其他字段正常。原因目标网站在某个版本更新后调整了摘要模块的CSS类名把原来的.quote .inq改成了.quote .intro。由于解析代码还停留在旧版所以select_one始终查不到结果返回了None。解决这时候debug.html又派上用场了。把最新页面保存到本地手动查一下inq这个class还存在不存在。如果改成了其他名字直接更新parser.py中的选择器即可。爬虫代码不是一劳永逸的维护成本就在这里。5.3 xlsx 写入乱码pandas 与 openpyxl 的爱恨情仇现象生成books.xlsx后打开Excel发现中文书名全部显示为乱码而控制台打印的title是正常的。原因写入xlsx时编码没有正确处理。常见情况是resp.encoding没有设置导致requests猜测页面编码错误拿到的HTML字符串本身就已经是乱码。解决在spider.py请求模块中强制指定resp.encoding utf-8。这是一个不起眼但致命的小步骤。同理在save_to_excel里用df.to_excel默认的ut-8编码就能让Excel正常解析不需要额外设置encoding参数。5.4 翻车实例请求频率过高导致 IP 被封现象连续跑了几百条URL之后控制台开始持续输出403即使带上完整headers也不管用浏览器访问同一站点也弹出了验证码。原因请求频率太高虽然单次间隔设了1到3秒但24小时累计请求量仍然触发了站点的反爬自动封禁策略。解决除了调整延时到random.uniform(3, 7)更稳妥的做法是在get_html里增加一个最大重试次数的概念。一旦连续失败至少6次就自动结束整个爬虫任务而不是无情地重试。这个项目里retries3是保守值正式抓取建议设置成5并增加当日总请求数的判断逻辑。6. 二次开发指南让这个爬虫长成你想要的样子这个项目的架构留好了扩展口二次开发的空间很大。如果只是想要爬下来的数据跑一遍就行但如果你想要更贴合自己的需求下面这几个方向值得动动手。6.1 扩展多页码与自动翻页目前这个项目只采集单页数据想采集更多只需给main.py加一个循环。豆瓣列表页的翻页规律很简单URL末尾加上?start0typeT其中start每页递增25。改造后可以用for循环生成多个URL并对上一页的数据进行去重# 在 main.py 中添加多页爬取逻辑 base_url https://目标站点/tag/小说 all_books [] for page in range(4): url f{base_url}?start{page * 25}typeT html get_html(url) if html: page_books parse_book_list(html) # 简单的书名去重避免翻页时出现重复条目 existing_titles {book[书名] for book in all_books} new_books [book for book in page_books if book[书名] not in existing_titles] all_books.extend(new_books)参数start的含义是书架起始偏移量配合typeT固定排序类型。这样改完后一次运行能拿到四个页面的数据效率翻倍。6.2 增加字段抓取详情页里的 ISBN 和出版社列表页里的信息终究有限想拿到更完整的书目信息比如ISBN号和出版社就需要继续深入详情页。这里的思路是先用列表页拿到详情链接再逐条进入详情页解析# 在 parser.py 中增加详情页字段的解析 def parse_detail(html): soup BeautifulSoup(html, lxml) isbn_tag soup.find(span, textISBN:) isbn isbn_tag.find_next_sibling().text.strip() if isbn_tag else 未知ISBN publisher_tag soup.find(span, text出版社:) publisher publisher_tag.find_next_sibling().text.strip() if publisher_tag else 未知出版社 return isbn, publisher需要注意的是进入详情页抓取会显著增加请求量务必把random.uniform的延时调高到3秒以上不然大概率触发封禁。这也是我在血泪经验里换来的教训。6.3 接入定时任务实现自动化追更如果你想用它来维护一个“每周新书关注榜”可以把它写成定时任务。在Linux上配置crontab每天凌晨两点跑一次0 2 * * * cd /path/to/douban_book_crawler /usr/bin/python3 main.py在Windows下也能通过任务计划程序设置触发器。定时任务跑完后用pandas读入上次的xlsx文件跟本次结果做一次df.merge把新增的图书用颜色高亮标记出来这就是一个简易的“新品追踪器”了。每次改完配置我都强制走一遍“备份原xlsx → 全量抓取 → 对比数据差异”的流程确保不会把历史数据弄丢。这个项目最大的价值在于它不是一个“一次性爬虫”而是一个能落地的数据工具。希望这份拆解能帮你快速跑通并沉淀出属于你自己的效率工具希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/18 12:57:44

MCP协议实战:从AI工具接入到模型上下文管理的完整指南

1. “USB接口”这个比喻是怎么来的:先看看AI接工具的现状1.1 没有MCP之前,接入一个工具有多麻烦上一份工作里,我所在的平台团队维护着十几套内部工具。每回有人想让人工智能助手去查订单、查工单、查知识库,基本都要走一遍“申请权…

2026/10/18 12:57:44

网络计划技术详解:关键路径法、PERT与Python自动计算

简介:项目管理中网络计划技术的教学课件,适合建筑施工组织与管理、工程管理及相关专业师生使用。内容完整覆盖网络计划基本概念、双代号网络图与单代号网络图的符号构成与绘制规则,结合箭线、节点、虚工作、紧前/紧后/平行工作等关键知识点&a…

2026/10/18 12:52:44

SolidEdge卡死不用强杀:外部发送ESC键方案

1. 为什么需要从窗口外部发送ESC键1.1 SolidEdge的典型卡死场景,这一点先讲透用SolidEdge做设计的人,多少都经历过那种“界面灰着、鼠标转圈、点哪都没反应”的时刻。大装配体里点一下更新,整机模型开始重建特征,成百上千个约束、…

2026/10/18 14:22:52

Vue+Node.js电子外设商城系统架构设计与实现

这个项目标题拿到手里,第一反应是"又一个商城系统",但真把它拆开看,"电子外设"这四个字才是关键。我前后做过几个商城类的项目,从图书到生鲜都碰过,电子外设这个品类在商品建模、订单流程、库存管…

2026/10/18 14:22:52

华为流程分级L1-L6深度拆解:从业务域到任务的操作指南

简介:这份PDF资料聚焦华为流程管理体系的L1至L6层级划分,面向企业管理者、流程架构师及希望系统学习流程建设的从业者,帮助解决流程框架搭建、模块设计与建模方法论等核心问题。资源包内含1个PDF文件,大小约2.53MB,内容…

2026/10/18 14:22:52

用代码织出自然纹理:artcraft如何将手工质感转化为算法逻辑

1. 项目概述:artcraft 到底在做什么artcraft 这个名字听起来很直接——art 加 craft,艺术加工艺。但真正上手做这个项目之后,我才意识到这两个词合成在一起的分量:它不是一个单纯的生成艺术工具,也不是一个传统手工艺的…

2026/10/18 14:22:52

模拟一个充值送token怎么保证幂等?一致性?原子性?

先分别解释: 幂等性:重复执行不会重复送 Token 原子性:要么全部成功,要么全部失败(即充值和token增加不能分开) 一致性:充值订单状态、token 账户、赠送流水三方数据对齐 Demo落地&#xff1…

2026/10/18 14:17:52

AI辅助学术论文写作全流程:7款工具从选题到查重实战拆解

帮人改论文这件事干得多了,会发现一个规律:大部分初稿的问题,根本不在“知识不够”,而在“表达没到位”。选题方向有了,资料也查了一大堆,落笔时却像挤牙膏,一句一句往外蹦;好不容易…

2026/10/18 0:01:50

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/18 0:01:50

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑