当前位置: 首页 > news >正文

用PHP异步协程控制python爬虫脚本,实现多协程分布式爬取

背景

公司需要爬取指定网站的产品数据。但是个人对python的多进程和协程不是特别熟悉。所以,想通过php异步协程,发起爬取url请求控制python爬虫脚本,达到分布式爬取的效果。

准备

  • 1.准备一个mongodb数据库用于存放爬取数据
  • 2.引入flask包,方便php通过调用url发起请求控制脚本
  • 3.引入selenium、BeautifulSoup4、webdriver等python包
  • 4.使用php的swoole异步协程发送url请求

python爬虫脚本

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from flask import Flask,jsonify,request
from pymongo import MongoClient
from datetime import datetime
from gevent import pywsgi
import sys
import requests
import re
from tornado.httpserver import HTTPServer
from tornado.wsgi import WSGIContainer
from tornado.ioloop import IOLoop#创建一个服务,赋值给APP
app = Flask(__name__)@app.route('/get_ic_product_list',methods=['post']) #指定接口访问的路径,支持什么请求方式get,post
def get_ic_product_list():# url = 'https://www.ic.com/psen/ClassList2.aspx?id=3'# url = 'https://www.ic.com/ClassList2.aspx?id=3'chrome_options = webdriver.ChromeOptions()chrome_options.add_argument('--no-sandbox')chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')chrome_options.add_argument('--ignore-certificate-errors')browser = webdriver.Chrome()conn = MongoClient('192.168.0.143', 27017)try:uri = request.form.get('url')# 是否按照零库存处理 yes-是 no-否isContinue = request.form.get('is_continue_stock', 'yes')# uri = sys.argv[1]# isContinue = sys.argv[2]if uri == '':return jsonify({'status': 0, 'msg': 'uri is null'})browser.get(uri)browser.implicitly_wait(3)time.sleep(2)# 连接mongodatabase = conn.iccollection = database.ic_product# 时间格式化nowtime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')spiderTime = datetime.now().strftime('%Y%m%d')spidertime = int(spiderTime)# 总页数pageTag = browser.find_element(By.CLASS_NAME, 'P_Page')total_page = pageTag.text.strip().split('/')[1]# 分类列表,不按照stock=0处理cateMap = ['21', '43', '175','179','184','241','250','268','306','317','325','343','397','394','409','467','547','556','606','638','657','678','1150','1158','2315','2383']if total_page:t_page = int(total_page)if t_page > 20:totalPage = 55else:totalPage = t_pageelse:totalPage = 20stop = 0soup = BeautifulSoup(browser.page_source, 'html.parser')tables = soup.findAll(name="table", attrs={"class": "prilist"})if len(tables) > 0:for table in tables:# 阶梯价price_tb = table.findAll('tr')price = [tb.text.strip() for tb in price_tb]# 行数据cols = table.parent.parentitem = [col.text.strip() for col in cols]time.sleep(2)print('==========item==========')print(item)# 库存为0,终止程序stock = item[11]if stock == '-':stop += 1continuepos = item[3].find('Promotion')if pos != -1:sku = str(item[3].replace('(Promotion)', ''))else:sku = item[3]# 格式化数据collection.insert_one({'datasheet': str(item[0]),'img_url': str(item[1]),'productDescEn': str(item[2]),'part': sku,'manufacturer': str(item[5]),'description': str(item[7]),'pdfLinkUrl': str(item[10]),'availability': str(item[11]),'price': price,'created_at': nowtime,'spider_time': spidertime,'sync_time': 20010101,'updated_at': '2001-01-01 00:00:00',})# 接口请求在clickfor i in range(totalPage):print('==========i==========')print(i)browser.find_element(By.XPATH, './/div[@id="Pager1"]/a[9]').click()soup = BeautifulSoup(browser.page_source, 'html.parser')tables = soup.findAll(name="table", attrs={"class": "prilist"})print('==========stop==========')print(stop)if stop>10 and isContinue=='yes':breakif len(tables) < 1:return jsonify({'status': 0, 'msg': 'product list empty'})print('==========tables==========')print(tables)mongoData = []for table in tables:# 阶梯价price_tb = table.findAll('tr')price = [tb.text.strip() for tb in price_tb]# 行数据cols = table.parent.parentitem = [col.text.strip() for col in cols]time.sleep(2)print('==========item==========')print(item)# 库存为0,终止程序stock = item[11]if stock == '-':stop += 1continuepos = item[3].find('Promotion')if pos != -1:sku = str(item[3].replace('(Promotion)', ''))else:sku = item[3]# 格式化数据mongoData.append({'datasheet': str(item[0]),'img_url': str(item[1]),'productDescEn': str(item[2]),'part': sku,'manufacturer': str(item[5]),'description': str(item[7]),'pdfLinkUrl': str(item[10]),'availability': str(item[11]),'price': price,'created_at': nowtime,'spider_time': spidertime,'sync_time': 20010101,'updated_at': '2001-01-01 00:00:00',})# 插入mongomongoData and collection.insert_many(mongoData)print('================mongoData================')print(mongoData)del price, tablestime.sleep(5)return jsonify({'status': 1,'msg': 'ok'})except Exception as error:return jsonify({'status':0,'msg':error})finally:conn.close()browser.close()if __name__ == '__main__':app.run(host='0.0.0.0',port=8005,debug=True)          # 启动服务器# Tornado启动服务
# s = HTTPServer(WSGIContainer(app))
# s.listen(8005) # 监听 8080 端口
# IOLoop.current().start()

