技术探索与实践踩坑记录:从零开始写一个简历爬虫
作者说:我是开源项目维护者,也是一名技术讲师。这几年带过不少刚入门的朋友,发现很多人对“爬虫”既好奇又害怕——好奇它能自动抓取数据,又怕一不小心就违法或把网站搞崩。其实,只要理解基本原理、遵守规则,爬虫是一个非常实用的工具。我当初学的时候,也是从一行
print("Hello World")开始,后来为了整理招聘信息,才尝试写第一个爬虫。今天这篇教程,就是想带你用最安全、最清晰的方式,亲手完成一个“爬取公开简历信息”的小项目,并在这个过程中建立正确的架构思维。
一、什么是爬虫?它能用来做什么?
简单说,爬虫(Web Crawler / Scraper) 就是模拟人类浏览网页的程序。你打开浏览器输入网址 → 网站返回 HTML 页面 → 你看到内容;爬虫做的是同样的事,但它会自动解析 HTML,提取出你关心的数据(比如职位名称、公司、薪资等),然后保存下来。
爬虫的典型用途
- ✅ 合法用途:
- 抓取公开的招聘数据,分析岗位趋势
- 采集新闻、天气、股票等公开信息
- 为自己的简历项目收集素材(本文重点!)
- ❌ 非法/高风险用途:
- 爬取用户隐私(如未公开的简历)
- 高频请求导致服务器崩溃
- 绕过登录验证获取付费内容
📌 重要提醒:本文所有示例均基于公开、可爬取、允许 robots 协议的网站。切勿用于商业爬取或侵犯他人隐私!
二、环境准备:5 分钟搭建开发环境
我们使用 Python,因为它语法简单、生态强大。以下是具体步骤:
1. 安装 Python
- 访问 https://www.python.org/downloads/
- 下载最新稳定版(如 Python 3.10+)
- 安装时勾选 “Add to PATH”
验证安装:
python --version
# 应输出类似:Python 3.10.12
2. 创建虚拟环境(推荐)
避免污染全局包:
# 创建项目文件夹
mkdir resume-crawler && cd resume-crawler
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境(Windows)
venv\Scripts\activate
# 激活虚拟环境(Mac/Linux)
source venv/bin/activate
3. 安装核心依赖
pip install requests beautifulsoup4 lxml pandas
| 包名 | 作用 |
|---|---|
requests |
发送 HTTP 请求(模拟浏览器访问) |
beautifulsoup4 |
解析 HTML,提取数据 |
lxml |
更快的 HTML/XML 解析器(BeautifulSoup 的后端) |
pandas |
保存和处理结构化数据 |
💡 避坑指南:不要用
urllib手动拼接请求头!requests更简洁安全。
三、核心概念:用大白话讲清楚关键点
1. HTTP 请求与响应
- 你访问
https://example.com→ 浏览器发送 GET 请求 - 服务器返回 HTML 内容 + 状态码(如 200 表示成功)
import requests
response = requests.get("https://httpbin.org/get")
print(response.status_code) # 200
print(response.text) # 返回的 JSON 字符串
2. HTML 结构与选择器
网页由标签组成,比如:
<div class="job-title">Python工程师</div>
<span class="salary">15k-25k</span>
我们要用 CSS 选择器 或 XPath 定位这些元素。
.job-title→ 选择class="job-title"的元素span.salary→ 选择class="salary"的<span>
3. Robots 协议:网站的“爬虫规则”
每个网站根目录下可能有 robots.txt,比如:
User-agent: *
Disallow: /private/
Allow: /public/
表示:所有爬虫(*)禁止访问 /private/,但允许访问 /public/。
务必先检查目标网站的 robots.txt!
四、实战项目:爬取公开简历模板网站
⚠️ 注意:我们不爬真实简历(涉及隐私),而是爬公开的简历模板展示页,比如一些提供“简历样例”的教学网站。
目标网站选择
我们使用一个虚构但结构典型的公开页面(实际教学中可用 https://quotes.toscrape.com 练习)。
假设目标 URL:https://resume-examples.demo/public/
页面结构如下:
<div class="resume-card">
<h2 class="name">张三</h2>
<p class="title">前端工程师</p>
<p class="experience">3年经验</p>
</div>
<div class="resume-card">
<h2 class="name">李四</h2>
<p class="title">数据分析师</p>
<p class="experience">2年经验</p>
</div>
步骤 1:发送请求并检查响应
import requests
url = "https://resume-examples.demo/public/"
headers = {
"User-Agent": "Mozilla/5.0 (compatible; ResumeCrawler/1.0)"
}
response = requests.get(url, headers=headers)
print("状态码:", response.status_code)
print("前200字符:", response.text[:200])
✅ 最佳实践:设置
User-Agent表明身份,避免被误判为恶意爬虫。
步骤 2:解析 HTML 并提取数据
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, 'lxml')
# 找到所有简历卡片
cards = soup.find_all('div', class_='resume-card')
resumes = []
for card in cards:
name = card.find('h2', class_='name').text.strip()
title = card.find('p', class_='title').text.strip()
experience = card.find('p', class_='experience').text.strip()
resumes.append({
"姓名": name,
"职位": title,
"经验": experience
})
print(resumes)
步骤 3:保存为 CSV 文件(方便放进简历项目!)
import pandas as pd
df = pd.DataFrame(resumes)
df.to_csv("resume_samples.csv", index=False, encoding='utf-8-sig')
print("✅ 数据已保存至 resume_samples.csv")
💡 为什么用
utf-8-sig?避免 Excel 打开中文乱码。
完整代码整合
# resume_crawler.py
import requests
from bs4 import BeautifulSoup
import pandas as pd
def crawl_resume_samples():
url = "https://resume-examples.demo/public/"
headers = {"User-Agent": "ResumeCrawler/1.0 (+https://yourblog.com)"}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 检查 HTTP 错误
soup = BeautifulSoup(response.text, 'lxml')
cards = soup.find_all('div', class_='resume-card')
resumes = []
for card in cards:
name = card.find('h2', class_='name').text.strip()
title = card.find('p', class_='title').text.strip()
experience = card.find('p', class_='experience').text.strip()
resumes.append({"姓名": name, "职位": title, "经验": experience})
df = pd.DataFrame(resumes)
df.to_csv("resume_samples.csv", index=False, encoding='utf-8-sig')
print(f"✅ 成功抓取 {len(resumes)} 条简历样例!")
except Exception as e:
print(f"❌ 爬取失败: {e}")
if __name__ == "__main__":
crawl_resume_samples()
五、新手常见问题 & 踩坑记录
❓ Q1:为什么我的请求返回 403 Forbidden?
原因:网站检测到你是爬虫(缺少 User-Agent 或请求频率过高)。
解决:
- 添加合理的
User-Agent - 加入延迟:
time.sleep(1)每次请求后等待 1 秒 - 使用代理池(进阶)
❓ Q2:为什么找不到元素?find() 返回 None?
原因:
- 网页结构变了(动态加载?)
- class 名写错了(注意空格、大小写)
- 内容由 JavaScript 动态生成(需用 Selenium)
排查步骤:
- 打印
response.text,确认 HTML 中确实有该元素 - 在浏览器按 F12 查看真实 class 名
- 如果是动态内容,考虑改用
selenium
❓ Q3:这样爬简历会不会违法?
绝对安全的前提:
- 目标数据是公开展示的(非登录后可见)
- 遵守
robots.txt - 请求频率低(≤1 次/秒)
- 不用于商业用途
- 在
User-Agent中留下联系方式
📜 法律底线:《网络安全法》规定不得非法获取个人信息。真实简历 ≠ 公开简历模板!
六、学习建议:下一步怎么走?
初学者路线图
1. 掌握基础爬虫(本文内容) →
2. 学习处理分页、翻页 →
3. 处理登录与 Cookie →
4. 应对反爬(验证码、IP 封禁) →
5. 使用 Scrapy 框架构建大型爬虫
推荐练习项目(安全合法)
| 项目 | 技术点 | 数据用途 |
|---|---|---|
| 爬取 GitHub Trending | 分页、JSON 解析 | 分析热门技术 |
| 抓取天气预报 | API 调用 | 做天气小工具 |
| 采集豆瓣电影 Top250 | CSS 选择器、CSV 导出 | 练手数据分析 |
架构设计思考(进阶)
当你项目变大,要思考:
- 可维护性:把解析逻辑、存储逻辑分离
- 可扩展性:支持新增网站只需改配置
- 健壮性:异常重试、日志记录、断点续爬
例如,用配置驱动:
# sites.yaml
- name: resume_demo
url: https://resume-examples.demo/public/
selectors:
card: "div.resume-card"
name: "h2.name"
title: "p.title"
结语:技术是工具,责任在手中
我当初写第一个爬虫时,也担心会不会“闯祸”。但只要守住合法、合理、透明三条底线,爬虫就能成为你探索数据世界的望远镜。这篇文章里的简历爬虫,不仅能帮你理解 Web 技术,还能作为你 GitHub 项目的一部分——面试时展示:“这是我写的爬虫,抓取公开数据用于学习”。
记住:最好的学习,是在实践中反思架构;最稳的成长,是在边界内大胆探索。
本文代码已开源在 GitHub(虚构链接):
github.com/yourname/resume-crawler-demo
欢迎 Star & 提 Issue!

评论 0