网络爬虫之多任务数据采集(多线程、多进程、协程)

news2025/1/15 23:08:37

进程:是操作系统中资源分配的基本单位

线程:使用进程分配的资源处理具体任务

一个进程中可以有多个线程:进程相当于一个公司,线程就是公司里面的员工。

一 多线程

多线程都是关于功能的并发执行。而异步编程是关于函数之间的非阻塞执行,我们可以将异步应用于单线程或多线程当中。多线程是与具体的执行者相关的,而异步是与任务相关的。

并发和并行

一个程序在计算机中运行,其底层是处理器通过运行一条条的指令来实现的。

并发

并发,英文叫作 concurrency。它是指同一时刻只能有一条指令执行,但是多个线程的对应的指令被快速轮换地执行。比如一个处理器它先执行线程A的指令一段时间,再执行线程 B 的指令一段时间,再切回到线程 A执行一段时间。
由于处理器执行指令的速度和切换的速度非常非常快,人完全感知不到计算机在这个过程中有多个线程切换上下文执行的操作,这就使得宏观上看起来多个线程在同时运行。但微观上只是这个处理器在连续不断地在多个线程之间切换和执行,每个线程的执行一定会占用这个处理器一个时间片段,同一时刻,其实只有一个线程在执行。

并行

并行,英文叫作 paralel。它是指同一时刻,有多条指今在多个处理器上同时执行,并行必须要依赖于多个处理器,不论是从宏观上还是微观上,多个线程都是在同一时刻一起执行的。
并行只能在多处理器系统中存在,如果我们的计算机处理器只有一个核,那就不可能实现并行。而并发在单处理器和多处理器系统中都是存在的,仅仅依靠一个核,就能实现并发。

总结:

当系统有一个以上CPU时,则线程的操作可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程不抢占CPU资源,可以同时进行,这种方式称之为并行(Parallel)

(一)单线程

import requests,time
import threading

def test(url):
    resp = requests.get(url)  # 发送GET请求到指定的URL
    # 可以在这里处理响应,例如打印状态码或内容
    # print(resp.status_code)
if __name__ == '__main__':
    start = time.time()
    url = 'https://www.baidu.com'
    for i in range(10):
        test(url)
    resp = time.time()-start
    print(resp,'单线程')

(二)多线程

import requests,time
import threading

def test(url):
    resp = requests.get(url)  # 发送GET请求到指定的URL
    # 可以在这里处理响应,例如打印状态码或内容
    # print(resp.status_code)
if __name__ == '__main__':
    start = time.time()
    url = 'https://www.baidu.com'
    for i in range(10):
        test(url)
    resp = time.time()-start
    print(resp,'单线程')

if __name__ == '__main__':
    start1 = time.time()  # 记录当前时间用于后面计算总的运行时间
    url = 'https://www.baidu.com'
    threads = []  # 用于存储线程对象的列表

    # 创建并启动10个线程
    for i in range(10): #创建10个线程
        # 创建一个线程对象 target代表调用的函数 args代表给函数传递的参数
        thread = threading.Thread(target=test, args=(url,))  # 注意args是一个元组,即使只有一个元素
        threads.append(thread)  # 将线程加入到列表中
        thread.start()
    # 等待所有线程完成
    for thread in threads:
        thread.join() #意思是等所有的线程完成了再执行下面的操作
    elapsed_time = time.time() - start1  # 计算总的运行时间
    print(elapsed_time, '多线程')  # 打印多线程执行时间

(三)线程样例

# 导入 threading 和 time 模块
import threading, time
# 定义一个名为 target 的函数,它接受一个参数 second
def target(second):
    # 打印当前线程的名称和一条开始运行的消息
    print(f'Threading {threading.current_thread().name} is running')
    # 打印当前线程将要休眠的秒数
    print(f'Threading {threading.current_thread().name} sleep {second}s')
    # 使线程休眠指定的秒数
    time.sleep(second)
    # 打印当前线程结束的消息
    print(f'Threading {threading.current_thread().name} is ended')

