开发记录:豆瓣电影榜单爬虫
概述旨在爬取豆瓣电影榜单https://movie.douban.com/chart中各电影的详细信息包括名称、年份、片长、类型、导演、演员和剧情简介并将数据保存为 CSV 文件。开发过程中逐步完善了请求头、XPath 解析、异常处理、数据持久化及反爬策略。版本迭代记录v0.1 (2026-07-20) - 基础框架搭建文件01_网络机器人-电影榜单.py主要更新定义基础请求头User-Agent、Referer 等。实现榜单页请求与状态码检查。使用lxml解析 HTML通过 XPath 定位电影条目//table[width100%]。预留get_movie_info()和save_all_movies()函数仅pass占位。遍历电影条目提取详情页链接并打印未调用详情函数。存在问题未实现详情页数据提取all_movie列表始终为空。缺少异常处理与重试机制。无随机延时容易被反爬。v0.2 (2026-07-22) - 详情页解析与数据保存文件02_网络机器人-电影榜单.py主要更新完善get_movie_info()发送详情页请求解析名称、年份、类型、导演、描述使用固定 XPath。实现save_all_movies()将数据写入 CSV 文件指定字段名。扩展全局HEADERS加入 Cookie、Accept-Encoding 等模拟真实浏览器。主函数中调用详情函数并打印调试信息。遗留缺陷关键 bugall_movie列表未被填充未执行append导致 CSV 始终只有表头。XPath 路径脆弱仅依赖单一表达式豆瓣页面结构变化时易失效。无请求失败重试网络波动可能中断程序。v0.3 (2026-07-25) - 健壮性与容错增强文件03_网络机器人-电影榜单.py主要更新修复核心 bug在main()循环中将获取到的movie_info通过all_movie.append(movie_info)正确添加到列表。增加异常处理对requests.get()捕获异常避免程序崩溃。增加内容长度校验详情页响应小于 1000 字节时视为被反爬打印前 500 字符辅助调试。扩展 XPath 备选路径针对名称、年份、类型、导演、描述均提供多种定位方式提高适应性。引入随机延时每部电影请求后time.sleep(random.uniform(1.0, 2.5))降低请求频率。自动创建 CSV 输出目录os.makedirs避免路径不存在错误。效果程序可完整获取榜单中所有电影的详情数据并正确写入 CSV。面对页面结构调整或临时反爬时容错性显著提升。v0.4 (2026-07-28) - 字段扩展与数据丰富文件04_网络机器人-电影榜单.py主要更新新增片长v:runtime和演员字段丰富电影信息维度。演员提取采用主演标签后的a文本多个演员用 / 连接。更新 CSV 列名[名称, 年份, 片长, 类型, 导演, 演员, 描述]。优化描述提取优先级优先使用v:summary其次span[classall]最后旧版路径。改进意义采集的数据更加全面满足后续分析或展示需求。字段结构更贴近电影主页实际内容。v0.5 (2026-07-30) - 最终稳定版本内容同 v0.4文件05_网络机器人-电影榜单.py说明此版本与 v0.4 代码完全一致未做功能改动。命名调整可能为了版本管理统一实际以 v0.4 为最终发行版。最终成果总结import requests import csv from lxml import html import os import time import random MOVIE_LIST_FILE csv_data/movie_list.csv DOUBAN_BASE_URL https://movie.douban.com DOUBAN_TOP_URL https://movie.douban.com/chart HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Cookie: ll118201; bidmV59O_L0Z88; _pk_id.100001.4cf6906f4c077c4e286a.1784944501.; _vwo_uuid_v2DA6D5EB58DC057096520FFE47A8EECEFD|78ce2ecada7a16f9034a20c3961fe216; dbcl2296323525:ylL/ts6TrE; push_noty_num0; push_doumail_num0; cksfI2; _pk_ref.100001.4cf6%5B%22%22%2C%22%22%2C1785149957%2C%22https%3A%2F%2Fcn.bing.com%2F%22%5D; _pk_ses.100001.4cf61; __utma30149280.570823089.1784944501.1784944501.1785149957.2; __utmb30149280.0.10.1785149957; __utmc30149280; __utmz30149280.1785149957.2.2.utmcsrcn.bing.com|utmccn(referral)|utmcmdreferral|utmcct/; __utma223695111.164060955.1784944501.1784944501.1785149957.2; __utmb223695111.0.10.1785149957; __utmc223695111; __utmz223695111.1785149957.2.2.utmcsrcn.bing.com|utmccn(referral)|utmcmdreferral|utmcct/; ap_v0,6.0; frodotk_dbb2d4bee9cf2e9d30187e3cbd6d8bd790, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1, Sec-Fetch-Dest: document, Sec-Fetch-Mode: navigate, Sec-Fetch-Site: same-origin, } def get_movie_info(movie_info_url): 获取单部电影的详细信息 print(f正在请求详情页: {movie_info_url}) try: movie_response requests.get(movie_info_url, headersHEADERS, timeout15) except Exception as e: print(f请求异常: {e}) return None if movie_response.status_code ! 200: print(f请求失败状态码: {movie_response.status_code}) return None if len(movie_response.text) 1000: print(内容过短可能被反爬前500字符) print(movie_response.text[:500]) return None movie_document html.fromstring(movie_response.text) # 提取字段使用多种备选 XPath # 名称 name_list movie_document.xpath(//h1/span[1]/text()) if not name_list: name_list movie_document.xpath(//*[idcontent]/h1/span[1]/text()) name name_list[0].strip() if name_list else # 年份 year_list movie_document.xpath(//h1/span[2]/text()) if not year_list: year_list movie_document.xpath(//*[idcontent]/h1/span[2]/text()) year year_list[0].strip() if year_list else # 时长 time_list movie_document.xpath(//div[idinfo]//span[propertyv:runtime]/text()) time time_list[0].strip() if time_list else # 类型 type_elems movie_document.xpath(//span[contains(text(), 类型)]/following-sibling::a/text()) if not type_elems: type_elems movie_document.xpath(//*[idinfo]/span[5]/text()) type_str ,.join([t.strip() for t in type_elems if t.strip()]) # 导演 director_elems movie_document.xpath(//span[contains(text(), 导演)]/following-sibling::a/text()) if not director_elems: director_elems movie_document.xpath(//*[idinfo]/span[1]/span[2]/a/text()) director director_elems[0].strip() if director_elems else # 演员 actor_elems movie_document.xpath(//div[idinfo]//span[contains(text(), 主演)]/following-sibling::span/a/text()) if actor_elems: actors / .join([name.strip() for name in actor_elems]) else: actors # 描述剧情简介 desc_elems movie_document.xpath(//span[propertyv:summary]/text()) if not desc_elems: desc_elems movie_document.xpath(//div[idlink-report]//span[classall]/text()) if not desc_elems: desc_elems movie_document.xpath(//*[idlink-report-intra]/span[1]/text()) description .join([d.strip() for d in desc_elems if d.strip()]) movie_info { 名称: name, 年份: year, 片长: time, 类型: type_str, 导演: director, 演员: actors, 描述: description, } print(movie_info) # 控制台显示当前电影信息 return movie_info def save_all_movies(all_movies): 保存所有电影信息到 CSV 文件自动创建目录 dir_name os.path.dirname(MOVIE_LIST_FILE) if dir_name and not os.path.exists(dir_name): os.makedirs(dir_name) print(f已创建目录: {dir_name}) print(f准备写入 {len(all_movies)} 条数据到 CSV) # 调试显示数据条数 with open(MOVIE_LIST_FILE, modew, newline, encodingutf-8) as file: writer csv.DictWriter(file, fieldnames[名称, 年份,片长, 类型, 导演, 演员,描述]) writer.writeheader() if all_movies: writer.writerows(all_movies) print(f成功写入 {len(all_movies)} 条数据) else: print(警告没有数据可写入仅创建了表头) def main(): print(正在获取榜单页...) try: response requests.get(DOUBAN_TOP_URL, headersHEADERS, timeout20) except Exception as e: print(f请求榜单页异常: {e}) return print(f状态码: {response.status_code}, 内容长度: {len(response.text)}) if response.status_code ! 200 or len(response.text) 100: print(榜单页获取失败或内容为空) return document html.fromstring(response.text) # 定位电影条目多种备选 movie_list document.xpath(//table[width100%]) if not movie_list: movie_list document.xpath(//div[classitem]) if not movie_list: movie_list document.xpath(//*[idcontent]/div/div/div/div/table[width100%]) print(f找到 {len(movie_list)} 个电影条目) all_movie [] # 用于收集所有电影数据 for idx, movie in enumerate(movie_list, 1): # 提取详情页链接 url_elements movie.xpath(.//a[classnbg]/href) if not url_elements: url_elements movie.xpath(.//a/href) if not url_elements: print(f第 {idx} 个条目未找到链接跳过) continue movie_info_url url_elements[0] if not movie_info_url.startswith(http): movie_info_url DOUBAN_BASE_URL movie_info_url # 获取电影信息 movie_info get_movie_info(movie_info_url) if movie_info: all_movie.append(movie_info) # --- 关键修复添加数据到列表 print(f当前已收集 {len(all_movie)} 部电影) else: print(f第 {idx} 个详情页获取失败返回 None) # 随机延时避免请求过快 time.sleep(random.uniform(1.0, 2.5)) # 保存数据 save_all_movies(all_movie) if __name__ __main__: main()功能稳定爬取豆瓣电影榜单当前榜单页所有电影的 7 项信息并保存为csv_data/movie_list.csv。技术要点请求头模拟真实浏览器含 Cookie降低反爬风险。多 XPath 备选策略适配页面结构变化。异常捕获与超时设置增强鲁棒性。随机延时控制请求频率避免 IP 被封。自动目录创建提升用户体验。已知限制仅处理榜单第一页/chart未实现分页爬取。Cookie 可能过期需定期更新。描述字段可能包含过长文本CSV 显示需注意。后续优化建议实现多页榜单爬取通过?start...参数。将 Cookie 独立为配置文件便于更新。增加日志记录模块替代print调试输出。使用代理池轮换 IP进一步提高稳定性。

相关新闻

3个核心功能+5个实用技巧:让你的华硕笔记本告别Armoury Crate臃肿体验

3个核心功能+5个实用技巧:让你的华硕笔记本告别Armoury Crate臃肿体验

3个核心功能5个实用技巧:让你的华硕笔记本告别Armoury Crate臃肿体验 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivoboo…

2026/10/5 17:34:45 阅读更多 →
LM393电压比较器深度解析:从核心参数到经典应用电路设计

LM393电压比较器深度解析:从核心参数到经典应用电路设计

1. 项目概述:从“比较”开始,聊聊LM393这颗经典芯片在电子设计的江湖里,有一种器件,它不像MCU那样能编程,也不像运放那样能放大,但它却是实现逻辑判断、状态切换的“裁判官”——这就是电压比较器。而LM393…

2026/10/9 9:29:21 阅读更多 →
终极Windows兼容性修复:让《恶霸鲁尼:奖学金版》在Win10/11稳定运行

终极Windows兼容性修复:让《恶霸鲁尼:奖学金版》在Win10/11稳定运行

终极Windows兼容性修复:让《恶霸鲁尼:奖学金版》在Win10/11稳定运行 【免费下载链接】SilentPatchBully SilentPatch for Bully: Scholarship Edition (fixes crashes on Windows 10) 项目地址: https://gitcode.com/gh_mirrors/si/SilentPatchBully …

