• day7 xpath解析和多线程(爬虫结束,剩下多加练习吧)


    xpath解析和多线程

    4.1 xpath语法

    1. xpath基本概念

      1)树:整个html(xml)代码结构就是一个树结构

      2)节点:树结构中的每一个元素(标签)就是一个节点

      3)根节点(根元素): html或者xml最外面的那个标签(元素)

      4)节点内容:标签内容

      5)节点属性:标签属性

    2. xml数据格式

    xml和json一样,是一种通用的数据格式(绝大部分编程语言都支持的数据格式)

    xml是通过标签(元素)的标签内容和标签属性来保存数据的。

    """
    示例:保存一个超市信息
    1)json数据
    {
        "name": "永辉超市",
        "address": "肖家河大厦",
        "staffs":[
            {"name":"小明", "id": "s001", "position": "收营员", "salary": 4000},
            {"name":"小花", "id": "s002", "position": "促销员", "salary": 3500},
            {"name":"张三", "id": "s003", "position": "保洁", "salary": 3000},
            {"name":"李四", "id": "s004", "position": "收营员", "salary": 4000},
            {"name":"王五", "id": "s005", "position": "售货员", "salary": 3800}
        ],
        "goodsList":[
            {"name": "泡面", "price": 3.5, "count": 120, "discount":0.9},
            {"name": "火腿肠", "price": 1.5, "count": 332, "discount":1},
            {"name": "矿泉水", "price": 2, "count": 549, "discount":1},
            {"name": "面包", "price": 5.5, "count": 29, "discount":0.85}
        ]
    }
    
    xml数据:
    
        
            
                小明
                收营员
                4000
            
            
                小花
                促销员
                3500
            
            
                张三
                保洁
                3000
            
            
                李四
                收营员
                4000
            
            
                王五
                售货员
                3800
            
        
    
        
            
                泡面
                3.5
                120
            
            
                火腿肠
                1.5
                332
            
            
                矿泉水
                2
                549
            
            
                面包
                5.5
                29
            
        
    
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    1. xpath语法

    1)创建树结构获取树的根节点

    etree.XML(xml数据)

    etree.HTML(html数据)

    from lxml import etree
    f = open('files/data.xml', encoding='utf-8')
    root = etree.XML(f.read())
    f.close()
    
    • 1
    • 2
    • 3
    • 4

    2)根据xpath获取指定标签

    节点对象.xpath(路径) - 返回路径对应的所有的标签,返回值是列表,列表中的元素是标签对象(节点

    对象)

    路径的写法:

    1. 绝对路径: 用"/"开头的路径 - /标签在树结构中的路径 (路径必须从根节点开始写)
    2. 相对路径: 路径开头用"."标签当前节点(xpath前面是谁,'.'就代表谁), "…"表示当前节点的上层节点
    3. 全路径: 用"//"开头的路径 - 在整个树中获取标签

    注意:绝对路径和全路径的写法以及查找方式和是用谁去点的xpath无关

    result = root.xpath('/supermarket/staffs/staff/name/text()')
    print(result)
    
    result = root.xpath('./staffs/staff/name/text()')
    print(result)
    
    staff1 = root.xpath('./staffs/staff')[0]  # 获取第一个员工对应的staff标签
    result = staff1.xpath('./name/text()')
    print(result)  # ['小明']
    
    result = staff1.xpath('../staff/name/text()')
    print(result)  # ['小明', '小花', '张三', '李四', '王五']
    
    result = root.xpath('//name/text()')
    print(result)
    
    result = staff1.xpath('//goods/name/text()')
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3)获取标签内容

    节点对象.xpath(获取标签的路径/text()) - 获取指定路径下所有标签的标签内容

    result = root.xpath('//position/text()')
    print(result)
    
    • 1
    • 2

    4)获取标签属性值

    节点对象.xpath(获取标签的路径/@属性名)

    result = root.xpath('/supermarket/@name')
    print(result)  # ['永辉超市']
    
    result = root.xpath('//staff/@id')
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5)谓语(条件)

    a. 位置相关谓语

    [N] - 第N个

    [last()] - 最后一个

    [last()-N]; [last()-1] - 倒数第2个 、 [last()-2] - 倒数第3个

    [position()>N]、[position()=N]、[position()<=N]

    result = root.xpath('//staff[1]/name/text()')
    print(result)  # ['小明']
    
    result = root.xpath('//staff[last()]/name/text()')
    print(result)  # ['王五']
    
    result = root.xpath('//staff[last()-1]/name/text()')
    print(result)  # ['李四']
    
    result = root.xpath('//staff[position()<3]/name/text()')
    print(result)  # ['小明', '小花']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    b.属性相关谓语

    [@属性名=属性值] - 指定属性是指定值的标签

    [@属性名] - 拥有指定属性的标签

    result = root.xpath('//staff[@class="c1"]/name/text()')
    print(result)
    
    result = root.xpath('//staff[@id="s003"]/name/text()')
    print(result)
    
    result = root.xpath('//goods[@discount]/name/text()')
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    c.子标签内容相关谓语 - 根据子标签的内容来筛选标签

    [子标签名>数据]

    [子标签名<数据]

    [子标签名>=数据]

    [子标签名<=数据]

    [子标签名=数据]

    result = root.xpath('//goods[price=2]/name/text()')
    print(result)
    
    • 1
    • 2

    6)通配符 - 写路径的时候用*来表示所有标签或者所有属性

    result = root.xpath('//staff[1]/*/text()')
    print(result)
    
    # *[@class="c1"]  == .c1
    result = root.xpath('//*[@class="c1"]/name/text()')
    print(result)
    
    result = root.xpath('//goods[@*]/name/text()')
    print(result)
    
    result = root.xpath('/supermarket/@*')
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    7)若干路径 - |

    路径1|路径2 - 同时获取路径1和路径2的内容

    result = root.xpath('//goods/name/text()|//staff/position/text()')
    print(result)
    
    • 1
    • 2

    4.2 xpath解析豆瓣

    import requests
    from lxml import etree
    import csv
    def get_html():
        headers = {
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
        }
        url='https://movie.douban.com/top250'
        response = requests.get(url=url,headers=headers)
        return response.text
    
    def get_data(html:str):
        root = etree.HTML(html)
        name_list = root.xpath('//ol[@class="grid_view"]/li/div/div/div/a/span[1]/text()')
        # plot_list = root.xpath('//div[@class="bd"]/p/text()')
        comment_list = root.xpath('//span[@class="rating_num"]/text()')
        discourse_list = root.xpath('//span[@class="inq"]/text()')
        comment_count = root.xpath('//div[@class="star"]/span[4]/text()')
        all_data = list(map(lambda i1,i2,i3,i4:[i1,i2,i3,i4],name_list,comment_list,comment_count,discourse_list))
        with open('files/电影.csv','w',encoding='utf-8',newline='') as f:
            writer = csv.writer(f)
            writer.writerow(['电影名称','评分','评论数','简介'])
            writer.writerows(all_data)
    
    
    if __name__ == '__main__':
        data = get_html()
        get_data(data)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4.3 多线程

    1. 基本理论

    1)进程和线程

    进程: 一个正在运行的应用程序就是一个进程,每个进程均运行在其专门且受保护的内存空间中

    线程: 线程是进程执行任务的基本单元(一个进程中的任务都是在线程中执行的)

    进程就是车间,线程就是车间里面的工人。

    一个进程中默认有一个线程,这个线程叫主线程。

    2)线程的特点

    如果在一个线程中执行多个任务,任务是串行执行的。

    (当一个程序中有很多个任务的时候,如果只有一个线程,那么程序的执行效率会很低)

    3)多线程

    一个进程中有多个线程就是多线程。

    多线程执行任务的时候,多个任务可以同时(并行)执行。

    4)多线程原理

    一个cpu同一时间只能调度一个线程,多线程其实是cpu快速的在多个线程之间进行切换,造成多个线程

    同时执行的假象。

    (提高cpu利用率)

    1. Python使用多线程的方法

    一个进程默认只有一个线程,这个线程叫主线程,主线程以外的线程都叫子线程。

    Python程序中如果需要子线程,必须创建线程类(Thread)的对象。

    from threading import Thread
    from time import sleep
    from datetime import datetime
    
    
    def download(name):
        print(f'{name}开始下载:{datetime.now()}')
        sleep(2)
        print(f'{name}下载结束:{datetime.now()}')
    
    
    if __name__ == '__main__':
        # 情况1:在一个线程(主线程)中下载3个电影
        # download('肖生克救赎')
        # download('霸王别姬')
        # download('阿甘正传')
    
        # 情况2:使用3个子线程分别下载3个电影
        # 1)创建线程对象
        """
        线程对象 = Thread(target=函数, args=元组)
        a.函数  - 可以是普通函数函数名,也可以是匿名函数。这个函数就是需要子线程中执行的任务。
        b.元组  - 元组中的元素就是在子线程中调用target对应的函数的时候需要的参数
        """
        t1 = Thread(target=download, args=('肖生克救赎',))
        t2 = Thread(target=download, args=('霸王别姬',))
        t3 = Thread(target=download, args=('阿甘正传',))
    
        # 2)启动线程 - 让子线程调用对应的函数
        t1.start()
        t2.start()
        t3.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.4 多线程获取豆瓣电影

    import requests
    from lxml import etree
    from threading import Thread
    from datetime import datetime
    import csv
    
    def get_html(page):
        headers = {
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
        }
        url=f'https://movie.douban.com/top250?start={page}&filter='
        response = requests.get(url=url,headers=headers)
        get_data(response.text)
    
    def get_data(html:str):
        root = etree.HTML(html)
        name_list = root.xpath('//ol[@class="grid_view"]/li/div/div/div/a/span[1]/text()')
        # plot_list = root.xpath('//div[@class="bd"]/p/text()')
        comment_list = root.xpath('//span[@class="rating_num"]/text()')
        discourse_list = root.xpath('//span[@class="inq"]/text()')
        comment_count = root.xpath('//div[@class="star"]/span[4]/text()')
        all_data = list(map(lambda i1,i2,i3,i4:[i1,i2,i3,i4],name_list,comment_list,comment_count,discourse_list))
        print(all_data)
        print(f'下载完成:{datetime.now()}')
        with open('files/电影.csv','a',encoding='utf-8',newline='') as f:
            writer = csv.writer(f)
            writer.writerow(['电影名称','评分','评论数','简介'])
            writer.writerows(all_data)
    
    
    if __name__ == '__main__':
        for page in range(0,256,25):
            t = Thread(target=get_html,args=(page,))
            t.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.5 线程的等待(阻塞)

    子线程对象.join() - 阻塞当前线程直到指定子线程任务完成

    示例1: 三个电影都下载结束后打印"全部下载完成"
    t1.start()
    t2.start()
    t3.start() 
    t1.join()
    t2.join()
    t3.join()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    多线程爬取下载豆瓣数据

    import requests
    import csv
    from bs4 import BeautifulSoup
    from threading import Thread
    from time import sleep
    from datetime import datetime
    
    f = open('file/多线程豆瓣.csv', 'a', encoding='utf-8', newline='')
    writer = csv.writer(f)
    writer.writerow(['名字', '分数', '评论人数', '介绍'])
    
    
    def get_html(url):
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
        }
        response = requests.get(url=url, headers=headers)
        return response.text
    
    
    def download(page, url: str):
        print(f'第{page}页开始下载:{datetime.now()}')
        html = get_html(url)
        soup = BeautifulSoup(html, 'lxml')
        all_li = soup.select('.grid_view>li')
        new_list = []
        for li in all_li:
            name = li.select_one('.item>.info>.hd .title').text
            scores = li.select_one('.bd>.star>.rating_num').text
            numbers = li.select_one('.item>.info>.bd>.star>span:nth-child(4)').text
            info = li.select_one('.bd>.quote')
            if info:
                info = li.select_one('.bd>.quote').text.strip()
            else:
                info=''
            new_list.append([name, scores, numbers, info])
        print(new_list)
        writer.writerows(new_list)
    
        print(f'第{page}页下载完成:{datetime.now()}')
    
    
    if __name__ == '__main__':
        ts = []
        for i in range(0, 51, 25):
            url = f'https://movie.douban.com/top250?start={i}&filter='
            t = Thread(target=download, args=(i, url))
            ts.append(t)
            t.start()
    
    
        # 等待所有的子线程任务都结束后再接着执行
        for x in ts:
            x.join()
        f.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    4.6 线程池

    1. 线程池 - 管理多个线程的工具

    线程池工作原理:先创建指定个数的线程,然后添加多个任务

    (任务数量>线程数量),

    ​ 让线程池中的线程去执行添加的所有任务,直到所有任务

    都执行完(线程池中的每个线程可能会执行多个任务)

    # 方案1:直接使用多线程下载1000个电影
    # num = 0
    # for _ in range(10):
    #     ts = []
    #     for x in range(100):
    #         num += 1
    #         t = Thread(target=download, args=(f'电影{num}',))
    #         ts.append(t)
    #         t.start()
    #     for x in ts:
    #         x.join()
    
    # 方案2:使用线程池下载1000个电影
    # 1. 创建线程池
    # ThreadPoolExecutor(线程数最大值)
    pool = ThreadPoolExecutor(3)
    
    # 2. 添加任务
    # 1) 一次添加一个任务: submit(函数, 实参1, 实参2, 实参3,...)
    # 注意:实参的数量由前面的函数在调用的时候需要的实参来决定
    pool.submit(download, '肖生克的救赎')
    pool.submit(download, '霸王别姬')
    
    # 2)同时添加多个任务: map(函数, 参数对应的序列)
    # 注意:使用map添加多个任务的时候,任务对应的函数必须是有且只有一个参数的函数
    pool.map(download, ['V字仇杀队', '恐怖游轮', '沉默的羔羊'])
    
    # 3. 关闭线程池
    # 线程池关闭后无法再添加新的任务,并且会阻塞当前线程等待整个线程池的任务都完成
    pool.shutdown()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    线程池爬豆瓣

    import requests
    from bs4 import BeautifulSoup
    import csv
    from concurrent.futures import ThreadPoolExecutor
    
    def get_net_data(url: str):
        headers = {
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
        }
        proxies = {
            'http': '121.206.253.171:4512',
            'https': '121.206.253.171:4512'
        }
        response = requests.get(url, headers=headers, proxies=proxies)
        # return response.text
        analysis_data(response.text)
    
    
    def analysis_data(html: str):
        # 解析数据
        soup = BeautifulSoup(html, 'lxml')
        all_film_div = soup.select('.grid_view>li>.item')
        all_data = []       # 保存每一页所有的电影
        for div in all_film_div:
            rank = div.select_one('.pic>em').text
            name = div.select_one('.title').text
            info = div.select_one('.bd>p').text.strip().split('\n')[-1].strip()
            # time, country, category = info.split('/')
            info_list = info.split('/')
            time = info_list[0]
            country = info_list[-2]
            category = info_list[-1]
            score = div.select_one('.rating_num').text
            comment_count = div.select('.star>span')[-1].text[:-3]
            intro_span = div.select_one('.inq')
            if intro_span:
                intro = intro_span.text
            else:
                intro = ''
            all_data.append([int(rank), name, score, time.strip(), country.strip(), category.strip(), comment_count, intro])
    
        films.append(all_data)
    
    
    if __name__ == '__main__':
        films = []      # 保存整个网站所有的电影
        # [
        #     [[26, ], [], [], ...],
        #     [[1], [], [],  ...],
        #     [[23], [], [], ...],
        # ]
        f = open('file/电影.csv', 'w', encoding='utf-8', newline='')
        writer = csv.writer(f)
        writer.writerow(['排名', '电影名称', '评分', '上映时间', '国家', '类型', '评论数', '简介'])
    
        pool = ThreadPoolExecutor(10)
        for page in range(0, 251, 25):
            url = f'https://movie.douban.com/top250?start={page}&filter='
            pool.submit(get_net_data, url)
    
        pool.shutdown()
        # 优化方案:按页排序
        films.sort()
        for x in films:
            writer.writerows(x)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    五 常见指令

    cd  -  打开文件夹,后面直接拖过去
    cd..  -  回到上一层
    在打开之前要先切盘,window电脑:  -  盘名:
    dir  -  查看文件夹内容ls(mac)
    
    1)创建虚拟环境:
    python    -m    venv    虚拟环境名
    python3    -m   venv   虚拟环境名
    2)切换环境(激活环境)
    虚拟环境目录\Scripts\activate.bat
    退出虚拟环境 deactivate
    
    1)pip - python包管理工具
    2)pip install 第三方库名称  - 下载并且安装指定的第三方库
    pip install 第三方库名称 -i 第三方库镜像地址
    pip install 第三方库名称==版本号 -i 第三方库镜像地址
    pip install 第三方库名称1 第三方库名称2 第三方库名称3
    3)pip install -r 依赖文件  -  批量安装
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    Python国内镜像地址:
    1.阿里云:https://mirrors.aliyun.com/pypi/simple/
    2.豆瓣:https://pypi.douban.com/simple/
    3.清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/
    4.中国科学技术大学 https://pypi.mirrors.ustc.edu.cn/simple/
    5.华中理工大学:https://pypi.hustunique.com/
    6.山东理工大学:https://pypi.sdutlinux.org/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    pip freeze > yilai.txt
    
    • 1
    常见的指令操作 
    
    执行指令的工具: Windows - 命令提示符(cmd) 、Mac - 终端
    
    1. 运行python程序:  -  运算程序的计算机必须先安装python环境   
    
    win:    python  py文件路径     
    mac:  python3  py文件路径     
    
    
    2. 进入文件夹:  cd
         cd  文件夹相对路径、文件夹绝对路径
    
        注意:如果是windows操作系统,cd操作如果要跨盘需要先切盘,然后再cd
             切盘方法:C:、E:、D:
    
    3. 查看当前文件夹的内容
       win:  dir
       Mac:ls
    
    
    4. 用指令创建虚拟环境
         第一步:找到一个用来放虚拟环境的文件夹
         第二步:通过cd指令进入到存放虚拟环境的文件夹中
    
         第三步:创建虚拟环境
                python    -m    venv    虚拟环境名
                python3    -m   venv   虚拟环境名
    
         第四步:激活虚拟环境
               (mac) source  虚拟环境目录/bin/activate
               (windows)  虚拟环境目录\ Scripts\activate.bat
    
       第五步:退出虚拟环境
                deactivate
    
    
    5.常用pip指令(pip - Python包管理工具)
       pip list      -     查看当前环境已经安装过的所有的第三方库
    
        pip install  第三方库名称       -     下载并且安装指定的第三方库
       pip install  第三方库名称 -i 镜像地址       -     在指定的镜像地址中下载安装
       pip install  第三方库名称==版本号   -i   镜像地址
    
       pip install  第三方库名称1   第三方库名称2  
    
       pip freeze > 依赖文件名     -  生成依赖文件 
       pip install -r 依赖文件路径        -     批量安装   
    
       pip uninstall 第三方库名称   -  卸载指定的第三方库
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    加油练习吧,下周是这个阶段最后一周了,期待全新的知识体系

  • 相关阅读:
    【pac文件】win10自动配置代理
    为 DevOps 战士准备的 Linux 命令
    java并发编程 ConcurrentLinkedQueue详解
    支持CT、MR三维后处理的医学PACS源码
    好用的考勤打卡APP
    vue3中withDefaults是什么
    Isito 入门(四):微服务可观测性
    【编程题】【Scratch二级】2019.09 制作蝙蝠冲关游戏
    正则表达式
    SpringMVC实战crud增删改查
  • 原文地址:https://blog.csdn.net/qq_59778168/article/details/126436181