简介:这份课程资源面向零基础或刚接触网络数据采集的初学者,用一份docx课件系统梳理Python爬虫的入门路径,帮助读者在动手写代码前先建立清晰的知识框架。内容从爬虫定义与四大步骤讲起,依次覆盖Python基础语法、HTML标签与文档结构、requests与BeautifulSoup等常用库的使用,并以抓取百度首页标题和图片作为实操案例,同时给出官方文档、简明教程与在线练习等延伸学习方向。资源包共1个docx文件,约100KB,篇幅精炼、结构完整,适合作为课堂讲义或自学提纲。目前已有228人学习,读者可据此快速了解爬虫工作原理、明确先修知识边界,并借助案例完成从概念理解到简单抓取的过渡,为后续深入学习HTTP协议、数据库与网络安全等内容打下基础。
1. 从一份 docx 课件说起:爬虫入门到底该先补哪几块
很多人第一次接触 Python 爬虫,是直接抄一段requests + BeautifulSoup的代码跑通,然后卡在第二个网站上——要么返回 403,要么解析出来一堆空列表,要么图片下载下来是 0 字节。翻车的根因往往不是库不会用,而是跳过了三块地基:Python 基础语法、HTML 结构认知、HTTP 请求与响应的基本模型。这份《Python 爬虫入门教程.docx》走的是另一条路,它先把「爬虫是什么、需要会什么」讲清楚,再用一个抓取首页标题和 logo 图片的最小案例把整条链路串起来,适合零基础或半路出家、想系统补齐概念再动手的从业者。课件本身是文档形态,不是源码包,所以它的价值在于知识骨架和可复现的示例代码,而不是拿来即跑的工具。
2. 拆开课件的知识骨架:四个前置问题与最小闭环
2.1 爬虫的本质是「请求—解析—存储—循环」
课件把网络爬虫归纳为四大步:根据 URL 获取 HTML 数据、解析 HTML 提取目标信息、存储数据、对新的 URL 重复第一步。这个抽象看着简单,但它决定了后面所有工具选型的边界。请求阶段关心的是协议、请求头、编码、超时和状态码;解析阶段关心的是文档结构、选择器表达能力和容错;存储阶段关心的是文件格式、去重和增量;循环阶段关心的是 URL 调度、限速和异常重试。初学者最容易犯的错是把四步揉成一坨脚本,出问题时无法定位是网络层、解析层还是存储层挂了。课件用「四大步」做切分,实际上是在教你建立分层排查的意识,这一点比记住某个 API 更有长期价值。
2.2 Python 要学到什么程度才够用
课件给出的判断标准很务实:数据类型、数据结构、运算符、逻辑结构、函数、文件 IO、错误处理这几样过关就能开始写爬虫,类、多线程、模块可以先放一放。这个取舍是合理的,因为入门阶段的爬虫脚本本质是「字符串处理 + 循环 + 异常捕获」的组合。但要注意一个隐含前提:列表、字典、字符串切片、if/for必须做到不查资料就能写,否则调试解析逻辑时会非常痛苦。课件建议用在线题单反复练手,这个思路对,但练习重点应该放在「从一段文本里按规则抽取字段」这类任务上,而不是纯语法题。下面这段代码就是入门阶段最典型的字符串与字典操作组合,能独立写出来并解释每一行,说明基础够用了:
# 模拟从一段伪 HTML 文本中抽取标题,练的是字符串定位与切片 raw = '<html><head><title>示例站点首页</title></head><body></body></html>' start = raw.find('<title>') + len('<title>') # 找到开始标签结束位置 end = raw.find('</title>') # 找到结束标签起始位置 title = raw[start:end] # 切片取出中间文本 info = {'title': title, 'length': len(title)} # 用字典承载结构化结果 print(info)这段代码没有用任何第三方库,目的是把「定位—截取—结构化」的动作拆开。find返回 -1 时切片会得到错误结果,所以真实脚本里必须加存在性判断,这正是课件强调错误处理的原因。参数上唯一需要注意的是len('<title>')不能写死成 7,标签一变就错,养成用常量表达式的习惯能少踩很多坑。
2.3 HTML 不是编程语言,但必须看得懂嵌套
课件花了不少篇幅讲 HTML 的标签、属性、成对标签与空元素,这个投入是值得的。爬虫解析的本质是在一棵 DOM 树上按路径找节点,如果你看不懂<img class="..." src="...">里哪个是类名、哪个是资源地址,后面用任何选择器都是盲猜。入门阶段不需要学 CSS 布局,但必须掌握三件事:标签的嵌套关系、属性的键值对形式、以及「数据可能藏在文本节点也可能藏在属性里」这个事实。课件用 Hello World 页面做示例,结构清晰,适合对照浏览器开发者工具逐层展开看。一个实用习惯是:拿到目标页先按 F12 看 Elements 面板,手动折叠几层,确认目标数据所在的标签路径,再回到代码里写选择器,比直接猜选择器快得多。
2.4 请求与解析的最小可运行闭环
课件给出的完整案例是抓取某首页的标题和 logo 图片,链路是urlopen取 HTML、BeautifulSoup解析、find_all定位图片、urlretrieve下载。这套组合用的是标准库加一个第三方解析库,依赖少、概念少,适合第一次跑通。把课件里的示例整理成可直接执行的脚本如下:
from urllib.request import urlopen, urlretrieve from bs4 import BeautifulSoup # 第一步:发请求拿 HTML,read() 得到 bytes,必须显式解码 resp = urlopen("https://www.example.com/", timeout=10) html_bytes = resp.read() html_text = html_bytes.decode('utf-8', errors='ignore') # 第二步:解析成结构化对象,html.parser 是标准库解析器,无需额外安装 soup = BeautifulSoup(html_text, 'html.parser') # 第三步:按标签路径取标题 title = soup.head.title print('标题:', title.get_text(strip=True) if title else '未找到') # 第四步:定位图片标签,按 class 过滤后取 src 属性 imgs = soup.find_all('img', class_='index-logo-src') if imgs: src = imgs[0].get('src', '') # 协议相对地址需要补全,这是课件示例里容易被忽略的一步 if src.startswith('//'): src = 'https:' + src urlretrieve(src, 'logo.png') print('已保存:', src) else: print('未匹配到目标图片标签')逐段说明:timeout=10是必须加的,默认无超时会卡死;decode显式指定编码并加errors='ignore',是因为部分页面声明编码与实际字节不一致,硬解码会直接抛异常;html.parser是内置解析器,速度一般但零依赖,入门够用;get_text(strip=True)比直接打印标签对象更干净;find_all的class_下划线不能省,因为class是 Python 关键字;get('src', '')带默认值可以避免属性缺失时抛KeyError。协议相对地址补全是课件示例里没展开但实际必踩的点,很多站点图片地址以//开头,直接传给下载函数会失败。
3. 把示例改造成可复用脚本:参数、编码与下载策略
3.1 请求头与超时:让脚本看起来像正常访问
课件示例用的是最裸的urlopen,没有设置请求头。这在教学场景没问题,但换到真实站点,默认的 User-Agent 很容易被识别为非浏览器流量而返回 403 或空内容。入门阶段不需要上复杂的会话管理,但至少要把请求头、超时、重试次数做成可配置参数。常见做法是构造一个Request对象并塞入User-Agent,同时给urlopen传timeout。参数怎么定:超时 10 到 15 秒对大多数页面够用,内网或慢站点可以放宽到 30 秒;重试次数 2 到 3 次即可,再多说明目标本身不稳定,应该换策略而不是硬扛。下面是一个带请求头和简单重试的请求函数:
from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError import time def fetch_html(url, retries=3, timeout=15): headers = { # 用常见浏览器 UA,避免被默认拦截 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Accept-Language': 'zh-CN,zh;q=0.9', } last_err = None for i in range(retries): try: req = Request(url, headers=headers) with urlopen(req, timeout=timeout) as resp: # 按响应头声明的编码解码,缺失时回退 utf-8 charset = resp.headers.get_content_charset() or 'utf-8' return resp.read().decode(charset, errors='ignore') except (HTTPError, URLError, TimeoutError) as e: last_err = e time.sleep(1 + i) # 退避等待,避免连续打同一目标 raise RuntimeError(f'请求失败: {last_err}') html = fetch_html('https://www.example.com/') print(len(html))逻辑上,HTTPError覆盖 4xx/5xx,URLError覆盖 DNS 失败和连接拒绝,TimeoutError覆盖读超时,三类分开捕获便于后续按类型做不同处理。退避等待用1 + i是入门级写法,真实项目会用指数退避,但入门阶段先建立「失败要等一等再试」的意识更重要。get_content_charset()从响应头取编码,比硬编码utf-8稳,但仍有站点不声明编码,所以保留errors='ignore'兜底。
3.2 解析器的选择与选择器写法
课件用html.parser,这是零依赖的稳妥选择。如果目标页面结构混乱、标签闭合不规范,html.parser可能解析出意料之外的树,这时可以换lxml,速度快、容错强,但需要额外安装。入门阶段建议先用html.parser跑通,遇到解析结果和浏览器不一致再换,不要一上来就堆依赖。选择器写法上,课件用的是find_all('img', class_='...'),这种按标签加属性的组合定位最直观。更通用的做法是 CSS 选择器select('img.index-logo-src'),表达力更强,但需要你对 CSS 选择器语法有基本认知。两种写法没有优劣,关键是定位要「窄」:只按标签名find_all('img')会拿到页面所有图片,过滤成本高且容易误伤;加上 class 或父级路径约束,命中率会明显提升。参数上注意class_匹配的是完整类名之一,如果元素有多个类名,用class_='a'仍能命中,但用 CSS 选择器时.a同理,不要写成完全相等匹配的思维。
3.3 图片下载的三种落盘方式与边界
课件用urlretrieve直接下载,简单但有三个边界:不返回响应状态、无法设置请求头、大文件下载没有进度。入门够用,稍微认真一点可以用urlopen读字节再写文件,这样能复用前面的请求头逻辑;再进一步可以用流式读写控制内存。三种方式的取舍如下表:
| 方式 | 依赖 | 能否带请求头 | 适用场景 |
|---|---|---|---|
urlretrieve | 标准库 | 否 | 教学示例、小文件快速验证 |
urlopen+ 写文件 | 标准库 | 是 | 需要统一请求头、需要判断状态码 |
| 流式分块读写 | 标准库 | 是 | 大文件、需要控制内存占用 |
用urlopen写文件的版本大致是这样:先Request带 UA 发请求,检查resp.status是否为 200,再open(path, 'wb')分块写入。参数上,块大小 8KB 到 64KB 都常见,太小系统调用多,太大内存占用高。文件名要做安全处理,从 URL 里截取的文件名可能带查询串或非法字符,入门阶段至少把?之后的部分去掉,避免写文件失败。
4. 避坑与排查:入门脚本最常见的五类翻车
4.1 现象:脚本能跑但解析结果全是空列表
原因通常是请求拿到的不是目标 HTML,而是验证页、跳转页或压缩后的字节流。解决顺序是:先把html_text的前 500 个字符打印出来看,确认是不是正常文档;再检查状态码和最终 URL 是否被重定向;最后确认编码解码是否正确,乱码会导致标签名匹配失败。这一步不要跳过,很多「选择器写错」的结论其实是请求层就错了。
4.2 现象:本地浏览器能看到数据,脚本里找不到
原因是页面数据由前端脚本渲染,初始 HTML 里根本没有目标节点。课件的示例针对的是静态 HTML,这个边界必须说清楚。判断方法是关掉浏览器脚本或直接查看「查看网页源代码」,如果源码里没有目标文本,解析库再强也取不到。入门阶段的处理方式是换静态页面练手,或者寻找页面内嵌的初始数据块,不要一上来就啃动态渲染。
4.3 现象:图片下载下来是 0 字节或打不开
原因多是地址不完整(协议相对地址、相对路径)或请求被拒。解决是先打印最终下载地址,确认以http开头;再用请求函数单独访问该地址看状态码;如果是相对路径,用urllib.parse.urljoin(base, src)拼接,不要手工拼字符串。另外注意保存时用二进制模式wb,文本模式写图片必然损坏。
4.4 现象:中文标题打印出来是乱码
原因是解码编码与页面实际编码不一致。解决是优先用响应头里的charset,没有再用页面meta声明,两者都没有再回退utf-8。不要用errors='ignore'掩盖问题后就当没事,忽略字符多了说明编码判断错了,应该回头查而不是将就。
4.5 现象:连续请求几次后开始超时或被拒
原因是请求频率过高或缺少必要请求头。解决是加固定间隔、限制并发、补全User-Agent和Accept类请求头。入门脚本最容易写成for循环里裸请求,几十次之后目标侧限流,表现为超时或 403。把间隔做成参数,默认 1 秒起步,比事后调参省事。
5. 从课件到练习:把最小案例扩成可验证的小工具
课件给的是单页抓取,真正让入门知识落地的方式是把它扩成一个能重复运行、有输出校验的小工具。我的习惯是给每个练习脚本加一个「自检」环节:抓完标题后断言非空、抓完图片后检查文件大小大于 0、记录本次请求耗时和状态码。这样每次改动选择器或请求参数,都能立刻知道有没有退化,而不是靠肉眼扫输出。下面是一个带自检的收尾版本,把前面的请求函数、解析、下载串起来:
import os from urllib.parse import urljoin def crawl_homepage(url, img_path='logo.png'): html = fetch_html(url) # 复用第 3 章的请求函数 soup = BeautifulSoup(html, 'html.parser') title_node = soup.head.title if soup.head else None title = title_node.get_text(strip=True) if title_node else '' assert title, '标题为空,检查请求结果或选择器' # 自检一:标题必须非空 imgs = soup.find_all('img', class_='index-logo-src') assert imgs, '未匹配到图片标签,检查 class 名' src = urljoin(url, imgs[0].get('src', '')) # 统一处理相对与协议相对地址 with urlopen(Request(src, headers={'User-Agent': 'Mozilla/5.0'}), timeout=15) as r: data = r.read() assert len(data) > 1024, '图片过小,可能下载失败' # 自检二:文件大小下限 with open(img_path, 'wb') as f: f.write(data) print(f'标题={title} 图片={img_path} 大小={os.path.getsize(img_path)}B') return title, img_path crawl_homepage('https://www.example.com/')关键点在两个断言:标题非空和图片大小下限。前者能第一时间暴露请求层或解析层问题,后者能拦住 0 字节和错误页被当成图片保存的情况。urljoin统一处理地址拼接,比手工判断//前缀更通用。参数上,图片大小下限设 1KB 是经验值,logo 类图片通常远大于此,设太小起不到校验作用。跑通之后可以做的进阶练习:把单页扩成「列表页取链接、详情页取字段」的两级结构,这时才会真正遇到 URL 去重、翻页边界和字段缺失的问题,也才算把课件的四大步完整走了一遍。从那以后我每次写抓取脚本,都会先把自检断言写进去再写业务逻辑,宁可多写两行校验,也不想在几十条结果里靠肉眼找哪一条是空的。希望帮到你。
本文还有配套的精品资源,点击获取