# 打印主线程正在运行的消息
print(f'Threading {threading.current_thread().name} is running')

# 创建一个循环,循环值为列表 [1, 5] 中的元素
for i in [1, 5]:
    # 创建一个新的线程对象,目标函数是 target,参数是列表中的当前元素
    thread = threading.Thread(target=target, args=[i])
    # 启动新创建的线程
    thread.start()
    # 打印主线程已经结束的消息
    print(f'Threading {threading.current_thread().name} is ended')

'''
输出
Threading MainThread is running
Threading Thread-1 is runningThreading MainThread is ended

Threading Thread-1 sleep 1s
Threading Thread-2 is runningThreading MainThread is ended

Threading Thread-2 sleep 5s
Threading Thread-1 is ended
Threading Thread-2 is ended
'''

(四)线程等待

# 导入 threading 和 time 模块
import threading, time
# 定义一个名为 target 的函数,它接受一个参数 second
def target(second):
    # 打印当前线程的名称和一条开始运行的消息
    print(f'Threading {threading.current_thread().name} is running')
    # 打印当前线程将要休眠的秒数
    print(f'Threading {threading.current_thread().name} sleep {second}s')
    # 使线程休眠指定的秒数
    time.sleep(second)
    # 打印当前线程结束的消息
    print(f'Threading {threading.current_thread().name} is ended')

# 主线程退出,子线程才退出 会出问题
print(f'Threading {threading.current_thread().name} is running')

t = []
# 创建一个循环,循环值为列表 [1, 5] 中的元素
for i in [1, 5]:
    # 创建一个新的线程对象,目标函数是 target,参数是列表中的当前元素
    thread = threading.Thread(target=target, args=[i])
    t.append(thread)
    # 启动新创建的线程
    thread.start()
    # 打印主线程已经结束的消息
for i in t:#这里面是线程1和线程2,主线程在外边
    i.join() # 作用 阻塞下
print(f'Threading {threading.current_thread().name} is ended')

(五)线程池

线程池,是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。

# 导入 ThreadPoolExecutor 类,这个类是 concurrent.futures 模块提供的一个高层接口
# 用于异步执行使用线程的调用
from concurrent.futures import ThreadPoolExecutor
# 打印出传入的 UR
def crawl(url):
    print(url)

if __name__ =='__main__':
    base_url ='https://jobs.51job.com/pachongkaifa/p{}'
    # 使用with 语句和 ThreadPoolExecutor(10) 创建一个可以容纳 10 个线程的线程池。
    # with 语句的上下文管理特性确保线程池在执行完毕后会被正确关闭。
    with ThreadPoolExecutor(10) as f:
        # 创建1到14 14个数字表示页码
        for i in range(1,15):
            # 使用f.submit(crawl, url=base_url.format(i))提交一个任务给线程池
            # submit方法安排执行函数crawl,并传入格式化后的URL作为参数
            f.submit(crawl,url=base_url.format(i))

多线程采集实例--采集王者荣耀皮肤图片


'''
头像地址
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/537/537-smallskin-3.jpg'
皮肤地址
'https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/537/537-bigskin-3.jpg'
露娜
https://game.gtimg.cn/images/yxzj/img201606/heroimg/146/146.jpg
	{"ename": 146,
	"cname": "露娜",
	"id_name": "luna",
	"title": "月光之女",
	"new_type": 0,
	"hero_type": 1,
	"hero_type2": 2,
	"skin_name": "月光之女|哥特玫瑰|绯红之刃|紫霞仙子|一生所爱",
	"moss_id": 3934}

'''
import requests
import os
import json
import threading
import time
from lxml import etree

