【Python 第三方库】Scrapy

2024-01-25 00:00:00

目录:

Scrapy 模块介绍

Scrapy 是一个爬虫框架,用于从网站中抓取结构化数据。

核心概念:

  1. 引擎 (Engine)

引擎是整个框架的核心,相当于系统的“CPU”。它负责控制所有组件之间的数据流,协调爬虫的启动与停止,并管理请求的发送与响应的传递。

  1. 调度器 (Scheduler)

调度器接收引擎传递过来的请求(URL),并将其组织成一个优先队列进行管理。它负责决定下一个要处理的请求是什么,并自动去除重复的 URL,避免无效工作。

  1. 下载器 (Downloader)

下载器负责根据引擎传递的请求,从互联网上下载网页内容,并将获取到的响应(Response)交还给引擎。它建立在高效的异步模型之上,负担最重但性能出色。

  1. 爬虫 (Spider)

爬虫是用户编写自定义抓取逻辑的核心部分。它负责处理下载器返回的响应,从中解析并提取所需的结构化数据(Item),同时还可以从中发现新的 URL 并提交给引擎,以便进行后续抓取。

  1. 项目管道 (Item Pipeline)

项目管道负责处理爬虫提取出来的数据项(Item)。它可以对数据进行清洗、验证、去重,并最终存储到数据库、文件或其他持久化介质中。

  1. 中间件 (Middlewares)

中间件是位于引擎与其他组件之间的钩子(Hook),提供了一种灵活的扩展机制。主要分为两类:

  • 下载器中间件 (Downloader Middlewares):位于引擎和下载器之间,可用于修改请求(如设置代理、更换请求头)或处理响应;
  • 爬虫中间件 (Spider Middlewares):位于引擎和爬虫之间,可用于处理爬虫的输入(响应)和输出(Items 或 Requests)。

安装依赖:

$ pip install scrapy

创建项目

$ scrapy startproject myproject

上述命令会在当前目录下初始化 myproject 爬虫项目,目录结构如下:

|-- myproject
    |-- myproject
        |-- spiders             # 存放所有爬虫文件的目录
        |-- items.py            # 定义要抓取的数据结构
        |-- middlewares.py      # 定义中间件
        |-- pipelines.py        # 定义数据处理管道
        |-- settings.py         # 项目设置文件, 如并发数、下载延迟、管道启用等
    |-- scrapy.cfg              # 项目部署配置文件

使用如下命令会在 spiders 目录下创建一个爬虫模版:

$ scrapy genspider example example.com

在 spiders 目录下会自动创建 example.py 文件,内容如下:

import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    allowed_domains = ["example.com"]
    start_urls = ["https://example.com"]

    def parse(self, response):
        pass

示例:抓取博客文章列表

目标网站: https://blog.dkvirus.com/ (我自己博客网站),抓取文章标题,文章分类以及文章创建时间。

创建项目

$ scrapy startproject myblog
$ cd myblog

定义数据模型 Item

在 items.py 中,定义要抓取的数据字段:

import scrapy


class MyblogItem(scrapy.Item):
    title = scrapy.Field()
    category = scrapy.Field()
    create_time = scrapy.Field()

编写爬虫逻辑 Spider

在 spiders/myblog_spider.py 中编写爬虫:

import scrapy
from myblog.items import MyblogItem  # 导入自定义的 Item

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

    def parse(self, response):
        """
        解析响应,提取数据并生成新的请求。
        """
        # 使用 CSS 选择器选取页面上文章列表
        li_list = response.css('.list-group-item')
        
        for li in li_list:
            # 实例化一个Item对象
            item = MyblogItem()
            # 提取文章标题(::text获取文本,extract_first()取第一个结果)
            item['title'] = li.css('a[target="_self"]::text').extract_first()
            # 提取文章分类
            item['category'] = li.css('ul.tags a::text').extract_first()
            # 提取文章创建时间
            item['create_time'] = li.css('.date::text').extract_first()
            # 将 Item 返回给引擎,交给管道处理
            yield item

        # 处理分页:查找“下一页”的链接
        next_page_url = response.css('a.next::attr(href)').extract_first()
        if next_page_url:
            # response.urljoin() 将相对 URL 转换为绝对 URL
            next_page_absolute_url = response.urljoin(next_page_url)
            # 生成一个新的 Request 对象,并指定回调函数为 parse 本身,实现递归抓取
            yield scrapy.Request(url=next_page_absolute_url, callback=self.parse)
  • parse 方法是默认的回调函数,用于处理起始URL的响应;
  • response.css() 使用 CSS 选择器定位元素,::text 获取元素文本,::attr(href) 获取属性值;
  • extract_first() 返回第一个匹配的结果(字符串),extract() 返回所有匹配结果的列表;
  • yield item 将数据项交给引擎。

配置数据处理管道 Pipeline

在 pipelines.py 中,可以编写管道来处理爬取的数据。例如,将数据保存为 JSON 格式。

import json


class MyblogPipeline:
    def open_spider(self, spider):
        """爬虫启动时执行"""
        self.file = open('blogs.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        """每个item都会经过此方法处理"""
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item  # 必须返回item,以便被后续管道处理

    def close_spider(self, spider):
        """爬虫关闭时执行"""
        self.file.close()

在 settings.py 中启用该管道,并设置其优先级,数值越小优先级越高。

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

运行爬虫

在项目根目录下,执行以下命令启动名为 myblog (spiders/myblog_spider.py 文件中类的 name 属性值) 的爬虫。

$ scrapy crawl myblog

运行结束后会在当前目录下生成 blogs.json 文件,里面是博客的所有文章信息。

返回首页

本文总阅读量  次
皖ICP备17026209号-3
总访问量: 
总访客量: