首页 文章 精选 留言 我的

精选列表

搜索[字节级],共10000篇文章
优秀的个人博客,低调大师

MyBatis自定义插件机制分析(源码级剖析)

1 插件概述 问题:什么是Mybatis插件?有什么作用? 一般开源框架都会提供扩展点,让开发者自行扩展,从而完成逻辑的增强。 基于插件机制可以实现了很多有用的功能,比如说分页,字段加密,监控等功能,这种通用的功能,就如同AOP一样,横切在数据操作上 而通过Mybatis插件可以实现对框架的扩展,来实现自定义功能,并且对于用户是无感知的。 2 Mybatis插件介绍 Mybatis插件本质上来说就是一个拦截器,它体现了JDK动态代理和责任链设计模式的综合运用 Mybatis中针对四大组件提供了扩展机制,这四个组件分别是: Mybatis中所允许拦截的方法如下: Executor 【SQL执行器】【update,query,commit,rollback】 StatementHandler 【Sql语法构建器对象】【prepare,parameterize,batch,update,query等】 ParameterHandler 【参数处理器】【getParameterObject,setParameters等】 ResultSetHandler 【结果集处理器】【handleResultSets,handleOuputParameters等】 能干什么? 分页功能:mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可 性能监控:对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间 如何自定义插件? 在使用之前,我们先来看看Mybatis提供的插件相关的类,过一遍它们分别提供了哪些功能,最后我们自己定义一个插件 用于定义插件的类 前面已经知道Mybatis插件是可以对Mybatis中四大组件对象的方法进行拦截,那拦截器拦截哪个类的哪个方法如何知道,就由下面这个注解提供拦截信息 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Intercepts { Signature[] value(); } 由于一个拦截器可以同时拦截多个对象的多个方法,所以就使用了Signture数组,该注解定义了拦截的完整信息 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Signature { // 拦截的类 Class<?> type(); // 拦截的方法 String method(); // 拦截方法的参数 Class<?>[] args(); } 已经知道了该拦截哪些对象的哪些方法,拦截后要干什么就需要实现Intercetor#intercept方法,在这个方法里面实现拦截后的处理逻辑 public interface Interceptor { /** * 真正方法被拦截执行的逻辑 * * @param invocation 主要目的是将多个参数进行封装 */ Object intercept(Invocation invocation) throws Throwable; // 生成目标对象的代理对象 default Object plugin(Object target) { return Plugin.wrap(target, this); } // 可以拦截器设置一些属性 default void setProperties(Properties properties) { // NOP } } 3 自定义插件 需求:把Mybatis所有执行的sql都记录下来 步骤:① 创建Interceptor的实现类,重写方法 ​ ② 使用@Intercepts注解完成插件签名 说明插件的拦截四大对象之一的哪一个对象的哪一个方法 ​ ③ 将写好的插件注册到全局配置文件中 ①.创建Interceptor的实现类 public class MyPlugin implements Interceptor { private final Logger logger = LoggerFactory.getLogger(this.getClass()); // //这里是每次执行操作的时候,都会进行这个拦截器的方法内 Override public Object intercept(Invocation invocation) throws Throwable { //增强逻辑 StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); logger.info("mybatis intercept sql:{}", sql); return invocation.proceed(); //执行原方法 } /** * * ^Description包装目标对象 为目标对象创建代理对象 * @Param target为要拦截的对象 * @Return代理对象 */ Override public Object plugin(Object target) { System.out.println("将要包装的目标对象:"+target); return Plugin.wrap(target,this); } /**获取配置文件的属性**/ //插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来 Override public void setProperties(Properties properties) { System.out.println("插件配置的初始化参数:"+properties ); } } ② 使用@Intercepts注解完成插件签名 说明插件的拦截四大对象之一的哪一个对象的哪一个方法 @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) }) public class SQLStatsInterceptor implements Interceptor { ③ 将写好的插件注册到全局配置文件中 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <plugins> <plugin interceptor="com.itheima.interceptor.MyPlugin"> <property name="dialect" value="mysql" /> </plugin> </plugins> </configuration> 核心思想: 就是使用JDK动态代理的方式,对这四个对象进行包装增强。具体的做法是,创建一个类实现Mybatis的拦截器接口,并且加入到拦截器链中,在创建核心对象的时候,不直接返回,而是遍历拦截器链,把每一个拦截器都作用于核心对象中。这么一来,Mybatis创建的核心对象其实都是代理对象,都是被包装过的。 4 源码分析-插件 插件的初始化:插件对象是如何实例化的? 插件的实例对象如何添加到拦截器链中的? 组件对象的代理对象是如何产生的? 拦截逻辑的执行 插件配置信息的加载 我们定义好了一个拦截器,那我们怎么告诉Mybatis呢?Mybatis所有的配置都定义在XXx.xml配置文件中 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <plugins> <plugin interceptor="com.itheima.interceptor.MyPlugin"> <property name="dialect" value="mysql" /> </plugin> </plugins> </configuration> 对应的解析代码如下(XMLConfigBuilder#pluginElement): private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { // 获取拦截器 String interceptor = child.getStringAttribute("interceptor"); // 获取配置的Properties属性 Properties properties = child.getChildrenAsProperties(); // 根据配置文件中配置的插件类的全限定名 进行反射初始化 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); // 将属性添加到Intercepetor对象 interceptorInstance.setProperties(properties); // 添加到配置类的InterceptorChain属性,InterceptorChain类维护了一个List<Interceptor> configuration.addInterceptor(interceptorInstance); } } } 主要做了以下工作: 遍历解析plugins标签下每个plugin标签 根据解析的类信息创建Interceptor对象 调用setProperties方法设置属性 将拦截器添加到Configuration类的IntercrptorChain拦截器链中 对应时序图如下: 代理对象的生成 Executor代理对象(Configuration#newExecutor) public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } // 生成Executor代理对象逻辑 executor = (Executor) interceptorChain.pluginAll(executor); return executor; } ParameterHandler代理对象(Configuration#newParameterHandler) public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 生成ParameterHandler代理对象逻辑 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } ResultSetHandler代理对象(Configuration#newResultSetHandler) public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); // 生成ResultSetHandler代理对象逻辑 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } StatementHandler代理对象(Configuration#newStatementHandler) public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 生成StatementHandler代理对象逻辑 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } 通过查看源码会发现,所有代理对象的生成都是通过InterceptorChain#pluginAll方法来创建的,进一步查看pluginAll方法 public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } InterceptorChain#pluginAll内部通过遍历Interceptor#plugin方法来创建代理对象,并将生成的代理对象又赋值给target,如果存在多个拦截器的话,生成的代理对象会被另一个代理对象所代理,从而形成一个代理链,执行的时候,依次执行所有拦截器的拦截逻辑代码,我们再跟进去 default Object plugin(Object target) { return Plugin.wrap(target, this); } Interceptor#plugin方法最终将目标对象和当前的拦截器交给Plugin.wrap方法来创建代理对象。该方法是默认方法,是Mybatis框架提供的一个典型plugin方法的实现。让我们看看在Plugin#wrap方法中是如何实现代理对象的 public static Object wrap(Object target, Interceptor interceptor) { // 1.解析该拦截器所拦截的所有接口及对应拦截接口的方法 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); // 2.获取目标对象实现的所有被拦截的接口 Class<?>[] interfaces = getAllInterfaces(type, signatureMap); // 3.目标对象有实现被拦截的接口,生成代理对象并返回 if (interfaces.length > 0) { // 通过JDK动态代理的方式实现 return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } // 目标对象没有实现被拦截的接口,直接返回原对象 return target; } 最终我们看到其实是通过JDK提供的Proxy.newProxyInstance方法来生成代理对象 以上代理对象生成过程的时序图如下: 拦截逻辑的执行 通过上面的分析,我们知道Mybatis框架中执行Executor、ParameterHandler、ResultSetHandler和StatementHandler中的方法时真正执行的是代理对象对应的方法。而且该代理对象是通过JDK动态代理生成的,所以执行方法时实际上是调用InvocationHandler#invoke方法(Plugin类实现InvocationHandler接口),下面是Plugin#invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } 注:一个对象被代理很多次 问题:同一个组件对象的同一个方法是否可以被多个拦截器进行拦截? 答案是肯定的,所以我们配置在最前面的拦截器最先被代理,但是执行的时候却是最外层的先执行。 具体点: 假如依次定义了三个插件:插件1,插件2 和 插件3。 那么List中就会按顺序存储:插件1,插件2 和 插件3。 而解析的时候是遍历list,所以解析的时候也是按照:插件1 ,插件2,插件3的顺序。 但是执行的时候就要反过来了,执行的时候是按照:插件3,插件2和插件1的顺序进行执行。 当 Executor 的某个方法被调用的时候,插件逻辑会先行执行。执行顺序由外而内,比如上图的执行顺序为 plugin3 → plugin2 → Plugin1 → Executor。 本文由传智教育博学谷教研团队发布。 如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。 转载请注明出处!

优秀的个人博客,低调大师

Go 企业级框架 GoFrame 发布 v2.1 版本

大家好,本次发布的v2.1版本包含一些与业务实践相关的功能特性、改进以及Bug Fix,建议大家升级。 新特性 开发工具新增gen service命令,支持自动化地根据logic层级代码,生成service接口代码、实现注入:接口维护-gen service 数据库组件特性: 新增WhereBuilder特性,用于更加灵活的SQL条件语句组合:ORM查询-Where/WhereOr/WhereNot 新增Hook特性,用于自定义钩子事件处理:ORM链式操作-Hook特性 框架新增DeepCopy特性,用于类型的深度拷贝: 新增gutil.Copy方法,用于深度拷贝指定内容。 泛型类型新增Copy方法,用于深度拷贝自身内容。 框架部分数据类型已支持深度拷贝特性,例如:gvar, garray, gmap等基础容器类型。 主要改进 社区组件 ORM驱动实现 新增drivers/clickhouse,用于对接clickhouse到goframe ORM组件。 完善clickhouse/mssql/pgsql/sqlite/oracle组件单元测试。 将mysql驱动从主库迁移到社区模块,便于将mysql从主库解耦。因此从后续版本开始,开发者需要手动引入驱动依赖:https://github.com/gogf/gf/tree/master/contrib/drivers 注册发现实现 新增polaris北极星服务注册接口实现:https://github.com/gogf/gf/tree/master/contrib/registry/polaris 改进etcd服务注册发现接口实现组件:https://github.com/gogf/gf/tree/master/contrib/registry/etcd 注册发现 将Service实现对象改进为接口定义,并提供默认的Service实现,提高扩展性和易用性。 改进HTTP/GRPC Client&Server对接实现。 负载均衡 改进Node接口定义,新增Nodes接口定义。 修复HTTP Client下的服务发现负载均衡问题。 网络组件 gclient 改进服务发现实现逻辑。 修复客户端关闭错误,引起的连接池无法复用问题。 ghttp 改进Request.GetUrl方法对URL Schema获取细节。 参数接收支持UploadFile属性自动接收。 新增接口文档自定义UI指导文档:接口文档-自定义UI 接口文档默认依赖的外部JS CDN改为unpkg.com。 改进服务注册实现逻辑。 改进内部细节实现逻辑。 修复参数为空判断问题。 goai 改进更规范化符合OpenAPIV3协议实现。 支持所有x-开头的自定义标签,自动添加OpenAPIV3的结果中。 系统组件 gcfg 默认的文件系统接口实现新增对property文件格式的支持。 gcmd 参数解析新增CaseSensitive配置,默认不区分大小写解析,特别针对结构化参数接收影响较大:命令管理-结构化参数 新增跨进程的链路跟踪特性:命令管理-链路跟踪 glog 新增全局的Handler设置功能,开发者可以全局自定义处理glog组件的所有日志,例如全局输出JSON文件格式:日志组件-Handler 新增默认的JSON格式Handler供开发者使用:日志组件-Handler gsession 解决当访问用户过多造成的内存占用过大的问题。 gproc 新增跨进程的链路跟踪特性:进程管理-链路跟踪 容器组件 garray 改进Unique方法性能,增加DeepCopy接口实现。 glist 增加DeepCopy接口实现。 gmap 增加DeepCopy接口实现。 gset 增加DeepCopy接口实现。 gtype 增加DeepCopy接口实现。 gvar 增加Copy方法,用于深度拷贝当前泛型对象。 增加DeepCopy接口实现。 数据库组件 gdb 新增WhereBuilder特性,用于更加灵活的SQL条件语句组合:ORM查询-Where/WhereOr/WhereNot 新增HOOK特性,用于自定义钩子事件处理:ORM链式操作-Hook特性 改进数据提交到底层driver前的数据转换处理逻辑。 将mysql驱动从主库迁移到社区模块,便于将mysql从主库解耦。因此从后续版本开始,开发者需要手动引入驱动依赖:https://github.com/gogf/gf/tree/master/contrib/drivers 编解码组件 gproperty 新增gproperty组件,用于解析Java Property格式文件。 gjson 新增对property文件格式的编解码、数据操作支持:通用编解码-gjson 修复对大整形数据读取的精度丢失问题。 文本处理 gstr 改进WordWrap方法,使得对Unicode特别是中文换行更加友好。 修复RepliceI忽略大小写字符串替换在特定场景下的问题。 错误处理 gerror 新增Unwrap方法(同Next方法),用以支持Golang新版本的Unwrap错误接口。 新增Equal方法,用于判断两个错误是否相等:错误处理-错误比较 新增Is方法,用于支持Golang新版本的Is错误接口:错误处理-错误比较 工具方法 gconv 去掉整型转换时对八进制字符串的支持。 改进内部实现逻辑,提高可读性保障可维护性。 gutil 新增gutil.Copy方法,用于深度拷贝指定内容。 改进gutil.Dump方法。 开发工具 相对于主库稳定的代码组件,CLI开发工具在近期的版本发布有一些非兼容更新,各位在升级时注意发布记录,细节请查看源码调整。 改进build命令,支持指定pack代码文件的生成目录,参数有个别调整。 改进docker命令,支持多个docker tag的重命名及仓库自动推送。 改进gen dao命令,支持自定义dao/do/entity代码生成目录,不再强制生成到service/internal目录下:数据规范-gen dao 新增gen service命令,支持自动化地根据logic层级代码,生成service接口代码:接口维护-gen service 修复run命令自定义程序启动参数问题、gofmt/goimports程序路径带空格问题。

优秀的个人博客,低调大师

“古董级”诺基亚功能机跑 Linux 是怎样的画风?

下面这台诺基亚 Linux 手机只是套了诺基亚手机的外壳,里面是作者全新设计的 PCB 主板,其尺寸与诺基亚的原始 PCB 完全相同,所以称其为"Notkia"。 上图是使用了诺基亚168x 系列手机外壳的「Notkia」Linux 手机。简要介绍: 采用 Nokia 168x 机身:方便单手操控,轻松放入口袋 运行主线版 Linux 内核 处理器:Ingenic X1000E, 2200+ CoreMark, 64MB RAM 存储:32MB NOR + 4GBSLCNAND 无线连接:Lora、WiFi 2.4G、Bluetooth、GPS+GLONASS 屏幕:2.0'' 240x320IPSLCD, 3/4 visible 声音:Yamaha MA-3 (YMU762) 音乐合成器+ 常规 I2S PCM 编解码器 相机:5MP AF(仅适用于 1680 外壳) 电源:使用来自 TI 的电池解决方案,以及标准BL-5C 电池 其他:Type-C OTG、RGB LED 指示灯、温湿度传感器、零冲突键盘 实际运行效果 根据作者的介绍,此项目已提交给众筹平台,筹款完成后将开发相关的必要软件,届时将开源所有 PCB 设计文件(包括原理图和电路板设计文件)。其中必要软件包括: 桌面环境 支持英文、中文、Linux 命令和 PL 关键字的 T9 输入法 IME 框架 终端工具 各种驱动程序 类 S60 多媒体播放器 经典游戏:贪吃蛇、俄罗斯方块 …… 对于 Notkia 目前所使用的外壳,诺基亚拥有其设计专利,不过此专利将于 2023 年 6 月 23 日到期。作者表示,专利到期后他们就会根据这个设计重新生产全新的塑料外壳,当然前提是筹款目标能如愿达成。 ▲ 成功运行 Debian 发行版 ▲ 和原版诺基亚1680的对比,左边是原版,右边是「Notkia」 「Notkia」主板图如下: 对于这个“极客”项目,作者表示自己的创造初衷是因为现代智能手机对隐私和“黑客”越来越不友好。他无法接受厂商对手机和 Android 的改动,虽然后来发现了 Librem 5 Linux 手机,不过被价格劝退了。与此同时,作者开始对现代的巨大智能手机感到厌烦——无法单手操控。就在今年,他意外地发现了诺基亚 168x 系列手机,并且在查看其 PCB 照片后,发现它拥有巨大的空间,作者认为这可以大有作为,于是就有了现在的「Notkia」。 点此查看关于「Notkia」的更多技术原理和细节。

优秀的个人博客,低调大师

领英开源 Feathr,企业级高性能特征存储

领英(LinkedIn)近日宣布开源 Feathr,这是领英为简化机器学习 (ML) 功能管理和提高开发者生产力而构建的特征存储。 特征存储是一个用于管理机器学习特征的数据管理系统,包括特征工程代码和特征数据。它是一个中央存储库,用于存储记录的、设计的和访问权限控制的特征,可以在整个团队的许多不同的 ML 模型中使用。它从各种来源获取数据,并执行定义的转换、聚合、验证和其他操作来创建特征。特征存储库注册了可用的特征,并使它们准备好被 ML 训练管道和推理服务所检索和消费。 在领英,有几十个应用程序使用 Feathr 来定义特征,计算它们的训练,在生产中部署它们,并在团队中分享它们。报告显示,与以前针对特定应用的特征管道解决方案相比,有了 Feathr,团队在模型训练工作流程中添加新特征所需的时间大大减少,运行时的性能也有所提高。 领英在公告中表示:“几年前,我们注意到一个模式:团队因维护其特征准备管道的成本增加而负担过重,这损害了他们在创新和改进其应用程序方面的生产力。需要从许多来源汇集时间敏感的数据,以时间点正确的方式将特征与训练标签连接起来,并将特征持久化到存储中以获得低延迟的在线服务。他们还需要确保以相同的方式为训练和推理环境准备特征,以防止训练服务的偏差。” 准备和管理特征一直是大规模运行我们的 ML 应用中最耗时的部分之一。 Feathr 作为一个抽象层,为用户提供了一个用于定义特征的通用特征命名空间,以及一个用于计算、服务和从 ML 工作流中 "按名称" 寻址的通用平台。Feathr 还带来了对特征转换的高级支持,使用户能够在原始数据集的基础上试验新特征。 Feathr 的抽象为特征创建了生产者和消费者角色。生产者定义特征并将它们注册到 Feathr,消费者访问/导入特征组到他们的 ML 模型工作流中。 领英团队正在继续开发围绕 Feathr 的生态,提供新的基础设施和工具,包括为特征工程启用 CI/CD。有了它,客户将能够创建广泛共享的 ML 功能的升级版本,然后针对依赖该功能的现有模型进行测试。

优秀的个人博客,低调大师

ShopXO 企业级免费开源商城 v2.2.1 发布

更新日志 1. 后台管理首页统计数据优化、新增可选时间和地区维度 2. 支持现金支付、货到付款、不影响订单正常进行 3. 图片上传组件安全增强、避免木马上传 4. 常量数据分离,支持钩子自动处理 5. 修复订单售后库存回滚优化,避免商品规格已无效导致失败无法继续 6. 优化订单售后退款支付金额使用实际支付金额 7. 动态数据列表格子支持钩子 8. 新增uniapp端小程序主题(目前仅支持微信小程序) 9. 新增批发插件、数量阶梯售价 10. 新增标签插件、对商品、用户进行分组管理 11. 新增支付宝证书加密支付接口插件 12. 新增API接口文档 小程序效果图片 可视化DIY拖拽装修展示 PC端展示 后台管理展示

优秀的个人博客,低调大师

ShopXO 企业级免费开源商城 v2.2.0 发布

更新日志 1. 底层框架 thinkphp v5.1 升级到 v6.x 2. 优化公共数据、增加更多公共数据缓存、缓存数据优化 3. 图片验证码增加随机数优化 4. 所有插件适配支持v2.2.0版本 5. 新增银联商务微信小程序支付插件 6. 在线客服插件新增微信 7. 小程序相册根据手机型号自动铺满屏幕 8. web端商品规格切换调试模式下错误修复 9. web端图片验证码随机数优化 10. 系统入口php版本检查优化 可视化DIY拖拽装修展示 小程序展示 PC端展示 后台管理展示

优秀的个人博客,低调大师

易车网实战+【保姆级】:Feapder爬虫框架入门教程

今天辰哥带大家来看看一个爬虫框架:**Feapder**,看完本文之后,别再说你不会Feapder了。本文辰哥将带你了解什么是Feapder?、如何去创建一个Feapder入门项目(实战:采集易车网轿车数据)。 其中实战部分包括**爬虫数据和存储到Mysql数据库**,让大家能够感受一下,数据从网页经框架Feapder采集后,直接存储到数据库的过程。 之前我们已经用了Scrapy爬虫框架来爬取数据([以『B站』为实战案例!手把手教你掌握爬虫必备框架『Scrapy』](http://mp.weixin.qq.com/s?__biz=MzU5NjM4MDY1Mw==&mid=2247486273&idx=1&sn=1cff5390f0e6482153977deff9f4d7e1&chksm=fe62d5c3c9155cd5ec50ee7a9e7e0dbeea835c06f4881980216c6f13b3b5b8d38e2e1ac7f48a&scene=21#wechat_redirect)),今天来试试使用Feapder写爬虫是**一种怎么样的体验**,**请往下看!!!!!** # 01、Feapder框架 ### 1.Feapder框架介绍 Feapder是一款上手简单、功能强大、快速、轻量级的爬虫框架的Python爬虫框架。支持轻量爬虫、分布式爬虫、批次爬虫、爬虫集成,以及完善的爬虫报警机制。 具体feapder项目结构每一块的功能是什么?怎么样用?接着往下看,下面的实战中有详细的讲解。 ### 2.Feapder的安装 feapder的安装很简单,通过下面的命令安装即可! ``` pipinstallfeapder ``` 出现下面的界面说明feapder成功安装! ![](https://oscimg.oschina.net/oscnet/1051d0f2-bdf0-4bc5-b0e2-f0313963b47c.png) feapder的介绍和环境安装就完成了,下面开始真正去使用fepader来爬取易车网数据,并存储到mysql数据库。 # 02、实战 ### 1.新建feapder项目 通过下方的命令去创建一个名为:**chenge\_yc\_spider**的的爬虫项目 ``` feapder create -p chenge_yc_spider ``` ![](https://oscimg.oschina.net/oscnet/f390f7a3-6dfb-4f30-9f3e-94096695d6ee.png) 创建好之后,我们看一下项目结构 ![](https://oscimg.oschina.net/oscnet/4ce80de6-653f-4067-b194-9802b8889db8.png) ### 2.编写爬虫 在终端中进入到项目(chenge\_yc\_spider)下的spiders文件夹下,通过下面的命令创建一个目标爬虫文件(target_spider) ``` feapder create -s target_spider ``` ![](https://oscimg.oschina.net/oscnet/231aba4f-074c-4b28-a1f7-f336f6128273.png) 此刻项目结构如下: ![](https://oscimg.oschina.net/oscnet/6b897349-a37d-482d-885c-97a21cc5c02a.png) 编辑**target_spider.py**文件 ![](https://oscimg.oschina.net/oscnet/e7962520-3d27-4e1e-b2cd-b9c71f175b65.png) 这里实战案例:采集易车网数据。直接执行这个py文件,先看一下请求有没有没问题。 ![](https://oscimg.oschina.net/oscnet/2b456ae7-1446-4c60-b858-ea0ba79ed147.png) 可以看到请求返回响应200,说明请求成功。下一步我们开始解析网页数据并设置爬虫框架自动采集下一页数据。 ### 3.解析网页 网页结果(待采集的数据)如下: ![](https://oscimg.oschina.net/oscnet/866bbecd-b88e-43d1-ba7e-0ca764c9e3c4.png) 通过查看源代码,分析数据所对应的网页标签 ![](https://oscimg.oschina.net/oscnet/75db5721-dc87-41bc-8c3a-7d1c06c9370a.png) 通过网页源码可以分析,汽车列表数据都是在class为search-result-list下。每一个class为search-result-list-item表示一条数据,每一条数据下都有汽车对应的属性(如:汽车名称、价格等) 这里仅作为实战案例去学习feapder爬虫框架,因此这里就只爬取汽车名称、价格;这两个字段属性。 ### 4.创建Mysql数据库 采集的数据需要存储到数据库(mysql)中,因此我们先来定义好数据库和表 ![](https://oscimg.oschina.net/oscnet/b1d52810-e1e3-4929-bd96-bbd4660c6353.png) 这里辰哥创建了一个数据库:chenge_yc,并在里面建了应该表:t_yc,其表结构如上图,这里如果不不熟悉mysql如何建立数据库表的可以参考辰哥的这篇文章([实战|教你用Python玩转Mysql](http://mp.weixin.qq.com/s?__biz=MzU5NjM4MDY1Mw==&mid=2247489794&idx=1&sn=5e04990e875970602f774fa6ff994952&chksm=fe62c780c9154e965758c6b316bd05b7c016f9fe5cac2021b54a04614e3b023aa64a21b01a60&scene=21#wechat_redirect)) 在爬虫项目中配置数据库,打开根目录下的setting.py文件 ![](https://oscimg.oschina.net/oscnet/5987bb1c-a53a-42be-8407-638a263e4e69.png) 可以看到feapder支持多种数据库的对接,咱们这里使用的是mysql,其配置如下: ![](https://oscimg.oschina.net/oscnet/e7291044-6dcc-40f2-b837-0af2bc0bf7d1.png) 接着在终端下,进入到根目录下的items文件夹,执行下面命令**生成数据库表对于的item** ``` feapder create -i t_yc ``` ![](https://oscimg.oschina.net/oscnet/cbd8586e-57ef-4b10-93ff-533d3c32de3a.png) **请注意**:命令中的t_yc是对于数据库表中的t_yc ![](https://oscimg.oschina.net/oscnet/5b10eea3-cc25-4457-9b48-2bf277b1fd26.png) 最后生成t\_yc\_item.py文件: ![](https://oscimg.oschina.net/oscnet/2525fb2b-eaa7-4d58-90c3-e14ba39cca96.png) 里面的name和price则是对应数据库中的字段。 ### 5.提取网页字段 上面已经获取到网页源码,也知道数据所在的标签,现在开始编写代码进行解析。 ![](https://oscimg.oschina.net/oscnet/67f273c4-bb2d-4c34-bef2-a38acee215f8.png) 执行结果: ![](https://oscimg.oschina.net/oscnet/9db89be6-54ed-43b9-ab59-8258dc3d2f17.png) 可以看到数据已经成功提出来,下一步将这些数据存储到数据库中。 ### 6.存储到数据库 ``` import feapder ``` 引入刚刚的t\_yc\_item.py,并创建对象TycItem。把爬取的name和price初始化到对象中。最后yieIdTycItem,实际上就直接存储到数据库了(因为数据库表和item是对应连接关系,这样就直接存储到数据库了)。 这太方便了,连sql语句都省了,6666666666 执行结果如下: ![](https://oscimg.oschina.net/oscnet/3dabc702-87f5-4f67-aa6e-a6c29036833a.png) 查看数据库: ![](https://oscimg.oschina.net/oscnet/07b3c820-c75d-4889-8242-df287778ba98.png) 同样可以看到数据直接就存储到数据库中。大功告成!!!!!! # 03、小结 相信看到这里的你已经完完全全掌握了 爬虫框架: **Feapder** ,你不仅知道了什么是feapder,同时还学会了如何使用feapder。 此外实战部分包括 **爬虫数据和存储到Mysql数据库**,让大家能够感受一下,数据从网页经框架Feapder采集后,直接存储到数据库的过程。 一定要 **动手尝试** **!** 一定要 **动手尝试** **!** 一定要 **动手尝试!**

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册