h = []
s = time.time()
def pa(j):
    num = j['ename']
    name = j['cname']
    res2 = requests.get('https://pvp.qq.com/web201605/herodetail/{}.shtml'.format(num))
    res2_decode = res2.content.decode('gbk')

    _element = etree.HTML(res2_decode)
    # 获取皮肤名称
    element_img = _element.xpath('.//div[@class="pic-pf"]/ul/@data-imgname')
    name_img= element_img[0].split('|')
    # 输出格式如下
    # ['正义爆轰&0', '地狱岩魂&12', '无尽征程&1', '寅虎·御盾&93']
    len1 = len(name_img)
    for i in range(0,10):
        res1 = requests.get('https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/{0}/{0}-bigskin-{1}.jpg'.format(num,i+1))
        if res1.status_code==200:
            try:
                aa = name_img[i].find('&')
                bb = name_img[i][:aa]
            except Exception as e:
                print(e)
            # 返回 如正义爆轰
            res_img = res1.content #将图片转换成二进制方便存储
            a = 'D:/桌面/王者荣耀/'+str(name)
            b = 'D:/桌面/王者荣耀/'+str(name)+'/'+bb+'.jpg'
            if not os.path.exists('D:/桌面/王者荣耀/'):
                os.makedirs('D:/桌面/王者荣耀/')
            if not os.path.exists(a):
                print(f'正在创建{name}文件夹')
                os.mkdir(a)
            with open(b,'wb') as f:
                f.write(res_img)
                print(name,bb)
        else:
            break


def duo():
    resp = requests.get('https://pvp.qq.com/web201605/js/herolist.json')
    data = json.loads(resp.text)

    for j in data:
        t = threading.Thread(target=pa,args=(j,))
        t.start()
        h.append(t)
    for k in h:
        k.join()

if __name__ == '__main__':
    duo()
    g = time.time()
    print("用时:",g-s)

二 多进程

参考文档:https://docs.python.org/zh-cn/3/library/multiprocessing.html

进程内置方法

run()

表示进程活动的方法。你可以在子类中重载此方法。标准run()方法调用传递给对象构造函数的可调用对象作为目标参数(如果有),分别从args和kwargs 参数中获取顺序和关键字参数。

start()

启动进程活动。这个方法每个进程对象最多只能调用一次。它会将对象的 run()方法安排在一个单独的进程中调用.

join(timeout)

如果可选参数 timeout是 one (默认值),则该方法将阻赛,直到调用 oin 方法的进程终止,如果 timeout是一个正数,它最多会阻塞 timeout秒,请注意,如果进程终止或方法超时,则该方法返回 None 。检查进程的 xitcode 以确定它是否终止。一个进程可以被join 多次。进程无法iin自身,因为这会导致死锁。尝试在启动进程之前ioin进程是错误的。

name()

进程的名称。该名称是一个字符串,仅用于识别目的。它没有语义。可以为多个进程指定相同的名称。初始名称由构造器设定。如果没有为构造器提供显式名称,则会造一个形式为Process-N1:N2:..Nk的名称,其中每个Nk 是其父亲的第 N 个孩子。

is_alive()

返回进程是否还活着。粗略地说,从 start()方法返到子进程终止之前,进程对象仍处于活动状态。

daemon

daemon 进程在 Python 的 multiprocessing 模块中有特殊的含义。它是一个指示该进程是否是守护进程的布尔标志。在计算机科学中,守护进程(或守护线程)一般指在后台运行的进程(或线程),它独立于控制终端,并且周期性地执行某种任务或等待处理某些发生的事件。然而,在 Python 的 multiprocessing 模块中,守护进程有点不同。

当你在一个 multiprocessing.Process 对象上设置 daemon = True 时,这意味着:

  1. 该进程是守护进程:该进程的生命周期不应比其父进程长。这意味着,当父进程结束时,守护进程也会被终止(不管守护进程是否完成了它的工作)。守护进程通常用于不需要明确停止的任务,因为它们会随着父进程的结束而自动停止。

  2. 在守护进程退出时,它的子进程也会被终止:守护进程不能创建子进程,如果尝试创建那将会抛出异常。这是为了防止产生孤儿进程,即当守护进程被终止时,它的子进程仍在运行,但没有任何进程管理它们。

  3. 它们不是 Unix 守护进程:在 Unix 中,守护进程是一个在后台运行的服务进程,通常在系统启动时启动,并直到系统关闭时才终止。Python 的守护进程不是这样的服务进程,而只是普通的进程,只不过它的生命周期受到父进程的控制。

  4. 它们在父进程退出后不会被操作系统的任何初始化系统“收养”:这意味着守护进程不会继续在后台运行,一旦父进程结束,守护进程也就结束了。

  5. 被终止的守护进程不会有机会进行资源清理:例如,打开的文件不会被正确关闭,所以使用守护进程时要小心。

