Flutter笔记:用于ORM的Floor框架简记

Flutter笔记
用于ORM的Floor框架简记

本文地址:https://blog.csdn.net/qq_28550263/article/details/133377191

floor 模块地址:https://pub.dev/packages/floor


【介绍】:最近想找用于Dart和Flutter的ORM框架,偶然间发现了Floor,觉得还不错,做一些记录。


  • 1. Floor 框架概述
    • 1.1 框架简介
    • 1.2 框架的构成
    • 1.3 安装
    • 1.4 架构体系
    • 2. 实体(Entity)
      • 支持的类型
      • 主键
      • 外键
      • 索引
      • 忽略字段
      • 继承
    • 数据库视图(Database Views)
  • 3. 数据访问对象(DAO - Data Access Object)
  • 4. 创建数据库
  • 5. 运行代码生成器
  • 6. 使用生成的代码


1. Floor 框架概述

1.1 框架简介

Floor 框架是一个用于在 Flutter 应用程序中使用 SQLite 数据库的开源库。它允许开发者轻松地将本地数据库集成到其应用程序中,并提供了一种类型安全、高效和易于使用的方法来执行数据库操作。

1.2 框架的构成

Floor 框架由多个关键模块组成,每个模块都有特定的功能,协同工作以实现在 Flutter 应用程序中使用 SQLite 数据库的目标。以下是 Floor 框架的主要模块以及它们的作用:

  1. floor(运行时库):

    • 作用: floor 是 Floor 框架的运行时库,包含用于运行时数据库操作的类和方法。
    • 关键类和方法:
    • FloorDatabase:抽象类,表示数据库的根类,生成的数据库类需要扩展它。
    • @Database:用于标记生成的数据库类,指定数据库版本号、实体列表和其他配置信息。
    • databaseBuilder:用于创建数据库实例的方法。
    • 数据库操作方法:生成的数据库类中包含与数据库表的交互方法,例如插入、查询、更新等。
  2. floor_generator(代码生成器):

    • 作用: floor_generator 是 Floor 框架的代码生成器模块,它会根据实体和 DAO 的定义生成数据库访问代码。
    • 生成的代码: 该模块生成包括数据库类、DAO 类以及其他与数据库交互相关的代码,使开发者能够轻松执行数据库操作,而无需手动编写大量的重复代码。
  3. floor_annotation(Floor 框架中的注解)

    • 作用: floor_annotation是Floor 框架中的注解。这些注解用于标记和配置数据库实体(Entities)、数据访问对象(DAOs)以及数据库操作,以便 Floor 框架的代码生成器 floor_generator 可以根据这些注解生成相应的数据库访问代码。
    • floor_annotation 提供了一种类型安全的方式来配置和定义数据库操作,并允许开发者在 Dart 代码中使用注解来描述数据库模型和操作,而无需手动编写大量的重复代码。
    • 例如:
      • @entity:用于标记 Dart 类,表示它是一个数据库实体(Entity)。实体类通常与数据库表相对应,用于定义表的结构和字段。
      • @dao:用于标记抽象类或接口,表示它是数据访问对象(DAO)。DAO 类包含了与数据库表相关的操作方法,如插入、查询、更新和删除。

等等

  1. sqflite(数据库驱动,由floor自动调用):
    • 作用: Floor 框架依赖于 sqflite 插件,它提供了在 Flutter 中与 SQLite 数据库进行底层交互的功能。
    • sqflite 插件负责管理 SQLite 数据库文件、执行原始 SQL 查询和处理数据库事务。

这些模块协同工作,使开发者能够轻松地在 Flutter 应用程序中集成和操作 SQLite 数据库。通过定义实体、DAO 和数据库,然后运行代码生成器,开发者可以获得类型安全、高效和易于维护的数据库访问代码,从而实现数据持久化和本地数据存储的需求。

  1. build_runner:build_runner 通常与 floor_generator 一起使用,以自动生成与数据库操作相关的 Dart 代码。

1.3 安装

flutter pub add floor
flutter pub add floor_generator build_runner --dev

其中:

  • floor: 包含了将在应用程序中使用的所有代码。
  • floor_generator: 包含了生成数据库类的代码。
  • build_runner: 允许以具体的方式生成源代码文件。

