Python爬虫实战:从零构建完整项目(数据采集+存储+异常处理)

爬虫不是简单的请求+解析,而是一个系统工程。本文将带你体验企业级爬虫开发的核心流程。

一、前言:为什么需要完整的爬虫项目?

作为初学者,你可能写过一些单文件的爬虫脚本,但当面对真实复杂的网络环境、海量数据存储需求及各种突发异常时,这些脚本往往脆弱不堪。真正的爬虫项目,是网络请求、数据解析、持久化存储和健壮性设计的交响乐。本次我们将综合运用Python核心技术,构建一个可投入生产环境的爬虫系统。

二、项目目标与技术栈

项目目标:爬取图书网站(以豆瓣读书为例)的书籍信息,包含:

  • 书名
  • 作者
  • 评分
  • 价格
  • ISBN
  • 简介

技术栈

  • 网络请求requests (处理HTTP请求/响应)
  • 数据解析BeautifulSoup4 (HTML解析)
  • 数据存储SQLite3 (轻量级数据库)
  • 异常处理:Python内置异常机制 + 自定义重试
  • 辅助工具logging (日志记录), time (速率控制)

三、环境搭建(新手友好)

# 创建虚拟环境(可选但推荐)
python -m venv spider-env
source spider-env/bin/activate  # Linux/Mac
spider-env\Scripts\activate    # Windows# 安装核心库
pip install requests beautifulsoup4

📌 重要提示:真实项目中务必添加 requirements.txt 管理依赖!

四、分步构建爬虫系统

4.1 数据采集模块:与网络对话

核心任务:稳定获取目标网页内容

import requests
from fake_useragent import UserAgent  # pip install fake-useragentdef fetch_page(url, retries=3):"""获取网页内容,含简单重试机制:param url: 目标URL:param retries: 最大重试次数:return: HTML内容 (str) 或 None"""headers = {'User-Agent': UserAgent().random}  # 动态生成UAfor attempt in range(retries):try:response = requests.get(url, headers=headers, timeout=10)response.raise_for_status()  # 自动抛出HTTP错误return response.textexcept (requests.exceptions.RequestException, requests.exceptions.Timeout) as e:print(f"请求失败 (尝试 {attempt+1}/{retries}): {e}")time.sleep(2 ** attempt)  # 指数退避策略return None

关键技术解析

  1. User-Agent轮换:绕过基础反爬
  2. timeout设置:防止请求阻塞
  3. raise_for_status():自动处理HTTP状态码(404, 500等)
  4. 指数退避重试:网络波动时的自愈能力

4.2 数据解析模块:从混沌中提取信息

核心任务:精准抽取目标数据

from bs4 import BeautifulSoupdef parse_book_page(html):"""解析图书详情页:param html: 网页HTML:return: 字典形式的结构化数据"""if not html:return Nonesoup = BeautifulSoup(html, 'html.parser')book_data = {}try:# 书名 (使用CSS选择器更稳定)book_data['title'] = soup.select_one('h1 span').text.strip()# 作者信息 (处理多作者情况)authors = [a.text.strip() for a in soup.select('.author a')]book_data['author'] = '; '.join(authors)# 评分 (处理可能缺失的情况)rating_tag = soup.select_one('.rating_num')book_data['rating'] = float(rating_tag.text) if rating_tag else 0.0# 使用更健壮的属性提取book_data['isbn'] = soup.find('meta', {'property': 'books:isbn'})['content']# 简介 (处理多段文本)summary_tag = soup.select_one('.intro')book_data['summary'] = '\n'.join([p.text for p in summary_tag.find_all('p')]) except (AttributeError, TypeError) as e:print(f"解析错误: {e}")return Nonereturn book_data

避坑指南

  1. 优先使用CSS选择器而非XPath(更简洁)
  2. 所有解析操作都需考虑标签不存在的情况
  3. 多层数据使用try-except局部捕获
  4. 处理多值数据时用分隔符连接