如果你想要一个进程在父进程结束后继续运行,那么你不应该将它设置为守护进程。守护进程的典型用例是作为某种形式的服务提供者,其中服务在父进程运行时保持活动,但不需要在父进程结束后保持运行。

除了 threading.Thread API,Process 对象还支持以下属性和方法

pid

返回进程ID。在生成该进程之前,这将是 None。

(一)进程样例

import multiprocessing

def progress(index):
    print(f'Process:{index}')

if __name__ == '__main__':
    for i in range(5):
        # 每循环一次,开启一个进程
        p = multiprocessing.Process(target=progress,args=(i,))
        p.start()

(二)进程等待

import multiprocessing

def progress(index):
    print(f'Process:{index}')

if __name__ == '__main__':
    processes = []
    for i in range(5):
        # 每循环一次,开启一个进程
        p = multiprocessing.Process(target=progress, args=(i,))
        p.start()
        processes.append(p)  # 将进程添加到列表中

    for p in processes:
        p.join()  # 等待所有进程完成

(三)进程池

from multiprocessing import Pool
import requests

def scrape(url):
    try:
        requests.get(url)
        print(f'URL- {url} -Scraped')
        requests.ConnectionError
        # 是在使用requests库进行HTTP请求时,如果在连接过程中遇到网络问题
        # (例如,DNS查询失败、拒绝连接等)时抛出的异常。
    except requests.ConnectionError :
        print(f'URL- {url} -not Scraped')

if __name__ == '__main__':
    pool = Pool(processes=3)
    urls = [
        'https://www.baidu.com',
        'http://www.meituan.com/',
        'http://blog.csdn.net/',
        'http://xxxyxxx.net'
    ]
    pool.map(scrape,urls)
    # for url in urls:
         # scrape(url)
    pool.close()

多进程爬取摩托信息

import requests
import multiprocessing
from multiprocessing import Pool
from lxml import etree
import pymysql
# maps1接收一个参数,然后根据X的值返回不同结果
# 1、如果X是一个非空序列(列表,元组,字符串等)返回序列第一个元素
# 2、如果X是一个空的序列或者None,返回X本身,此时是一个空的序列或None
maps1 = lambda x:x[0] if x else x  #通俗点 如果X不为空返回X[0],否则返回X
'''
xpath取值返回的是列表
如果使用[0]数据为空就会给程序报错
使用lambda表达式进行数据判断不为空才取值,为空就返回原值
'''
datas = []
def request(url):
    '''
    请求模块,负责网络请求
    '''
    headers = {
        'Cookie':'countsql=%5BS%5Fchexi%5Dwhere+1%3D1; fenyecounts=1218; '
                 'Hm_lvt_f0b29a0b9bbbbaf0f3027855bba2f05a=1703216256; '
                 'ASPSESSIONIDSESQSQAD=GDBEFCGCAPFOGEEOMJIAAIIB; '
                 'Hm_lpvt_f0b29a0b9bbbbaf0f3027855bba2f05a=170323319',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
    }
    try:
        res = requests.get(url,headers=headers)
        if res.status_code == 200:
            res.encoding = 'gb2312'
            parse(res.text)
            return res.text
        else:
            res.raise_for_status()
    except requests.RequestException as e:
        print(f'请求出错信息是{e}')


