Python爬虫零基础入门【第三章:Requests 静态爬取入门·第1节】你的第一个爬虫:抓取页面并保存 HTML!

🔥本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~持续更新中!!

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [📌 上期回顾](#📌 上期回顾)
      • [🎯 本节目标](#🎯 本节目标)
      • [一、requests 库快速入门](#一、requests 库快速入门)
        • [1.1 为什么选择 requests?](#1.1 为什么选择 requests?)
        • [1.2 最简单的请求](#1.2 最简单的请求)
        • [1.3 requests 的核心方法](#1.3 requests 的核心方法)
      • 二、编写第一个爬虫脚本
        • [2.1 基础版本:最小可用](#2.1 基础版本:最小可用)
        • [2.2 改进版本:添加异常处理](#2.2 改进版本:添加异常处理)
      • [三、保存 HTML 到本地(核心技能)](#三、保存 HTML 到本地(核心技能))
        • [3.1 为什么要保存原始 HTML?](#3.1 为什么要保存原始 HTML?)
        • [3.2 基础保存方法](#3.2 基础保存方法)
        • [3.3 文件命名规范(重要!)](#3.3 文件命名规范(重要!))
        • [3.4 带元数据的保存方式](#3.4 带元数据的保存方式)
      • 四、完整的爬虫脚本(可复用模板)
      • 五、验证保存的文件
        • [5.1 用浏览器打开](#5.1 用浏览器打开)
        • [5.2 统计保存的文件](#5.2 统计保存的文件)
      • 六、本节小结
      • [📝 课后作业(必做,验收进入下一节)](#📝 课后作业(必做,验收进入下一节))
      • [🔮 下期预告](#🔮 下期预告)
      • 🌟文末
        • [📌 专栏持续更新中|建议收藏 + 订阅](#📌 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 👉 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅/关注专栏《Python爬虫实战》

订阅后更新会优先推送,按目录学习更高效~

📌 上期回顾

在第二章《网页基础》中,我们系统学习了网页基础知识:HTTP 请求响应机制、HTML 结构解析、JSON 数据处理,以及数据质量控制。现在,你已经掌握了爬虫的理论基础。

从本章开始,我们进入实战编码阶段!你将亲手编写真正可用的爬虫程序,从最简单的"抓取单个页面"开始,逐步构建工程化的采集系统。

🎯 本节目标

通过本节学习,你将能够:

  1. 使用 requests 库发起 HTTP 请求
  2. 正确处理响应内容(编码、状态码)
  3. 保存原始 HTML 到本地(便于调试复现)
  4. 设计合理的文件命名规范
  5. 实现基本的异常捕获和错误处理
  6. 交付验收:成功抓取一个网页并保存到本地,文件可用浏览器打开

一、requests 库快速入门

1.1 为什么选择 requests?

对比标准库 urllib

python 复制代码
# ❌ urllib:代码冗长
import urllib.request
req = urllib.request.Request(url, headers={'User-Agent': '...'})
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')

# ✅ requests:简洁优雅
import requests
response = requests.get(url, headers={'User-Agent': '...'})
html = response.text

requests 的优势

  • ✅ 语法简洁,人类友好
  • ✅ 自动处理编码
  • ✅ 内置会话管理
  • ✅ 强大的异常处理
  • ✅ 社区活跃,文档完善
1.2 最简单的请求
python 复制代码
import requests

# 最基础的 GET 请求
response = requests.get('https://httpbin.org/get')

# 查看响应信息
print(f"状态码: {response.status_code}")
print(f"内容类型: {response.headers['Content-Type']}")
print(f"响应内容: {response.text[:200]}")  # 前 200 字符
1.3 requests 的核心方法
python 复制代码
import requests

# GET 请求(获取资源)
response = requests.get(url)

# POST 请求(提交数据)
response = requests.post(url, data={'key': 'value'})

# 带参数的请求
params = {'page': 1, 'size': 20}
response = requests.get(url, params=params)

# 带请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
    'Referer': 'https://example.com/'
}
response = requests.get(url, headers=headers)

# 带超时设置(重要!)
response = requests.get(url, timeout=10)  # 10秒超时

二、编写第一个爬虫脚本

2.1 基础版本:最小可用
python 复制代码
"""
第一个爬虫:抓取单个网页
文件名:spider_v1.py
"""

import requests

def fetch_page(url):
    """
    抓取网页内容
    
    Args:
        url: 目标URL
        
    Returns:
        str: 网页HTML内容
    """
    # 设置请求头(模拟浏览器)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    # 发起请求
    response = requests.get(url, headers=headers, timeout=10)
    
    # 检查状态码
    if response.status_code == 200:
        return response.text
    else:
        print(f"请求失败,状态码:{response.status_code}")
        return None

# 使用示例
if __name__ == "__main__":
    url = "https://news.sina.com.cn/"
    html = fetch_page(url)
    
    if html:
        print(f"✅ 成功获取网页,长度:{len(html)} 字符")
        print(html[:500])  # 打印前 500 字符预览
    else:
        print("❌ 获取失败")

运行测试

bash 复制代码
python spider_v1.py

期望输出

json 复制代码
✅ 成功获取网页,长度:123456 字符
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>新浪首页</title>
...
2.2 改进版本:添加异常处理
python 复制代码
"""
改进版爬虫:添加完整的错误处理
文件名:spider_v2.py
"""

import requests
from requests.exceptions import RequestException, Timeout, ConnectionError

def fetch_page(url, timeout=10):
    """
    抓取网页内容(带错误处理)
    
    Args:
        url: 目标URL
        timeout: 超时时间(秒)
        
    Returns:
        str: 网页HTML内容,失败返回 None
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    try:
        print(f"正在请求: {url}")
        response = requests.get(url, headers=headers, timeout=timeout)
        
        # 检查状态码
        response.raise_for_status()  # 4xx/5xx 会抛出异常
        
        # 处理编码
        if response.encoding == 'ISO-8859-1':
            response.encoding = response.apparent_encoding
        
        print(f"✅ 请求成功,状态码:{response.status_code}")
        return response.text
        
    except Timeout:
        print(f"❌ 请求超时:{url}")
        return None
    
    except ConnectionError:
        print(f"❌ 连接错误:{url}")
        return None
    
    except RequestException as e:
        print(f"❌ 请求失败:{e}")
        return None
    
    except Exception as e:
        print(f"❌ 未知错误:{e}")
        return None

# 测试多个URL
if __name__ == "__main__":
    test_urls = [
        "https://news.sina.com.cn/",
        "https://httpbin.org/status/404",  # 测试 404
        "https://httpbin.org/delay/15",    # 测试超时
    ]
    
    for url in test_urls:
        print(f"\n{'='*60}")
        html = fetch_page(url, timeout=5)
        if html:
            print(f"成功获取 {len(html)} 字符")
        print(f"{'='*60}")

三、保存 HTML 到本地(核心技能)

3.1 为什么要保存原始 HTML?

工程化理由

  1. 调试便利:可以离线分析页面结构
  2. 可复现性:出问题时能回溯原始数据
  3. 法律保护:证明采集时的页面内容
  4. 版本对比:对比网站改版前后的变化
  5. 减少重复请求:避免频繁访问同一页面
3.2 基础保存方法
python 复制代码
import os
from datetime import datetime

def save_html(html, filename):
    """
    保存 HTML 到本地文件
    
    Args:
        html: HTML 内容
        filename: 文件名
    """
    # 确保目录存在
    os.makedirs('data/raw', exist_ok=True)
    
    # 完整路径
    filepath = os.path.join('data/raw', filename)
    
    # 写入文件
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(html)
    
    print(f"✅ 已保存到:{filepath}")

# 使用示例
html = fetch_page("https://news.sina.com.cn/")
if html:
    save_html(html, "sina_homepage.html")
3.3 文件命名规范(重要!)

不好的命名

python 复制代码
# ❌ 太简单,容易覆盖
"page.html"

# ❌ 没有时间信息
"sina.html"

# ❌ 特殊字符会报错
"https://news.sina.com.cn/.html"

推荐的命名规范

python 复制代码
def generate_filename(url, prefix="page"):
    """
    生成标准化的文件名
    
    Args:
        url: 页面URL
        prefix: 文件名前缀
        
    Returns:
        str: 文件名
        
    格式:prefix_域名_时间戳.html
    示例:page_sina.com.cn_20250121_103045.html
    """
    from urllib.parse import urlparse
    import re
    
    # 提取域名
    parsed = urlparse(url)
    domain = parsed.netloc.replace('www.', '')
    
    # 清理域名中的特殊字符
    domain = re.sub(r'[^\w\.]', '_', domain)
    
    # 生成时间戳
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    
    # 组合文件名
    filename = f"{prefix}_{domain}_{timestamp}.html"
    
    return filename

# 使用示例
url = "https://news.sina.com.cn/"
filename = generate_filename(url, prefix="news")
print(filename)
# 输出:news_sina.com.cn_20250121_103045.html
3.4 带元数据的保存方式
python 复制代码
import json

def save_with_metadata(html, url, save_dir='data/raw'):
    """
    保存 HTML 并附带元数据
    
    Args:
        html: HTML 内容
        url: 原始URL
        save_dir: 保存目录
    """
    # 生成文件名
    filename = generate_filename(url)
    base_name = filename.replace('.html', '')
    
    # 确保目录存在
    os.makedirs(save_dir, exist_ok=True)
    
    # 保存 HTML
    html_path = os.path.join(save_dir, filename)
    with open(html_path, 'w', encoding='utf-8') as f:
        f.write(html)
    
    # 保存元数据
    metadata = {
        'url': url,
        'filename': filename,
        'fetch_time': datetime.now().isoformat(),
        'content_length': len(html),
        'status': 'success'
    }
    
    meta_path = os.path.join(save_dir, f"{base_name}.json")
    with open(meta_path, 'w', encoding='utf-8') as f:
        json.dump(metadata, f, indent=2, ensure_ascii=False)
    
    print(f"✅ HTML 保存到:{html_path}")
    print(f"✅ 元数据保存到:{meta_path}")
    
    return html_path

# 使用示例
url = "https://news.sina.com.cn/"
html = fetch_page(url)
if html:
    save_with_metadata(html, url)

保存后的文件结构

json 复制代码
data/
└── raw/
    ├── news_sina.com.cn_20250121_103045.html
    └── news_sina.com.cn_20250121_103045.json

元数据内容示例

json 复制代码
{
  "url": "https://news.sina.com.cn/",
  "filename": "news_sina.com.cn_20250121_103045.html",
  "fetch_time": "2025-01-21T10:30:45.123456",
  "content_length": 123456,
  "status": "success"
}

四、完整的爬虫脚本(可复用模板)

python 复制代码
"""
标准爬虫模板 v1.0
功能:抓取单个页面并保存
"""

import requests
import os
import json
from datetime import datetime
from urllib.parse import urlparse
import re

class SimpleFetcher:
    """简单的网页抓取器"""
    
    def __init__(self, save_dir='data/raw'):
        """
        初始化
        
        Args:
            save_dir: HTML 保存目录
        """
        self.save_dir = save_dir
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                         '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        }
        
        # 确保保存目录存在
        os.makedirs(save_dir, exist_ok=True)
    
    def fetch(self, url, timeout=10):
        """
        抓取网页
        
        Args:
            url: 目标URL
            timeout: 超时时间
            
        Returns:
            str: HTML内容,失败返回 None
        """
        try:
            print(f"📡 正在请求: {url}")
            
            response = requests.get(
                url, 
                headers=self.headers, 
                timeout=timeout
            )
            
            # 检查状态码
            response.raise_for_status()
            
            # 处理编码
            if response.encoding == 'ISO-8859-1':
                response.encoding = response.apparent_encoding
            
            print(f"✅ 请求成功 (状态码: {response.status_code})")
            return response.text
            
        except requests.exceptions.Timeout:
            print(f"⏱️ 请求超时: {url}")
        except requests.exceptions.ConnectionError:
            print(f"🔌 连接错误: {url}")
        except requests.exceptions.HTTPError as e:
            print(f"❌ HTTP 错误: {e}")
        except Exception as e:
            print(f"❌ 未知错误: {e}")
        
        return None
    
    def generate_filename(self, url, prefix="page"):
        """生成标准文件名"""
        parsed = urlparse(url)
        domain = parsed.netloc.replace('www.', '')
        domain = re.sub(r'[^\w\.]', '_', domain)
        
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        return f"{prefix}_{domain}_{timestamp}.html"
    
    def save(self, html, url, prefix="page"):
        """
        保存 HTML 及元数据
        
        Args:
            html: HTML 内容
            url: 原始URL
            prefix: 文件名前缀
            
        Returns:
            str: 保存的文件路径
        """
        # 生成文件名
        filename = self.generate_filename(url, prefix)
        base_name = filename.replace('.html', '')
        
        # 保存 HTML
        html_path = os.path.join(self.save_dir, filename)
        with open(html_path, 'w', encoding='utf-8') as f:
            f.write(html)
        
        # 保存元数据
        metadata = {
            'url': url,
            'filename': filename,
            'fetch_time': datetime.now().isoformat(),
            'content_length': len(html),
            'html_path': html_path
        }
        
        meta_path = os.path.join(self.save_dir, f"{base_name}.json")
        with open(meta_path, 'w', encoding='utf-8') as f:
            json.dump(metadata, f, indent=2, ensure_ascii=False)
        
        print(f"💾 已保存: {html_path}")
        print(f"📝 元数据: {meta_path}")
        
        return html_path
    
    def fetch_and_save(self, url, prefix="page"):
        """
        抓取并保存(一站式)
        
        Args:
            url: 目标URL
            prefix: 文件名前缀
            
        Returns:
            str: 保存的文件路径,失败返回 None
        """
        html = self.fetch(url)
        if html:
            return self.save(html, url, prefix)
        return None

# ========== 使用示例 ==========

if __name__ == "__main__":
    # 创建抓取器
    fetcher = SimpleFetcher(save_dir='data/raw')
    
    # 测试URL列表
    urls = [
        ("https://news.sina.com.cn/", "sina_news"),
        ("https://www.people.com.cn/", "people_news"),
    ]
    
    print("=" * 60)
    print("🕷️  开始爬取...")
    print("=" * 60)
    
    # 批量抓取
    for url, prefix in urls:
        print(f"\n【任务】{prefix}")
        filepath = fetcher.fetch_and_save(url, prefix)
        
        if filepath:
            print(f"✅ 成功!文件:{filepath}\n")
        else:
            print(f"❌ 失败!\n")
        
        # 礼貌延迟
        import time
        time.sleep(2)
    
    print("=" * 60)
    print("🎉 爬取完成!")
    print("=" * 60)

实际运行结果展示如下:

实际保存文件于当前data文件夹,效果截图展示如下:

五、验证保存的文件

5.1 用浏览器打开
python 复制代码
import webbrowser

# 打开保存的 HTML 文件
html_path = "data/raw/sina_news_20250121_103045.html"
webbrowser.open(f'file://{os.path.abspath(html_path)}')
5.2 统计保存的文件
python 复制代码
def list_saved_files(directory='data/raw'):
    """列出已保存的所有文件"""
    html_files = []
    
    for filename in os.listdir(directory):
        if filename.endswith('.html'):
            filepath = os.path.join(directory, filename)
            size = os.path.getsize(filepath)
            html_files.append({
                'filename': filename,
                'size': size,
                'size_kb': round(size / 1024, 2)
            })
    
    print(f"\n📂 目录:{directory}")
    print(f"📊 共 {len(html_files)} 个 HTML 文件\n")
    
    for file_info in html_files:
        print(f"  {file_info['filename']} ({file_info['size_kb']} KB)")

# 使用
list_saved_files()

六、本节小结

本节我们完成了第一个实用爬虫:

requests 使用 :掌握 GET 请求的基本用法

异常处理 :捕获超时、连接错误等异常

编码处理 :正确解码不同编码的网页

文件保存 :规范化的文件命名和目录结构

元数据管理 :保存抓取时间、URL 等信息

可复用模板:SimpleFetcher 类可用于后续项目

核心原则

  • 永远设置超时(避免程序卡死)
  • 永远捕获异常(避免程序崩溃)
  • 永远保存原始数据(便于调试和复现)
  • 使用标准化的文件命名(便于管理)

📝 课后作业(必做,验收进入下一节)

任务1:运行模板脚本
  1. 复制本节的 SimpleFetcher

  2. 抓取以下三个网站的首页:

  3. 检查 data/raw 目录,确认文件已保存

  4. 用浏览器打开保存的 HTML,验证内容正确

任务2:扩展功能

SimpleFetcher 添加以下方法:

python 复制代码
def fetch_with_retry(self, url, max_retries=3):
    """带重试机制的抓取(下一节会详细讲)"""
    pass

def get_save_stats(self):
    """统计已保存的文件数量和总大小"""
    pass
任务3:真实场景练习

选择一个你感兴趣的新闻网站,完成:

  1. 抓取首页并保存
  2. 打开保存的 HTML,找到新闻列表
  3. 记录新闻标题所在的 CSS 选择器
  4. 为下一步的解析做准备

验收方式:在留言区提交:

  • 保存文件的目录结构截图
  • 浏览器打开 HTML 文件的截图
  • 扩展功能的代码
  • 你选择的网站和分析结果

🔮 下期预告

下一节《伪装与会话:Headers、Session、Cookie》,我们将学习:

  • 如何设置完整的请求头(User-Agent、Referer等)
  • 使用 Session 对象管理会话
  • 处理需要 Cookie 的网站
  • 合规使用身份验证(不涉及破解)
  • 避免被识别为"机器人"

预习建议

打开浏览器开发者工具,观察一个请求的完整请求头,特别关注 User-Agent、Referer、Cookie 字段。


💬 恭喜完成第一个爬虫!从此开启工程化之路! 🚀✨

记住:保存原始数据是工程师的好习惯。今天的麻烦,可能是明天的救命稻草。养成规范,受益终身!💪😊

🌟文末

好啦~以上就是本期 《Python爬虫实战》的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

📌 专栏持续更新中|建议收藏 + 订阅

专栏 👉 《Python爬虫实战》,我会按照"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一篇都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】写成专栏实战?

评论区留言告诉我你的需求,我会优先安排更新 ✅


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。

相关推荐
喵手2 小时前
Python爬虫零基础入门【第三章:Requests 静态爬取入门·第2节】伪装与会话:Headers、Session、Cookie(合规使用)!
爬虫·python·python爬虫实战·python爬虫工程化实战·requests静态爬取·伪装与会话·零基础python爬虫入门
小白学大数据3 小时前
绕过拼多多 App 反抓包机制的综合逆向解决方案
开发语言·爬虫·python·自动化
使者大牙3 小时前
【单点知识】 Python装饰器介绍
开发语言·数据库·python
Jackson@ML3 小时前
2026最新版Sublime Text 4安装使用指南
java·python·编辑器·sublime text
TonyLee0173 小时前
半监督学习介绍
人工智能·python·深度学习·机器学习
kong79069283 小时前
Python核心语法-Python自定义模块、Python包
开发语言·python·python核心语法
OLOLOadsd1233 小时前
基于Mask-RCNN和RegNetX的茎蛀虫检测识别系统详解
python
半路_出家ren3 小时前
1.古典密码概述
python·网络安全·密码学·古典密码·加密方式
CJenny4 小时前
Claude Code常用操作和使用方法
人工智能·python