ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python第二次作业实战:从语法进阶到爬虫与数据分析

Python第二次作业实战:从语法进阶到爬虫与数据分析 python第二次作业从语法到实战的进阶之旅我做对了哪些事说实话刚看到“python第二次作业”这个题目的时候我心里是有点犯嘀咕的。第一次作业无非是打印“Hello World”、算个加减乘除、写个九九乘法表感觉Python也就那么回事。但第二次作业上来就要求用numpy构建邻接矩阵还要用pandas处理DataFrame甚至要自己写一个能自动拉取网页数据的爬虫脚本——这跨度一下就从“会读”跳到了“能用”。我把这次作业完整做下来之后最大的感受是第二次作业不是第一次的“加强版”而是第一次的“升级版”。前者考验的是你懂不懂语法后者考验的是你会不会用语法解决实际问题。这篇文章我准备把整个“python第二次作业”从环境搭建、语法进阶、核心库实践到爬虫和数据分析小项目完整拆开揉碎讲一遍。不管你是正在做类似课程作业还是自学Python刚好学到第二阶段都能从里面捞到一些能直接抄的代码、能少踩的坑以及真正管用的排查思路。1. 这次作业到底在考什么阶段目标与内容拆解我在动工之前先把作业要求从头到尾读了三遍。第一次读的时候只看到了一堆名词邻接矩阵、DataFrame、正则表达式、爬虫……脑子直接有点发懵。第二次读的时候我开始把每个名词归类发现这些内容其实都指向同一个能力用Python处理结构化数据。第三次读完之后我心里就有数了——这次作业不是要你背语法而是要你完成一次“从原始数据到程序化处理”的闭环。1.1 核心需求解析从语法记忆到能力应用第一次作业的典型特征是“老师出题你查书”。比如“用print输出以下图案”“用for循环计算1到100的和”这些题目的答案直接写在教程里你只需要把代码敲一遍能跑通就交差。第二次作业完全变了味道。它给的是一个场景比如“给定一张图的顶点和边构建它的邻接矩阵”“抓取某个页面的标题和链接并存入表格”“对一份CSV数据做清洗和统计”。这些题目没有现成答案你需要自己决定用什么库、怎么写函数、怎么处理异常、怎么把结果整理成符合要求的格式。换句话说第二次作业考察的是“项目拆解能力”和“资源整合能力”这两种能力恰恰是真正工作中天天用到的。我当时给自己定的策略很简单先画一张脑图把作业里所有的任务点列出来然后给每个任务点标注了“能用标准库解决”还是“必须用第三方库”。比如邻接矩阵用纯Python的列表套列表也能写但用numpy矩阵运算和后续的读写都会省事太多。再比如爬虫标准库的urllib能勉强用但用requests加BeautifulSoup效率和稳定性都上一个台阶。这个“先判断再动手”的步骤让我后面省了一大半返工的力气。1.2 方案选型为什么我选了这个技术组合我的环境是Windows笔记本Python版本选的3.8。为什么是3.8因为当时很多第三方库尤其是涉及机器学习的库对3.8的兼容性最好安装几乎不会遇到“找不到对应版本”的情况。后来我再装环境的时候发现3.10以上的版本在某些库的安装上确实会更折腾一点不是说不能用而是对于写作业来说没必要和兼容性问题较劲。技术选型方面我最后敲定的组合是任务场景我用的库选它的理由数值计算与矩阵操作numpy构建邻接矩阵、矩阵转置、求和等操作自带函数比手写for循环快且不容易出错表格数据处理pandas读取CSV/Excel、筛选行和列、分组统计、处理缺失值一套API全搞定网页数据采集requests BeautifulSouprequests负责下载网页BeautifulSoup负责解析HTML二者搭配最简单直接数据可视化matplotlib绘图基础库只要你不是要那种花里胡哨的交互图它完全够用机器学习小实验scikit-learn作业里如果涉及简单的分类或回归调库就出结果能快速验证思路这里多说一句很多人一上来就喜欢追求新潮的库但作业阶段我的经验是能用大而稳的库就别用小而新的库。第二次作业的核心是理解流程不是炫技。你用一个社区用户基数大的库遇到问题搜答案都方便很多。2. 从零把环境盘顺安装、配置和依赖管理环境问题看似简单实则能卡死一大半新手。我帮同学看过太多次作业其中有三成的问题根本不是代码逻辑而是import导入失败、pip安装报错、或者代码在别人的电脑上跑不起来。环境这块我踩过的坑基本都集中在下面几个环节。2.1 Python安装与环境变量一次性配清楚如果你用的是Windows安装Python时最容易犯的一个错误是下载完安装包一路点“Next”就完事了完全没注意到安装界面最下方那个“Add Python to PATH”的复选框。这个复选框一定要勾上否则装完之后你在命令提示符里输入python会提示“不是内部或外部命令”原因就是系统不知道去哪找python.exe。如果第一次没勾后面也不用重装手动加环境变量也行。我的操作是右键“此电脑”-“属性”-“高级系统设置”-“环境变量”在“系统变量”里找到Path新增两条路径一条指向Python安装目录比如C:\Python38一条指向它的Scripts子目录C:\Python38\Scripts。加了Scripts目录是为了让后面pip install安装的命令行工具也能被直接调用。顺手说一个实用步骤装完之后开一个全新的命令行窗口输入python --version如果能显示Python版本号说明安装成功再输入pip --version确认pip可用。注意一定要开新窗口因为环境变量的修改不会自动反映到已经打开的终端里。2.2 pip安装第三方库的正确姿势第二次作业要用到numpy、pandas、matplotlib、scikit-learn如果一个个执行pip install也不是不行但容易遇到下载慢、超时甚至安装一半失败的情况。后来我在公司前辈的博客里学到一套稳妥的流程python -m pip install --upgrade pip pip install numpy pandas matplotlib scikit-learn requests beautifulsoup4 -i https://pypi.tuna.tsinghua.edu.cn/simple第一句是先把pip自身升级到最新版因为老版本pip有时会解析不了新版库的依赖。第二句用清华镜像源把所有库一次性装完。这里的关键是-i参数它指定了下载源。默认的官方源在国内下载经常慢到怀疑人生换成清华源之后速度基本都是满格。安装过程中你可能会看到一堆“Collecting xxx”和“Installing collected packages”只要最终出现类似“Successfully installed numpy-1.24.x pandas-2.0.x”的字样就说明装全了。如果中途某个包报错先记下包名和错误信息然后到网上搜。绝大多数情况下错误信息里最后一行就是破案线索。装完别急着写代码先跑一个“冒烟测试”把所有库import一遍import numpy, pandas, matplotlib, sklearn, requests, bs4 print(全部导入成功)这一步能确认库和当前Python版本是否兼容。某些包安装成功但导入失败多半是版本冲突比如numpy和pandas版本太旧或者Python是32位而库是64位。遇到这个情况直接用pip uninstall删除后重装通常能解决。2.3 用虚拟环境隔离项目依赖刚开始学的时候我没用虚拟环境所有库一股脑装在全局环境里。后来作业要求越来越复杂有的项目需要pandas 1.x有的需要2.x全局环境里一升级前一个项目直接跑不了了。第二次作业这种“周期长、依赖多”的项目我认真建议你从一开始就用虚拟环境。创建虚拟环境的命令没有难度python -m venv py312_env这里py312_env是环境文件夹的名字你可以随便起。创建完以后Windows下激活环境的命令是py312_env\Scripts\activateLinux或macOS下是source py312_env/bin/activate激活之后命令行前面会多出一个(py312_env)的提示符。在这个状态下执行的所有pip install都只安装到当前环境里不影响系统全局。这个习惯现在已经成为我做任何Python项目的肌肉记忆了几乎等于程序员界的“出门戴口罩”。3. 语法进阶第二次作业的真正门槛环境弄好只是万里长征第一步。第二次作业真正筛人的地方是在语法和逻辑层面。如果说第一次作业只需要“知道有这么个东西”第二次作业就要求你“熟练运用知道什么时候选哪个”。3.1 变量与类型转换别让类型偷偷坑你第二次作业的练习题里有一道很经典的用户输入两个数字字符串求它们的和。乍一看代码就两行a input(请输入第一个数) b input(请输入第二个数) print(a b)跑起来你会发现输入1和2输出的是“12”而不是3。原因很简单input函数返回的永远是字符串字符串的就是拼接不是相加。这就是类型转换存在的意义。改成这样才对a float(input(请输入第一个数)) b float(input(请输入第二个数)) print(a b)int或者float包一下字符串就变成了能参与数学运算的数字类型。如果你不确定某个变量到底是什么类型随时用type()函数打回原形别靠猜。类似的坑还有从CSV读取的列往往是带引号的字符串你直接做数值计算就会报错。你要么在读取时指定dtype要么事后用pd.to_numeric()批量转换。总之见到数据第一件事就是想清楚它是字符串、整型、浮点型还是列表/字典数据类型的混乱是所有逻辑错误的万恶之源。3.2 函数定义把重复代码关进笼子第二次作业要求写一个函数完成特定计算这是很多同学从“流水账代码”过渡到“结构化代码”的关键一步。我第一次写邻接矩阵的时候把所有代码堆在一起写了80行自己看得都头疼。后来被迫改成函数立刻清爽了很多。函数最容易出问题的几个点我列一下参数顺序定义def build_matrix(edges, num_nodes)调用时就要传两个参数顺序不能乱。如果担心顺序记错可以用关键字参数build_matrix(num_nodes5, edgesedge_list)。返回值记住如果函数体里没有return函数默认返回None。我见过同学写了个函数明明在里面算好了结果最后却忘了写return调用之后输出的就是None找了半天bug。局部变量和全局变量函数内部给变量赋值默认只会创建一个新的局部变量不会改变外面同名变量的值。如果确实要改外部变量需要用global声明。不过作业阶段我建议少用global容易把逻辑搅乱不如直接在函数里return新值在外面重新赋值。3.3 列表、字典和字符串处理结构化数据的基石邻接矩阵、DataFrame、JSON、爬虫解析结果……所有这些高级概念底层都是列表、字典和字符串的排列组合。第二次作业里我花时间最多的其实不是numpy的功能而是怎么用原生语法把数据“串”起来。比如构建邻接矩阵最直观的思路是先初始化一个全零的二维列表然后把每一条边的两个端点对应的格子置为1。# 输入节点数num_nodes边的列表edges每条边是(起始点, 终点)元组 num_nodes 5 edges [(0, 1), (1, 2), (2, 0), (2, 3), (3, 4)] # 方法一用列表推导式初始化二维列表 matrix [[0 for _ in range(num_nodes)] for _ in range(num_nodes)] for start, end in edges: matrix[start][end] 1 # 方法二直接用numpy更利索 import numpy as np matrix_np np.zeros((num_nodes, num_nodes), dtypeint) for start, end in edges: matrix_np[start, end] 1这里有个特别容易犯的错初始化二维列表时写[[0] * num_nodes] * num_nodes。表面上看着挺对但运行之后你会发现修改任意一个元素整列都被改掉了。原因在于* num_nodes只是复制了列表的引用并没有真正创建独立的新列表。这是一个非常经典的“新手陷阱”我用这个例子考过好几个自称“Python熟练”的同事居然一半人都栽过。所以在Python里创建嵌套列表老老实实用列表推导式别用乘法偷懒。3.4 循环与推导式用最少的代码干最多的活第二次作业里需要遍历边、遍历矩阵、格式化输出、筛选满足条件的行。如果都写for循环代码会很长如果只用推导式很多场景一行代码就能搞定。我的经验是能用推导式的地方尽量用推导式。比如筛选出DataFrame中所有得分大于60的行pandas写法是filtered_df df[df[score] 60]这行代码背后做的事情如果用纯Python写至少要五六行。理解这个简洁语法之后你的代码从“可读”变成了“优雅”。当然推导式也不要滥用。当一个表达式嵌套超过两三层的时候可读性会急剧下降。这时候宁可拆成普通的for循环也不要追求一行流。写作业是为了让人看懂的不是为了秀操作的。4. 核心库实战邻接矩阵、DataFrame与绘图第二次作业的“重头戏”全在第三方库的使用上。我把最常碰到的三个场景展开讲每一个我都放了当时调通之后的代码和心得。4.1 用numpy构建邻接矩阵图和矩阵的自由转换构建邻接矩阵在理论课上看起来很简单就是“有边填1无边填0”。但一旦节点数量上到几十个、边有上百条手写循环就变得又慢又容易错。用numpy处理逻辑瞬间变清晰。一个更实际的需求是给定一个图判断它是否有重边、是否有自环然后把邻接矩阵存成文件。我的做法是import numpy as np node_list [A, B, C, D] edge_list [(A, B), (B, C), (C, A), (C, D), (D, A)] idx_map {node: i for i, node in enumerate(node_list)} num len(node_list) adj np.zeros((num, num), dtypeint) for start, end in edge_list: adj[idx_map[start], idx_map[end]] 1 print(邻接矩阵) print(adj) # 保存到文件方便后续查看 np.savetxt(adj_matrix.csv, adj, delimiter,, fmt%d)这里的关键是建立“节点名到索引”的映射字典idx_map。有了这个字典不管边里写的是字符串还是数字都能统一变成矩阵的行列坐标。再把结果用np.savetxt保存成CSV交作业的时候老师一眼就能看见矩阵长什么样。4.2 pandas DataFrame操作表格数据处理的瑞士军刀第二次作业里有一个任务是分析班级成绩表。老师给的是一个Excel或者CSV文件里面有好几列姓名、语文、数学、英语。要求是统计总分、排名、各科平均分最后输出一个包含所有信息的新表格。如果纯用Python标准库你得一行一行读文件自己切分字符串然后手动做分组和排序。用pandas只要几分钟import pandas as pd df pd.read_csv(scores.csv, encodingutf-8) df[总分] df[语文] df[数学] df[英语] df[平均分] round(df[总分] / 3, 2) df_sorted df.sort_values(by总分, ascendingFalse).reset_index(dropTrue) print(df_sorted) df_sorted.to_excel(scores_result.xlsx, indexFalse)这里面有两个细节常坑人。第一是encodingutf-8。如果你的CSV文件是Excel另存的很可能默认是gbk编码用utf-8读会直接报“UnicodeDecodeError”。这时候把编码改成gbk或者gb2312基本就好了。我的习惯是打开CSV看一眼乱不乱如果乱就换编码要不就写个try-except自动尝试几种常见编码。第二是reset_index(dropTrue)这个操作是把排序后的行索引重新从0编号。如果不加这个行索引还是原来的位置打印出来中间会跳号看着很难受。4.3 绘图必修课解决横坐标太密集的尴尬第二次作业如果有数据可视化要求八成会画折线图或者柱状图。很多同学会碰一个很尴尬的场景数据很多比如一年365天的温度变化直接用plt.plot(x, y)横坐标的刻度全部挤成一团黑根本看不清数字。这个问题我在第一次遇到时也懵了很久。后来总结出三个最有效的解决办法按优先级排序方案一旋转刻度标签。import matplotlib.pyplot as plt plt.xticks(rotation45, haright)这个最简单只要把坐标轴标签旋转45度或60度原本重叠的文字就能错开。注意haright表示标签水平居右对齐这样旋转后能更自然地对准刻度点。方案二减少刻度数量。如果数据点特别多旋转也无济于事那就只显示其中一部分刻度。用plt.xticks传入一个子集比如只显示每隔20天的刻度x_labels list(range(1, 366)) x_subset x_labels[::20] # 每隔20天取一个 plt.xticks(ticksx_subset, labels[f第{i}天 for i in x_subset])这样图面立刻干净很多不会因为刻度太多而糊掉。方案三改用图表类型。如果横坐标是时间序列且点非常密集折线图本身就容易变得一团糊。这种情况下我会把它改成柱状图或者用plt.figure(figsize(12, 6))把画布拉大给每个点留出更多横向空间。画布尺寸是我经常被人忽略的一个参数很多“图丑”的问题其实加一个合适的figsize就解决了。还要提一个老生常谈的中文乱码问题。matplotlib默认字体是不支持中文的你画图时只要标题或者标签里有中文显示出来就是一堆方块。解决办法是在绘图前设置字体plt.rcParams[font.sans-serif] [SimHei] # 黑体 plt.rcParams[axes.unicode_minus] False # 解决负号显示异常《SimHei》是Windows自带黑体如果你在Mac上可以换成PingFang SC或Arial Unicode MS。这个配置我几乎每次写matplotlib都要带上已经成条件反射了。5. 小项目实战从爬虫到数据分析的完整链路第二次作业的压轴部分往往是一个“迷你项目”。我当时拿到的题目是爬取一个图书网站的所有书籍名称和价格清洗数据后分析价格分布最后画出一张柱状图。这个流程走完基本把前面几个章节的内容全串起来了。5.1 用requests BeautifulSoup抓取页面数据爬虫本身不难难的是应对各种“意料之外”。我先给出一段最朴素但能跑的代码import requests from bs4 import BeautifulSoup url http://example.com/books headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 # 如果乱码就改为 gbk 或 gb2312 soup BeautifulSoup(resp.text, html.parser) books [] for item in soup.select(.book-item): title item.find(h3).text.strip() price item.find(span, class_price).text.strip() books.append({title: title, price: price})这段代码里最核心的是headers。很多网站会检查请求来源如果一个请求没有User-Agent会被直接拒绝。加一个常见的浏览器UA是通过反爬门槛的基本操作。soup.select(.book-item)是CSS选择器语法选取所有class为“book-item”的元素然后再在其中分别找书名和价格。如果你不太熟悉CSS选择器也可以用find_all()但效率上选择器会更简洁。5.2 数据清洗与结构化把杂乱的变成可分析的爬下来的数据往往是“价格59.00”这种带字符的字符串直接画图必报错。所以必须清洗。我的通用清洗流程分四步去掉空白、提取数字、统一类型、处理缺失值。import re def clean_price(text): # 只保留数字和小数点 match re.search(r[\d.], text) return float(match.group()) if match else 0.0 for book in books: book[price] clean_price(book[price]) df_books pd.DataFrame(books) df_books[price] pd.to_numeric(df_books[price], errorscoerce) df_books df_books.dropna(subset[price])这里用到了一点正则表达式但不用背只要记住[\d.]的意思是“匹配连续的数字或小数点”后面遇到类似情况直接抄就行。errorscoerce的作用是如果字段里没有数字转换为NaN而不是抛异常紧接着dropna把这些NaN的行删掉保证数据完整性。5.3 一个轻薄版的“量化交易策略”体温计热搜词里有个“python量化交易策略代码”虽然第二次作业不太可能直接要求写一个完整交易系统但老师可能会让你模拟计算一个均线策略的收益。我当时的理解是这不就是“数据处理 循环逻辑”的综合应用吗于是我写了一个极简的版本import pandas as pd # 假设df里有两列date日期和close收盘价 df pd.read_csv(stock_data.csv, parse_dates[date]) df[ma5] df[close].rolling(window5).mean() df[signal] (df[close] df[ma5]).astype(int) df[position] df[signal].diff() # 1表示买入-1表示卖出 df[return] df[close].pct_change() df[strategy_return] df[position].shift(1) * df[return] df[cum_return] (1 df[strategy_return]).cumprod() print(df[[date, close, ma5, signal, cum_return]].tail())这个代码虽然只有几行但背后的思想很重要用移动平均线生成交易信号收盘价在5日均线之上就持有在之下就空仓然后计算每期收益率最后累乘得到累积净值。你看这里面根本没有用到任何“黑科技”全是pandas的rolling、diff、shift、cumprod这几个操作。这也验证了我一直坚信的观点量化交易入门阶段数据清洗和pandas操作能力比数学功底更重要。如果非要说这段代码有什么坑那就是df[position].diff()产生的值有NaN、1、-1三种而NaN会导致后面的乘算出错。所以稳妥起见要先fillna(0)。这种“差一小步就报错”的坑恰好是作业里最容易扣分的地方。6. 常见问题与排查技巧实录最后把我这次做“python第二次作业”期间遇到的高频问题整理成一个速查表。这些问题我在帮同学排查的时候发现它们出现频率极高几乎每个初学Python的人都会踩中至少两个。现象原因解决办法命令行输入python提示“不是内部或外部命令”Python未加入PATH环境变量手动将Python安装目录及Scripts目录加入Pathpip install下载慢、超时默认源在国外访问不稳定使用清华镜像源-i https://pypi.tuna.tsinghua.edu.cn/simpleimport numpy时提示DLL load failed库版本与Python版本不兼容卸载后重装pip uninstall numpy再指定版本安装打开CSV文件报UnicodeDecodeError文件编码不是utf-8可能是gbk读取时指定encodinggbk或encodingutf-8-sigDataFrame列名是中文打印出来乱码控制台编码问题设置环境变量PYTHONIOENCODINGutf-8或用to_csv(encodingutf-8-sig)导出matplotlib绘图中文字体变方块默认字体不支持中文设置plt.rcParams[font.sans-serif] [SimHei]绘图中负号显示成方框负号字体问题同时设置plt.rcParams[axes.unicode_minus] False横坐标刻度重叠点太多或标签太长旋转标签、减少刻度数量、增大画布尺寸二维列表乘法初始化修改一列导致全变列表乘法复制的是引用使用列表推导式[[0 for _ in range(n)] for _ in range(n)]创建函数最后没有写return结果输出None函数默认返回None记得在函数结尾写上return 要返回的变量爬虫请求返回403 Forbidden没有设置User-Agent或反爬拦截在headers中带上浏览器User-Agent加timeout和重试网页内容爬下来是乱码网页编码与解析不一致先看HTTP头里的charset或者用resp.apparent_encoding安装时间很久怀疑卡住依赖包太多了正在解析依赖不要关终端多看几遍日志正常情况下几分钟内会结束排查问题的时候我有一套自己的“排雷顺序”分享给大家第一步看报错信息最后一句排除语法和类型错误第二步检查数据格式比如编码、列名、类型第三步检查环境用pip list确认库版本第四步检查逻辑拿一个极小的数据集逐步打印中间过程。这四步能解决九成以上的报错。另外我发现自己初学时常犯一个毛病代码报错后有病乱投医把网上搜到的一大段代码直接粘贴替换。这个做法非常不推荐因为网上很多代码是针对特定环境和场景的直接粘贴只能导致更多问题。正确做法是把报错整个复制到搜索引擎然后筛选那些“解释原因”而不是“只给代码”的答案。我亲自试过这个习惯的效率提升非常明显。对了还有一个非常实用的小技巧自己写代码的时候用print把每一步的中间结果打出来。比如构建完矩阵后先print一下矩阵看看有没有填错清洗完数据后先print一下前5行确认格式。这种“分阶段验尸”的方法比写完一整段再调试要省心得多。7. 我做完这次作业的最大体会如果你也是正在做类似的第二次Python作业我希望你能明白一件事交上作业不是终点把作业里的方法变成自己的思维习惯才是终点。第二次作业里用到的邻接矩阵、DataFrame、函数、爬虫这些名词以后都会在不同项目中反复出现。你现在花三小时调通一个功能相当于为以后省下三十小时的摸索时间。我在做完这个作业后又回头把第一次作业里的“九九乘法表”用函数和列表推导式重写了一遍。同样的输出代码量少了一半可读性却高了不止一倍。这种感觉很奇妙有点像你刚学会开车时还在手忙脚乱地找刹车过了一个月之后你已经能边开车边听广播了。第二次作业逼着你从“找刹车”进入到“踩油门”的阶段所以好好享受这种手忙脚乱的感觉吧等你习惯了之后会觉得这段经历还挺可爱的。
返回列表