def parse_xpath(obj,tag):
    '''
    负责页面的数据解析工作
    '''
    # 将获得网页代码根据tag路径解析出想要的内容并返回
    html = etree.HTML(obj)
    text = html.xpath(tag)
    return text
def parse(res):
    '''
    总体业务:获取需要的数据
    '''
    url = '//ul[@class = "goods_list"]/li'
    items = parse_xpath(res,url)
    # html = etree.HTML(res)
    # items = html.xpath(url)
    for item in items:
        title = maps1(item.xpath('./p[@class="name"]/a/text()'))
        price = maps1(item.xpath('./p[@class="price_wrap"]//text()'))
        price = int(price[1:])
        # print({'品牌':title,'价格':price})
        # print('='*50)
        datas.append([title,price])
        for data in datas:
            save_date(data)
def mysql_conn():
    '''
    数据库连接模块
    '''
    db = pymysql.connect(host='localhost',user='root',password='123456',db='test1',port=3306)
    cuesor = db.cursor()
    return db,cuesor

def save_date(data):
    '''
    保存数据模块
    '''
    db,cursor = mysql_conn()
    try:
        sql = 'insert into moto(name,price) values(%s,%s)'
        cursor.execute(sql,(data[0],data[1]))
        db.commit()
    except Exception as e:
        print(f'出错信息{e}')
        db.rollback()
    finally:
        cursor.close()
        db.close()
def run():
    '''
    入口函数
    开启任务
    多任务从这里面出发
    '''
    import time
    s = time.time()
    url = 'https://www.2smoto.com/pinpai.asp'
    res = request(url)
    #获得总页数
   # # htmls = etree.HTML(res)
   # # html = maps1(htmls.xpath('.//div[@id="prolist"]/table//a[contains(text(),"尾页")]/@href'))[0]
   # # html = maps1(parse_xpath(res,'.//div[@id="prolist"]/table//a[contains(text(),"尾页")]/@href'))
    html = parse_xpath(res,'.//div[@id="prolist"]/table//a[contains(text(),"尾页")]/@href')
    if html:
        html = html[0]
        count = html.split('=')[-1]
        print(f'总共{count}页')
    else:
        print("没有获取到总页数")
    cpu_count = multiprocessing.cpu_count() #获取系统CPU数量
    print("CPU数量是:",cpu_count)
    pool = Pool(processes=cpu_count) # 创建进程数量等于cpu个数的进程池
    for i in range(1,int(count)+1):
        url = 'https://www.2smoto.com/pinpai.asp?ppt=&slx=0&skey=&page={}'.format(i)
        #开启多任务 每一条进程处理1个页面的数据
        pool.apply_async(request,(url,))
    pool.close()  #关闭进程池,关闭之后,不能再向进程池里面添加进程
    pool.join()  # 当进程池中所有的进程执行完毕后,主进程才能执行
    print(f'程序耗时{time.time()-s}s')

if __name__ == '__main__':
    run()

三 异步协程

        我们知道爬虫是 I/O(输入输出) 密集型任务,比如如果我们使用 requests 库来爬某个站点,发出个请求之后,程序必须要等待网站返回响应之后才能接着运行,而在等待响应的过程中,整个爬虫程序是一直在等待的,实际上没有做任何的事情,对于这种情况我们有没有优化方案呢?

协程

在编程中,协程(Coroutine)是一种比线程更细粒度的计算运行单位,它是协作式多任务的子程序,可以在任何位置暂停或开始执行。与传统的多线程或多进程模型相比,协程提供了一种不同的并发执行机制。

以下是一些关键点来帮助理解协程:

  1. 自主性:协程与传统的函数调用不同,它们有自己的执行状态,包括调用位置、内部变量的状态和控制流程。这种状态在协程暂停时得以保留,在下一次恢复执行时可以使用。

  2. 协作式调度:协程执行可以在任意位置暂停,并在稍后恢复。它们不是被操作系统内核管理,而是完全由程序和程序员控制。这种暂停和恢复通常是显示的,通过关键字实现,例如 Python 中的 yieldawait

  3. 低开销:协程的开销比线程小得多,因为它们不需要线程的上下文切换。因此,创建数千甚至数百万个协程在资源上是可行的,而同样数量的线程可能会耗尽系统资源。

  4. 非抢占式:协程是非抢占式的,意味着它们不会像线程那样被操作系统强制从CPU上下文切换出去。相反,协程必须显式地交出控制权,这通常发生在 I/O 操作或通过特定的协程切换函数时。

