Python爬虫实战:爬取得到App电子书畅销榜 - 从零到交付的完整实战!

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

㊙️本期爬虫难度指数:⭐⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [0️⃣ 前言](#0️⃣ 前言)
      • [1️⃣ 摘要](#1️⃣ 摘要)
      • [2️⃣ 背景与需求](#2️⃣ 背景与需求)
      • [3️⃣ 合规与注意事项](#3️⃣ 合规与注意事项)
      • [4️⃣ 技术选型与整体流程](#4️⃣ 技术选型与整体流程)
      • [5️⃣ 环境准备与依赖安装](#5️⃣ 环境准备与依赖安装)
      • [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
      • [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示](#9️⃣ 运行方式与结果展示)
      • [🔟 常见问题与排错](#🔟 常见问题与排错)
      • [1️⃣1️⃣ 进阶优化](#1️⃣1️⃣ 进阶优化)
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

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

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

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

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

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

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

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

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

0️⃣ 前言

用requests+BeautifulSoup爬取得到App网页版电子书畅销榜,最终生成一份包含排名、书名、作者、定价、听书时长的结构化数据表。

读完本文你将获得:

  • 一套可直接运行的爬虫代码,零门槛复现
  • 掌握静态页面爬取的完整工作流:请求→解析→存储
  • 学会处理真实场景中的反爬、容错、去重策略

1️⃣ 摘要

本文以得到App电子书畅销榜为案例,使用Python的requests库获取页面、BeautifulSoup解析HTML、Pandas处理数据,最终输出CSV文件。整个流程涵盖环境搭建、请求头伪装、CSS选择器定数据清洗和导出全链路。

核心收获:

  • 学会如何分析目标网站的数据结构并制定抓取策略
  • 掌握静态网页爬取的标准化流程和常见坑点
  • 了解合规爬取的边界和基本的反反爬技巧

2️⃣ 背景与需求

为什么要爬取这个榜单?

得到App作为知识付费领域的头部平台,其电子书畅销榜反映了当前优质内容的流行趋势。通过定期采集榜单数据,可以:

  • 趋势分析:观察某类图书(如商业、心理学、历史)的热度变化
  • 选书参考:为个人阅读或团队学习提供数据支撑
  • 内容运营:了解市场偏好,辅助内容创作方向

目标字段清单:

字段名 说明 示例值
rank 排行榜排名 1
title 书籍名称 《人生海海》
author 作者姓名 麦家
price 电子书定价 ¥39.99
duration 听书时长(如有) 5小时12分钟

3️⃣ 合规与注意事项

⚠️ 请务必遵守以下原则:

  1. 查看robots.txt

    得到App的robots.txt位于https://www.igetget.com/robots.txt,明确了哪些路径允许/禁止爬取。一般来说,公开榜单页属于允许范围,但仍需核查。

  2. 频率控制

    • 单机爬取建议每次请求间隔≥1秒
    • 避免短时间内发起数百次请求,这会被识别为攻击行为
    • 使用time.sleep()主动限速
  3. 不触碰敏感信息

    • 本文只采集公开展示的榜单数据(书名、作者、价格等)
    • 不采集需要登录才能看到的用户评论、购买记录等
    • 不绕过付费墙去下载完整电子书内容
  4. 数据用途声明

    • 仅用于个人学习、数据分析、非商业研究
    • 不得将爬取的数据用于二次销售或侵犯版权的行为

风险提示:

如果你的IP被封,通常是因为请求频率过高。解决办法:增加延时、更换IP(代理池)、降低并发数。

4️⃣ 技术选型与整体流程

本文爬取的目标属于哪种类型?

得到App的网页版电子书榜单是静态HTML渲染 ,也就是说打开网页后,内容直接写在HTML源码里,不需要JavaScript动态加载。这种情况下,用requests发起GET请求就能拿到完整页面。

流程图:

json 复制代码
用户指定URL → requests发起HTTP请求 → 服务器返回HTML
    ↓
BeautifulSoup解析HTML → 提取目标字段(排名/书名/作者/价格/时长)
    ↓
数据清洗(去空格/统一格式/类型转换) → 存入列表/字典
    ↓
Pandas转DataFrame → 去重/验证 → 导出CSV文件

为什么选择requests+BeautifulSoup?

  • 轻量级:适合中小规模爬取任务(几十到几千条数据)
  • 上手快:requests只需5行代码就能发起请求,bs4选择器类似jQuery
  • 生态好:遇到问题Stack Overflow和中文社区有大量解答
  • 够用:对于静态页面,没必要上Scrapy或Playwright这种重型框架

如果未来需要爬取大量动态页面(比如需要模拟登录、处理AJAX加载的数据),可以考虑升级到Selenium/Playwright或Scrapy。

5️⃣ 环境准备与依赖安装

Python版本要求:

Python 3.8+ (本文测试环境为3.10,向下兼容至3.8)

依赖库安装:

json 复制代码
pip install requests beautifulsoup4 lxml pandas --break-system-packages

库说明:

  • requests:发送HTTP请求
  • beautifulsoup4:解析HTML文档
  • lxml:bs4的高性能解析器(可选,默认html.parser也行)
  • pandas:数据处理和CSV导出

项目结构建议:

json 复制代码
dedao_scraper/
├── main.py           # 主程序入口
├── config.py         # 配置项(URL、headers等)
├── fetcher.py        # 请求层
├── parser.py         # 解析层
├── storage.py        # 存储层
├── utils.py          # 工具函数(日志、重试等)
├── data/             # 数据输出目录
│   └── books.csv
└── logs/             # 日志文件
    └── scraper.log

如果是快速验证,也可以把所有代码写在main.py里,但分层架构便于后续维护和扩展。

6️⃣ 核心实现:请求层(Fetcher)

fetcher.py完整代码:

python 复制代码
import requests
import time
import random
from typing import Optional

class Fetcher:
    def __init__(self):
        self.session = requests.Session()
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive',
            'Referer': 'https://www.igetget.com/',
            'Upgrade-Insare-Requests': '1'
        }
    
    def fetch(self, url: str, max_retries: int = 3) -> Optional[str]:
        """
        发起HTTP请求并返回响应内容
        
        Args:
            url: 目标URL
            max_retries: 最大重试次数
        
        Returns:
            响应的HTML文本,失败返回None
        """
        for attempt in range(max_retries):
            try:
                # 随机延时0.5-2秒,模拟人类行为
                time.sleep(random.uniform(0.5, 2.0))
                
                response = self.session.get(
                    url,
                    headers=self.headers,
                    timeout=10  # 10秒超时
                )
                
                # 检查状态码
                if response.status_code == 200:
                    response.encoding = response.apparent_encoding  # 自动检测编码
                    return response.text
                elif response.status_code == 403:
                    print(f"[403错误] 可能触发反爬,尝试增加延时...")
                    time.sleep(5)
                elif response.status_code == 429:
                    print(f"[429错误] 请求过于频繁,等待10秒后重试...")
                    time.sleep(10)
                else:
                    print(f"[HTTP {response.status_code}] 请求失败")
                
            except requests.exceptions.Timeout:
                print(f"[超时] 第{attempt+1}次尝试超时")
            except requests.exceptions.ConnectionError:
                print(f"[连接错误] 网络不稳定,第{attempt+1}次重试")
            except Exception as e:
                print(f"[未知错误] {str(e)}")
        
        print(f"[失败] 重试{max_retries}次后仍无法获取数据")
        return None

关键点解析:

  1. 为什么要伪装User-Agent?

    很多网站会检查UA,如果发现是python-requests/2.x,直接拒绝访问。伪装成Chrome浏览器可以绕过这种基础检测。

  2. 为什么用Session而不是直接requests.get?

    Session会复用TCP连接,速度更快,且能自动管理Cookie(虽然这个案例用不到Cookie)。

  3. 超时设置(timeout)

    网络波动时,如果不设超时,程序可能卡死数十秒。设置10秒超时是经验值。

  4. 重试与退避

    遇到临时性错误(如429限流),等待后重试是标准做法。这里用指数退避会更优雅,但简单场景下固定延时够用。

  5. 编码问题
    response.apparent_encoding会自动检测页面编码(通常是UTF-8),避免中文乱码。

7️⃣ 核心实现:解析层(Parser)

parser.py完整代码:

python 复制代码
from bs4 import BeautifulSoup
from typing import List, Dict

class Parser:
    def __init__(self, html: str):
        self.soup = BeautifulSoup(html, 'lxml')
    
    def parse_book_list(self) -> List[Dict[str,str]]:
        """
        解析畅销榜页面,提取书籍信息
        
        Returns:
            书籍信息列表,每本书是一个字典
        """
        books = []
        
        # 假设HTML结构如下(实际需要F12查看真实结构):
        # <div class="book-item">
        #   <span class="rank">1</span>
        #   <h3 class="title">人生海海</h3>
        #   <p class="author">麦家</p>
        #   <span class="price">¥39.99</span>
        #   <span class="duration">5小时12分钟</span>
        # </div>
        
        book_items = self.soup.select('div.book-item')  # CSS选择器
        
        if not book_items:
            print("[警告] 未找到任何书籍条目,请检查选择器是否正确")
            return books
        
        for item in book_items:
            try:
                book = {}
                
                # 提取排名
                rank_elem = item.select_one('span.rank')
                book['rank'] = rank_elem.text.strip() if rank_elem else 'N/A'
                
                # 提取书名
                title_elem = item.select_one('h3.title')
                book['title'] = title_elem.text.strip() if title_elem else 'N/A'
                
                # 提取作者
                author_elem = item.select_one('p.author')
                book['author'] = author_elem.text.strip() if author_elem else 'N/A'
                
                # 提取价格
                price_elem = item.select_one('span.price')
                book['price'] = price_elem.text.strip() if price_elem else 'N/A'
                
                # 提取听书时长
                duration_elem = item.select_one('span.duration')
                book['duration'] = duration_elem.text.strip() if duration_elem else 'N/A'
                
                books.append(book)
                
            except Exception as e:
                print(f"[解析错误] 跳过异常条目: {str(e)}")
                continue
        
        print(f"[成功] 解析到{len(books)}本书籍")
        return books
    
    @staticmethod
    def clean_price(price_str: str) -> float:
        """
        清洗价格字符串,提取数字
        例如: "¥39.99" -> 39.99
        """
        import re
        match = re.search(r'\d+\.?\d*', price_str)
        return float(match.group()) if match else 0.0
    
    @staticmethod
    def clean_duration(duration_str: str) -> int:
        """
        清洗时长字符串,转为分钟数
        例如: "5小时12分钟" -> 312
        """
        import re
        hours = re.search(r'(\d+)小时', duration_str)
        minutes = re.search(r'(\d+)分钟', duration_str)
        
        total_minutes = 0
        if hours:
            total_minutes += int(hours.group(1)) * 60
        if minutes:
            total_minutes += int(minutes.group(1))
        
        return total_minutes

关键点解析:

  1. 为什么用CSS选择器而不是XPath?

    BeautifulSoup的.select()语法和jQuery类似,前端开发者会觉得很亲切。XPath功能更强大,但学习成本稍高。如果你熟悉XPath,也可以换成lxml库直接用XPath。

  2. 如何确定选择器?

    • 打开目标网页,按F12进入开发者工具
    • 点击左上角的"元素选择器"图标(或Ctrl+Shift+C)
    • 鼠标悬停在要抓取的内容上,查看HTML结构
    • 右键 → Copy → Copy selector,直接复制CSS选择器
  3. 容错处理

    每个字段都用if xxx else 'N/A'做了默认值处理。现实中网页结构可能不规范,某些书可能缺少作者或时长信息,直接.text会报AttributeError,必须先判断是否为None。

  4. 数据清洗函数
    clean_priceclean_duration是典型的ETL(Extract-Transform-Load)中的Transform步骤。爬到的原始数据往往带有格式噪音(如货币符号、中文单位),需要统一转为数值类型才能做后续分析。

8️⃣ 数据存储与导出(Storage)

storage.py完整代码:

python 复制代码
import pandas as pd
import os
from typing import List, Dict
from datetime import datetime

class Storage:
    def __init__(self, output_dir: str = './data'):
        self.output_dir = output_dir
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
    
    def save_to_csv(self, books: List[Dict], filename: str = None):
        """
        保存书籍数据到CSV文件
        
        Args:
            books: 书籍信息列表
            filename: 文件名(不指定则自动生成带时间戳的文件名)
        """
        if not books:
            print("[警告] 数据为空,跳过保存")
            return
        
        # 转为DataFrame
        df = pd.DataFrame(books)
        
        # 数据清洗
        if 'price' in df.columns:
            from parser import Parser
            df['price_num'] = df['price'].apply(Parser.clean_price)
        
        if 'duration' in df.columns:
            from parser import Parser
            df['duration_minutes'] = df['duration'].apply(Parser.clean_duration)
        
        # 去重(基于书名+作者)
        if 'title' in df.columns and 'author' in df.columns:
            original_count = len(df)
            df = df.drop_duplicates(subset=['title', 'author'], keep='first')
            dropped = original_count - len(df)
            if dropped > 0:
                print(f"[去重] 移除了{dropped}条重复数据")
        
        # 生成文件名
        if not filename:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f'dedao_books_{timestamp}.csv'
        
        filepath = os.path.join(self.output_dir, filename)
        
        # 导出CSV
        df.to_csv(filepath, index=False, encoding='utf-8-sig')  # utf-8-sig解决Excel打开中文乱码
        print(f"[成功] 数据已保存到: {filepath}")
        print(f"[统计] 共{len(df)}条记录")
        
        return filepath
    
    def save_to_json(self, books: List[Dict], filename: str = None):
        """
        保存为JSON格式(备用)
        """
        import json
        
        if not filename:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f'dedao_books_{timestamp}.json'
        
        filepath = os.path.join(self.output_dir, filename)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(books, f, ensure_ascii=False, indent=2)
        
        print(f"[成功] JSON已保存到: {filepath}")

字段映射表:

原始字段 清洗后字段 类型 示例值
rank rank str "1"
title title str "人生海海"
author author str "麦家"
price price / price_num str / float "¥39.99" / 39.99
duration duration / duration_minutes str / int "5小时12分钟" / 312

去重策略说明:

  1. 为什么用(title, author)组合去重?

    • 单用书名可能误伤(比如《活着》有余华版和英文版)
    • 单用作者不够准确(同一作者可能有多本书)
    • 组合键可以最大程度保证唯一性
  2. keep='first'的含义?

    当发现重复时,保留第一次出现的记录,删除后续的。也可以改成keep='last'keep=False(全删)。

  3. 为什么用utf-8-sig?

    Windows Excel打开UTF-8编码的CSV会出现中文乱码,加上BOM头(utf-8-sig)可以解决。如果只在Mac/Linux下用,直接utf-8即可。

9️⃣ 运行方式与结果展示

main.py主程序:

python 复制代码
from fetcher import Fetcher
from parser import Parser
from storage import Storage

def main():
    # 目标URL(这里用假设的URL,实际需要替换为真实地址)
    url = 'https://www.igetget.com/books/bestseller'
    
    print("="*50)
    print("得到App电子书畅销榜爬虫")
    print("="*50)
    
    # 1. 请求页面
    print("\n[步骤1] 正在获取页面...")
    fetcher = Fetcher()
    html = fetcher.fetch(url)
    
    if not html:
        print("[错误] 无法获取页面,程序退出")
        return
    
    # 2. 解析数据
    print("\n[步骤2] 正在解析数据...")
    parser = Parser(html)
    books = parser.parse_book_list()
    
    if not books:
        print("[错误] 解析失败,程序退出")
        return
    
    # 3. 存储数据
    print("\n[步骤3] 正在保存数据...")
    storage = Storage()
    filepath = storage.save_to_csv(books)
    
    # 4. 展示示例
    print("\n[示例数据] 前5条记录:")
    import pandas as pd
    df = pd.read_csv(filepath)
    print(df.head().to_string())
    
    print("\n" + "="*50)
    print("爬取完成!")
    print("="*50)

if __name__ == '__main__':
    main()

运行方式:

json 复制代码
cd dedao_scraper
python main.py

预期输出:

json 复制代码
==================================================
得到App电子书畅销榜爬虫
==================================================

[步骤1] 正在获取页面...
[成功] 状态码200

[步骤2] 正在解析数据...
[成功] 解析到50本书籍

[步骤3] 正在保存数据...
[去重] 移除了2条重复数据
[成功] 数据已保存到: ./data/dedao_books_20260212_143022.csv
[统计] 共48条记录

[示例数据] 前5条记录:
   rank      title author  price          duration  price_num  duration_minutes
0     1     人生海海     麦家  ¥39.99      5小时12分钟      39.99               312
1     2  县中的孩子   林小英  ¥49.00      6小时30分钟      49.00               390
2     3      原则   瑞·达利欧  ¥68.00     12小时15分钟      68.00               735
3     4    活着     余华  ¥18.00      3小时40分钟      18.00               220
4     5  置身事内   兰小欢  ¥59.00      8小时05分钟      59.00               485

==================================================
爬取完成!
==================================================

🔟 常见问题与排错

Q1: 运行后提示403 Forbidden怎么办?

原因:

  • User-Agent被识别为爬虫
  • IP在短时间内请求过于频繁
  • 网站开启了CloudFlare等WAF防护

解决方案:

python 复制代码
# 方案1: 更换更真实的UA
headers['User-Agent'] = 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...'

# 方案2: 加入更多header伪装
headers['Sec-Fetch-Site'] = 'none'
headers['Sec-Fetch-Mode'] = 'navigate'
headers['Sec-Fetch-User'] = '?1'
headers['Sec-Fetch-Dest'] = 'document'

# 方案3: 使用代理IP
proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}
response = session.get(url, headers=headers, proxies=proxies)

Q2: HTML抓到了但是是空壳,没有目标数据?

原因:

页面内容是通过JavaScript动态加载的,requests只能拿到初始HTML框架。

排查方法:

  1. 打开浏览器,访问目标页面
  2. F12 → Network → 刷新页面
  3. 筛选XHR/Fetch请求,找到返回JSON数据的API接口
  4. 复制这个API的URL,直接用requests抓接口(比抓HTML更高效)

示例:

python 复制代码
api_url = 'https://www.igetget.com/api/books/rank?type=bestseller&limit=50'
response = requests.get(api_url, headers=headers)
data = response.json()  # 直接拿到JSON,不用解析HTML

Q3: 解析时报AttributeError: 'NoneType' object has no attribute 'text'

原因:

选择器没找到元素,返回了None,再调.text就报错。

解决方案:

python 复制代码
# 错误写法
title = item.select_one('h3.title').text  # 如果找不到h3,直接崩溃

# 正确写法
title_elem = item.select_one('h3.title')
title = title_elem.text.strip() if title_elem else 'N/A'

Q4: CSV文件在Excel里打开全是乱码?

原因:

Excel默认用GBK编码打开CSV,而你的文件是UTF-8。

解决方案:

python 复制代码
# 改用utf-8-sig编码(带BOM头,Excel能识别)
df.to_csv(filepath, index=False, encoding='utf-8-sig')

Q5: 选择器明明写对了,但总是找不到元素?

排查步骤:

  1. 打印soup.prettify(),看看拿到的HTML是不是你预期的
  2. 检查class名是否有空格(如class="book item",CSS要写.book.item)
  3. 有些网站会动态生成class名(如React的hash class),这种情况改用XPath或属性选择器

1️⃣1️⃣ 进阶优化

  1. 并发加futures import ThreadPoolExecutor, as_completed
json 复制代码
def fetch_page(url):
fetcher = Fetcher()
return fetcher.fetch(url)

def main_concurrent():
urls = [
'[https://www.igetget.com/books/bestseller?page=1](https://www.igetget.com/books/bestseller?page=1)',
'[https://www.igetget.com/books/bestseller?page=2](https://www.igetget.com/books/bestseller?page=2)',
'[https://www.igetget.com/books/bestseller?page=3](https://www.igetget.com/books/bestseller?page=3)',
]

all_books = []

with ThreadPoolExecutor(max_workers=3) as executor:
    future_to_url = {executor.submit(fetch_page, url): url for url in urls}
    
    for future in as_completed(future_to_url):
        url = future_to_url[future]
        try:
            html = future.result()
            parser = Parser(html)
            books = parser.parse_book_list()
            all_books.extend(books)
        except Exception as e:
            print(f"{url} 爬取失败: {e}")

return all_books

注意:

  • max_workers不要设太大(建议3-5),否则会被当成攻击
  • 线程并发适合I/O密集型任务(等待网络响应),CPU密集型用多进程

2. 断点续跑(已爬URL集合)

python 复制代码
import json
import os

CHECKPOINT_FILE = './data/checkpoint.json'

def load_checkpoint():
    if os.path.exists(CHECKPOINT_FILE):
        with open(CHECKPOINTcheckpoint(crawled_urls):
    with open(CHECKPOINT_FILE, 'w') as f:
        json.dump(list(crawled_urls), f)

def main_with_checkpoint():
    crawled = load_checkpoint()
    all_urls = ['url1', 'url2', 'url3', ...]
    
    for url in all_urls:
        if url in crawled:
            print(f"[跳过] {url} 已爬过")
            continue
        
        # 爬取逻辑...
        
        crawled.add(url)
        save_checkpoint(crawled)

3. 日志与监控

python 复制代码
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler('./logs/scraper.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

# 使用示例
logger.info("开始爬取...")
logger.warning("请求失败,正在重试")
logger.error("解析异常", exc_info=True)

4. 定时任务(每天自动爬一次)

方案A: Linux cron

bash 复制代码
crontab -e
# 每天凌晨2点执行
0 2 * * * /usr/bin/python3 /path/to/main.py

方案B: Python schedule库

python 复制代码
import schedule
import time

def job():
    ()

schedule.every().day.at("02:00").do(job)

while True:
    schedule.run_pending()
    time.sleep(60)

1️⃣2️⃣ 总结与延伸阅读

✅ 从零搭建了一个完整的静态页面爬虫

✅ 实现了请求层、解析层、存储层的分离架构

✅ 处理了反爬、容错、去重、编码等真实场景问题

✅ 提供了并发、断点续跑、定时任务等进阶优化方案

你现在可以独立完成:

  • 爬取任何公开的静态HTML榜单/列表页
  • 将数据清洗并导出为CSV/JSON/Excel
  • 应对基础的反爬措施(UA、延时、重试)

下一步可以做什么?

  1. 升级到Scrapy框架

    适合大规模爬取(数万-数百万条数据),内置去重、分布式、中间件等高级功能。

  2. 处理动态页面(Playwright/Selenium)

    如果目标网站用React/Vue渲染,或需要模拟登录,用无头浏览器是更稳妥的选择。

  3. 学习反反爬技巧

    • 代理IP池(轮换IP)
    • 验证码识别(OCR/打码平台)
    • 字体反爬破解(CSS字体映射)
  4. 分布式爬虫(Scrapy-Redis)

    多台机器协同爬取,适合海量数据采集。

  5. 数据分析与可视化

    用Pandas做趋势分析,用Matplotlib/Pyecharts画图表。

推荐资源:


最后说一句:

爬虫技术本身是中性的,关键在于如何使用。希望你用它去探索数据的价值,而不是侵犯他人权益。记住:尊重robots.txt,控制频率,不做恶。

祝你爬虫之旅顺利! 🎉

🌟 文末

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

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

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

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

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

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

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

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


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

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

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


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
2401_828890642 小时前
实现变分自编码器 VAE- MNIST 数据集
人工智能·python·深度学习·cnn·transformer
PD我是你的真爱粉2 小时前
RabbitMQ架构实战
python·架构·rabbitmq
我的xiaodoujiao2 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 49--CI/CD-开始探索使用Jenkins
python·学习·测试工具·ci/cd·jenkins·pytest
南 阳2 小时前
Python从入门到精通day35
数据库·python·oracle
geovindu10 小时前
python: Memento Pattern
开发语言·python·设计模式·备忘录模式
寻星探路11 小时前
【JVM 终极通关指南】万字长文从底层到实战全维度深度拆解 Java 虚拟机
java·开发语言·jvm·人工智能·python·算法·ai
lbb 小魔仙11 小时前
【Java】Java 实战项目:手把手教你写一个电商订单系统
android·java·python
岱宗夫up11 小时前
FastAPI入门(上篇):快速构建高性能Python Web API
开发语言·前端·python·fastapi
Dxy123931021611 小时前
中文乱码恢复方案
开发语言·python