python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python Requests  Scrapy分布式架构

Python 爬虫全面使用指南:从Requests到Scrapy分布式架构详解

作者:独隅

本文介绍了Python网络爬虫生态中的的三大核心工具:Requests、BeautifulSoup和Scrapy,覆盖了从简单静态网页抓取到复杂动态渲染抓取的全流程,提供了丰富的实战示例和注意事项,感兴趣的朋友跟随小编一起看看吧

—— 从 Requests 到 Scrapy 的实战进化

💡 核心定位:本指南涵盖 Python 爬虫生态的**“三驾马车”**。

📝 摘要

在数据驱动的时代,网络爬虫 (Web Scraping) 是获取外部数据的核心手段。

本指南旨在为开发者提供构建高效、稳定网络爬虫的完整技术路线图。我们将深入解析 Python 爬虫生态的“三驾马车”:Requests(HTTP 请求库)、BeautifulSoup(HTML 解析利器)以及 Scrapy(企业级异步爬虫框架)。文章涵盖从简单的静态网页抓取到复杂的动态渲染、反爬虫对抗、分布式爬取的全流程。通过新闻聚合、电商价格监控、数据归档等实战案例,展示不同场景下的最佳技术选型。特别地,本文重点剖析了法律伦理边界、反爬机制突破、内存泄漏及封号风险等关键陷阱,并提供权威的学习资源,助您安全、合规地获取互联网数据价值。

📌 一、背景、发展历史与方向

1. 为什么需要爬虫?

2. 发展历史

3. 核心作用与发展方向

🔧 二、基础语法与核心库详解

1. Requests:HTTP 请求的艺术

核心概念: GET/POST, Headers, Cookies, Session, Timeout.

import requests
url = "https://api.example.com/data"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept": "application/json"
}
# 发送 GET 请求
response = requests.get(url, headers=headers, timeout=10)
# 检查状态码
if response.status_code == 200:
    data = response.json() # 解析 JSON
    # text = response.text # 解析 HTML 文本
    print(f"Success: {len(data)} events fetched")
else:
    print(f"Error: {response.status_code}")
# 保持会话 (自动处理 Cookies)
session = requests.Session()
session.get('https://example.com/login') # 登录
resp = session.get('https://example.com/profile') # 访问需要登录的页面

2. BeautifulSoup:HTML 解析利器

核心概念: Tag, NavigableString, find(), find_all(), CSS Selectors.

from bs4 import BeautifulSoup
html_doc = """
<html><head><title>Demo Page</title></head>
<body>
<p class="title"><b>The Demo Title</b></p>
<p class="story">Once upon a time...</p>
<a href="http://example.com" rel="external nofollow"  class="link">Example</a>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
# 查找标签
title = soup.find('b').text
print(f"Title: {title}")
# 查找所有特定类名的标签
links = soup.find_all('a', class_='link')
for link in links:
    print(f"Link: {link['href']}, Text: {link.text}")
# CSS 选择器 (更强大)
story = soup.select_one('p.story').text
print(f"Story: {story}")
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" rel="external nofollow"  class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" rel="external nofollow"  class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" rel="external nofollow"  class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
"""
soup = BeautifulSoup(html_doc, 'html.parser') # 指定解析器
# 查找标签
title = soup.title.string
first_link = soup.find('a') # 找到第一个 a 标签
all_links = soup.find_all('a', class_='sister') # 找到所有 class 为 sister 的 a 标签
# CSS 选择器 (更强大)
lacie = soup.select_one("a#link2") 
all_sisters = soup.select("p.story > a")
for link in all_links:
    print(f"Name: {link.get_text()}, URL: {link['href']}")

3. Scrapy:工业级框架骨架

核心概念: Spider, Item, Pipeline, Middleware, Selector.
项目结构

myproject/
├── scrapy.cfg
└── myproject/
    ├── __init__.py
    ├── settings.py
    ├── items.py       # 定义数据结构
    ├── pipelines.py   # 数据清洗与存储
    └── spiders/       # 爬虫逻辑
        ├── __init__.py
        └── example_spider.py

Spider 示例 (example_spider.py):

import scrapy
class ExampleSpider(scrapy.Spider):
    name = "example"
    allowed_domains = ["example.com"]
    start_urls = ["https://www.example.com"]
    def parse(self, response):
        # 提取数据
        for quote in response.css("div.quote"):
            yield {
                'text': quote.css("span.text::text").get(),
                'author': quote.css("small.author::text").get(),
                'tags': quote.css("div.tags a.tag::text").getall(),
            }
        # 分页跟进
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

项目结构:

myproject/
    scrapy.cfg
    myproject/
        __init__.py
        items.py       # 定义数据结构
        pipelines.py   # 数据清洗与存储
        settings.py    # 全局配置
        spiders/       # 爬虫逻辑
            quote_spider.py

Spider 示例 (quote_spider.py):