将在你的yaml中看到:

dependencies:flutter:sdk: flutterfloor: ^1.4.0dev_dependencies:floor_generator: ^1.4.0build_runner: ^2.1.2

版本可能随时间变化

1.4 架构体系

存储和访问数据的组件是 实体数据访问对象(DAO) 和 数据库

  • 一个 实体 代表一个 持久类,也就是一个 数据库表
  • Dao管理对实体的访问,并负责内存中 对象表行 之间的 映射
  • 数据库是底层 SQLite 数据库的中心访问点。它 保存Dao,除此之外,还负责 初始化数据库及其 模式。

https://raw.githubusercontent.com/vitusortner/floor/develop/img/floor-architecture.png

2. 实体(Entity)

一个实体是一个持久化类。Floor 自动创建内存中对象与数据库表行之间的映射。可以通过向 Entity 注释添加可选值来向 Floor 提供自定义元数据。它具有表名 tableName 的附加属性,该属性打开了为特定实体使用自定义名称的可能性,而不是使用类名。foreignKeys 允许向实体添加外键。有关如何使用这些内容的更多信息,请参阅外键部分。还支持索引。可以通过将索引添加到实体的 indices 值来使用它们。

  • @entity 注解用于标记了该类为持久类;
  • @PrimaryKey 将类的属性标记为主键列。该属性必须是 int 类型。当启用 autoGenerate 时,SQLite 可以自动生成该值;
  • 必须通过将 @primaryKey 注解添加到一个 int 属性上为你的表添加一个主键,例如:
    // entity/person.dartimport 'package: floor/floor.dart';
    class Person {final int id;final String name;Person(this.id, this.name);
    }
    
  • @ColumnInfo 使单个表列的自定义映射变得可能。使用该注释,可以为列指定自定义名称。如果要将表的列设置为可空,请将实体字段标记为可空;
  • Floor 自动使用实体类中定义的第一个构造函数来从数据库行创建内存中对象

支持的类型

Floor 实体可以保存以下 Dart 类型的值,这些类型与它们对应的 SQLite 类型相互映射。

  • int - INTEGER
  • double - REAL
  • String - TEXT
  • bool - INTEGER(0 = false,1 = true)
  • Uint8List - BLOB
  • enum - INTEGER(按索引 0…n 记录)

如果要存储可以由上述类型之一表示的复杂 Dart 对象,请查看类型转换器。

主键

每当需要复合主键(例如 n-m 关系)时,设置主键的语法与前面提到的设置主键的方式不同。不是使用 @PrimaryKey 注释字段,而是使用 @Entity 注释的 primaryKey 属性。它接受一个由列名组成的列表,这些列名组成复合主键。

(primaryKeys: ['id', 'name'])
class Person {final int id;final String name;Person(this.id, this.name);
}

外键

向引用实体的 Entity 注释添加 ForeignKeys 列表。childColumns 定义当前实体的列,而 parentColumns 定义父实体的列。在 onUpdate 和 onDelete 属性中定义外键操作后,可以触发外键操作。

// Dog 类,表示数据库中的狗实体
(tableName: 'dog', // 指定表名为 'dog'foreignKeys: [ForeignKey(childColumns: ['owner_id'], // 子列名为 'owner_id'parentColumns: ['id'], // 父列名为 'id'entity: Person, // 引用的实体为 Person 类)],
)
class Dog {() // PrimaryKey 注解,将 id 标记为主键列final int id;final String name; // 名称(name: 'owner_id') // ColumnInfo 注解,自定义列名为 'owner_id'final int ownerId; // 所有者的 ID// 构造函数,接受 id、name 和 ownerId 参数Dog(this.id, this.name, this.ownerId);
}
  • @Entity 注解标记了 Dog 类,指定了表名为 ‘dog’,同时指定了一个外键(foreign key),该外键将在 ‘owner_id’ 列上创建,它引用了另一个实体 Person。这意味着在数据库中将创建名为 ‘dog’ 的表,并在 ‘owner_id’ 列上创建一个外键,以确保数据的完整性。
  • @PrimaryKey 注解标记了 id 字段作为主键列,这意味着它在数据库中将用作主键。
  • @ColumnInfo 注解标记了 ownerId 字段,并自定义了列名为 ‘owner_id’,这允许在数据库表中使用不同的列名,而不是默认的字段名。
  • 构造函数接受 idnameownerId 参数,并用于创建 Dog 实例。

这些注释和注释说明了 Dog 类的结构,以及如何配置表名、外键和自定义列名。

索引

索引有助于加速查询、连接和分组操作。有关 SQLite 索引的更多信息,请参阅官方文档。要在 floor 中创建索引,请将索引列表添加到 @Entity 注释。下面的示例演示了如何在实体的 custom_name 列上创建索引。

此外,可以使用其 name 属性命名索引。要将索引设置为唯一索引,请使用 unique 属性。

// Person 类,表示数据库中的个人实体
(tableName: 'person', indices: [Index(value: ['custom_name'])])
class Person { // PrimaryKey 注解,将 id 标记为主键列final int id;(name: 'custom_name') // ColumnInfo 注解,自定义列名为 'custom_name'final String name; // 姓名// 构造函数,接受 id 和 name 参数Person(this.id, this.name);
}
  • @Entity 注解标记了 Person 类,指定了表名为 ‘person’,同时指定了一个索引,该索引将在 ‘custom_name’ 列上创建。这意味着在数据库中将创建名为 ‘person’ 的表,并在 ‘custom_name’ 列上创建一个索引,以提高查询性能。
  • @primaryKey 注解标记了 id 字段作为主键列,这意味着它在数据库中将用作主键。
  • @ColumnInfo 注解标记了 name 字段,并自定义了列名为 ‘custom_name’,这允许在数据库表中使用不同的列名,而不是默认的字段名。
    构造函数接受 id 和 name 参数,并用于创建 Person 实例。

忽略字段

默认情况下,实体的 getter、setter 和所有静态字段都会被忽略,并因此从库的映射中排除。如果要进一步忽略字段,应使用 @ignore 注释,并按以下示例中所示应用它。

// 人员类,表示数据库中的人员实体
class Person { // PrimaryKey 注解,将 id 标记为主键列final int id;final String name; // 姓名 // Ignore 注解,指定 nickname 字段将被忽略String nickname; // 昵称// 默认情况下被忽略的字段,不会包含在库的映射中String get combinedName => "$name ($nickname)";// 构造函数,接受 id 和 name 参数Person(this.id, this.name);
}

继承

与 Dao 一样,实体(和数据库视图)可以从共同的基类继承并使用它们的字段。实体只需扩展基类。这个构造将被视为如果基类的所有字段都是实体的一部分,这意味着数据库表将包含实体和基类的所有列。

基类不必为类单独注释。它的字段可以像正常的实体列一样进行注释。外键和索引必须在实体中声明,不能在基类中定义。

// 基础对象类,用于继承到其他实体
class BaseObject {// 主键标记,指定 id 作为主键列()final int id;// ColumnInfo 注解,用于自定义列名 create_time(name: 'create_time')final String createTime;// ColumnInfo 注解,用于自定义列名 update_time(name: 'update_time')final String updateTime;// 构造函数,接受 id、updateTime 和可选的 createTime 参数BaseObject(this.id,this.updateTime, {String createTime,}) : this.createTime = createTime ?? DateTime.now().toString();// 重写父类的 props 方法,通常用于数据比较List<Object> get props => [];
}// Comment 类,继承自 BaseObject 类
(tableName: 'comments') // Entity 注解,指定表名为 'comments'
class Comment extends BaseObject {final String author; // 评论作者final String content; // 评论内容// 构造函数,接受 author、可选的 id、content、createTime 和 updateTime 参数Comment(this.author, {int id,this.content = '', // 默认为空字符串String createTime,String updateTime,}) : super(id, updateTime, createTime: createTime); // 调用父类构造函数,传递 id、updateTime 和 createTime 参数
}

数据库视图(Database Views)

如果希望定义返回与实体不同类型的静态 SELECT 语句,最佳选择是使用 @DatabaseView。数据库视图可以被理解为虚拟表,可以像真实表一样进行查询。

在 Floor 中,数据库视图的定义和使用方式与实体类似,主要区别在于只读访问,这意味着无法执行更新、插入和删除操作。与实体类似,如果未设置 viewName,则使用类名。

('SELECT distinct(name) AS name FROM person', viewName: 'name')
class Name {final String name;Name(this.name);
}

数据库视图不具有任何外键、主键或索引。相反,您应该手动定义与您的语句匹配的索引,并将它们放入涉及实体的 @Entity 注释中。

与实体类似,setter、getter 和静态字段将自动被忽略(与实体类似),您可以使用 @ignore 注解来指定要忽略的附加字段。

在代码中定义数据库视图后,您需要将它添加到 @Database 注释的 views 字段中:

(version: 1, entities: [Person], views: [Name])
abstract class AppDatabase extends FloorDatabase {// DAO 获取器
}

然后,您可以通过 DAO 函数查询视图,就像查询实体一样。

数据库视图可以像实体一样从基类继承共同字段。

现在可以从 DAO 方法返回一个 Stream 对象,该方法查询数据库视图。但是,它将在整个数据库中的任何 @update@insert@delete 事件触发时发出,这可能会对运行时产生相当大的负担。请仅在明白自己在做什么时才添加它!这主要是因为检测哪些实体涉及到数据库视图的复杂性所致。

3. 数据访问对象(DAO - Data Access Object)

这个组件负责管理对底层的 SQLite 数据库的访问。抽象类包含了查询数据库的方法签名,这些方法必须返回 Future 或 Stream。

你可以通过将 @Query 注解添加到方法上来定义查询。SQL 语句必须放在括号中。方法必须返回你查询的实体的 Future 或 Stream。
@insert 标记了一个方法作为插入方法。

// dao/person_dao.dartimport 'package:floor/floor.dart';
abstract class PersonDao {// 查询并返回所有的 Person 实体对象列表('SELECT * FROM Person')Future<List<Person>> findAllPersons();// 根据提供的 id 查询并返回匹配的 Person 实体对象// 这是一个异步数据流,当数据发生变化时,会自动更新('SELECT * FROM Person WHERE id = :id')Stream<Person?> findPersonById(int id);// 将提供的 Person 对象插入到数据库中// 这是一个异步操作,不返回任何数据Future<void> insertPerson(Person person);
}

4. 创建数据库

它必须是一个继承自 FloorDatabase 的抽象类。此外,必须在类的签名中添加 @Database()。确保将创建的实体添加到 @Database 注解的 entities 属性中。为了使生成的代码工作,还需要添加列出的导入项。

确保在这个文件的导入项下面添加 part ‘database.g.dart’;。重要的是要注意 ‘database’ 必须与数据库定义的文件名进行交换。在这种情况下,文件名命名为 database.dart。

// database.dartimport 'dart:async';
import 'package:floor/floor.dart';
import 'package:sqflite/sqflite.dart' as sqflite; import 'dao/person_dao.dart'; 
import 'entity/person.dart'; part 'database.g.dart'; // Floor代码生成器将在这里生成相关的代码(version: 1, entities: [Person]) // 声明数据库版本号和包含的实体类
abstract class AppDatabase extends FloorDatabase {PersonDao get personDao; // 获取PersonDao实例,用于执行数据库操作
}

5. 运行代码生成器

使用以下命令来运行生成器:

flutter packages pub run build_runner build

上面的命令用于在 Flutter 项目中运行代码生成工具以手动构建生成的代码。build命令 只会运行一次生成过程,然后退出。还可以在 Flutter 项目中使用 build_runner 模块的观察模式,以便在开发过程中自动更新生成的代码。如果想要在文件更改时自动运行它,请使用以下命令:

flutter packages pub run build_runner watch

6. 使用生成的代码

要获取数据库的实例,使用生成的 $FloorAppDatabase 类,它允许访问数据库生成器。名称由 $Floor 和数据库类名组成。传递给 databaseBuilder() 的字符串将是数据库文件的名称。要初始化数据库,调用 build() 并确保等待结果。

为了检索 PersonDao 实例,只需在数据库实例上调用 persoDao getter 即可。
可以参考下面的代码。

// 使用数据库生成器创建数据库实例,传递数据库文件的名称('app_database.db')
final database = await $FloorAppDatabase.databaseBuilder('app_database.db').build();// 从数据库实例中获取 PersonDao 实例
final personDao = database.personDao;// 创建一个 Person 对象
final person = Person(1, 'Frank');// 将新的 Person 对象插入到数据库中
await personDao.insertPerson(person);// 通过 ID 查找数据库中的 Person 记录
final result = await personDao.findPersonById(1);// 输出查找到的 Person 记录
print('Found Person: $result');

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

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

相关文章

Oracle的递归公共表表达式

查询节点id为2的所有子节点的数据&#xff0c;包括向下级联 WITH T1 (id, parent_id, data) AS (SELECT id, parent_id, dataFROM nodesWHERE id 2UNION ALLSELECT t.id, t.parent_id, t.dataFROM nodes tJOIN T1 n ON t.parent_id n.id ) SELECT * FROM T1; --建表语句 C…

什么是Times New Roman 字体

如何评价 Times New Roman 字体&#xff1f;&#xff1a;https://www.zhihu.com/question/24614549?sortcreated 新罗马字体是Times New Roman字体&#xff0c;是Office Word默认自带的英文字体之一。 中英文字体 写作中&#xff0c;英文和数字的标准字体为 Times New Roma…

华为云云耀云服务器L实例评测 | 实例使用教学之软件安装:华为云云耀云服务器环境下安装 Docker

华为云云耀云服务器L实例评测 &#xff5c; 实例使用教学之软件安装&#xff1a;华为云云耀云服务器环境下安装 Docker 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀云…

十一,从摄像机打印HDR环境贴图

越来越接近真相了。我们很自然地想到&#xff0c;如果把漫游器放在中心打印&#xff0c;是不是就可以打印整个等距柱状投影图了呢&#xff1f;是的&#xff0c;但是&#xff0c;只是要注意的是&#xff0c;立方体贴图的内部和外部尽管一样&#xff0c;但是还是稍微有点模糊&…

spring6-IOC容器

IOC容器 1、IoC容器1.1、控制反转&#xff08;IoC&#xff09;1.2、依赖注入1.3、IoC容器在Spring的实现 2、基于XML管理Bean2.1、搭建子模块spring6-ioc-xml2.2、实验一&#xff1a;获取bean①方式一&#xff1a;根据id获取②方式二&#xff1a;根据类型获取③方式三&#xff…

安卓机型不需要解锁bl 不需要root 即可安装模块 框架 VirtualXposed使用步骤分析

​​​​​​安卓玩机教程---全机型安卓4----安卓12 框架xp edx lsp安装方法【一】 安卓系列机型 框架LSP 安装步骤 支持多机型 LSP框架通用安装步骤 通过以上两个博文基本可以了解手机正常安装框架的步骤。但很多机型局限于不能解锁bl和root&#xff0c;那么这些机型能不能使…

vue前端项目中添加独立的静态资源

如果想要在vue项目中放一些独立的静态资源&#xff0c;比如html文件或者用于下载的业务模板或其他文件等&#xff0c;需要在vue打包的时候指定一下静态资源的位置和打包后的目标位置。 使用的是 copy-webpack-plugin 插件&#xff0c;如果没有安装则需要先安装一下&#xff0c;…

大数据Flink(九十):Lookup Join(维表 Join)

文章目录 Lookup Join(维表 Join) Lookup Join(维表 Join) Lookup Join 定义(支持 Batch\Streaming):Lookup Join 其实就是维表 Join,比如拿离线数仓来说,常常会有用户画像,设备画像等数据,而对应到实时数仓场景中,这种实时获取外部缓存的 Join 就叫做维表 Join。…

python抓取网页视频

1. 喜马拉雅音频 1-1 喜马拉雅 import requests import json import time import random import hashliburl https://www.ximalaya.com/revision/play/v1/audio?id46103875&ptype1headers { user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.3…

HUAWEI悦盒ec6108v9c 如何刷成海纳思系统(家用低功耗服务器,使用Home Assistant服务)

环境&#xff1a; 1.HW悦盒ec6108v9c一套 2.16G U盘 3.格式化软件USB_format.exe 4.固件 mv100-mdmo1g-usb-flash.zip&#xff08;底层是Ubuntu 20.04系统&#xff09; 5.十字螺丝刀 6.翘片/薄铲子 7.有线网络环境 8.镊子/回形针 问题描述&#xff1a; 最近玩智能家居…

CH347读写SPI Flash

CH347读写SPI Flash 前面耽搁了几天&#xff0c;今天终于把CH347 SPI接口调试好了。 CH347动态库中SPI接口函数如下&#xff1a; typedef struct _SPI_CONFIG{UCHAR iMode; // 0-3:SPI Mode0/1/2/3UCHAR iClock; // 060…

【数据库】形式化关系查询语言(一):关系代数Relational Algebra

目录 一、关系代数Relational Algebra 1. 基本运算 a. 选择运算&#xff08;Select Operation&#xff09; b. 投影运算&#xff08;Project Operation&#xff09; 组合 c. 并运算&#xff08;Union Operation&#xff09; d. 集合差运算&#xff08;Set Difference Op…

python: 用百度API读取增值税发票信息

# encoding: utf-8 # 版权所有 2023 涂聚文有限公司 # 许可信息查看&#xff1a; # 描述&#xff1a; # Author : geovindu,Geovin Du 涂聚文. # IDE : PyCharm 2023.1 python 311 # Datetime : 2023/9/30 6:56 # User : geovindu # Product : PyCharm # Proj…

机器学习笔记 - 基于强化学习的贪吃蛇玩游戏

一、关于深度强化学习 如果不了解深度强化学习的一般流程的可以考虑看一下下面的链接。因为这里的示例因为在PyTorch 之上实现深度强化学习算法。 机器学习笔记 - Deep Q-Learning算法概览深度Q学习是一种强化学习算法,它使用深度神经网络来逼近Q函数,用于确定在给定状态下采…

架构设计第七讲:数据巡检系统之daily线上表结构自动化比对

架构设计第七讲&#xff1a;数据巡检系统之daily&线上表结构自动化比对 本文是架构设计第七讲&#xff0c;数据巡检系统之daily&线上表结构自动化比对&#xff0c;避免正式环境与测试环境数据库/表、列结构不一致带来问题。 文章目录 架构设计第七讲&#xff1a;数据巡…

193419-86-2,用于蛋白电泳检测的Fluorescein o-acrylate

产品简介&#xff1a;Fluorescein o-acrylate 中FITC具有荧光素衍生物的普遍特性&#xff0c;FITC也经常被用于蛋白电泳检测和荧光能量激发转移测试。 荧光染料及其荧光标记技术一直是生物领域常用的产品和技术&#xff0c;荧光物质是指具有共轭双键体系化学结构的化合物&…

LabVIEW开发实时自动化多物镜云计算全玻片成像装置

LabVIEW开发实时自动化多物镜云计算全玻片成像装置 数字病理学领域正在迅速发展&#xff0c;这主要是由于计算机处理能力、数据传输速度、软件创新和云存储解决方案方面的技术进步。因此&#xff0c;病理科室不仅将数字成像用于图像存档等简单任务&#xff0c;还用于远程病理学…

monkeyrunner录制脚本和回放

Monkeyrunner关于使用录制、生成脚本、编译脚本及执行脚本。 首先在计算机上下载和安装SDK、python 2.将recorder.py文件放置SDK文件夹里tools文件夹下 3.USB连接手机&#xff0c;手机端&#xff0c;开启USB调试&#xff0c;并在计算机DOS中输入adb devices命令&#xff0c;查看…

XXE 漏洞及案例实战

文章目录 XXE 漏洞1. 基础概念1.1 XML基础概念1.2 XML与HTML的主要差异1.3 xml示例 2. 演示案例2.1 pikachu靶场XML2.1.1 文件读取2.1.2 内网探针或者攻击内网应用&#xff08;触发漏洞地址&#xff09;2.1.4 RCE2.1.5 引入外部实体DTD2.1.6 无回显读取文件 3. XXE 绕过3.1 dat…

网络爬虫--伪装浏览器

从用户请求的Headers反反爬 在访问某些网站的时候&#xff0c;网站通常会用判断访问是否带有头文件来鉴别该访问是否为爬虫&#xff0c;用来作为反爬取的一种策略。很多网站都会对Headers的User-Agent进行检测&#xff0c;还有一部分网站会对Referer进行检测&#xff08;一些资…