资讯详情

零基础Python爬虫:requests+XPath+openpyxl抓取豆瓣影评

📅 2026/10/9 9:18:10 | 华诺云谱 👁 阅读
零基础Python爬虫:requests+XPath+openpyxl抓取豆瓣影评
最近不少朋友私信问我同一个问题网上Python爬虫教程一大堆为什么照着别人的代码跑豆瓣影评不是各种报错就是被拒绝访问偶尔能跑通一次数据又存不进Excel我复盘了一下问题通常集中在三块环境没配好、页面解析逻辑没讲透、最后落盘的时候踩了坑。这篇文章就把整条链路完整拆开——用Python发请求获取豆瓣影评页面用XPath把评论内容从HTML里精准挖出来再用openpyxl写入带格式的Excel表格。我尽量按零基础的标准写每一步的为什么也顺带讲清楚你不需要会正则、不需要懂框架跟着思路一步步走就能跑通。1. 项目拆解一个零基础爬虫真正要解决的四个问题1.1 明确目标和边界我们要爬什么、存什么项目的目标很简单给定一部豆瓣电影的ID获取它的前若干页热门影评把评论者昵称、评分星级、评论时间、有用数和评论正文这几个字段抓下来存进一个能直接双击打开的Excel文件。很多人一上来就写代码结果写一半发现不知道解析什么、存给谁看。我建议动手之前先回答四个问题数据从哪里来、数据结构长什么样、用什么方式存、被目标网站拦截了怎么办。这四个问题想清楚你写的每一行代码都有明确指向调试起来也快得多。从技术角度看这个项目的完整链路是requests请求影评页面HTMLlxml把HTML解析成可查询的节点树XPath按条件定位评论节点最后把清洗过的数据逐行写入Excel。整条链路覆盖了爬虫最常见的四个环节这也是我推荐它作为入门项目的原因——麻雀虽小五脏俱全。1.2 技术选型为什么是requests lxml openpyxlPython爬虫的方案不少新手最容易困惑的是选择太多。我直接说我为什么选这三件套环节工具选择理由网络请求requests语法简单十几行就能发起请求拿到HTML不像Scrapy需要理解框架和中间件页面解析lxml XPath用路径表达式定位节点比正则表达式直观太多不需要记忆各种转义规则数据存储openpyxl能生成真正的.xlsx文件还能设置样式保存后Excel直接打开就能看不是说Scrapy不好、正则不能用而是这个项目是给零基础入门用的工具的上手成本必须低。requests发请求、XPath提取数据、openpyxl写文件三个库的职责非常清晰学完以后你也能清楚地把爬虫这件事拆成几个独立模块。等你理解了这套流程以后再去看Scrapy或pandas会发现它们的核心思路其实是一样的。另外说一下为什么不用csv推荐Excelcsv虽然简单但中文用记事本打开容易乱码也没法加样式和筛选。openpyxl生成的文件是标准的.xlsx格式带表头、带列宽、还能冻结首行给人看或者继续做数据分析都更方便。2. 环境准备零基础也能一次装齐的开发环境2.1 安装Python时最容易忽略的那一步如果你电脑上还没有Python去官网下载3.10或3.11的稳定版本即可。安装时有一件事必须做勾选Add Python to PATH。我见过太多新手代码写好了双击运行却提示python不是内部或外部命令根源就是漏了这一步。安装完成后打开命令行Windows按WinR输cmd输入python回车看到类似Python 3.11.x的版本信息就说明安装成功了。我再多说一句命令行里输完python之后再输入exit()退出交互模式咱们后面要用命令行来装依赖库。IDE这一块我建议新手首选VS Code加Python插件也可以用PyCharm Community版。VS Code更轻量启动快装个插件就能写代码和运行脚本PyCharm对初学者更大而全但启动和索引时间会更长。我个人的经验是如果你只是学Python和爬虫VS Code完全够用别在编辑器上花太多时间纠结。2.2 安装requests、lxml、openpyxl三件套打开命令行执行下面这条命令安装运行本项目所需的三个库pip install requests lxml openpyxl如果下载速度特别慢或者超时可以用国内镜像源加速pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests lxml openpyxl这里顺便解释一下镜像源是什么。pip默认从国外官方源下载而国内网络访问国外服务器偶尔不稳定清华、阿里这些机构做了同步备份你从这些备份地址下载会快很多。这不算什么高深操作但是新手经常会卡在这一步——库装不上后续代码跑不起来又不知道去哪找原因。2.3 验证环境是否真的可用环境装没装好不要等跑项目的时候再验证先用一个三行代码把底探清楚。新建一个Python文件比如叫test_env.py写入import requests import lxml import openpyxl print(所有库导入成功)在命令行里运行python test_env.py如果打印出所有库导入成功说明环境已经全部就绪。这一步值得每次都做它能帮你把代码问题和环境问题先切分开后面报错时心里有数。3. 第一层核心requests请求豆瓣页面的正确姿势3.1 为什么直接爬会被拒User-Agent与请求头的必要性先说一个很多人踩过的坑直接用requests默认配置去请求豆瓣大概率会得到一个不太正常的响应比如403状态码或者一段提示检测到异常请求的页面——豆瓣通过User-Agent就能识别出你不是真实浏览器。User-Agent是HTTP请求头里的一串文本用来告诉服务器我是谁。requests的默认UA是python-requests/版本号暴露了这是一个爬虫脚本。破解方法不是去伪造一个假UA硬刚而是把你的请求头设置得和正常浏览器一样。比如这样headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, }这个UA就是正常Chrome浏览器会带的信息豆瓣会把它当作真实用户放行。理解这一点很重要所谓反爬大多数情况就是服务器在识别你是不是真人而请求头是它最基础的判断依据。我实测下来光是把UA设置正确爬取成功率就能提升一大截。3.2 目标URL是怎么拼出来的豆瓣影评页面的URL结构有规律https://movie.douban.com/subject/{电影ID}/comments?start{偏移量}。先解释电影ID是什么——你在豆瓣点开任意一部电影网址里subject/后面那一串数字就是它的ID比如《肖申克的救赎》是1292052。下面是几个关键参数start从第几条评论开始取第一页是0第二页是20第三页是40以此类推。这是豆瓣的偏移量分页逻辑不是页数。limit每页条数默认20保持20就好。statusP只看看过这部电影的评论过滤掉想看和在看。sortnew_score按最新排序这样能拿到比较新的影评。把URL拼接和请求封装成一个函数后面翻页就方便了def fetch_comments_page(movie_id, start, headers): url fhttps://movie.douban.com/subject/{movie_id}/comments?start{start}limit20statusPsortnew_score resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 return resp.text注意resp.encoding这行不能少。豆瓣页面是UTF-8编码你不手动指定requests可能会用错误的方式解码导致HTML里中文变成乱码后面XPath怎么解析都是空的——很多人卡在这一步根本不是XPath的问题是编码没设置。3.3 判断请求成功的三个信号拿到返回值后先别急着解析先确认这次请求是有效的。我给新手三个判断信号HTTP状态码是200不是403或418返回的HTML文本里有div classcomment-item这样的节点说明影评内容真的在页面里返回文本里没有出现检测到或安全验证之类的字样。用代码快速验证一下html fetch_comments_page(1292052, 0, headers) print(resp.status_code) print(len(html)) print(comment-item in html)如果第三个输出是True恭喜你页面请求成功了可以进入下一环节。如果输出False先回头检查请求头和编码再考虑是不是被风控拦了。我强烈建议每个爬虫项目都做这步请求结果体检它能把问题提前暴露比一股脑写完几十行再debug快得多。4. 第二层核心用XPath精准定位影评数据4.1 XPath基础把网页当成一棵可以精确寻址的树XPath是这门课的第二个门槛但你别被语言两个字吓到。它本质上就是一套在HTML树里找东西的规则核心就三个概念节点路径、条件筛选、属性选取。//div 表示从任意位置找所有div节点//div[classcomment-item] 表示只要class属性等于comment-item的div.//span 表示从当前节点往下找span这个点在解析循环里非常重要代表以当前位置为起点href 表示取某个节点的href属性值。你可以在浏览器里右键点击评论内容选择检查然后按CtrlF打开搜索框输入XPath比如//div[classcomment-item]浏览器会高亮匹配到的节点。这个方法是我最推荐新手用的调试技巧不需要任何额外工具能实时验证你的XPath写没写对。4.2 豆瓣影评页面的结构与XPath推导豆瓣的影评列表每个评论的整体结构大致是这样的一个class为comment-item的div节点里面包含评论者信息区comment-info、评分类节点allstar开头、评论时间comment-time、评论正文short或full、有用数votes。注意大致这个词。网站改版是常有的事所以比起死记下面的XPath你更需要掌握的是定位思路。以2025年初我实测的情况来看比较稳定的定位写法如下from lxml import etree tree etree.HTML(html) items tree.xpath(//div[classcomment-item]) for item in items: author item.xpath(.//span[classcomment-info]/a//text()) star_class item.xpath(.//span[contains(class, allstar)]/class) time item.xpath(.//span[classcomment-time]/title) votes item.xpath(.//span[classvotes]/text()) short item.xpath(.//span[classshort]/text())这里有个非常重要的语法细节XPath里的//和./. 我们在item上继续向下找必须用 .// 而不是 //。.//表示从当前评论节点往下搜索//则表示从整个文档根节点搜索。如果写错成//span就会把所有评论的span全部混在一起提取结果乱套。4.3 text()的常见误用为什么有的text()取不到内容XPath里的text()是新手掉坑的重灾区。它的准确含义是取当前节点的直接文本子节点。所以如果HTML结构长这样span classcomment-info a张三/a /span你写 .//span[classcomment-info]/text() 是取不到张三的因为张三在a标签里面不在span的直接文本里。正确写法是 .//span[classcomment-info]/a/text() 或者更宽容一点用 .//span[classcomment-info]//text()。我建议新手统一用先定位到最细的标签再取text()这个思路尽最大可能避免多层嵌套带来的问题。比如评论者昵称就定位到a标签评论时间就定位到comment-time的title属性这样每一步都清晰可查。4.4 评分、展开评论和空值的细节处理评分字段比较特殊它不是文本内容而是藏在class属性里比如class里有allstar40就代表4星。我们提取到class字符串后用正则把数字抠出来import re star_text .join(star_class) match re.search(rallstar(\d), star_text) star str(int(match.group(1)) // 10) if match else 正则在这里只干一件事从allstar40里提取出40再除以10变成4。这算是本项目里唯一用到的正则如果你完全不想用正则也可以直接判断allstar后面的数字再切片不过正则一行写完更简洁。评论正文还有一个坑部分长评论在列表页默认只显示摘要完整内容在点击展开后才能看到体现在HTML里就是full类节点。所以提取正文时要做兜底——优先取short如果short为空再尝试取fullif short: content .join(short).strip() else: full item.xpath(.//span[classfull]//text()) content .join(full).strip()顺便说一句整页评论里偶尔会混入几条只有有用数但没有评分的评论提取结果为空很正常后面清洗时统一补默认值就行不用为这个纠结。5. 数据清洗与写入Excel让结果能直接打开看5.1 清洗的三件事去空白、去重复、补缺失很多新手把HTML解析完就算完事了直接拿去写Excel结果打开一看评论内容里全是换行和空格还有重复行观感很差。清洗这步花不了几行代码但决定文件质量。我的做法是先把每条评论整理成一个字典收集到一个列表里all_comments [] # 在解析循环里把字段填入data_dict data { 作者: .join(author).strip() if author else 匿名, 评分: star, 时间: time[0] if time else , 有用数: votes[0].strip() if votes else 0, 内容: content, } all_comments.append(data)字段少、格式统一后面写Excel就变成简单的循环逐行写入。这里我顺手处理两件事一是内容里的换行符替换成空格避免Excel单元格自动换行导致行高混乱二是基于作者内容这个组合键去重防止翻页时评论重复出现。seen set() cleaned [] for item in all_comments: key (item[作者], item[内容]) if key not in seen: seen.add(key) cleaned.append(item)5.2 openpyxl写入工作簿、表头、循环写行openpyxl的用法非常直接。先创建工作簿获取活动工作表用append逐行写入最后保存from openpyxl import Workbook from openpyxl.styles import Font, Alignment from openpyxl.utils import get_column_letter wb Workbook() ws wb.active ws.title 豆瓣影评 headers [作者, 评分, 时间, 有用数, 内容] ws.append(headers) for d in cleaned: ws.append([d[作者], d[评分], d[时间], d[有用数], d[内容]]) wb.save(豆瓣影评.xlsx)见过不少人不用append而是先ws.cell(row1, column1)去设置每个单元格那样代码冗余、容易出错。append每次写入一行完美契合我们列表嵌套字典的数据结构。5.3 样式与打开兼容性别让Excel打不开或者看不清如果只是把数据存进去上面那段代码已经够了。但作为拿得出手的成果我建议再加三点润色# 表头加粗、居中 for cell in ws[1]: cell.font Font(boldTrue) cell.alignment Alignment(horizontalcenter) # 根据内容长度设置列宽 widths [12, 8, 20, 10, 60] for i, w in enumerate(widths, start1): ws.column_dimensions[get_column_letter(i)].width w # 冻结首行方便滚动查看 ws.freeze_panes A2这个步骤不是必须的但加上以后Excel文件打开的第一眼观感完全不同。另外有一个非常常见的坑如果电脑上已经打开了这个Excel文件脚本再次保存会报PermissionError。这不是你代码写错了而是文件被占用。我通常在保存前先os.path.exists检查文件是否存在如果存在就让用户先关掉Excel或者在文件名里加上当前时间戳生成新文件from datetime import datetime filename f豆瓣影评_{datetime.now().strftime(%Y%m%d_%H%M%S)}.xlsx wb.save(filename)加了时间戳以后每次运行生成的文件名都不一样彻底避开文件占用问题也方便多次爬取留档对比。6. 实测反馈与合规边界关于反爬拦截的几点现实提醒6.1 我实测中遇到的正常与异常情况我按每页间隔3秒左右从首屏一直翻到第10页整个过程比较顺利偶尔有一两次返回提醒检测到异常请求遇到这种情况我的处理方式是立即停止当前循环等30秒左右再续跑而不是换个UA硬刚或者去折腾各种绕过手段。从实测结果看保持合理频率后豆瓣对低频、少量的爬取是有容忍空间的。但你千万别把有容忍空间理解成随便爬。如果你用一个循环把start从0爬到几万全是短短几毫秒内发起请求那没有哪个网站能扛住被拦截是必然的。无论从技术上还是从礼貌上限制请求频率都应该是爬虫脚本的标配。我在翻页循环里一定会写上time.sleep(3)这个sleep不是给你看的装饰而是给双方服务器都留出喘息空间。6.2 什么样的用法算合规尊重规则、控制规模和用途这一部分我多说几句。很多人学爬虫的第一步就是拿豆瓣开刀因为它结构清晰、公开可见适合当教学案例。但公开可见不等于可以无限抓取。我的建议是第一控制规模个人学习使用抓几十页以内足够不要有抓完整个网站的想法第二控制频率任何请求间隔至少1秒以上合理做法是2到3秒第三限定用途拿到的数据只用于个人学习、本地演示、数据格式练习不用于商业用途也不对外发布或转售。如果你用requests学明白了request的写法也不是说就万事大吉。很多网站的数据不是直接包在HTML里的而是前端异步请求JSON接口你需要打开浏览器开发者工具的Network面板去观察真实的数据请求。这个技能和你学XPath一样本质上都是看网页结构、找数据规律是同一个分析思维方式在不同场景下的延伸。6.3 从复制代码到会拆新网站进阶的关键一步最后分享一个我自己的体会这个项目做完你能得到的最大收获不是一段能跑的代码而是拆解陌生网页的能力——面对任何网站你不慌而是会分析请求发到哪个URL、数据藏在哪个标签、翻页的规律是什么、落地用什么格式。这个流程才是真正的通用技能。如果你还想往深走一步可以尝试自己改改需求比如换一部电影、多爬几个维度的字段评论者主页、IP属地等或者把数据导入到pandas里做排序和统计。这些扩展都是在同一个框架里加内容操作起来没有本质难度。我个人最喜欢拿这个项目做压箱底的练习每过一段时间重写一遍因为每次都能发现自己对请求、解析、存储这三层有了新的理解。说到底爬虫不是一件神秘的事它就是有节制、有边界地利用公开数据。你把这个思路和内功练好后面再学什么框架都是顺水推舟。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