import scrapy
from myproject.items import QuoteItem
class QuoteSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        for quote in response.css('div.quote'):
            item = QuoteItem()
            item['text'] = quote.css('span.text::text').get()
            item['author'] = quote.css('small.author::text').get()
            item['tags'] = quote.css('div.tags a.tag::text').getall()
            yield item
        # 分页处理
        next_page = response.css('li.next a::attr(href)').get()
        if next_page:
            yield response.follow(next_page, self.parse)

🌟 三、基本使用与进阶场景实例

场景一:快速抓取新闻标题 (Requests + BS4)

需求:抓取某新闻网站首页的前 10 条新闻标题和链接。

import requests
from bs4 import BeautifulSoup
def scrape_news():
    url = "https://news.ycombinator.com/"
    headers = {"User-Agent": "Mozilla/5.0"}
    resp = requests.get(url, headers=headers)
    resp.encoding = 'utf-8'
    soup = BeautifulSoup(resp.text, 'html.parser')
    stories = soup.find_all('tr', class_='athing')
    results = []
    for story in stories[:10]:
        title_tag = story.find('span', class_='titleline').find('a')
        title = title_tag.text
        link = title_tag['href']
        results.append({'title': title, 'link': link})
    return results
# print(scrape_news())

场景二:构建带数据清洗的 Scrapy 项目

需求:抓取书籍信息,清洗价格字段,存入 CSV。
items.py:

import scrapy
class BookItem(scrapy.Item):
    title = scrapy.Field()
    price = scrapy.Field()
    rating = scrapy.Field()

spiders/books_spider.py:

import scrapy
from myproject.items import BookItem
class BooksSpider(scrapy.Spider):
    name = "books"
    start_urls = ['http://books.toscrape.com/']
    def parse(self, response):
        for book in response.css('article.product_pod'):
            item = BookItem()
            item['title'] = book.css('h3 a::attr(title)').get()
            # 清洗价格:去掉 '£'
            raw_price = book.css('p.price_color::text').get()
            item['price'] = raw_price.replace('£', '') if raw_price else None
            item['rating'] = book.css('p.star-rating::attr(class)').get()
            yield item
        next_page = response.css('li.next a::attr(href)').get()
        if next_page:
            yield response.follow(next_page, self.parse)

运行命令
scrapy crawl books -o output.csv

场景三:应对 JavaScript 动态渲染 (Scrapy + Splash/Playwright)

痛点:目标网站数据由 JS 异步加载,requests 拿不到内容。
方案 A (Scrapy-Splash):需部署 Splash 服务。
方案 B (Scrapy-Playwright) (2026 推荐):直接在 Scrapy 中集成 Playwright。

# settings.py
DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
# spider.py
import scrapy
class DynamicSpider(scrapy.Spider):
    name = "dynamic"
    custom_settings = {
        "PLAYWRIGHT_BROWSER_ARGS": {"headless": True},
    }
    def start_requests(self):
        yield scrapy.Request(
            "https://www.example-dynamic.com",
            meta={"playwright": True}, # 启用 Playwright
        )
    def parse(self, response):
        # 此时 response.body 已是 JS 渲染后的 HTML
        titles = response.css("div.loaded-content h2::text").getall()
        yield {"titles": titles}

场景四:分布式爬虫去重与断点续传

需求:千万级 URL 去重,支持暂停后继续。
Scrapy 原生支持

场景五:静态新闻网站数据采集 (Requests + BS4)

任务: 抓取某新闻列表页的标题、链接和时间,存入 CSV。
特点: 简单快速,适合一次性任务。

import requests
from bs4 import BeautifulSoup
import csv
def scrape_news():
    url = "https://news.ycombinator.com/"
    headers = {'User-Agent': 'Mozilla/5.0'}
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'html.parser')
    stories = []
    for row in soup.find_all('tr', class_='athing'):
        title_tag = row.find('span', class_='titleline').find('a')
        title = title_tag.text
        link = title_tag['href']
        score = row.find_next_sibling('tr').find('span', class_='score').text
        stories.append({'title': title, 'link': link, 'score': score})
    with open('news.csv', 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['title', 'link', 'score'])
        writer.writeheader()
        writer.writerows(stories)

场景六:全站深度爬取与数据清洗 (Scrapy)

任务: 爬取整个书籍网站,提取书名、价格、星级,并清洗价格字段(去除货币符号),存入 MySQL。
特点: 高并发、自动去重、管道化处理。

Items (items.py):

import scrapy
class BookItem(scrapy.Item):
    title = scrapy.Field()
    price = scrapy.Field()
    rating = scrapy.Field()

Pipeline (pipelines.py):

class PriceCleaningPipeline:
    def process_item(self, item, spider):
        if 'price' in item:
            # 清洗逻辑:去除 £ 符号并转为 float
            raw_price = item['price']
            item['price'] = float(raw_price.replace('£', ''))
        return item