4.3 数据存储模块:持久化艺术

为什么用数据库而非CSV?

  • 避免重复爬取
  • 支持复杂查询
  • 数据一致性保障
import sqlite3DB_NAME = 'books.db'def init_database():"""初始化数据库表结构"""conn = sqlite3.connect(DB_NAME)c = conn.cursor()c.execute('''CREATE TABLE IF NOT EXISTS books(id INTEGER PRIMARY KEY AUTOINCREMENT,title TEXT NOT NULL,author TEXT,rating REAL,isbn TEXT UNIQUE,  -- ISBN唯一标识书籍summary TEXT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')conn.commit()conn.close()def save_book_data(book):"""保存单条图书数据"""if not book: return Falsetry:conn = sqlite3.connect(DB_NAME)c = conn.cursor()# 使用UPSERT操作避免重复c.execute('''INSERT OR IGNORE INTO books (title, author, rating, isbn, summary)VALUES (?, ?, ?, ?, ?)''',(book['title'], book['author'], book['rating'], book['isbn'], book['summary']))conn.commit()return c.rowcount > 0except sqlite3.Error as e:print(f"数据库错误: {e}")return Falsefinally:conn.close()  # 确保连接关闭

高级技巧

  1. 使用UNIQUE约束防止重复数据
  2. INSERT OR IGNORE实现去重插入
  3. finally保证资源释放
  4. 添加时间戳追踪数据采集时间

4.4 异常处理模块:构建抗脆弱系统

爬虫常见崩溃原因

  • 网络波动(超时/断连)
  • 网站改版(解析失败)
  • 反爬机制(IP被封)
  • 数据异常(类型转换错误)
class SpiderException(Exception):"""自定义爬虫异常基类"""passclass RetryExhaustedError(SpiderException):"""重试耗尽异常"""def __init__(self, url):self.url = urlsuper().__init__(f"重试耗尽: {url}")def safe_crawl(url):"""带完整异常处理的爬取流程:return: 成功保存返回True"""try:# 1. 网络请求html = fetch_page(url)if not html:raise SpiderException(f"获取页面失败: {url}")# 2. 数据解析book_data = parse_book_page(html)if not book_data:raise SpiderException(f"解析页面失败: {url}")# 3. 数据存储if not save_book_data(book_data):raise SpiderException(f"数据保存失败: {url}")print(f"成功处理: {url}")return Trueexcept SpiderException as e:print(f"业务流程异常: {e}")return Falseexcept Exception as e:  # 捕获未预料异常print(f"系统未知异常: {type(e).__name__} - {e}")# 此处可添加邮件/钉钉报警return False

异常处理金字塔

  1. 基础异常:网络/解析/存储错误
  2. 业务异常:自定义SpiderException
  3. 全局兜底:捕获所有未处理异常
  4. 重试机制:网络请求层已实现

五、项目整合:让模块协同工作

import time
from urllib.parse import urljoinBASE_URL = "https://book.douban.com/tag/小说"
MAX_PAGES = 5  # 演示用控制页数def main():init_database()session = requests.Session()  # 复用连接提升性能for page in range(MAX_PAGES):list_url = f"{BASE_URL}?start={page*20}"print(f"正在爬取列表页: {list_url}")try:list_html = fetch_page(list_url)if not list_html: continuesoup = BeautifulSoup(list_html, 'html.parser')book_links = soup.select('.subject-item .info h2 a')for link in book_links:detail_url = link['href']safe_crawl(detail_url)  # 核心调度time.sleep(1.5)  # 礼貌爬取间隔except Exception as e:print(f"列表页处理异常: {e}")time.sleep(3)  # 页间延迟if __name__ == "__main__":main()

六、生产级优化建议

  1. 代理IP池:解决IP封锁问题
    proxies = {"http": "http://10.10.1.10:3128"}
    requests.get(url, proxies=proxies)
    
  2. 分布式任务队列:使用Celery+Redis
  3. 浏览器渲染:对JS渲染页面使用Selenium
  4. 增量爬取:基于数据库时间戳过滤
  5. 配置文件:分离敏感参数(数据库密码等)

七、常见问题排查手册

现象可能原因解决方案
返回403错误IP被封/请求头异常更换代理/更新UserAgent
解析到空数据网站改版重新分析DOM结构
数据库插入失败字段超长/类型不匹配增加字段长度检查
内存持续增长未关闭数据库连接使用with语句管理资源
被重定向到登录页触发反爬添加Cookie模拟登录

八、总结:爬虫工程师的思维模式

  1. 防御性编程:所有外部交互都可能失败
  2. 可观测性:完善的日志体系(推荐使用logging模块)
  3. 弹性设计:重试/降级/熔断机制
  4. 伦理边界:遵守robots.txt,控制爬取频率

项目完整代码已托管Github:https://github.com/yourname/douban-spider (包含详细注释)

爬虫项目的终极目标不是获取数据,而是构建可持续的数据管道。当你掌握了数据采集、结构化存储和异常处理这三驾马车,就能应对互联网上90%的数据获取需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87614.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87614.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87614.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据时代UI前端的用户体验设计新思维:以用户为中心的数据可视化

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言:大数据重构用户体验设计的底层逻辑在数据爆炸式增长的今天,用…

FreeRTOS 中任务控制块(Task Control Block,TCB)用于管理和描述任务的核心数据结构

在 FreeRTOS 中,任务控制块(Task Control Block,TCB)是用于管理和描述任务的核心数据结构。每个任务都有一个对应的 TCB,它包含了任务的所有相关信息。 TCB 的主要功能 存储任务状态信息:TCB 中包含了任务…

前端-HTML-day1

目录 1、标签语法 2、HTML基本骨架 3、标签的关系 4、注释 5、标题标签 6、段落标签 7、换行和水平线标签 8、文本格式化标签 9、图像标签-基本使用 10、图像标签-属性 11、相对路径 12、绝对路径 13、超链接 14、音频 15、视频 16、综合案例1--个人简介 17、…

OpenCV篇——项目(二)OCR文档扫描

目录 文档扫描项目说明 前言 文档扫描代码总体演示 OCR文档识别代码总体演示: ​编辑 代码功能详解 1. 预处理阶段 2. 边缘检测 3. 轮廓处理 4. 透视变换 5. 后处理 主要改进说明: 使用建议: 文档扫描项目说明 前言 本项目实现了一个自动…

Java 中导出包含多个 Sheet 的 Excel 文件

在 Java 中导出包含多个 Sheet 的 Excel 文件,可以使用 Apache POI 或 EasyExcel(阿里开源库)。以下是两种方法的详细实现: 方法 1:使用 Apache POI(支持 .xls 和 .xlsx) 1. 添加 Maven 依赖 …

OneCode采用虚拟DOM结构实现服务端渲染的技术实践

一、技术背景与挑战 随着企业级应用复杂度的提升,传统服务端渲染(SSR)面临页面交互性不足的问题,而纯前端SPA架构则存在首屏加载慢和SEO不友好的缺陷。OneCode框架创新性地将虚拟DOM技术引入服务端渲染流程,构建了一套兼顾性能与开发效率的企…

变幻莫测:CoreData 中 Transformable 类型面面俱到(八)

概述 各位似秃似不秃小码农们都知道,在苹果众多开发平台中 CoreData 无疑是那个最简洁、拥有“官方认证”且最具兼容性的数据库框架。使用它可以让我们非常方便的搭建出 App 所需要的持久存储体系。 不过,大家是否知道在 CoreData 中还存在一个 Transfo…

汽车LIN总线通讯:从物理层到协议栈的深度解析

目录一、物理层:单线传输的信号奥秘1.1 电平定义与信号传输1.2 关键硬件组件作用二、数据链路层:帧结构与通信协议2.1 LIN帧的组成与功能2.2 主从式通信机制三、波特率同步:从节点的时钟校准原理四、软件实现:基于S32K144的主节点…

马尔可夫链:随机过程的记忆法则与演化密码

本文由「大千AI助手」原创发布,专注用真话讲AI,回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我,一起撕掉过度包装,学习真实的AI技术! 一、核心定义:无记忆的随机演化 马尔可夫链(M…

【vue3+tauri+rust】如何实现下载文件mac+windows

项目背景:【vue3taurirust】 由于Safari对于下载总是有诸多阻拦,目前需求windowsmac可以实现: 后端返回的url文件可以下载;前端根据dom元素生成的PDF报告可以下载(无远程URL); 我的尝试: 方法…

SQL 快速参考手册-SQL001

SQL 快速参考手册: 为方便快速学习和实践,提供了一份 SQL 快速参考手册,您可以打印出来随时查看,了解常见 SQL 命令的语法和用法。 SQL 数据类型 SQL 数据类型根据不同的数据库系统(如 Microsoft Access、MySQL、SQL…

学习java集合

集合与数组的对比集合的长度可变, 数组的长度不可变集合实际上跟数组一样, 是一种容器, 可以存放数据数组可以直接存放基本数据类型和引用数据类型集合可以存放引用数据类型, 但是不能直接存放基本数据类型, 如果要存放基本数据类型, 需要变成一个包装类才行泛型: 限定集合中存…

python训练day49 CBAM

import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self, in_channels, ratio16):"""通道注意力机制初始化参数:in_channels: 输入特征图的通道数ratio: 降维比例,用于减少参数量,默认…

在小程序中实现实时聊天:WebSocket最佳实践

前言 在当今互联网应用中,实时通信已经成为一个标配功能,特别是对于需要即时响应的场景,如在线客服、咨询系统等。本文将分享如何在小程序中实现一个高效稳定的WebSocket连接,以及如何处理断线重连、消息发送与接收等常见问题。 W…

Python网络爬虫编程新手篇

网络爬虫是一种自动抓取互联网信息的脚本程序,广泛应用于搜索引擎、数据分析和内容聚合。这次我将带大家使用Python快速构建一个基础爬虫,为什么使用python做爬虫?主要就是支持的库很多,而且同类型查询文档多,在同等情…

LeetCode.283移动零

题目链接:283. 移动零 - 力扣(LeetCode) 题目描述: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行…

2025年7月4日漏洞文字版表述一句话版本(漏洞危害以及修复建议),通常用于漏洞通报中简洁干练【持续更新中】,漏洞通报中对于各类漏洞及修复指南

漏洞及修复指南 一、暗链 危害:攻击者通过技术手段在用户网页中插入隐藏链接或代码,并指向恶意网站,可导致用户信息泄露、系统感染病毒,用户访问被劫持至恶意网站,泄露隐私或感染恶意软件,被黑客利用进行…

python --飞浆离线ocr使用/paddleocr

依赖 # python3.7.3 paddleocr2.7.0.2 paddlepaddle2.5.2 loguru0.7.3from paddleocr import PaddleOCR import cv2 import numpy as npif __name__ __main__:OCR PaddleOCR(use_doc_orientation_classifyFalse, # 检测文档方向use_doc_unwarpingFalse, # 矫正扭曲文档use…

数据结构与算法:贪心(三)

前言 感觉开始打cf了以后贪心的能力有了明显的提升,让我们谢谢cf的感觉场。 一、跳跃游戏 II class Solution { public:int jump(vector<int>& nums) {int n=nums.size();//怎么感觉这个题也在洛谷上刷过(?)int cur=0;//当前步最远位置int next=0;//多跳一步最远…

【Redis篇】数据库架构演进中Redis缓存的技术必然性—高并发场景下穿透、击穿、雪崩的体系化解决方案

&#x1f4ab;《博主主页》&#xff1a;    &#x1f50e; CSDN主页__奈斯DB    &#x1f50e; IF Club社区主页__奈斯、 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对…