简介:一份面向Python零基础或刚入门编程者的爬虫入门课件,帮助读者理解网络爬虫自动获取网页数据的基本原理,并掌握用Python编写简单爬虫的完整流程。课件以docx格式呈现,共1个文件,压缩包约100KB,内容轻量精炼,适合课前预习或自学查阅。目前已有227人学习下载。文件系统梳理了Python基础语法、HTML标签结构、requests与BeautifulSoup等常用爬虫库的用法,并以抓取百度首页标题和图片为实战案例,拆解发送GET请求、解析HTML、提取数据、存储数据这四大步骤。同时给出牛客网刷题、Python官方文档等学习路径与进阶方向,帮助初学者少走弯路,快速从理论过渡到实际爬虫项目。
1. 从一份 docx 到能落地的一线爬虫课:这份讲义该怎么写才不误人子弟
《教程课件-Python爬虫入门教程.docx》这个标题,乍看是份再普通不过的教学文档,但真要把它做成一份能让零基础学生照着敲、敲完能跑、跑完敢说自己“入门了”的课件,难度比想象中大得多。大部分市面上的爬虫讲义要么堆了一堆 requests 和 BeautifulSoup 的 API 说明,学生看完只会复制粘贴;要么一上来就讲 Scrapy 框架,把刚学会 print 的学生直接吓退。我见过太多号称“入门”的教程,实际讲的是框架源码分析,和“入门”两个字完全不沾边。
这份 docx 要解决的真正问题不是“怎么写爬虫代码”,而是“怎么让一个只会 Python 基础语法的人,在 4 到 6 节课内建立起对爬虫的完整认知框架——知道抓包是什么、解析选什么、反爬怎么绕、数据存哪里”。它适合三类读者:一是高校或培训机构里准备开爬虫课的老师,二是想用一份现成讲义带新人入门的团队技术负责人,三是自学 Python 但始终卡在“看完教程不会写”阶段的初学者。接下来的内容,我会从课件结构、演示代码、常见坑位三个层面,把这份 docx 应该有的骨架和血肉拆开来讲。
2. 把 docx 变成真正能上课的讲义:结构设计先于代码设计
2.1 爬虫入门课的知识点边界:哪些必须讲,哪些讲了就是在劝退
一份合格的爬虫入门 docx,首先要划清楚知识的边界。很多讲义翻车,就是因为什么都想塞进去。比如把 Selenium 的隐式等待和显式等待讲一整章,或者把 scrapy 的中间件原理塞进第一节课,学生连 HTML 结构都还没搞明白,就被框架概念砸晕了。
我的习惯是,入门课只覆盖三条主线。第一条是 HTTP 基础,学生需要知道 URL、请求头、响应状态码、Cookie 和 Session 是怎么回事,不需要懂 TCP 三次握手,但必须知道 GET 和 POST 的区别以及什么时候用哪个。第二条是 HTML 解析,这里只讲 BeautifulSoup 和正则表达式的最基本用法,xpath 可以作为选学内容放最后。第三条是数据落盘,csv 和 json 两种格式就够,因为这是最直观、最容易验证成果的存储方式。
这三条主线对应的典型教学案例是什么?就是一个静态页面的列表抓取。比如一个新闻网站的标题列表,不需要登录、不需要处理验证码、没有动态加载。把这样一个小而完整的案例讲透,学生才能真正理解爬虫的工作流:请求 → 响应 → 解析 → 存储。如果一上来就抓淘宝或者微博,学生十条请求有九条被反爬拦截,课程后半段全在报错和调试,那不是入门课,那是劝退课。
常见做法是先把这三条主线对应的知识点做成一个简表放进讲义的“课程地图”页面,让学生第一节课就知道自己要学什么、学到什么程度算毕业。这样做的另一个好处是,老师后续备课可以直接对着地图分配课时,不至于讲着讲着跑偏到某个犄角旮旯的知识点上。
2.2 讲义中的代码排版规范:每一段示例代码都要能独立运行
技术文档和参考手册最大的区别在于,讲义里的每一段代码都要能独立运行,并且运行结果要可预期。很多老师写讲义时图省事,代码片段之间互相依赖——上一节定义了 session,下一节直接用这个变量而不做说明。学生复制下来运行直接报错,然后就开始怀疑自己环境配置有问题,实际上只是讲义本身有 bug。
我写讲义代码时有几个硬规矩。第一,每个示例代码文件必须有完整的 import 语句,绝对不出现“省略导入”这种写法,因为入门学生根本不知道省略的是什么。第二,每个代码块下面必须紧跟“预期输出”的说明,如果输出异常或者网络请求失败会收到什么报错,提前写在讲义里。第三,代码里的 URL 请求地址用公开稳定的测试站点,比如常见的 httpbin 或者 example 网站,避免用到真实业务站点。
这个规矩对应到 docx 里的排版格式也有讲究。代码段落的字体、缩进、背景色都要统一设置,代码和正文之间有清晰的视觉区分。docx 的“标题 1、标题 2、正文、代码块”这四类样式要提前规划好,学生拿到讲义不光是读,还会照着敲代码。如果讲义里代码段落和正文段落混在一起,字号大小不一致,这会直接增加学生阅读负担,而他们一旦开始频繁问“老师我的环境是不是有问题”,课程的进度就全乱套了。
2.3 章节课时建议与练习配置:一个例子从讲解到上手至少需要两个课时
按照面向零基础学生的节奏,一份 4 到 6 课时的爬虫入门 docx,最佳分配方式是第一课讲 HTTP 协议和 requests 库的基本用法,第二课讲 HTML 结构和 BeautifulSoup 解析,第三课讲一个综合案例把前两课串起来,第四课可以是 csv/json 存储和翻页处理。如果课时充裕,第五、六课讲动态页面的初步认识和处理方案。
每一课后面必须配置 3 到 5 个递进练习题。第一课的练习是修改请求头、处理超时;第二课的练习是解析不同的 HTML 结构,比如列表页、表格页、嵌套 div;第三课的练习是综合案例的变体,比如换一个相同结构的目标站。练习题不能太跳脱,要恰好落在刚讲过的知识点上,难度是“跳一跳够得着”的程度。
配套的讲义里我会在每一章末尾加一个小节,叫“本节最容易踩的坑”,把我在实际教学里遇到过的高频报错集中列出来。这不是凑篇幅,而是真的有用。Requests 库的 SSL 证书报错、BeautifulSoup 解析出来是空列表、csv 写入乱码,这些都是入门学生必然遇到的问题。提前把这些问题写进讲义,学生自己排查,老师就不用反复回答同一个问题。
3. 讲义里的核心案例设计:从静态页面抓取到数据落地全流程拆解
3.1 选什么目标站点:稳定、公开、结构简单的三个选择标准
写爬虫讲义最难的一步不是代码,而是选目标站点。我踩过最大的坑是选了某个结构很典型但反爬策略突然升级的站点,导致讲义印发后第二天代码就不可用了。从那以后我就总结出了三个标准:第一,站点必须有公开的测试接口或者明确允许爬虫访问;第二,页面结构三年内大概率不会大变;第三,不需要登录、不需要处理复杂 Cookie。
httpbin.org 适合讲 requests 的基础用法因为它能回显请求数据,但这个站点不适合做解析案例。解析案例要找一个 HTML 结构规整的公开网站,比如一些静态文档站、公开数据集展示页。如果是给高校学生上课,可以选学校内部的新闻公告页面,这种页面一般没有反爬,结构也很规整。但要注意把这个 URL 作为变量放在代码顶部,加注释说明「如果页面改版请替换此地址」,这样讲义的生命周期会长很多。
另一个常见做法是干脆在讲义附带一个本地 HTML 文件,让学生直接解析本地文件而不是发网络请求。这样能彻底避免网络不稳定和站点改版带来的教学事故,但缺点是没有真正发过 HTTP 请求,学生少了“网络交互”这层体验。我一般会在综合案例里用真实站点,在前期解析练习里用本地文件,两全其美。
3.2 最小可运行的请求示例:headers、超时和响应检查一个都不能少
一份讲义里第一个让学生上手的请求代码,讲究的是“最小但完整”。很多课件只写两行代码,一行 requests.get,一行 print(response.text),看起来简洁,但完全没有工程意识。学生学到的就是“发请求只要一行代码”,遇到超时不处理、状态码不检查、headers 不设置,拿到的是 403 页面还以为是自己的问题。
下面这段是我在讲义里常用的第一段完整代码。它虽小,但包含了一个正经爬虫请求该有的全部基础要素:
import requests # 目标地址:使用 httpbin 提供的测试服务,可以回显请求内容 url = "https://httpbin.org/get" # 设置请求头:模拟浏览器访问,很多站点会拦截不带 UA 的请求 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } try: # timeout 同时设置连接超时和读取超时,避免程序卡死 resp = requests.get(url, headers=headers, timeout=(3, 5)) # 主动检查状态码,非 200 直接抛异常,避免后面解析无效页面 resp.raise_for_status() print(resp.status_code) print(resp.text[:500]) except requests.exceptions.Timeout: print("请求超时,请检查网络或稍后重试") except requests.exceptions.HTTPError as e: print(f"HTTP 错误:{e},状态码:{resp.status_code}") except requests.exceptions.RequestException as e: print(f"请求失败:{e}")这段代码在讲义里的讲解重点是三个地方。第一,headers 就是告诉服务器“我是一个浏览器”,虽然这不是绝对有效但能过滤掉一大半最基础的拦截;第二,timeout 用元组同时指定连接和读取超时,这是一个在真实工程里很有用的习惯,能防止一个坏连接卡住整个爬虫进程;第三,raise_for_status 和异常捕获让代码在出错时能给出明确提示而不是直接崩溃。
代码后面的“预期输出”要写成:正常情况会打印 200 和一段 JSON 格式的响应内容;超时情况会打印“请求超时”提示;如果遇到 403 会打印对应状态码。把这些明明白白写进讲义,学生跑代码时会更容易对号入座。这一点是很多讲义忽略的细节,也是让学生建立信心的关键环节。
网页解析的第一课,我会用本地 HTML 文件来教学。文件放在讲义的附件资源里,代码里用 open 打开而不是用 requests 去拿。这样可以绕开网络因素,让学生专注于“解析”本身。等他们已经熟悉了 BeautifulSoup 的 find 和 find_all 再切换到真实站点去解析,整个学习曲线会平滑很多。
3.3 解析与存储的组合拳:BeautifulSoup + 循环 + csv 写入的完整闭环
综合案例是讲义的核心章节。有了前面的基础,这里要把 requests、BeautifulSoup、csv 三个模块串成一个完整流程,目标是从一个公开的数据展示页抓取多行数据存成 csv,其中涉及翻页和字段清洗。
import csv import requests from bs4 import BeautifulSoup # 以某公开的静态列表页为例,正常课堂演示请在本地网络环境访问 base_url = "https://example.com/data/list?page={}" def fetch_page(page_num): resp = requests.get(base_url.format(page_num), timeout=(3, 5)) resp.raise_for_status() # 注意有些站点返回 gbk 编码,这里需要根据页面 meta 标签调整 resp.encoding = resp.apparent_encoding return resp.text def parse_page(html): soup = BeautifulSoup(html, "html.parser") # 假设列表页每条数据都在 li 标签下,标题和日期分别在 a 和 span 里 items = [] for li in soup.select("ul.list > li"): title_tag = li.find("a") date_tag = li.find("span", class_="date") if title_tag and date_tag: items.append([title_tag.text.strip(), date_tag.text.strip()]) return items all_data = [] for page in range(1, 4): # 抓取前 3 页 html = fetch_page(page) page_items = parse_page(html) all_data.extend(page_items) print(f"第 {page} 页抓取 {len(page_items)} 条") # 写入 csv:utf-8-sig 编码避免 Excel 打开出现乱码 with open("output.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["标题", "日期"]) writer.writerows(all_data) print("数据已保存到 output.csv")这段代码在讲义里的讲解要拆成三层。第一层是流程感:先定义两个函数分别负责请求和解析,主循环负责分页和控制流程,最后统一写文件,这样读代码的人一眼能看到整条流水线。第二层是参数意识:页面 URL 的页码参数用循环变量去替换,range(1, 4) 只抓 3 页,改这个值就能控制抓取量。第三层是防御意识:title_tag 和 date_tag 做空值判断是因为真实页面里总有一些行缺字段,直接取 text 会报 AttributeError。
存储格式这块用 csv 而不是 Excel,因为 csv 是纯文本格式,简单、通用、可追加、适合教学演示。写入时用 utf-8-sig 编码,这是给 Excel 用户的保底技巧。把这段存入讲义的“编码那些坑”里,学生会少走很多弯路。
3.4 翻页参数和 URL 设计:从写死到可配置的演变过程
在综合案例里,翻页是学生第一次接触“可配置”的概念。讲义要从需求出发去解释,而不是直接丢出参数化的代码。可以这样引导:如果只有一页数据,写死地址没问题;但真实场景有几十页,难道要复制粘贴几十次代码?自然引出翻页循环的做法。
接下来讲设计原则:URL 里的查询参数不要拼字符串拼接,而是用 requests 的 params 参数。这是很多资深开发者都在用的好习惯。代码改成这样更规整:
# 不直接拼接字符串,用 params 字典让 requests 处理编码和拼装 params = {"page": page, "size": 20, "keyword": "python"} resp = requests.get("https://example.com/data/list", params=params, timeout=(3, 5)) print(resp.url) # 可以看到真实请求的完整 URL 是 https://example.com/data/list?page=1&size=20&keyword=python print(resp.text[:200])这段代码放在讲义里的价值在于让学生看到:第一,requests 会自己处理 URL 编码,不需要手动做;第二,debug 时打印 resp.url 可以确认参数到底传对了没有;第三,真实项目的 URL 参数往往很多,用字典维护比用字符串拼接清晰得多。
例题配置里我会让学生尝试改参数值来改变返回结果,然后观察 URL 变化。这个练习本身能帮学生建立起“参数决定响应”的直觉。很多学生学到后面会一头扎进解析代码而忘了请求参数的调试,这个练习就是在提前帮他们改掉这个习惯。
4. 讲义教学的常见翻车现场:4 个高频坑与排查路径
4.1 编码乱码:所有人都遇到过的“锟斤拷”是怎么来的
现象:requests 拿到的文本内容打印出来全是乱码,中文变成了一堆看不懂的符号,或者 Excel 打开 csv 文件时中文直接变“锟斤拷”。
原因:服务器返回的编码和客户端解码时用的编码不一致。很多老旧站点用的是 gbk 编码,requests 默认用 utf-8 猜测,猜测失败就会乱。另一个坑是 csv 文件写入了 utf-8 编码,但 Excel 默认用 gbk 打开,中文就全花了。
解决:请求侧用 resp.encoding = resp.apparent_encoding 或者直接从响应头 Content-Type 里读 charset 参数。写入侧用 encoding="utf-8-sig" 保存 csv 而不是纯 utf-8。讲义里要同时写这两种场景下的处理代码,因为学生会分别在不同阶段遇到这两个坑。
4.2 SSL 证书报错:请求直接中断和解决方案的边界
现象:requests.get 直接抛异常,报错信息里有 SSL: CERTIFICATE_VERIFY_FAILED 字样,程序终止。很多学生的第一反应是“我的 Python 坏了”。
原因:目标站点证书链不完整,或者本机 Python 环境的 CA 证书库太旧。这不一定是因为访问了什么特殊网站,一些正规但运维粗糙的业务系统也会遇到这个情况。
解决:最稳妥的做法是升级 requests 和 certifi 库,而不是在代码里关掉证书验证。常见做法是 pip install --upgrade certifi。如果目标站点是内部测试环境,确实没有合法证书,可以在请求参数里加 verify=False,但要同时加一个警告注释。讲义里必须区分这两种情况,否则学生要么遇到生产环境风险,要么卡在环境问题上无法继续。
4.3 列表页抓取数据为空:选择器没有错的错觉
现象:单独测试选择器时能定位到元素,但放进完整代码里跑出来是空列表。学生反复检查,看不出任何问题。
原因:最常见的根源有三个。第一,页面内容是通过 JavaScript 异步加载的,requests 拿到的是 HTML 骨架而数据在后面才填进去;第二,代码里用了 select 但页面上有多个模块匹配,数据不在第一个匹配位置;第三,请求头缺失导致服务器返回了移动版页面或者反爬页面,结构完全不同。
解决:先打印前 500 字响应内容对比一下是否包含目标数据特征。如果能看到数据就直接改选择器;如果看不到,说明是动态加载。动态加载的入门处理方案是先讲“服务器端有没有返回 JSON 数据接口”,用浏览器开发者工具去抓数据接口,很多站点其实有隐藏的 JSON 接口可以直接拿数据,不一定非要上 Selenium。讲义里把这个排查流程写成一个决策分支列表,比直接给一个万能答案有用得多。
4.4 请求速度过快被封 IP:延迟控制与频率限制的入门策略
现象:前几页抓取正常,到后面突然拿不到数据或收到 403,有些站点还会直接限制整个网段的访问。
原因:初学者写循环抓取时没有考虑请求频率。一个 for 循环连续访问几十次,每分钟请求量远超正常读者的访问速度,触发站点反爬机制。
解决:最基础的方案是在循环体里加 time.sleep(random.uniform(1, 3)),把请求间隔控制在一个合理范围内。讲义里要解释清楚这个 sleep 的意义不在于精确延时,而在于让请求模式更像人的行为。进阶一点的做法是用一个请求计数变量记录已经发起的请求数。但讲到这里就可以了,不要展开讲代理池或分布式爬虫,那是进阶课的内容,写进入门讲义会冲淡主线。
5. 把静态讲义变成能自我验证的实操课:最后的三个课件打磨技巧
5.1 代码文件与 docx 分离:讲义只留关键片段,完整代码随附件分发
我在最终排版讲义时有一个习惯:docx 正文里的代码块只保留“核心逻辑片段”,完整可运行的代码文件随附件一并分发。这样做有两个原因。第一,讲义是给人读的,正文里塞一大段完整代码会把重点淹没,学生一眼看过去只觉得全是代码;第二,附件提供完整代码文件,学生可以直接运行验证,如果跑不通,停下来想一想哪里需要改,整个学习过程就活了起来。
附件可以打成 zip,内部按照章节编号组织文件。比如 c02_requests_demo.py、c03_spider_demo.py、c03_output.csv,每个文件头部写清楚运行环境、需要安装的依赖和预期输出。这样做的好处是学生不用自己从头把讲义抄成代码文件,节省时间的同时也能减少抄错引入的报错。
代码文件头部我会加一段 README 说明,把每个脚本运行时需要的 Python 版本和第三方库版本写清楚。这不难但很关键,很多新人一开始报错就是因为版本不兼容导致的,问题本身和爬虫逻辑毫无关系。
5.2 用“修改后结果”验证理解:每一章配一个破坏性练习
光让他跑通代码还不够,入门课真正有效的教学手段是破坏性练习。把一个能跑的代码修改一个位置让他猜结果,或者反过来给一个坏代码让他修。这个思路比“再写一个同样的爬虫”更能检验学生的理解深度。
在讲义里我会设计一个“故意写错”的小节。比如给出一个没有异常处理的请求代码,让学生运行并描述报错;再给出一个没有设置 encoding 的解析代码,让学生观察乱码现象并修复。这些练习做一遍,比单纯重复代码有用得多。学生通过自己动手修复,会对代码的行为产生直觉记忆,下次再遇到类似问题就不用绕弯了。
5.3 阶段成果的可视化:让学生体会到“我的代码在真实改变世界”
课程最后的收尾应该落在成果可视化上。让学生把自己抓取的数据清洗后生成一个简单的图表,就算只是用 matplotlib 画一个柱状图,也比单纯存 csv 有成就感得多。虽然图表不是爬虫本身的内容,但它提供了一种正向反馈,让学生体会到数据从获取到呈现的完整过程。
讲义可以在最后一章放一段极简的可视化代码示例如下。这段代码不算爬虫严格意义上的教学内容,但我会把它放进附件的 bonus 目录,因为它在引导学生“下一步可以往哪走”。爬虫入门课的目的不是培养爬虫工程师,而是让学生发现数据获取和分析的乐趣。这份 docx 最终的形态不只是一本操作手册,更是帮学生把“数据世界的地图”摊开在面前。
# 读取之前保存的 csv 数据,简单统计每天的数据量并画柱状图 import csv from collections import Counter with open("output.csv", encoding="utf-8-sig") as f: reader = csv.DictReader(f) dates = [row["日期"] for row in reader] date_count = Counter(dates) for date, count in sorted(date_count.items()): print(f"{date}: {count} 条")上面这段代码的价值体现在:第一,它复用了之前的 csv 输出,让学生明白每一步的产出都可以成为下一步的输入;第二,它接触到数据处理中的基础分组和聚合操作,顺带理解了为什么爬虫抓数据要规范清洗。这部分我自己的习惯是给学生演示一次,布置成课后思考,不占用正课时间,效果反而比正课讲还好。
写讲义这几年我最深的体会是,爬虫入门课真正难的不是爬虫技术本身,而是让零基础的人相信“我也能行”。课件里每一个能说明预期输出的注释、每一次对坑的提前预警、每一段能独立运行的完整代码,都是在降低学生挂在半路的概率。希望这份思路对正在做爬虫讲义的人有点帮助。
本文还有配套的精品资源,点击获取