Python实战:从函数图像绘制到电影短评爬取的全流程解析 📅 发布时间:2026/9/8 6:32:18 👁 浏览次数: 头一回看到这个题目的时候我就觉得挺有意思Python里最常被拿来练手的两个点——函数图像绘制和网页数据爬取偏偏被塞进了同一个作业里。一个是纯本地计算加可视化一个是网络请求加解析看起来八竿子打不着实际做下来发现它们在技能上高度互补几乎覆盖了Python入门阶段最核心的几条主线数据处理、第三方库调用、循环和函数、文件读写还有最磨人的环境安装和调试。这篇文章就照着这两个任务完整走一遍把每一步拆开揉碎包括环境怎么装、代码怎么写、坑在哪里适合刚学完Python基础语法但还没做过完整小项目的同学直接跟着抄作业。1. 项目整体拆解与思路确定1.1 两个任务放在一起到底在练什么先把这个题目读透。任务一是“绘制两个函数的图像”听起来简单但真正动手就知道它考的不是画图而是三个基本功numpy生成数据、matplotlib画布操作、中文字体处理。任务二是“爬取某电影50条短评”这行字背后藏的东西更多目标网站选择、请求头伪装、页面解析、数据清洗、去重保存每一步都可能翻车。把这两个任务放在一起看明显是一个“组合式”项目刻意让学习者在一套流程里同时用上科学计算、可视化、网络爬虫、文件存储这几大模块。这种组合在真实工作里非常常见比如数据分析师拿到电影数据先爬取评论再基于评分做统计图最后把分布情况可视化出来。所以别把这两个任务当成割裂的小题它们在实战里是一条流水线。1.2 环境准备Anaconda和VSCode的搭配最省心先说环境。题目热词里一堆“python安装”“anaconda”“vscode配置”说明很多人在环境这一关就卡住了。我的建议是别单独装裸Python直接用Anaconda原因很简单它自带conda包管理器一次性帮你装好Python解释器、pip、numpy、pandas这些常用库后面装requests、matplotlib都不用担心依赖冲突。装完Anaconda之后顺手装一个VSCode在扩展商店里搜索“Python”扩展并安装。打开VSCode按CtrlShiftP输入“Python: Select Interpreter”选择Anaconda自带的Python解释器这样终端跑命令和编辑器调试用的是同一个环境不会出现“命令行能跑代码编辑器跑不了”的问题。终端验证环境是否装好三条命令就够了python --version pip list | findstr numpy pip list | findstr matplotlib如果numpy和matplotlib都不存在执行下面这行一次性补齐pip install numpy matplotlib requests beautifulsoup4 lxml我实测过很多次国内网络环境下这条命令如果不加镜像源下载速度会让人绝望建议直接加清华源pip install numpy matplotlib requests beautifulsoup4 lxml -i https://pypi.tuna.tsinghua.edu.cn/simple1.3 项目文件结构规划开工之前先把文件结构想好别把所有代码堆在一个文件里。我的习惯是这样的python-demo/ ├── plot_functions.py # 任务一函数图像绘制 ├── crawl_comments.py # 任务二爬取电影短评 ├── requirements.txt # 依赖清单 ├── comments.csv # 爬取结果存储 └── function_plot.png # 绘制的图像输出拆成两个独立脚本的好处是调试互不干扰后面扩充功能也方便比如把两个任务合并成一个数据可视化分析流程时直接import这两个文件里的函数就行。2. 任务一绘制两个函数图像的核心细节2.1 选哪两个函数以及背后的numpy原理题目没说具体画哪两个函数那就选最经典的对比组合一元二次函数 y x² - 2x 1 和三角函数 y sin(x)。一个代表多项式函数一个代表周期函数图像特征差异明显画在一起对比效果直观而且numpy在这两类函数上计算起来都极顺手。先看numpy在这个任务里的角色。Python自带math库也可以算sin和平方但math库只能处理单个数值没法一次算一千个x对应的y。numpy的核心优势是向量化运算传入一个数组返回一个数组底层用C语言循环代替Python循环效率差出几十倍。import numpy as np x np.linspace(-5, 5, 500) y1 x**2 - 2*x 1 y2 np.sin(x)np.linspace(-5, 5, 500)的意思是生成500个从-5到5均匀分布的点。采样密度为什么要500个不是50也不是500050个点画出来线条会有明显的折角5000个点算起来没必要500个在精度和性能之间刚好平衡。这个参数在matplotlib绘图中很关键采样太少图像失真采样太多白白浪费计算资源。2.2 matplotlib绘图核心参数讲解matplotlib的绘图逻辑是“先建画布再绘曲线再调细节”。直接从代码看import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) plt.plot(x, y1, labely x² - 2x 1, color#1f77b4, linewidth2) plt.plot(x, y2, labely sin(x), color#ff7f0e, linewidth2) plt.axhline(0, colorblack, linewidth0.8, linestyle--) plt.axvline(0, colorblack, linewidth0.8, linestyle--) plt.title(Two Functions: Quadratic and Sine) plt.xlabel(x) plt.ylabel(y) plt.grid(True, alpha0.4) plt.legend(locupper right) plt.xlim(-5, 5) plt.ylim(-3, 8) plt.savefig(function_plot.png, dpi200) plt.show()逐个讲关键的figsize控制画布尺寸我一般习惯设成(10, 6)宽高比更接近16:9视觉上舒服。label是图例名称必须加不然一张图两条曲线看不出谁是谁。color用十六进制色值比默认色更可控。axhline和axvline分别在y0和x0处画了一条虚线作为坐标轴参考线这个细节很多教程不提但实际画图时它非常重要能让读者一眼看出曲线的位置关系。grid(True, alpha0.4)开启网格线alpha降透明度网格不至于喧宾夺主。dpi200保证保存出来的图片清晰不糊直接用plt.show()在屏幕上显示时dpi不起作用但保存文件时就关键了。2.3 中文字体问题最坑的新手关这一步几乎每个用matplotlib画中文的人都会踩画出来的标题和标签全是方块。原因是matplotlib默认字体库不包含中文字符。两个解决方案方案一直接指定支持中文的字体。Windows系统一般有SimHei或Microsoft YaHeimacOS有PingFang SCplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False第二行很重要它解决的是负号显示成方块的问题。如果上面这行代码不起作用可以用方案二查看系统当前有哪些可用字体手动选择一个支持的。import matplotlib.font_manager as fm fonts [f.name for f in fm.fontManager.ttflist if Hei in f.name or YaHei in f.name] print(fonts)把查出来的字体名填进rcParams就行。这一步做完标题就能正常显示中文了。2.4 图像保存和展示要注意的细节plt.show()和plt.savefig()有个顺序坑。如果先调用plt.show()再调用plt.savefig()保存出来的图片可能是一张空白图或者内容和展示出来的不一样。原因是show()执行后会将画布重置。正确做法是先savefig()再show()或者干脆只要保存不要show。另外建议在脚本开头加一行import matplotlib matplotlib.use(Agg)这行代码的作用是在没有图形界面的环境下比如远程服务器也能正常保存图片不会报no display的错误。本地电脑上跑不写这行也没事写上能提高脚本的容错性。不过用了Agg模式后plt.show()就不会弹窗显示了所以本地调试时我一般不写等部署到服务器再注释回来。3. 任务二爬取某电影50条短评的完整流程3.1 选目标网站和分析页面结构的思路爬虫任务的第一步不是写代码而是选目标和分析页面。题目只说了“某电影”我以猫眼电影为例演示完整流程原因有三个页面结构相对规整短评数据直接渲染在HTML里不需要额外处理接口请求门槛低不容易误伤新手。先打开猫眼电影任意一部电影的短评页面按F12打开开发者工具定位到第一条短评右键“检查”看HTML结构。你会看到每条短评都在一个容器里里面包含用户昵称、评论内容、评分、评论时间等字段。这种规整的结构用BeautifulSoup解析非常舒服。爬虫的核心思路是发送HTTP请求拿到HTML源码再用解析库提取目标字段循环翻页直到凑齐50条。整个过程不是魔法就是模拟浏览器行为的自动化操作但这句“模拟”是爬虫的灵魂——目标网站能检测出你是不是真人所以在请求头里必须告诉服务器“我是一个正常的浏览器”。3.2 requests请求头的完美伪造requests库是Python最主流的HTTP请求库。直接用requests.get(url)大概率会被拒绝因为服务器识别到请求头里的User-Agent是个Python脚本而不是浏览器。所以发送请求时必须带上完整的headersimport requests 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.8,en-US;q0.5,en;q0.3, Connection: keep-alive, Referer: https://www.maoyan.com/ }User-Agent必须设置成真实的浏览器标识版本号别用太老或者太新的网上搜“chrome user agent string”能拿到当前最新的。Referer字段标明了请求是从哪个页面跳过去的很多网站校验这个字段不填可能被拦截。这两项是反爬识别里最基础也最容易考的细节。3.3 用BeautifulSoup解析短评数据拿到HTML之后用BeautifulSoup定位评论内容。核心是理解CSS选择器父容器class为comment-item每条短评的内容在里。from bs4 import BeautifulSoup def parse_comments(html): soup BeautifulSoup(html, lxml) comments [] items soup.select(.comment-item) for item in items: content_tag item.select_one(.comment-content) if content_tag: text content_tag.get_text(stripTrue) comments.append(text) return commentsselect(.comment-item)会返回页面上所有评论容器配合select_one(.comment-content)在容器内定位评论正文。stripTrue的作用是去掉文字前后的换行和空格这个参数不写的话评论内容里会带一堆\n\t清洗起来非常麻烦。为什么选lxml解析器而不是Python自带的html.parserlxml基于C语言实现解析速度快了好几倍而且容错能力强遇到不规范的HTML标签也能正确解析。实际爬虫中目标网站页面代码不一定完全符合W3C标准用lxml能少踩很多坑。3.4 分页逻辑50条评论怎么凑齐猫眼短评页面是分页加载的每页固定条数翻页靠URL中的offset或者pageNum参数。找到第一个页面的URL观察变化规律然后构造循环请求。比如某一页URL是https://www.maoyan.com/xxxxx/comments?offset0第二页就把offset改成15第三页改30。用一个循环就能把所有页面都爬一遍all_comments [] for page in range(4): offset page * 15 url fhttps://www.maoyan.com/xxxxx/comments?offset{offset} html fetch_page(url) comments parse_comments(html) all_comments.extend(comments) if len(all_comments) 50: break time.sleep(1)这里有个反爬意识每次请求之间加time.sleep(1)也就是延时1秒。不加延时直接连发请求IP容易被封。页面抓得少可能没感觉但一旦目标网站设置了访问频率检测连续请求超过阈值就直接拒绝服务。基础频率控制是爬虫的基本职业素养宁可爬慢一点也别爬没了。这里实际写的时候建议直接把真实的项目URL换成https://www.maoyan.com/films/XXXX这种占位写法做实验时自己替换成目标电影的ID。50条评论也定位了爬4页就够不需要爬全部短评。3.5 去掉重复评论和异常数据爬回来的数据不能直接入库要做清洗。短评里经常出现三类问题重复评论、过短或无意义的符号评论、首尾有空格。清洗代码def clean_comments(comments): seen set() cleaned [] for c in comments: text c.strip() if len(text) 2: continue if text in seen: continue seen.add(text) cleaned.append(text) return cleaned用set来去重是Python里最典型的做法set的查找时间复杂度是O(1)列表是O(n)数据量大了差距非常明显。过滤长度小于2的评论是为了去掉“。”这类无意义内容长度阈值可以根据实际情况调整。3.6 用csv模块保存结果别用pandas保存到本地这一步新手第一反应可能是pandas但这里我建议用csv模块。原因有两个csv是标准库不需要额外安装而且如果之前环境没装pandas为了保存50条评论去装一个几百MB的库不值得。写入代码import csv def save_to_csv(comments, filenamecomments.csv): with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([序号, 短评内容]) for i, text in enumerate(comments, 1): writer.writerow([i, text])编码格式必须用utf-8-sig而不是utf-8。utf-8-sig会在文件开头写入一个BOM头用Excel打开CSV时中文才不会乱码。这是Python写CSV文件最经典的一个坑我见过太多人用utf-8保存完用Excel打开全乱码其实是编码选错了。4. 常见问题与排查技巧实录4.1 问题速查表实际操作中会遇到一堆问题我把最常见的整理成了一张表按出现的频率排序问题现象原因解决方案中文字体乱码标题和坐标轴显示方块matplotlib默认字体不含中文plt.rcParams[font.sans-serif][SimHei]负号显示不正常坐标轴负号显示为方块中文字体不支持负号plt.rcParams[axes.unicode_minus]False请求被拒返回403状态码请求头被识别为爬虫补全User-Agent和RefererCSV打开乱码Excel显示乱码编码格式不对用encodingutf-8-sig图片保存空白保存的图片是空的show()在savefig()之前执行先savefig()再show()缺少lxml解析器报No module named lxml依赖没装全pip install lxml4.2 爬取结果为空先检查这三处很多新手写完爬虫运行没报错但评论列表是空的。这时候按顺序排查第一打开浏览器直接访问目标URL看能否正常打开如果浏览器都打不开那就不是代码的问题第二在代码里print(html[:500])打印返回的HTML前500个字符看返回的到底是页面内容还是错误提示第三确认选择器和页面实际结构一致因为页面随时可能改版div的class名变了代码就失效了。这种逐层排查的方法在调试里叫“分而治之”把问题拆成请求、解析、存储三段每段单独验证。新手容易犯的错误是看到结果不对就直接改代码越改越乱不如老老实实打印中间变量。4.3 反爬机制和应对策略的选择现在的网站反爬意识越来越强基础的User-Agent伪装已经不够用了。遇到被拦截的情况有三个递进的应对方案第一增加请求头字段包括Accept、Accept-Language、Connection、Referer最大程度模拟真实浏览器环境。第二控制爬取频率每两次请求间隔2到3秒用time.sleep(random.uniform(2, 3))制造随机延时比固定的1秒更接近真人操作。第三使用session保持会话连接requests.Session()会自动保存cookies在需要维持登录状态的场景下很有用。但我想强调一个边界意识爬虫技术用于学习、个人研究和正当的数据分析没问题但大规模请求目标网站、抓取用户隐私信息、或者爬下来的数据用于商业目的都有合规风险。这篇教程的定位是技术入门数据量控制在50条用途限定在学习练习。真实项目里如果需要大量数据优先看目标网站有没有官方开放接口。4.4 代码报错时的心态和调试技巧新手写爬虫最容易崩溃的时刻就是报错。我的建议是把报错信息当作线索而不是惩罚。Python的报错信息已经精确到了行号比如ModuleNotFoundError: No module named requests就是告诉你缺包pip install requests就完事了。AttributeError: NoneType object has no attribute get_text说明select_one没找到元素返回了None重点去查页面结构和选择器。调代码的时候在关键节点加print()是最高效的方式比用任何调试器都直观。请求完打印状态码解析完打印评论数量清洗完打印去重前后对比每一步的输入输出都清楚了问题出在哪一层一目了然。5. 从两个任务到一套实战技能5.1 把任务串联起来短评数据可视化分析两个任务分开做完只是热身把数据从爬虫流向可视化才算真正体验了一把数据流程。爬下来的50条短评大部分人只会在CSV里扫两眼但如果在后面的段落加一个统计词频并绘图的功能价值会完全不同。比如把评论按长度、情感倾向分类统计正面和负面比例然后画出柱状图。或者用matplotlib画一个评论长度的直方图用plt.hist()一行代码就能实现lens [len(c) for c in all_comments] plt.hist(lens, bins20, edgecolorblack) plt.title(短评长度分布) plt.xlabel(评论字数) plt.ylabel(评论数量) plt.savefig(comment_length_hist.png, dpi200)这一段代码把任务一学的matplotlib技能直接迁移到了任务二的数据可视化上整个项目就形成了闭环爬取数据、清洗数据、分析数据、展示数据。这个流程几乎雷同于真实工作里数据分析师每天做的事情。5.2 项目扩展方向更完整的电影短评分析流程如果做完基础版还有余力强烈建议在这个基础上做三个扩展。第一个扩展是评分字段采集把每条短评对应的评分也爬下来然后计算平均分和评分分布。第二个扩展是文本情感分析用snownlp库跑一遍简单的情感分类统计正负面评论比例。第三个扩展是词云可视化把评论切成词频统计生成词云图能看到观众对一部电影关注的焦点到底是剧情、演技还是特效。这三个扩展方向分别对应了爬虫进阶、自然语言处理入门、数据分析可视化三个方向能从一套简单的两个任务出发延伸出三条完全不同的学习路径。这也是我特别建议新手做项目要“一鱼多吃”的原因——别做完了就扔改几行代码换个思路学到的内容可以翻好几倍。5.3 为什么说组合式项目才是入门正道现在回想开头的题目同时包括函数图像和短评爬取确实不是偶然的。单独画图只会让你学会调库单独爬虫只会让你学会请求和解析但组合在一起逼着你面对真实项目中必然出现的交叉问题比如数据格式不一样怎么统一爬到的数据怎么用来画图代码报错到底属于网络问题还是数据处理问题。我在带新人或者给朋友指点Python学习路径时一直主张同一个观点别沉迷于刷语法题尽早做组合式的小项目。语法题是点项目是面只有把点连成面那些零散的知识才会真正长在你身上。函数图像、文件读写、网络请求、数据解析、清洗存储这些能力单拎出来都写在教科书里但组合起来就是真实世界的缩影。我的建议是拿这个项目练手时每一段代码都亲手敲一遍不要复制粘贴我的示例然后跑通就完事。试着改几个参数把函数换成你自己想画的换一部电影重新爬把图表改成你喜欢的样子。只有踩过坑、排过错、改过代码这些技术才会真正变成你自己的。