开发记录:豆瓣电影榜单爬虫

发布时间:2026/11/11 16:26:35

开发记录:豆瓣电影榜单爬虫 概述旨在爬取豆瓣电影榜单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进一步提高稳定性。
延伸阅读

更多相关文章

2026/11/11 16:26:35

Linux下PHY寄存器访问:从原理到实战的调试指南

1. 项目概述:为什么要在Linux下访问PHY寄存器?搞嵌入式或者网络驱动开发的朋友,应该都跟PHY芯片打过交道。PHY,这个负责把数字信号和物理线路上的模拟信号互相转换的小东西,是任何有线网络设备的基石。在Linux系统里&a…

2026/11/9 6:29:29

电容式传感器原理、电路设计与工程实践全解析

1. 从“触摸”到“感知”:电容式传感器的核心价值你可能每天都在和它打交道,却未必真正了解它。从智能手机的触摸屏,到汽车门把手的无钥匙进入,再到工厂里精确测量液位的传感器,背后都有一个共同的技术核心——电容式传…

2026/11/9 10:34:51

STM32程序下载全解析:串口ISP、ST-LINK调试与Utility烧录实战指南

1. 项目概述:STM32程序下载的三种核心路径对于每一位STM32开发者来说,给芯片“灌入”程序是项目从代码到实物的第一步,也是最基础、最频繁的操作。我刚接触STM32时,面对五花八门的下载方式也犯过迷糊:为什么有的要用串…

2026/11/11 16:24:56

滨州高压电工作业证网上查询,外省证书怎么转过来?

滨州高压电工作业证网上查询,外省证书怎么转过来? 之前考的证在外省能不能转过来?这个问法,我听得太多了,尤其是像你这样在滨州想从事高压电工作业的朋友,手里拿着外地的证,却不知道能不能在滨州用。别急,今天我就来给你讲清楚,怎么查滨州高压电工作…

2026/11/11 16:24:56

滨州高压电工作业证在哪考?怕考不过白交培训费?这篇讲透

滨州高压电工作业证在哪考?怕考不过白交培训费?这篇讲透 怕考不过白交培训费?别急,滨州高压电工作业证在哪考,这事儿咱们得讲明白。很多电工师傅都遇到过这种情况,花了钱报名培训,结果考试一塌糊涂,证书没拿到,钱也打水漂。其实,只要搞清楚流程、准…

2026/11/11 16:24:56

电工证考试考几门郑州报考避坑指南

电工证考试考几门郑州报考避坑指南 实操考试心里没底怕挂科,这是很多准备考电工证的学员最真实的状态。尤其是对于在三门峡水利工程从业的朋友来说,电工证不仅是上岗的门槛,更是保障施工安全的重要前提。但很多人对电工证考试到底考几门、需要哪些准备、怎…

2026/11/11 16:19:56

建筑施工特种工证办理怎么考?官方回应实操考试不再难

建筑施工特种工证办理怎么考?官方回应实操考试不再难 实操考试心里没底怕挂科?别急,官方回应来了,2024年特种作业证考试新规已经明确,实操考试难度适中,只要掌握方法,完全可以通过。建筑施工特种工证办理流程虽然繁琐,但只要一步一个脚印,其实并…

2026/11/11 12:42:59

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/11 0:02:26

考低压电工证通过率考试真题与备考技巧

低压电工证通过率高不高?学历不高也能考,值不值得考? 你是不是总听说电工证能帮你找高薪工作,但又担心自己学历不够,考不上?其实,低压电工证考试门槛并没有你想象中那么高,关键是方法对不对。这篇文章就告诉你, 考低压电工证通过率到底有多高 ,…

2026/11/11 0:02:26

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名 你是不是也和我一样,刚毕业就听说低压电工证学时必须达标才能上岗?可一打听,发现报名机构五花八门,价格参差不齐, 不知道去哪报名怕被中介坑…

2026/11/10 20:08:15

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

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

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

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