简介:本文为零基础读者梳理Python爬虫的完整入门路径:从明确目标与网页结构分析,到正则解析HTML、字符集、贪婪与非贪婪匹配与调试排错,再到两级匹配、数据精炼与key函数排序,并覆盖函数组织与注释规范,配合可运行代码示例,助你独立完成一次干净、可维护的网页数据采集任务。
初学者最常见的误区是拿到网址就急着写代码,结果页面结构没看懂、字段没圈定,写出的正则一旦页面改版就全部失效。规范的做法是把前期分析当作一个独立阶段来完成:
一条通用的开发流程可以概括为:明确目的 → 分析页面 → 抓取HTML → 正则提取 → 数据精炼 → 排序输出 → 持久化存储。前期分析做得越扎实,后续每一步的返工成本就越低。
在数据直接嵌在HTML中的场景下,正则是入门成本最低的解析手段,核心思路是“先定位、再取值”。
(.*?)提取标签之间的文本;先用re.compile编译正则以便复用,再用re.findall一次性取出所有匹配结果。
import reimport urllib.request# 第一步:抓取页面TARGET_URL = "http://<目标站点>/list" # 替换为实际页面地址with urllib.request.urlopen(TARGET_URL) as resp:html = resp.read().decode("utf-8")# 第二步:定位标签并捕获内容pattern = re.compile(r'<span class="item-name">(.*?)</span>')names = pattern.findall(html)print(names) # ['条目A', '条目B', ...]
\d(数字)、\w(字母数字与下划线)、\s(空白字符)、.(换行以外的任意字符),以及方括号自定义集合如[a-zA-Z0-9]。用字符集代替手写枚举,正则既简洁又不容易遗漏边界情况。.*会匹配尽可能多的字符,称为贪婪匹配;加上问号写成.*?则匹配尽可能少的字符,称为非贪婪匹配。解析HTML时几乎总应使用非贪婪模式,否则一条贪婪匹配可能把多个条目连成一大段错误结果。re.findall返回结果的差异,这是理解贪婪机制最直观的方式:
html = "<li>A</li><li>B</li>"print(re.findall(r"<li>(.*)</li>", html)) # 贪婪: ['A</li><li>B']print(re.findall(r"<li>(.*?)</li>", html)) # 非贪婪: ['A', 'B']
r'class="hot">(\d[\d,]*万?)<'既限定了数字开头,又兼容千分位逗号与“万”字单位。爬虫的报错高度集中在三类,对应三类排查手段:
UnicodeDecodeError)需确认页面实际编码并正确decode;网络错误(超时、状态码异常)需用try/except包裹请求;匹配错误(返回空列表或结果偏多、偏少)则需要回头修正正则。html变量是否抓到完整源码、每个正则的返回值是什么形态。实际页面里,名称和数值往往分散在同一层级的不同标签中,一条正则很难同时捕获。此时推荐“两级匹配”策略:
# re.S 让 . 可以匹配换行,适配多行标签块items = re.findall(r'<div class="item">(.*?)</div>', html, re.S)result = []for block in items:name = re.search(r'class="title">(.*?)<', block).group(1)count = re.search(r'class="hot">(\d[\d,]*万?)<', block).group(1)result.append((name, count))
其原理是“先粗后细”:粗匹配负责切分结构,细匹配负责提取字段。相比一条大正则硬扛,两级匹配更易读、更易改,页面微调时往往只需修改其中一层。
数据精炼与HTML提取是两个概念:提取解决“从无到有”,精炼解决“从有到优”,它属于业务处理阶段的收尾环节,通常包括:
strip()一键清除;"\n".join()把列表合并为单一字符串,方便打印或写文件;map对整列数据做统一清洗,一行完成遍历。
names = list(map(lambda x: x.strip(), names))text = "\n".join(names)
拿到数据后常需生成排行榜,这里藏着新手最容易踩的几个坑:
sorted()返回新列表、不改原数据;list.sort()原地排序、返回None。忘了这一点,把sort()的返回值赋给变量只会得到None。(名称, 数值)元组必须通过key指定按哪一列排序,否则会按名称的字典序排列。reverse=True即可。"1.2万"、"3,456"是带单位的字符串,直接排序会按字典序得到错误顺序。正确做法是在key函数里先归一化为数值:
def to_number(s: str) -> int:"""把 '3,456' / '1.2万' 等字符串统一转换为整数"""s = s.replace(",", "")if s.endswith("万"):return int(float(s[:-1]) * 10000)return int(float(s))ranking = sorted(result, key=lambda x: to_number(x[1]), reverse=True)
re、URL请求在urllib、表格文件在csv),比死记函数签名高效得多。enumerate同时拿下标与值,zip并行遍历多个列表,推导式一行完成过滤与转换。爬虫入门的关键不是背正则,而是建立“分析 → 提取 → 精炼 → 输出”的完整数据思维。把前期分析做扎实,掌握非贪婪匹配、两级匹配与key函数排序这几个关键点,再配合清晰的函数组织与注释习惯,你就能独立交付一次干净、健壮、可维护的网页数据采集任务——这也是从“会写爬虫”迈向“会做数据工程”的第一步。