Python 第二阶段 - 爬虫入门


🎯 本周知识回顾

网络请求与网页结构基础
HTML解析入门(使用 BeautifulSoup)
实现爬虫多页抓取与翻页逻辑
模拟登录爬虫与 Session 维持
使用 XPath 进行网页解析(lxml + XPath)
反爬虫应对策略 & Headers / Cookies 模拟请求


🧪 综合实战挑战任务

🎯 项目名称:多页名言抓取系统 Plus 版


🛠️ 任务目标

使用本周所学,构建一个完整的爬虫系统,功能如下:

📌 功能需求:
  1. 自动翻页抓取 quotes.toscrape.com
  2. 支持 BeautifulSoup 与 XPath 两种方式切换
  3. 支持数据保存为 JSON 文件
  4. 记录抓取日志(包括时间、页数、条数)
  5. 模拟登录后抓取用户登录后的页面内容

📂 推荐文件结构

quotes_scraper/
├── main.py               # 程序入口
├── bs4_parser.py         # 使用 BeautifulSoup 提取
├── xpath_parser.py       # 使用 lxml + XPath 提取
├── login_session.py      # 登录模块,返回登录后的 session
├── utils.py              # 通用函数,如保存 json,打印日志
└── data/└── quotes.json       # 抓取结果

