技术探索与实践踩坑记录:从零开始写一个简历爬虫

萧超
2025-12-13 05:03
阅读 437

作者说:我是开源项目维护者,也是一名技术讲师。这几年带过不少刚入门的朋友,发现很多人对“爬虫”既好奇又害怕——好奇它能自动抓取数据,又怕一不小心就违法或把网站搞崩。其实,只要理解基本原理、遵守规则,爬虫是一个非常实用的工具。我当初学的时候,也是从一行 print("Hello World") 开始,后来为了整理招聘信息,才尝试写第一个爬虫。今天这篇教程,就是想带你用最安全、最清晰的方式,亲手完成一个“爬取公开简历信息”的小项目,并在这个过程中建立正确的架构思维。


一、什么是爬虫?它能用来做什么?

简单说,爬虫(Web Crawler / Scraper) 就是模拟人类浏览网页的程序。你打开浏览器输入网址 → 网站返回 HTML 页面 → 你看到内容;爬虫做的是同样的事,但它会自动解析 HTML,提取出你关心的数据(比如职位名称、公司、薪资等),然后保存下来。

爬虫的典型用途

  • 合法用途
    • 抓取公开的招聘数据,分析岗位趋势
    • 采集新闻、天气、股票等公开信息
    • 为自己的简历项目收集素材(本文重点!)
  • 非法/高风险用途
    • 爬取用户隐私(如未公开的简历)
    • 高频请求导致服务器崩溃
    • 绕过登录验证获取付费内容

📌 重要提醒:本文所有示例均基于公开、可爬取、允许 robots 协议的网站。切勿用于商业爬取或侵犯他人隐私!


二、环境准备:5 分钟搭建开发环境

我们使用 Python,因为它语法简单、生态强大。以下是具体步骤:

1. 安装 Python

验证安装:

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)

排查步骤

  1. 打印 response.text,确认 HTML 中确实有该元素
  2. 在浏览器按 F12 查看真实 class 名
  3. 如果是动态内容,考虑改用 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

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