基本概念

异步

为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。

例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确定

同步

不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,我们称这些程序单元是同步执行的。

阻塞

阻塞状态指程序未得到所需计算资源时被挂起的状态。

程序在等待某个操作完成期间,自身无法继续处理其他的事情,则称该程序在该操作上是阻塞的。

非阻塞

程序在等待某操作过程中,自身不被阻塞,可以继续处理其他的事情,则称该程序在该操作上是非阻塞的

同步/异步关注的是消息通信机制(synchronous communication/asynchronous communication)。阻塞/非阻塞关注的是程序在等待调用结果(消息,返回值)【是否有可用的资源】时的状态

同步执行流程图

异步执行流程图

(一)概念

使用之前需要先安装aiohttp库

pip install aiohttp

aiohttp 是一个基于 asyncio 的异步 HTTP 客户端/服务端框架,它允许你编写异步代码来处理 HTTP 请求。与传统的同步框架(如 Flask 或 Django)不同,aiohttp 适用于需要处理大量并发 HTTP 连接的情况,通过异步 I/O 编程模型提高性能。它既提供了服务端,又提供了客户端。其中我们用服务端可以搭建一个支持异步处理的服务器。
asvnc用来声明一个函数为异步函数
awat 用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂起,去执行其他的异步程序

(二)同步
import time
import httpx

def main():
    with httpx.Client() as client:
        for i in range(50):
            res = client.get('https://www.example.com')
            print(f'第{i+1}次请求,响应状态码:{res.status_code}')

if __name__ == '__main__':
    start = time.time()
    main()
    end =time.time()
    print(f'同步发送50次请求,耗时{start-end}秒')
(三)异步概念

异步函数是在现代编程中处理并发执行的一种方法,特别是在涉及到输入/输出(I/O)操作,如网络请求、文件系统操作等,这些操作往往是应用程序中延迟最大的部分。异步函数允许程序在等待这些长时间操作的过程中释放执行线程,以便可以处理其他任务。

在 Python 中,异步函数通常是指使用 async def 语法定义的函数。这些函数在调用时不会立即执行,而是返回一个协程对象。为了执行协程对象,你必须将它排入事件循环中。

import asyncio
import time
import httpx
async def req(client,i):
    res = await client.get('https://www.example.com')
    print(f'第{i+1}次请求,响应状态码:{res.status_code}')
    return res
async def main():
    async with httpx.AsyncClient() as client:
        task_lisk = []
        for i in range(50):
            res = req(client,i)
            task = asyncio.create_task(res)
            task_lisk.append(task)
        await asyncio.gather(*task_lisk)
if __name__ =='__main__':
    start = time.time()
    asyncio.run(main())
    end = time.time()
    print(f'异步发送50次请求,耗时{end-start}')

在这个例子中,req是一个异步函数,它使用await关键字暂停其执行,等待client.get(‘http://www/example.com’)执行完成。这允许其他协程在此期间运行,提高程序整体运行效率。

要运行这个异步函数,你需要使用 asyncio.run() 或其他机制来“驱动”事件循环

异步函数的关键优点包括:

  • 非阻塞行为:异步函数在遇到 await 时会暂停其执行,这让其他函数或协程可以在同一线程中运行。
  • 提高吞吐量:在 I/O 密集型应用程序中,可以同时处理多个 I/O 操作,这可以显著提高程序的吞吐量。
  • 改善用户体验:在 Web 应用程序中,异步函数可以提供更快的响应时间,从而改善用户体验。

在使用异步函数时需要注意:

  • 必须在支持异步操作的环境中使用异步函数,例如在由 asyncio 库提供的事件循环中。
  • 异步函数中的 await 表达式应该用于等待另一个异步函数或返回 awaitable 对象的操作(例如,由 asyncio 库提供的 I/O 函数)。
  • 编写异步代码需要一种不同的思维方式,因为不是所有的库都能在异步环境中工作,所以你可能需要寻找专门为异步编程设计的库。

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

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

相关文章

持续集成交付CICD:Jira 远程触发 Jenkins 实现更新 GitLab 分支

目录 一、实验 1.环境 2.GitLab 查看项目 3.Jira新建模块 4. Jira 通过Webhook 触发Jenkins流水线 3.Jira 远程触发 Jenkins 实现更新 GitLab 分支 二、问题 1.Jira 配置网络钩子失败 2. Jira 远程触发Jenkins 报错 一、实验 1.环境 (1)主机 …

uniapp自定义头部导航怎么实现?

一、在pages.json文件里边写上自定义属性 "navigationStyle": "custom" 二、在对应的index页面写上以下&#xff1a; <view :style"{ height: headheight px, backgroundColor: #24B7FF, zIndex: 99, position: fixed, top: 0px, width: 100% …

STM32——CAN协议

文章目录 一.CAN协议的基本特点1.1 特点1.2 电平标准1.3 基本的五个帧1.4 数据帧 二.数据帧解析2.1 帧起始和仲裁段2.2 控制段2.3 数据段和CRC段2.4 ACK段和帧结束 三.总线仲裁四.位时序五.STM32CAN控制器原理与配置5.1 STM32CAN控制器介绍5.2 CAN的模式5.3 CAN框图 六 手册寄存…

Hago 的 Spark on ACK 实践

作者&#xff1a;华相 Hago 于 2018 年 4 月上线&#xff0c;是欢聚集团旗下的一款多人互动社交明星产品。Hago 融合优质的匹配能力和多样化的垂类场景&#xff0c;提供互动游戏、多人语音、视频直播、 3D 虚拟形象互动等多种社交玩法&#xff0c;致力于为用户打造高效、多样、…

P4 音频知识点——PCM音频原始数据

目录 前言 01 PCM音频原始数据 1.1 频率 1.2 振幅&#xff1a; 1.3 比特率 1.4 采样 1.5 量化 1.6 编码 02. PCM数据有以下重要的参数&#xff1a; 采样率&#xff1a; 采集深度 通道数 ​​​​​​​ PCM比特率 ​​​​​​​ PCM文件大小计算&#xff1a; ​…

计算机毕业设计 基于SpringBoot的房屋租赁管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

HTML---网页布局

目录 文章目录 一.常见的网页布局 二.标准文档流 标准文档流常见标签 三.display属性 四.float属性 总结 一.常见网页布局 二.标准文档流 标准文档流常见标签 标准文档流的组成 块级元素<div>、<p>、<h1>-<h6>、<ul>、<ol>等内联元素<…

Hadoop入门学习笔记——一、VMware准备Linux虚拟机

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.…

机密计算容器前沿探索与 AI 场景应用

作者&#xff1a;壮怀、朱江云 企业与个人对数据隐私保护日益关切&#xff0c;从数据&#xff0c;网络的可信基础设施扩展到闭环可信的计算基础设施&#xff0c;可信的计算&#xff0c;存储&#xff0c; 网络基础设施必定成为云计算的标配。 机密计算技术应运而生&#xff0c;…

Python遥感影像深度学习指南(1)-使用卷积神经网络(CNN、U-Net)和 FastAI进行简单云层检测

【遥感影像深度学习】系列的第一章,Python遥感影像深度学习的入门课程,介绍如何使用卷积神经网络(CNN)从卫星图像中分割云层 1、数据集 在本项目中,我们将使用 Kaggle 提供的 38-Cloud Segmentation in Satellite Images数据集。 该数据集由裁剪成 384x384 (适用…

AWD认识和赛前准备

AWD介绍 AWD: Attack With Defence, 北赛中每个队伍维护多台服务器&#xff0c;服务器中存在多个漏洞&#xff0c;利 用漏洞攻击其他队伍可以进行得分,修复漏洞可以避免被其他队伍攻击失分。 一般分配Web服务器&#xff0c;服务器(多数为Linux)某处存在flag(一般在根目录下)&am…

在Excel中,如何简单快速地删除重复项,这里提供详细步骤

当你在Microsoft Excel中使用电子表格时&#xff0c;意外地复制了行&#xff0c;或者如果你正在制作其他几个电子表格的合成电子表格&#xff0c;你将遇到需要删除的重复行。这可能是一项非常无脑、重复、耗时的任务&#xff0c;但有几个技巧可以让它变得更简单。 删除重复项 …

【MybatisPlus快速入门】(3)SpringBoot整合MybatisPlus 之 Lombok插件安装及MybatisPlus分页代码示例

目录 1.Lombok1.1 步骤1:添加lombok依赖 2.2 步骤2:安装Lombok的插件1.3 步骤3:模型类上添加注解2 分页功能2.1 步骤1:调用方法传入参数获取返回值2.2步骤2:设置分页拦截器2.3 步骤3:运行测试程序 之前我们已学习MyBatisPlus在代码示例与MyBatisPlus的简介&#xff0c;在这一节…

Could not resolve com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.28.

1、首先进入阿里云maven仓库&#xff0c;在搜索栏输入无法下载的依赖名称&#xff0c;查询现有版本号&#xff0c;可以看到这里有2.9.34。 2、在build.gradle(Project)的buildscript闭包下替换为阿里云maven仓库&#xff1a; maven { url https://www.jitpack.io } maven { u…

在k8s中使用Helm安装harbor并将Chart推送到私有仓库harbor

使用Helm安装harbor并将Chart推送到私有仓库harbor 注意&#xff1a;如果你的harbor是之前docker-compose安装的&#xff0c;还需要额外做一个动作&#xff0c;让它支持chart docker-compose stop ./install.sh --with-chartmuseum1&#xff09;下载harbor的chart包 Harbor的…

【经典LeetCode算法题目专栏分类】【第10期】排序问题、股票问题与TOP K问题:翻转对、买卖股票最佳时机、数组中第K个最大/最小元素

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

Docker - 镜像 | 容器 日常开发常用指令 + 演示(一文通关)

目录 Docker 开发常用指令汇总 辅助命令 docker version docker info docker --help 镜像命令 查看镜像信息 下载镜像 搜索镜像 删除镜像 容器命令 查看运行中的容器 运行容器 停止、启动、重启、暂停、恢复容器 杀死容器 删除容器 查看容器日志 进入容器内部…

CGAL的网格简化

1、介绍 曲面网格简化是减少曲面网格中使用的面数&#xff0c;同时尽可能保持整体形状、体积和边界的过程。它是细分法的反面。 这里提出的算法可以使用称为边折叠的方法简化任何有向2流形曲面&#xff0c;具有任意数量的连接组件&#xff0c;有或没有边界&#xff08;边界或孔…

体验一下 CodeGPT 插件

体验一下 CodeGPT 插件 0. 背景1. CodeGPT 插件安装2. CodeGPT 插件基本配置3. (可选)CodeGPT 插件预制提示词原始配置(英文)4. CodeGPT 插件预制提示词配置(中文)5. 简单验证一下 0. 背景 看到B站Up主 “wwwzhouhui” 一个关于 CodeGPT 的视频&#xff0c;感觉挺有意思&#…

leetcode 268. 丢失的数字(优质解法)

链接&#xff1a;268. 丢失的数字 代码: class Solution {public int missingNumber(int[] nums) {int result0;for(int i0;i<nums.length;i){result^i;}for(int i0;i<nums.length;i){result^nums[i];}return result;} } 题解&#xff1a; 本题是比较简单的题&#xff…