🚀 挑战加分项(可选)

  • ✨ 使用命令行参数切换解析方式(如:--parser xpath
  • ✨ 抓取后展示作者出现次数统计(使用 collections.Counter
  • ✨ 每条数据添加时间戳字段
  • ✨ 自动保存抓取失败日志(如网络错误等)

🧪 示例挑战结果展示(JSON 数据片段)

[{"text": "“The world as we have created it is a process of our thinking.”","author": "Albert Einstein","tags": ["change", "deep-thoughts"],"timestamp": "2025-06-16 21:30:00"},...
]

✅ 今日练习任务

  1. 完成多页抓取器功能(任选解析方式)
  2. 写出能复用的函数和模块
  3. 成功模拟登录(CSRF + Cookie)
  4. 保存 JSON,并打印总抓取条数
  5. [可选] 完善命令行支持 & 错误日志输出

好的!以下是 第4周第7天 · 项目实战 的完整代码实现 —— 一个结构化、可扩展的多页名言爬虫系统,支持:

  • 多页抓取
  • BeautifulSoup 与 XPath 两种方式切换
  • 数据保存为 JSON
  • 日志记录
  • 简易模拟登录(session)

🗂 项目结构建议(quotes_scraper)

quotes_scraper/
├── main.py               # 程序入口
├── bs4_parser.py         # BeautifulSoup 解析器
├── xpath_parser.py       # XPath 解析器
├── login_session.py      # 登录模拟(requests.Session)
├── utils.py              # 工具函数(保存、打印、日志)
├── config.py             # 配置项(起始页、保存路径等)
└── data/└── quotes.json       # 保存结果

✅ 1. config.py(配置文件)

# config.py
BASE_URL = "https://quotes.toscrape.com"
START_PAGE = "/page/1/"
USE_XPATH = False  # True 使用 lxml,否则用 BS4
SAVE_PATH = "data/quotes.json"

✅ 2. utils.py

# utils.py
import json
import os
import logging
from datetime import datetimedef save_to_json(data, filepath):os.makedirs(os.path.dirname(filepath), exist_ok=True)with open(filepath, 'w', encoding='utf-8') as f:json.dump(data, f, ensure_ascii=False, indent=2)print(f"✅ 数据已保存到 {filepath}")def log(message):print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}")def setup_logging(debug=False):os.makedirs("logs", exist_ok=True)level = logging.DEBUG if debug else logging.WARNINGlogging.basicConfig(level=level,format='%(asctime)s [%(levelname)s] %(message)s',handlers=[logging.FileHandler("logs/error.log", encoding='utf-8'),logging.StreamHandler()])

✅ 3. bs4_parser.py

# bs4_parser.py
from bs4 import BeautifulSoupdef parse_quotes_bs4(html):soup = BeautifulSoup(html, "html.parser")quotes = []for q in soup.select(".quote"):quotes.append({"text": q.select_one(".text").text.strip(),"author": q.select_one(".author").text.strip(),"tags": [tag.text for tag in q.select(".tags .tag")]})return quotes

✅ 4. xpath_parser.py

# xpath_parser.py
from lxml import etreedef parse_quotes_xpath(html):tree = etree.HTML(html)quotes = []quote_elements = tree.xpath('//div[@class="quote"]')for q in quote_elements:quotes.append({"text": q.xpath('.//span[@class="text"]/text()')[0],"author": q.xpath('.//small[@class="author"]/text()')[0],"tags": q.xpath('.//div[@class="tags"]/a[@class="tag"]/text()')})return quotes

✅ 5. login_session.py

# login_session.py
import requestsdef create_session():session = requests.Session()login_url = "https://quotes.toscrape.com/login"headers = {"User-Agent": "Mozilla/5.0","Referer": login_url}# 先获取 csrf_tokenresp = session.get(login_url, headers=headers)token = resp.text.split('name="csrf_token" value="')[1].split('"')[0]login_data = {"csrf_token": token,"username": "test","password": "test"}session.post(login_url, data=login_data, headers=headers)return session

✅ 6. main.py(程序入口)

# main.py
import argparse
import logging
import time
from login_session import create_session
from bs4_parser import parse_quotes_bs4
from xpath_parser import parse_quotes_xpath
from utils import save_to_json, log, setup_loggingimport configdef parse_args():parser = argparse.ArgumentParser(description="Quotes Scraper - 支持 BS4 / XPath")parser.add_argument('--use-xpath', action='store_true', help="使用 XPath 解析器")parser.add_argument('--output', type=str, default="data/quotes.json", help="保存文件路径")parser.add_argument('--debug', action='store_true', help="启用调试模式(输出更多日志)")return parser.parse_args()def scrape(use_xpath=False, save_path="data/quotes.json"):log("开始爬取...")session = create_session()url = config.BASE_URL + config.START_PAGEall_quotes = []page = 1while True:try:log(f"请求第 {page} 页:{url}")resp = session.get(url, headers={"User-Agent": "Mozilla/5.0","Referer": config.BASE_URL})if resp.status_code != 200:logging.error(f"请求失败:{resp.status_code}")breakhtml = resp.textquotes = parse_quotes_xpath(html) if use_xpath else parse_quotes_bs4(html)if not quotes:log("未获取到内容,结束。")breakall_quotes.extend(quotes)log(f"第 {page} 页抓取 {len(quotes)} 条")# 判断是否还有下一页if 'href="/page/{}/"'.format(page + 1) in html:page += 1url = f"{config.BASE_URL}/page/{page}/"time.sleep(1)else:breakexcept Exception as e:logging.exception(f"第 {page} 页抓取异常:{e}")breaklog(f"共抓取 {len(all_quotes)} 条名言")save_to_json(all_quotes, save_path)if __name__ == "__main__":args = parse_args()setup_logging(debug=args.debug)scrape(use_xpath=args.use_xpath, save_path=args.output)

✅ 如何运行:

# 默认使用 BeautifulSoup,保存到 data/quotes.json
python main.py# 使用 XPath + 自定义保存路径
python main.py --use-xpath --output data/xpath_output.json# 开启调试模式(输出详细错误)
python main.py --debug

🎯 示例运行结果(终端输出)

1. 运行python main.py
python main.py

输出:

[2025-06-17 17:39:29] 开始爬取...
[2025-06-17 17:39:31] 正在请求第 1 页:https://quotes.toscrape.com/page/1/
[2025-06-17 17:39:31] 本页获取 10 条名言。
[2025-06-17 17:39:32] 正在请求第 2 页:https://quotes.toscrape.com/page/2/
[2025-06-17 17:39:33] 本页获取 10 条名言。
[2025-06-17 17:39:34] 正在请求第 3 页:https://quotes.toscrape.com/page/3/
[2025-06-17 17:39:34] 本页获取 10 条名言。
[2025-06-17 17:39:35] 正在请求第 4 页:https://quotes.toscrape.com/page/4/
[2025-06-17 17:39:35] 本页获取 10 条名言。
[2025-06-17 17:39:36] 正在请求第 5 页:https://quotes.toscrape.com/page/5/
[2025-06-17 17:39:37] 本页获取 10 条名言。
[2025-06-17 17:39:38] 正在请求第 6 页:https://quotes.toscrape.com/page/6/
[2025-06-17 17:39:38] 本页获取 10 条名言。
[2025-06-17 17:39:39] 正在请求第 7 页:https://quotes.toscrape.com/page/7/
[2025-06-17 17:39:39] 本页获取 10 条名言。
[2025-06-17 17:39:40] 正在请求第 8 页:https://quotes.toscrape.com/page/8/
[2025-06-17 17:39:41] 本页获取 10 条名言。
[2025-06-17 17:39:42] 正在请求第 9 页:https://quotes.toscrape.com/page/9/
[2025-06-17 17:39:42] 本页获取 10 条名言。
[2025-06-17 17:39:43] 正在请求第 10 页:https://quotes.toscrape.com/page/10/
[2025-06-17 17:39:43] 本页获取 10 条名言。
[2025-06-17 17:39:44] 正在请求第 11 页:https://quotes.toscrape.com/page/11/
[2025-06-17 17:39:45] 没有找到更多数据,停止爬取。
[2025-06-17 17:39:45] 共抓取 100 条名言
✅ 数据已保存到 data/quotes.json
2. 运行python3 main.py --use-xpath --output data/xpath_output.json
python3 main.py --use-xpath --output data/xpath_output.json

输出:

[2025-06-17 17:50:38] 开始爬取...
[2025-06-17 17:50:39] 请求第 1 页:https://quotes.toscrape.com/page/1/
[2025-06-17 17:50:40]1 页抓取 10[2025-06-17 17:50:41] 请求第 2 页:https://quotes.toscrape.com/page/2/
[2025-06-17 17:50:41]2 页抓取 10[2025-06-17 17:50:42] 请求第 3 页:https://quotes.toscrape.com/page/3/
[2025-06-17 17:50:42]3 页抓取 10[2025-06-17 17:50:43] 请求第 4 页:https://quotes.toscrape.com/page/4/
[2025-06-17 17:50:43]4 页抓取 10[2025-06-17 17:50:44] 请求第 5 页:https://quotes.toscrape.com/page/5/
[2025-06-17 17:50:45]5 页抓取 10[2025-06-17 17:50:46] 请求第 6 页:https://quotes.toscrape.com/page/6/
[2025-06-17 17:50:46]6 页抓取 10[2025-06-17 17:50:47] 请求第 7 页:https://quotes.toscrape.com/page/7/
[2025-06-17 17:50:47]7 页抓取 10[2025-06-17 17:50:48] 请求第 8 页:https://quotes.toscrape.com/page/8/
[2025-06-17 17:50:49]8 页抓取 10[2025-06-17 17:50:50] 请求第 9 页:https://quotes.toscrape.com/page/9/
[2025-06-17 17:50:50]9 页抓取 10[2025-06-17 17:50:51] 请求第 10 页:https://quotes.toscrape.com/page/10/
[2025-06-17 17:50:51]10 页抓取 10[2025-06-17 17:50:51] 共抓取 100 条名言
✅ 数据已保存到 data/xpath_output.json
3. 运行python3 main.py --debug
python3 main.py --debug

输出:

[2025-06-17 17:50:09] 开始爬取...
2025-06-17 17:50:09,517 [DEBUG] Starting new HTTPS connection (1): quotes.toscrape.com:443
2025-06-17 17:50:10,583 [DEBUG] https://quotes.toscrape.com:443 "GET /login HTTP/1.1" 200 1880
2025-06-17 17:50:10,889 [DEBUG] https://quotes.toscrape.com:443 "POST /login HTTP/1.1" 302 189
2025-06-17 17:50:11,172 [DEBUG] https://quotes.toscrape.com:443 "GET / HTTP/1.1" 200 11928
[2025-06-17 17:50:11] 请求第 1 页:https://quotes.toscrape.com/page/1/
2025-06-17 17:50:11,461 [DEBUG] https://quotes.toscrape.com:443 "GET /page/1/ HTTP/1.1" 200 11928
[2025-06-17 17:50:11]1 页抓取 10[2025-06-17 17:50:12] 请求第 2 页:https://quotes.toscrape.com/page/2/
2025-06-17 17:50:12,756 [DEBUG] https://quotes.toscrape.com:443 "GET /page/2/ HTTP/1.1" 200 14597
[2025-06-17 17:50:12]2 页抓取 10[2025-06-17 17:50:13] 请求第 3 页:https://quotes.toscrape.com/page/3/
2025-06-17 17:50:14,122 [DEBUG] https://quotes.toscrape.com:443 "GET /page/3/ HTTP/1.1" 200 10888
[2025-06-17 17:50:14]3 页抓取 10[2025-06-17 17:50:15] 请求第 4 页:https://quotes.toscrape.com/page/4/
2025-06-17 17:50:15,419 [DEBUG] https://quotes.toscrape.com:443 "GET /page/4/ HTTP/1.1" 200 11188
[2025-06-17 17:50:15]4 页抓取 10[2025-06-17 17:50:16] 请求第 5 页:https://quotes.toscrape.com/page/5/
2025-06-17 17:50:16,717 [DEBUG] https://quotes.toscrape.com:443 "GET /page/5/ HTTP/1.1" 200 10891
[2025-06-17 17:50:16]5 页抓取 10[2025-06-17 17:50:17] 请求第 6 页:https://quotes.toscrape.com/page/6/
2025-06-17 17:50:18,017 [DEBUG] https://quotes.toscrape.com:443 "GET /page/6/ HTTP/1.1" 200 11299
[2025-06-17 17:50:18]6 页抓取 10[2025-06-17 17:50:19] 请求第 7 页:https://quotes.toscrape.com/page/7/
2025-06-17 17:50:19,300 [DEBUG] https://quotes.toscrape.com:443 "GET /page/7/ HTTP/1.1" 200 11591
[2025-06-17 17:50:19]7 页抓取 10[2025-06-17 17:50:20] 请求第 8 页:https://quotes.toscrape.com/page/8/
2025-06-17 17:50:20,598 [DEBUG] https://quotes.toscrape.com:443 "GET /page/8/ HTTP/1.1" 200 12243
[2025-06-17 17:50:20]8 页抓取 10[2025-06-17 17:50:21] 请求第 9 页:https://quotes.toscrape.com/page/9/
2025-06-17 17:50:21,969 [DEBUG] https://quotes.toscrape.com:443 "GET /page/9/ HTTP/1.1" 200 11862
[2025-06-17 17:50:21]9 页抓取 10[2025-06-17 17:50:22] 请求第 10 页:https://quotes.toscrape.com/page/10/
2025-06-17 17:50:23,270 [DEBUG] https://quotes.toscrape.com:443 "GET /page/10/ HTTP/1.1" 200 10795
[2025-06-17 17:50:23]10 页抓取 10[2025-06-17 17:50:23] 共抓取 100 条名言

✅ 数据保存示例片段

[{"text": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”","author": "Albert Einstein","tags": ["change","deep-thoughts","thinking","world"]},...
]

📌 今日总结

你已完成基础爬虫技能的初步掌握,包括网页请求、信息提取、登录模拟、数据存储与翻页抓取等核心技能,具备构建结构化数据采集工具的能力。


题外话

在这里插入图片描述

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

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

相关文章

WebRTC(七):媒体能力协商

目的 在 WebRTC 中,每个浏览器或终端支持的音视频编解码器、分辨率、码率、帧率等可能不同。媒体能力协商的目的就是: 确保双方能“听得懂”对方发的媒体流;明确谁发送、谁接收、怎么发送;保障连接的互操作性和兼容性。 P2P的基…

可信启动方案设计

安全之安全(security)博客目录导读 目录 一、引言 二、关键数据(Critical Data) 三、度量槽(Measurement Slot) 四、可信启动后端 1、事件日志(Event Log) 2、离散型 TPM(Discrete TPM) 3、RSE(运行时安全引擎) 五、平台接口 平台接口的职责: 1、函数:b…

✨通义万相2.1深度解析:AI视频生成引擎FLF2V-14B全流程指南(命令行参数+模型架构+数据流)

🌟 从零详解:如何用AI模型生成视频?命令行、模型结构、数据流全解析! 本文通过一个实际案例,详细解析使用AI模型生成视频的整个流程。从命令行参数解读到模型结构,再到数据在模型间的流动,一步步…

在 TypeScript 前端中使用 Umi-Request 调用 Java 接口的完整指南

下面我将详细介绍如何在基于 TypeScript 的前端项目中使用 umi-request 调用 IntelliJ IDEA 中开发的 Java 接口,包括完整的实现方案和代码示例。 整体方案设计 一、Java 后端接口准备 1. 创建 Spring Boot 控制器 // src/main/java/com/example/demo/controller…

GO Gin Web框架面试题及参考答案

目录 Gin 与 net/http 有哪些主要区别?为什么选择 Gin? 如何使用 Gin 启动一个 HTTP 服务并设置默认路由? Gin 的默认路由和自定义路由器组是如何工作的? 如何在 Gin 中绑定请求参数(Query、Form、JSON、XML)? 如何在 Gin 中使用中间件?中间件执行顺序是怎样的? …

asp.net core Razor动态语言编程代替asp.net .aspx更高级吗?

For Each item In products<tr><td>item.Id</td><td>item.Name</td><td>item.Price.ToString("C")</td></tr>Next为什么要用<tr> ? 在Blazor的Razor语法中&#xff0c;使用<tr>是为了在VB.NET代码块中…

css语法中的选择器与属性详解:嵌套声明、集体声明、全局声明、混合选择器

嵌套声明 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>嵌套声明</title> <!-- 这里p span 的含义是p标签下面的span标签 所以有嵌套关系--><style>p span {font-weight:…

Linux 系统中,/usr/bin/ 和/bin/的区别?

在 Linux 系统中&#xff0c;/bin/ 和 /usr/bin/ 都是存放可执行程序&#xff08;命令&#xff09;的目录&#xff0c;但它们在历史定位、用途、挂载策略和系统设计上有一定区别。 ✅ 快速对比总结 项目/bin//usr/bin/全称含义binary&#xff08;核心二进制&#xff09;user b…

苍穹外卖--WebSocket、来单提醒、客户催单

WebSocket 1.介绍 WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传送。 HTTP协议和WebSocket协议对比&#xff1a; ①Http是短连接 ②W…

Linux 信号(Signal)与信号量(Semaphore)区别

特性信号 (Signal)信号量 (Semaphore)本质软件中断进程间同步机制用途通知进程发生了某个事件控制对共享资源的访问通信方向单向 (内核→进程 或 进程→进程)多进程共享数据类型整数信号编号内核维护的计数器持久性瞬时,不排队持久,直到显式释放实现层次内核实现内核或用户空…

华为OD机考-观看文艺汇演问题-区间问题(JAVA 2025B卷)

import java.util.*; /*** version Ver 1.0* date 2025/6/20* description 观看文艺汇演*/ public class WatchMovie {public static void main(String[] args) {Scanner sc new Scanner(System.in);int num Integer.parseInt(sc.nextLine());List<Movie> movies new …

DeepSeek今天喝什么随机奶茶推荐器

用DeepSeek生成了一个随机奶茶推荐器-今天喝什么&#xff0c;效果非常棒&#xff01;UI界面美观。 提示词prompt如下 用html5帮我生成一个今天喝什么的网页 点击按钮随机生成奶茶品牌等&#xff0c;要包括中国常见的知名的奶茶品牌 如果不满意还可以随机再次生成 ui界面要好看 …

【国产AI服务器】全国产PCIE5.0交换板,替代博通89104/89144,支持海光、龙芯等平台

实物图 核心硬件配置 1、控制器芯片‌ 采用国产TL63104控制芯片‌&#xff0c;支持2.5GT/s、5GT/s、8GT/s、16GT/s、32GT/s的PCIe传输速率&#xff0c;支持968Lanes。支持6个x16的group和1个x8的group&#xff0c;每个group支持1至8个端口。x16group支持x16、x8、x4、x2端口…

GPIO-LED驱动

一、LED引脚说明 寄存器地址地图&#xff1a; 原理图&#xff1a; 关于MOS管的说明&#xff1a; 总结&#xff1a;当GPIO0_B5这个引脚输出高电平的时候&#xff0c;对应的N-MOS管导通—LED点亮 当GPIO0_B5这个引脚输出低电平的时候&#xff0c;对应的N-MOS管截止---LED熄灭 二…

Gartner《Generative AI Use - Case Comparison for Legal Departments》

概述 这篇文章由 Gartner, Inc. 出品,聚焦于生成式人工智能(GenAI)在法律部门中的应用情况,通过对 16 个较为突出的 GenAI 法律技术应用场景进行分析,从商业价值和可行性两个维度进行评估,旨在为法律总顾问等提供战略对话依据,以便更好地做出技术投资决策,推动法律部门…

Vue 中 filter 过滤的语法详解与注意事项

Vue 中 filter 过滤的语法详解与注意事项 在 Vue.js 中,"过滤"通常指两种不同概念:模板过滤器(Vue 2 特性)和数组过滤(数据过滤)。由于 Vue 3 已移除模板过滤器,我将重点介绍更实用且通用的数组过滤语法和注意事项。 一、数组过滤核心语法(推荐方式) 1. …

webpack+vite前端构建工具 -6从loader本质看各种语言处理 7webpack处理html

6 从loader本质看各种语言处理 语法糖&#xff1f; 6.1 loader的本质 loader本质是一个方法&#xff0c;接收要处理的资源的内容&#xff0c;处理完毕后给出内容&#xff0c;作为打包结果。 所有的loader&#xff08;例如babel-loader, url-loader等&#xff09;export出一个方…

算法第41天|188.买卖股票的最佳时机IV、309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

188.买卖股票的最佳时机IV 题目 思路与解法 基于 买卖股票的最佳时机iii&#xff0c;得出的解法。关键在于&#xff0c;每一天的卖或者买都由前一天推导而来。 class Solution { public:int maxProfit(int k, vector<int>& prices) {if(prices.size() 0) return …

【AI News | 20250623】每日AI进展

AI Repos 1、tools Strands Agents Tools提供了一个强大的模型驱动方法&#xff0c;通过少量代码即可构建AI Agent。它提供了一系列即用型工具&#xff0c;弥合了大型语言模型与实际应用之间的鸿沟&#xff0c;涵盖文件操作、Shell集成、内存管理&#xff08;支持Mem0和Amazon…

Python装饰器decorators和pytest夹具fixture详解和使用

此前一直认为fixture就叫python中的装饰器&#xff0c;学习后才发现decorators才是装饰器&#xff0c;fixture是pytest框架的夹具&#xff0c;只是通过装饰器去定义和使用。所以要了解fixture就得先了解python装饰器。 一、装饰器(decorators) 1.定义 装饰器&#xff08;dec…