2026/10/12 16:31:39 阅读更多 →

最新新闻

MySQL 从入门到实战(04):SQL 基础与一条查询的执行流程

MySQL 从入门到实战(04):SQL 基础与一条查询的执行流程

SQL 基础与执行流程 本篇所有 SQL 在本机 MySQL 8.0.46 实测。 核心问题:你敲下回车后,MySQL 到底做了什么? 理解这条链路,才知道索引该加在哪、为什么有些 SQL 慢。 一、一条 SQL 的完整生命周期 #mermaid-svg-o1uI88gGmZdgQ5sW{font-family:"trebuchet ms",ver…

2026/10/14 3:25:35 阅读更多 →
栈与队列:从原理到应用

栈与队列:从原理到应用

文章目录1. 引言2. 栈(Stack):后进先出的线性表3. 队列(Queue):先进先出的线性表4. 代码实现示例栈的实现代码5. 栈与队列的对比总结6. 总结与学习建议1. 引言 栈和队列是计算机科学中最基础、也最常用的两…

2026/10/14 3:25:35 阅读更多 →
# 京东客服离线自动回复怎么设置?叮当小宝CS补上夜间与高峰的接待缺口

# 京东客服离线自动回复怎么设置?叮当小宝CS补上夜间与高峰的接待缺口

京东的考核里,回复速度和应答率都算分。客服下班、午休或者大促期间排队太长时,买家的咨询不能一直挂着——离线自动回复就是兜底的那一道。这篇把入口、生效范围和常见的「配了但没用」讲清楚,再给一套补位做法。## 先分清两个入口&#xff…

2026/10/14 3:25:35 阅读更多 →
MySQL 从入门到实战(03):数据类型与建表规范

MySQL 从入门到实战(03):数据类型与建表规范

数据类型与建表规范 本篇所有 SQL 在本机 MySQL 8.0.46 实测。 核心问题:为什么金额要用 DECIMAL 而不是 FLOAT?VARCHAR(255) 是不是越大越好? 一、一个真实的踩坑:浮点数存金额 先看实测结果: CREATE TABLE demo.t_money_bad (id INT PRIMARY KEY AUTO_INCREMENT,amoun…

2026/10/14 3:25:35 阅读更多 →
COBOL报表开发实战:物理布局、文件绑定与状态机生命周期

COBOL报表开发实战:物理布局、文件绑定与状态机生命周期

简介:本资源是一份面向COBOL初学者与金融/政务系统维护人员的报表开发实践指南,聚焦COBOL环境下的结构化报表生成全流程。内容以真实可运行的rpt001程序为核心,完整呈现ID、Environment、Data(含File Section与Working-Storage&am…

2026/10/14 3:25:35 阅读更多 →
TikTok Shop 多店铺:平台侧哪些信息会被交叉比对

TikTok Shop 多店铺:平台侧哪些信息会被交叉比对

一、先说结论:官方对「关联」的定义,比你想的宽 TikTok Shop 官方在卖家学习中心有一篇《关联账号风险指南》,原文写得很直白——平台对关联账号的判定,通常不是依据单一的重合信息,而是多项风险信号综合判断&#xff…

2026/10/14 3:24:35 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →