用 tensorflow.js 做了一个动漫分类的功能(一)
前言:
浏览某乎网站时发现了一个分享各种图片的博主,于是我顺手就保存了一些。但是一张一张的保存实在太麻烦了,于是我就想要某虫的手段来处理。这样保存的确是很快,但是他不识图片内容,最近又看了 mobileNet 的预训练模型,想着能让程序自己对图片分类,以下就通过例子从内容采集到分类的过程。
内容和资源的采集,反手就是某虫了。在网络上,经过近几年的营销渲染,可能首选是用 Python 做脚本。而这次是用 PHP 的 QueryList 来做采集,下面也就是采集的编码过程和踩坑解决方法,最后再对采集图片进行标注和训练。

环境:
PHP7.4
QueryList4.0
QueryList-CurlMulti

编码:
以下例子是基于 TP5.1,所以只需要安装上面两个依赖包。采集启动通过自定义命令实现,接下来分别以普通采集和多线程采集两种方式展示。
1. 普通采集
<?php/*** @Notes: 公众号:ZERO开发* @Interface getCondition* @Return mixed* @Author: bqs* @Time: 2021/4/19 15:28*/namespaceapp\common\command;usethink\console\Command;
usethink\console\Input;
usethink\console\Output;
usethink\console\input\Argument;
usethink\console\input\Option;
usethink\Db;
usethink\facade\Hook;
usethink\facade\Log;
useQL\QueryList;classQueryListSpiderSingleextendsCommand{protectedfunctionconfigure(){$this->setName('querylist:single')->setDescription('采集');}protectedfunctionexecute(Input $input, Output $output){ini_set('memory_limit', '512M');$output->writeln("=========date:" . date('Y-m-d H:i:s') . "===============");// 北桥苏奥特曼//$slImgsUrl = "https://zhuanlan.zhihu.com/p/377571373";$slImgsUrl = "https://zhuanlan.zhihu.com/p/344680014";// 原生query_list$list = QueryList::get($slImgsUrl)->find('.RichText')->find('noscript')->find('img')->attrs('src');$path = 'E:\2setsoft\1dev\phpstudy_pro\WWW\4test\tensorflowJs\js-ml-code\t7\动漫分类\train\奥特曼\\';foreach($list as $key => $value) {$index = $key + 1 + 42;$filename = $index < 10 ? str_pad($index, 2, "0", STR_PAD_LEFT) : $index;$filend = pathinfo($value, PATHINFO_EXTENSION);$file = file_get_contents($value);file_put_contents($path . $filename . "." . $filend, $file);$output->writeln($index . "--" . $value . "已保存--");}$output->writeln("============date:" .date("Y-m-d H:i:s") . "采集完成==============");}}2. 多线程采集
<?php/*** @Notes: 文件描述* @Interface getCondition* @Return mixed* @Author: bqs* @Time: 2021/4/19 15:28*/namespaceapp\common\command;usethink\console\Command;
usethink\console\Input;
usethink\console\Output;
usethink\console\input\Argument;
usethink\console\input\Option;
usethink\Db;
usethink\facade\Hook;
usethink\facade\Log;
useQL\QueryList;
useQL\Ext\CurlMulti;classQueryListSpiderextendsCommand{protectedfunctionconfigure(){$this->setName('query:list')->setDescription('采集');}protectedfunctionexecute(Input $input, Output $output){ini_set('memory_limit', '512M');$output->writeln("=========date:" . date('Y-m-d H:i:s') . "===============");// 地址与目录映射$dirMap = ["假面骑士" => "https://zhuanlan.zhihu.com/p/376119915","龙珠" => "https://zhuanlan.zhihu.com/p/340048917","火影忍者" => ["https://zhuanlan.zhihu.com/p/352717188", "https://zhuanlan.zhihu.com/p/393213201", "https://zhuanlan.zhihu.com/p/358228745"],"海贼王" => ["https://zhuanlan.zhihu.com/p/357683518", "https://zhuanlan.zhihu.com/p/338160632"]];// 采集地址$multiArr = [];$multiArr = array_reduce(array_values($dirMap), function($res, $value){$res = array_merge($res, (array)$value);return $res;}, []);// 采集映射$multiMap = [];foreach($dirMap as $key => $value) {if (!is_array($value)) {$multiMap[$value] = $key;} else {$temp = array_fill_keys($value, $key);$multiMap = array_merge($multiMap, $temp);}}// 开始使用多线程采集$ql = QueryList::use (CurlMulti::class);$ql->curlMulti($multiArr)->success(function(QueryList $ql, CurlMulti $curl, $r)use($multiMap){$path = 'E:\2setsoft\1dev\phpstudy_pro\WWW\4test\tensorflowJs\js-ml-code\t7\动漫分类\train\\';$queryUrl = $r['info']['url'];$className = $multiMap[$queryUrl] ?? "";$targetDir = $path . $className;$path = $targetDir . '\\';$endFileIndex = 0;$existFileList = $this->scanFile($targetDir);if ($existFileList) {// 取出所有数字文件名最大值$endFileName = max($existFileList);$endFileIndex = explode(".", $endFileName)[0];}$data = $ql->find('.RichText')->find('noscript')->find('img')->attrs('src');foreach($data as $key => $value) {$index = $key + 1 + $endFileIndex;$filename = $index < 10 ? str_pad($index, 2, "0", STR_PAD_LEFT) : $index;$filend = pathinfo($value, PATHINFO_EXTENSION);$file = file_get_contents($value);file_put_contents($path . $filename . "." . $filend, $file);}})// 每个任务失败回调->error(function($errorInfo, CurlMulti $curl){echo"Current url:{$errorInfo['info']['url']} \r\n";print_r($errorInfo['error']);})->start([// 最大并发数'maxThread' => 10,// 错误重试次数'maxTry' => 5,]);$output->writeln("============date:" . date("Y-m-d H:i:s") . "采集完成==============");}// 扫描目录下所有文件protectedfunctionscanFile($path){$result = [];$files = scandir($path);foreach ($files as $file) {if ($file != '.' && $file != '..') {if (is_dir($path . '/' . $file)) {$this->scanFile($path . '/' . $file);} else {$result[] = basename($file);}}}return $result;}}问题解决:
由于普通采集的请求使用 GuzzleHttp 客户端,而多线程采集是 CURL,所以运行时报 curl 状态码 60 错误。

1. 解决方法:
(1). 下载 cacert
下载地址:https://curl.haxx.se/ca/cacert.pem
(2). 修改 php.ini , 并重启
在 php.ini 中找到 curl.cainfo 改为文件的绝对路径如:curl.cainfo =E:\2setsoft\1dev\phpstudy_pro\Extensions\php\php7.4.3nts\cacert.pem

图片训练:
以上的图片已经采集的差不多了,因为博主的图片有限,我也没有再去其他地方找,整个文件夹下的图片在 200 张左右。按理说图片当然是越多越好,但是整个分类标注起来耗时(看文章的配图,应该已经知道有哪几类了吧),所以就这样了。最后就是读取图片转换 Tensor 进行训练,后一篇再具体介绍吧,提醒一下。下一篇需要提前安装 Node, Http-Server,Parcel 工具。

相关文章:
用 tensorflow.js 做了一个动漫分类的功能(一)
前言:浏览某乎网站时发现了一个分享各种图片的博主,于是我顺手就保存了一些。但是一张一张的保存实在太麻烦了,于是我就想要某虫的手段来处理。这样保存的确是很快,但是他不识图片内容,最近又看了 mobileNet 的预训练模…...
看完这篇Vue-element-admin,跟面试官聊骚没问题
Vue-element-admin vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富…...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(5)
目录 模块A 基础设施设置与安全加固 一、项目和任务描述: 二、服务器环境说明 三、具体任务(每个任务得分以电子答题卡为准) A-1任务一 登录安全加固(Windows) 1.密码策略 a.密码策略必须同时满足大小写字母、数…...
基于Java+SpringBoot+Vue+Uniapp前后端分离商城系统设计与实现
博主介绍:✌全网粉丝3W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌ 博主作品:《微服务实战》专栏是本人的实战经验总结,《Spring家族及…...
新建ES别名 添加别名 切换别名
# 查询别名指向到哪个索引 GET bebd_factory_search/_alias # 查询这个索引使用了什么别名 GET bebd_factory_search_1588250935622/_alias # 删除索引 DELETE bebd_factory_search_1588250935622 # 新建别名 POST /_aliases { "actions": [ { "ad…...
MySQL —— 内外连接
目录 表的内外连接 一、内连接 二、外连接 1. 左外连接 2. 右外连接 表的内外连接 表的连接分为内连和外连 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我们前面博客中的查询都是内连接,也是在开发过程中使用的最多…...
EXCEL中文本和数字的相互转换方法
将EXCEL中存为文本的数字转换成数字 如果在 Excel 中,将数字存储为文本格式,可以通过以下步骤将其转换为数字: 选中需要转换格式的单元格或者整列;右键单击,选择“格式单元格”;在弹出的对话框中选择“常…...
React源码分析6-hooks源码
本文将讲解 hooks 的执行过程以及常用的 hooks 的源码。 hooks 相关数据结构 要理解 hooks 的执行过程,首先想要大家对 hooks 相关的数据结构有所了解,便于后面大家顺畅地阅读代码。 Hook 每一个 hooks 方法都会生成一个类型为 Hook 的对象ÿ…...
Windows10神州网信政府版麦克风、摄像头的使用
Windows10神州网信政府版默认麦克风摄像头是禁用状态,此禁用状态符合版本规定。 在录课和直播过程中,如果需要使用麦克风和摄像头的功能,可以这样更改: 1、鼠标右键点击屏幕左下角的开始菜单图标,选择windows中的“运…...
微机原理学习总结0:前言
近期结束了微机课程的学习,(指刚考完试),正常情况下,后面应该不会再接触这门课程了,故在此记录自己这段时间的收获。 首先,十分推荐b站的一门课程,老师讲的很细致,很适合…...
LeetCode 1828. 统计一个圆中点的数目
给你一个数组 points ,其中 points[i] [xi, yi] ,表示第 i 个点在二维平面上的坐标。多个点可能会有 相同 的坐标。 同时给你一个数组 queries ,其中 queries[j] [xj, yj, rj] ,表示一个圆心在 (xj, yj) 且半径为 rj 的圆。 对…...
Spring Boot + Vue3 前后端分离 实战 wiki 知识库系统<一>---Spring Boot项目搭建
前言: 接下来又得被迫开启新的一门课程的学习了,上半年末尾淘汰又即将拉开序幕【已经记不清经历过多少次考试了】,需要去学习其它领域的技术作为考试内容,我选了spring boot相关技术,所以。。总之作为男人,…...
leetcode 11~20 学习经历
LeetCode 习题 11 - 2011. 盛最多水的容器12. 整数转罗马数字13. 罗马数字转整数14. 最长公共前缀15. 三数之和16. 最接近的三数之和17. 电话号码的字母组合18. 四数之和19. 删除链表的倒数第 N 个结点20. 有效的括号小结11. 盛最多水的容器 给定一个长度为 n 的整数数组 heigh…...
LeetCode 双周赛 98,脑筋急转弯转不过来!
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 昨晚是 LeetCode 第 98 场双周赛,你参加了吗?这场周赛需要脑筋急转弯,转不过来 Medium 就会变成 Hard&#…...
函数的栈帧的创建和销毁
文章目录本章主题:一.什么是函数栈帧1.什么是栈2.什么是函数栈帧二.理解函数栈帧能解决什么问题呢?三.函数栈帧的创建和销毁解析1.预备知识(1) 认识相关寄存器和汇编指令(2)栈帧空间的维护2.解析函数栈帧的…...
python filtermapreducezip
一、filter 过滤 filter 过滤, 从可迭代对象中,筛选出满足条件的元素,再将这些满足条件的元素,组成一个新的可迭代对象。 方式一:filter(过滤方法,可迭代对象) 举例:将一个list中…...
Centos7搭建hadoop3.3.4分布式集群
文章目录1、背景2、集群规划2.1 hdfs集群规划2.2 yarn集群规划3、集群搭建步骤3.1 安装JDK3.2 修改主机名和host映射3.3 配置时间同步3.4 关闭防火墙3.5 配置ssh免密登录3.5.1 新建hadoop部署用户3.5.2 配置hadoopdeploy用户到任意一台机器都免密登录3.7 配置hadoop3.7.1 创建目…...
骨传导耳机工作原理,骨传导耳机优缺点
骨传导耳机虽说最近是十分火爆的一款单品,但还是有很多人对骨传导耳机不是很了解,骨传导耳机更多使用场景还是在户外运动使用,骨传导耳机对于长时间使用耳机的人来说十分友好,这主要还是得益于骨传导耳机传输声音的特殊性。 下面我…...
IDEA高效插件和设置
安装好Intellij idea之后,进行如下的初始化操作,工作效率提升十倍。 一. 安装插件 1. Codota 代码智能提示插件 只要打出首字母就能联想出一整条语句,这也太智能了,还显示了每条语句使用频率。 原因是它学习了我的项目代码&…...
Linux之网络流量监控工具ntopng YUM安装
一、ntopng简介 Ntop是一种监控网络流量工具,用ntop显示网络的使用情况比其他一些网络管理软件更加直观、详细。Ntop甚至可以列出每个节点计算机的网络带宽利用率。他是一个灵活的、功能齐全的,用来监控和解决局域网问题的工具;尤其当ntop与n…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
