Scrapy [Python库]

Marimo_z
2025-04-18 / 0 评论 / 4 阅读 / 正在检测是否收录...

Scrapy 是 Python 最强大的开源网络爬虫框架之一,专为高效、结构化数据抓取设计。它基于 Twisted 异步网络库,支持高并发爬取,并提供了完整的爬虫生命周期管理。
官方文档: Scrapy Documentation


1. Scrapy 核心架构

Scrapy 的架构清晰分层,各组件协同工作:

组件作用
Engine调度全局请求与数据流
Scheduler管理待爬取的请求队列
Downloader发送 HTTP 请求,获取响应
Spiders解析响应,提取数据或生成新请求
Item Pipeline处理清洗、验证、存储数据
Downloader Middlewares处理请求/响应(如代理、UA 伪装)
Spider Middlewares处理 Spider 输入/输出

2. 安装与环境配置

pip install scrapy

验证安装:

scrapy version  # 输出版本,如 Scrapy 2.11.0

3. 创建 Scrapy 项目

快速生成项目骨架:

scrapy startproject myproject
cd myproject
scrapy genspider example example.com

生成的文件结构:

myproject/
├── scrapy.cfg
└── myproject/
    ├── items.py          # 定义数据结构
    ├── middlewares.py    # 中间件配置
    ├── pipelines.py      # 数据处理管道
    ├── settings.py       # 全局设置(如并发数、延迟)
    └── spiders/          # 爬虫代码目录
        └── example.py

4. 编写 Spider 核心逻辑

修改 spiders/example.py,定义爬取规则与数据解析:

import scrapy

class ExampleSpider(scrapy.Spider):
    name = "example"  # 爬虫唯一标识
    allowed_domains = ["example.com"]  # 允许爬取的域名
    start_urls = ["https://example.com"]  # 起始 URL

    def parse(self, response):
        # 解析响应内容
        title = response.css('h1::text').get()
        yield {'title': title}  # 生成 Item 或 Request

        # 跟踪分页链接示例
        next_page = response.css('a.next-page::attr(href)').get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

5. 定义数据结构(Item)

items.py 中定义结构化数据:

import scrapy

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

6. 数据存储(Item Pipeline)

pipelines.py 中处理数据存储:

class JsonWriterPipeline:
    def open_spider(self, spider):
        self.file = open('books.json', 'w', encoding='utf-8')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

settings.py 中启用 Pipeline:

ITEM_PIPELINES = {
    'myproject.pipelines.JsonWriterPipeline': 300,
}

7. 中间件高级应用

示例:随机 User-Agent 和代理
修改 middlewares.py

from scrapy import signals
import random

class RandomUserAgentMiddleware:
    user_agents = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...'
    ]

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)

class ProxyMiddleware:
    def process_request(self, request, spider):
        request.meta['proxy'] = 'http://proxy_ip:port'

settings.py 中激活中间件:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomUserAgentMiddleware': 543,
    'myproject.middlewares.ProxyMiddleware': 544,
}

8. 配置调优(settings.py)

关键参数设置:

CONCURRENT_REQUESTS = 16          # 并发请求数
DOWNLOAD_DELAY = 1                # 请求延迟(秒)
AUTOTHROTTLE_ENABLED = True       # 自动限速
DEPTH_LIMIT = 3                   # 最大爬取深度
COOKIES_ENABLED = False           # 禁用 Cookies(减少指纹)
FEED_EXPORT_ENCODING = 'utf-8'    # 导出文件编码

9. 运行与调试

  • 启动爬虫

    scrapy crawl example -o output.json
  • Shell 调试

    scrapy shell 'https://example.com'
    >>> response.css('h1::text').get()

10. 应对动态内容(如 JavaScript 渲染)

使用 Splash 或 Scrapy-Selenium 处理动态页面:

# 示例:集成 Splash
class JSSpider(scrapy.Spider):
    def start_requests(self):
        yield scrapy.Request(
            url='http://example.com',
            meta={'splash': {'args': {'wait': 2.5}}},  # 等待 JS 执行
            callback=self.parse
        )

11. 分布式爬虫(Scrapy-Redis)

扩展 Scrapy 实现分布式爬取:

  1. 安装扩展:

    pip install scrapy-redis
  2. 修改 settings.py

    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    REDIS_URL = 'redis://user:pass@host:port'

12. 反爬策略与道德规范

  • 遵守 robots.txt:在 settings.py 中设置:

    ROBOTSTXT_OBEY = True
  • 限速与请求头伪装:如设置 DOWNLOAD_DELAY 和随机 User-Agent
  • 验证码处理:使用第三方服务(如 2Captcha)或手动介入

实战案例:爬取图书信息

# spiders/book_spider.py
class BookSpider(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()
            item['price'] = book.css('p.price_color::text').get()
            yield item

        next_page = response.css('li.next a::attr(href)').get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)
0

评论 (0)

取消