class MySQLPipeline:
    def open_spider(self, spider):
        # 初始化数据库连接
        pass
    def process_item(self, item, spider):
        # 执行 SQL INSERT
        return item

配置 (settings.py):

ITEM_PIPELINES = {
    'myproject.pipelines.PriceCleaningPipeline': 300,
    'myproject.pipelines.MySQLPipeline': 400,
}
ROBOTSTXT_OBEY = True # 遵守 robots 协议
CONCURRENT_REQUESTS = 16 # 并发数
DOWNLOAD_DELAY = 0.5 # 下载延迟,防止被封

场景七:动态 JavaScript 渲染网站 (Scrapy + Splash/Playwright)

任务: 抓取由 React/Vue 渲染的电商商品详情页,数据在 JS 加载后才出现。
方案: 使用 Scrapy 配合 scrapy-playwright 插件。

# settings.py
DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
# spider.py
import scrapy
from scrapy_playwright.page import PageMethod
class DynamicSpider(scrapy.Spider):
    name = "dynamic_shop"
    start_urls = ["https://example-shop.com/products"]
    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={
                    "playwright": True,
                    "playwright_page_methods": [
                        PageMethod("wait_for_selector", ".product-card"), # 等待元素加载
                    ],
                },
                callback=self.parse,
            )
    def parse(self, response):
        # 此时 response.body 已包含 JS 渲染后的 HTML
        titles = response.css(".product-title::text").getall()
        yield {"titles": titles}

⚠️ 四、致命陷阱与避坑指南

陷阱 1:忽视 User-Agent 与请求头

陷阱 2:过快请求导致 IP 被封

陷阱 3:XPath/CSS 选择器脆弱

陷阱 4:内存泄漏 (Scrapy)与性能瓶颈

陷阱 5:法律与道德风险

陷阱 六:反爬虫机制对抗

陷阱 七:编码问题

📚 五、学习资源推荐 (2026版)

1. 官方文档 (必读)

2. 经典书籍

书名作者推荐理由
《Python 3 网络爬虫开发实战 (第 2 版/第 3 版)》崔庆才中文权威。涵盖 Requests, Scrapy, Selenium, App 抓取,案例更新及时,贴合国内环境。
《Web Scraping with Python (2nd Ed)》Ryan Mitchell英文经典。O’Reilly 出版,讲解原理深入,包含反爬对抗策略。
《精通 Python 爬虫框架 Scrapy》[美] Diogo Silva专注 Scrapy 深度定制,适合进阶开发者。

3. 在线工具与社区

4. 进阶方向

💡 六、实践总结

  1. 先分析,后编码:打开浏览器开发者工具 (F12),分析 Network 面板,确定数据是静态 HTML 还是 AJAX/API 加载。能抓 API 绝不渲染 HTML
  2. 礼貌爬虫
    • 始终检查 robots.txt
    • 设置合理的 User-Agent
    • 添加延迟 (DOWNLOAD_DELAY),模拟人类行为。
  3. 异常处理健壮性:网络波动是常态。务必处理 Timeout, ConnectionError, HTTPError,并实现重试机制 (Scrapy 自带 RETRY_TIMES)。
  4. 数据持久化分离:爬虫只负责“抓”,Pipeline 负责“存”。不要将数据库逻辑耦合在 Spider 解析函数中。
  5. 模块化与配置化:将 URL 规则、选择器、Headers 提取到配置文件或数据库中,便于维护和多站点适配。
  6. 监控与报警:生产环境必须监控抓取成功率、数据量波动,异常时发送钉钉/邮件报警。

💡 终极建议
“爬虫技术是一把双刃剑。它赋予你获取世界数据的能力,但也要求你承担相应的责任。永远保持对数据的敬畏,对法律的遵守,对目标服务器的礼貌。做一个‘绅士’爬虫开发者。”

Python 爬虫技术是一把双刃剑。一方面,它是获取互联网公开数据、赋能数据分析的强大工具;另一方面,不当使用可能触犯法律和道德底线。

掌握爬虫不仅仅是学会 requests.getscrapy crawl,更重要的是理解 HTTP 协议本质HTML DOM 结构异步编程模型 以及 反爬对抗的逻辑。从简单的 Requests+BS4 脚本入手,逐步过渡到 Scrapy 架构,最后挑战动态渲染和逆向工程,这是一条充满挑战但也极具成就感的成长路径。

请始终铭记:技术无罪,但在使用技术时,请务必保持对规则的敬畏和对数据的尊重。 做一个负责任的爬虫工程师。

掌握 Requests, BeautifulSoup 和 Scrapy,你就拥有了连接现实世界与数字世界的桥梁。现在,去探索数据的海洋吧!

到此这篇关于Python 爬虫全面使用指南:从Requests到Scrapy分布式架构详解的文章就介绍到这了,更多相关Python Requests Scrapy分布式架构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文