php协程调用curl请求封装

在laravel 框架下创建command服务
此处有用到redis队列的,有不熟悉的请阅读redis相关资料

<?php
/*** 爬取IC任务*/
namespace App\Console\Commands;use App\Models\IcProductCategoryModel;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Swoole\Timer;class SpiderIcProductList extends Command
{/*** The name and signature of the console command.** @var string*/protected $signature = 'spider:ic_product';/*** The console command description.** @var string*/protected $description = '每天定时爬取IC网站数据';/*** Create a new command instance.** @return void*/public function __construct(){parent::__construct();}/*** Execute the console command.** @return int*/public function handle(){$this->getProduct();return 0;}public function getProduct(){/*** ic product_num> 0, 分类约345个* 目前15min【900 * 1000】爬取10个分类,一天能爬取960个分类,一天能爬2次*/Timer::tick(60 * 15 * 1000,function (){$categoryModel = new IcProductCategoryModel();$key = $categoryModel::getCategoryQueue();echo 'time:' .date('H:i:s') .space();if (Redis::lLen($key) > 0) {$data = Redis::lRange($key,0,9);foreach ($data as $categoryId){go(function () use ($categoryId){$client = new \Swoole\Client(SWOOLE_SOCK_TCP);// 尝试与指定 TCP 服务端建立连接(IP和端口号需要与服务端保持一致,超时时间为0.5秒)if ($client->connect("127.0.0.1", 9505, 0.5)) {// 建立连接后发送内容$data = ['service' => 'RemoteApiSpider','method' => 'getProduct','param' =>  ['category_id' => $categoryId,'platform' => PLATFORM_IC,],];$sendData = jsonE($data);echo  'send=' .$sendData  .' ,'. date('H:i:s')   .space();$client->send($sendData);// 打印接收到的消息echo $client->recv();// 关闭连接$client->close();} else {echo "connect failed.";}});}Redis::lTrim($categoryModel::getCategoryQueue(),10,-1);unset($data);}});echo 'spider product end time:' .date('H:i:s') .space();}
}

创建服务端Server

<?phpnamespace App\Console\Commands;use App\Services\RemoteApiSpider;
use App\Services\TcpService;
use App\Services\TestService;
use Swoole\Database\MysqliConfig;
use Swoole\Database\MysqliPool;
use Swoole\Server;class Swoole extends Base
{protected $ws;/*** The name and signature of the console command.** @var string*/protected $signature = 'swoole {action} {--d}';/*** The console command description.** @var string*/protected $description = 'swoole service command';/*** Create a new command instance.** @return void*/public function __construct(){parent::__construct();}/*** Execute the console command.** @return int*/public function handle(){global $argv;$action = $this->argument('action');$argv[0] = 'wk';$argv[1] = $action;$argv[2] = $this->option('d') ? '-d' : '';$this->startServer();return 0;}public function startServer(){$server = new \Swoole\Server("127.0.0.1", 9505);// 设置异步任务的工作进程数量$log_path = storage_path() . '/logs/server.log';$server->set(['daemonize' => true,'log_file' =>  $log_path,'task_worker_num' => 50,]);//收到请求时触发$server->on('receive', function(\Swoole\Server $server, $fd, $from_id, $data) {//投递异步任务$task_id = $server->task($data);echo "异步任务投递成功: id=$task_id , recv={$data} " .his() . space();$server->send($fd, "task_id[{$task_id}],recv={$data},数据已接收,处理中... " .his() . space());});// 处理异步任务$server->on('Task', array($this, 'onTask'));//        $server->on('task', function (\Swoole\Server $server, $task_id, $from_id, $data) {
//            echo "新的待处理异步任务[id=$task_id] ".his() . space();
//            /**
//             $data = ['data' => [],'code' => '200','msg' => 'false']
//             */
//            $service = new RemoteApiSpider();
//            $service->setDebug(true);
//            if($data && is_string($data)){
//                $data = jsonD($data);
//            }
//            $ret = false;
//            if($data['code']==200 && $data['msg'] == 'ok'){
//                if(isset($data['data']['category_id']) && $data['data']['category_id']){
//                    $categoryId = $data['data']['category_id'];
//                    $ret = $service->getProduct($categoryId);
//                }
//            }
//            // todo 处理异步任务
//            // 返回任务执行的结果
//            $msg = "task_id[{$task_id}] ,category_id:" .$data . " ,ret:".$ret . " ," . his() . space();
//            $server->finish($msg);
//        });// 处理异步任务的结果$server->on('finish', function (\Swoole\Server $server, $task_id, $data) {echo "异步任务[$task_id] 处理完成: $data ". his() . space();});$server->start();}public function process(){$server = new TcpService();// 定义连接建立回调函数$server->onConnect = function ($conn) {echo "onConnect -- accepted " . stream_socket_get_name($conn, true) . "\n";};// 定义收到消息回调函数$server->onMessage = function ($conn, $msg) {echo "onMessage --" . $msg . "\n";$service = new RemoteApiSpider();$service->setDebug(true);$content = $service->getProduct($msg);fwrite($conn, "received " . $msg . "\n");};// 定义连接关闭回调函数$server->onClose = function ($conn) {echo "onClose --" . stream_socket_get_name($conn, true) . "\n";};// 启动服务器主进程$server->run();}public function onTask($serv, $task_id, $from_id, $data){//传类名、方法、参数实现公共使用try {if (!$data) {throw new \Exception(' task_id=' . $task_id . ',暂无需要处理的task任务', 400);}if (is_string($data)) {$data = jsonD($data);}echo 'recv: ' . jsonE($data) . space();$className = "App\Services\\{$data['service']}";if (!class_exists($className)) {throw new \Exception(' task_id=' . $task_id . ',未找到服务类名', 401);}$class = new $className;$func = $data['method'];if(isset($data['param'])){$ret = $class->$func($data['param']);}else{$ret = $class->$func();}return jsonE($ret);} catch (\Exception $e) {$msg = ' task_id=' . $task_id . ' ,error: ' . $e->getMessage();echo jsonE($msg) . space();}$serv->finish($msg);//每分钟执行一下service下的start方法,发送二维数组请求给tcp服务端,服务端调用service下的process方法实现业务逻辑//服务端拿到数据后,处理后结果格式:['code'=> 200,'msg'=> 'ok','data' => maxid]
//        $data = [
//            'service' => 'TestService',
//            'method' => 'process',
//            'param' => ['debug'=> true,'send' =>'sucess'],
//        ];//暂时留空 2.3部分会完善}}

curl发送异步请求调用python脚本

<?php
/*** 爬取接口封装*/
namespace App\Services;use App\Library\Oneyac\Oneyac;
use App\Models\HqchipProductCategoryModel;
use App\Models\HqchipProductMongoModel;
use App\Models\IcProductCategoryModel;
use App\Models\IcProductMongoModel;
use App\Models\LcscProductCategoryModel;
use App\Models\LcscProductMongoModel;
use App\Models\OneyacProductCategoryModel;
use App\Models\OneyacProductMongoModel;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;class RemoteApiSpider extends Base
{/*** 根据分类循环分页爬取产品列表* @param $data* @return int* @throws \GuzzleHttp\Exception\GuzzleException*/public function getProduct($data){$cateId = 0;$categoryId = $data['category_id'] ?? $cateId;$platform = $data['platform'] ?? PLATFORM_LCSC;$platform = strtolower(trim($platform));$categoryId = (int)$categoryId;switch ($platform) {case PLATFORM_IC:$cateModel = new IcProductCategoryModel();Redis::hDel($cateModel::getCategoryListKey(),$categoryId);//TODO 调用python API接口爬取产品数据//s2 20分钟处理4个 与 s1 20分钟处理6个 $header = [];$header['Content-Type'] = 'application/json;charset=UTF-8';$query_param['page'] = 1;$res = $this->getIcProduct($categoryId, $platform,$query_param, $header);$res && $cateId = $categoryId;break;}return $cateId;}/*** 获取ic产品信息* @param $categoryId* @param $platform* @param $query_param* @param $header* @return bool* @throws \GuzzleHttp\Exception\GuzzleException*/public function getIcProduct($categoryId, $platform, $query_param, $header){$module =  'product_list';$this->getPlatformConf(PLATFORM_IC,$module);$msg = ' ' . $platform . '_' . $module . ' ';echo $msg. ' ,category_id:' . $categoryId . ' spidering' .space();try {//$uri = 'https://www.ic.com/psen/ClassList2.aspx?id=3';$uri = $this->base_uri . $this->uri . '?id=' . $categoryId;# todo 3的倍数推入s2,其他推入s1$config = config('ic');# 重点:此处可以部署到不同服务器进行爬取,我这里只部署了2台服务,s1与s2if(($categoryId%3)===0){//s2$curl = $config['spider_uri']['s2'] ?: 'http://192.168.0.124:8005/get_ic_product_list';}else{//s1$curl = $config['spider_uri']['s1'] ?: 'http://192.168.0.127:8005/get_ic_product_list';}$response = curlPost($curl,['url' => $uri,'is_continue_stock' => 'yes']);$flag = false;if(isset($response) && !empty($response)){$response = is_array($response) ? $response : jsonD($response);$flag = ($response['msg']=='ok' && $response['status']==1);}if ($flag) {$this->debug && Log::info($msg . 'category_id:' . $categoryId . ' ,mongo-batch-insert:' . $flag);} else {throw new \Exception($msg.'ic爬虫异常,请检查python爬虫');}unset($response);return  $flag;}catch (\Exception $e){Log::error($msg .'爬取产品异常,异常原因:'.$e->getMessage());return false;}}

nginx配置

server {listen 8080;server_name spidertest.test *.spidertest.test;root "D:/WWW/spidertest/";location / {proxy_pass http://127.0.0.1:8005;}charset utf-8;
}
  • 注:
    1.在不同服务器上部署python爬虫脚本,并配置nginx服务。配置完成后安装下面命令启动脚本。接下来开启异步多线程执行python爬虫脚本即可。
    2.记得启动swoole服务。命令如下:
php artisan swoole start

运行

python脚本启动
在这里插入图片描述

postman测试:
![在这里插入图片描述](https://img-blog.csdnimg.cn/c24295c2a30c4f79a377856df4bb9767.png

php启动协程命令:
在这里插入图片描述

爬取回来的数据结果:在这里插入图片描述

相关文章:

用PHP异步协程控制python爬虫脚本,实现多协程分布式爬取

背景 公司需要爬取指定网站的产品数据。但是个人对python的多进程和协程不是特别熟悉。所以&#xff0c;想通过php异步协程&#xff0c;发起爬取url请求控制python爬虫脚本&#xff0c;达到分布式爬取的效果。 准备 1.准备一个mongodb数据库用于存放爬取数据2.引入flask包&a…...

VUE3写后台管理(3)

VUE3写后台管理&#xff08;3&#xff09; 1.环境1.node2.vite3.Element-plus4.vue-router5.element icon6.less7.vuex8.vue-demi9.mockjs10.axios11.echarts 2.首页1.布局Main2.头部导航栏CommonHeader3.左侧菜单栏CommonLeft4.首页Home1.从后端获取数据显示到前端table的三种…...

机器学习笔记之最优化理论与算法(十二)无约束优化问题——共轭梯度法

机器学习笔记之最优化理论与方法——共轭梯度法 引言回顾&#xff1a;共轭方向法的重要特征线性共轭梯度法共轭方向公式的证明过程 关于线搜索公式中参数的化简关于线搜索公式中步长部分的化简关于线搜索公式中共轭方向系数的化简参数化简的目的 非线性共轭梯度法(FR,PRP方法)关…...

JVM中的java同步互斥工具应用演示及设计分析

1.火车站售票系统仿真 某火车站目前正在出售火车票&#xff0c;共有50张票&#xff0c;而它有3个售票窗口同时售票&#xff0c;下面设计了一个程序模拟该火车站售票&#xff0c;通过实现Runnable接口实现&#xff08;模拟网络延迟&#xff09;。 伪代码&#xff1a; Ticket类…...

数据治理-数据质量

实现数据质量的前提就是数据本身是可靠和可信的。 导致数据质量低下的因素 组织缺乏对低质量数据影响的理解&#xff0c;缺乏规划、孤岛式系统设计、不一致的开发过程、不完整的文档、缺乏标准或缺乏治理等。 所有组织都会遇到与数据质量有关的问题。数据质量需要跨职能的承诺…...

[sqoop]hive3.1.2 hadoop3.1.1安装sqoop1.4.7

参考: Hadoop3.2.4Hive3.1.2sqoop1.4.7安装部署_hadoop sqoop安装_alicely07的博客-CSDN博客 一、安装 1、解压 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /home/data_warehouse/module mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop-1.4.72、配置文件 sqoop-env.s…...

js事件的详细介绍

11.事件 1.什么是事件 js属于事件驱动编程,把驱动,执行,调用通过一些交互,触发一些函数事件:发起-->执行绑定事件-->触发事件on 绑定 emit触发 off解绑2.事件分类 鼠标事件 点击事件 onclick 双击事件 ondblclick 按下事件 onmousedown 抬起事件 onmouseup 鼠标进…...

虚幻4学习笔记(12)操控导入的角色、动画蓝图、播放蒙太奇和打包、角色重定向

虚幻4学习笔记 操控导入的角色设置鼠标旋转关掉动态模糊 动画蓝图、播放蒙太奇和打包角色走路奔跑动画shift 奔跑F 跳舞移动打断 跳舞 打包角色重定向姿势调整解决跑步 腿分太开隐藏剑 B站UP谌嘉诚课程&#xff1a;https://www.bilibili.com/video/BV164411Y732 操控导入的角色…...

hive with tez:无法从链中的任何提供者加载aws凭据

环境信息 hadoop 3.1.0 hive-3.1.3 tez 0.9.1 问题描述 可以从hadoop命令行正确地访问s3a uri。我可以创建外部表和如下命令&#xff1a; create external table mytable(a string, b string) location s3a://mybucket/myfolder/; select * from mytable limit 20; 执行正…...

Ubuntu修改静态IP、网关和DNS的方法总结

Ubuntu修改静态IP、网关和DNS的方法总结 ubuntu系统&#xff08;其他debian的衍生版本好像也可以&#xff09;修改静态IP有以下几种方法。&#xff08;搜索总结&#xff0c;可能也不太对&#xff09; /etc/netplan (use) Ubuntu 18.04开始可以使用netplan配置网络&#xff0…...

Eureka服务器注册

一。Eureka服务器注册 1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…...

Windows安装GPU版本的pytorch详细教程

文章目录 chatGLM2-6B安装教程正式安装 chatGLM2-6B ChatGLM2-6B版本要装pytorch2.0&#xff0c;而且要2.0.1 &#xff0c;因此CUDA不能用12.0 &#xff0c;也不能用10.0&#xff0c;只能用11.x 版本。 安装教程 pip install直接下载安装 官网&#xff1a; https://pytorch.…...

理解Kruskal算法的前提----深入理解并查集【超简单~】

并查集的实现思路 并查集主要分为两个部分&#xff1a;第一部分就是需要找到点对应的祖宗节点&#xff0c;第二部分&#xff0c;是要将属于同一个集合节点的祖宗节点进行统一&#xff0c;也就是结合操作。 Find函数实现 // parent数组用来存储下标值所对应的父节点值 // 比如…...

Jenkins+Gitee+Docker+Ruoyi项目前后端分离部署

前言 描述&#xff1a;本文主要是用来记录 如何用标题上的技术&#xff0c;部署到云服务器上通过ip正常访问。 一、总览 1.1、Docker做的事 拉取 mysql 镜像拉取 redis 镜像拉取 jdk 镜像拉取 nginx 镜像 解释说明&#xff1a;前端项目的打包文件放在 nginx容器运行。后端…...

笙默考试管理系统-MyExamTest----codemirror(23)

笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 目录 笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙…...

重学Java (一) 泛型

1. 前言 泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话&#xff0c;可以参考下面两个链接 Java 泛型详解The Java™ Tutorial…...

Docker 部署 Redis 服务

拉取最新版本的 Redis 镜像: $ sudo docker pull redis:latest在本地预先创建好 data 目录和 conf/redis.conf 文件。 使用以下命令来运行 Redis 容器: $ sudo docker run -itd --name redis --privilegedtrue -p 6379:6379 -v /home/ubuntu/docker/redis/data:/data -v /ho…...

阿里云产品试用系列-负载均衡 SLB

阿里云负载均衡&#xff08;Server Load Balancer&#xff0c;简称SLB&#xff09;是云原生时代应用高可用的基本要素。通过将流量分发到不同的后端服务来扩展应用系统的服务吞吐能力&#xff0c;消除单点故障并提升应用系统的可用性。阿里云SLB包含面向4层的网络型负载均衡NLB…...

drf 对象级权限

drf 对象级权限 Django REST Framework&#xff08;DRF&#xff09;提供了对象级别权限&#xff08;Object-level permissions&#xff09;来控制特定对象的访问权限。 简单来说&#xff1a;通过视图类中的self.get_object(pk)得到一个obj对象(视图对象)&#xff0c;在与requ…...

八大排序(二)--------冒泡排序

本专栏内容为&#xff1a;八大排序汇总 通过本专栏的深入学习&#xff0c;你可以了解并掌握八大排序以及相关的排序算法。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;八大排序汇总 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...