Spider10多线程

1. b站数据爬虫

from selenium.webdriver import Chrome, ChromeOptions
import time
from bs4 import BeautifulSoup
from re import search
from concurrent.futures import ThreadPoolExecutor
import csv
from queue import Queue

options = ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})

# b = Chrome(options=options)
# b.get('https://www.bilibili.com/v/anime/serial/#/')
# time.sleep(1)
# print(b.page_source)

def get_total_page():
    b = Chrome(options=options)
    b.get('https://www.bilibili.com/v/anime/serial/#/')
    b.implicitly_wait(3)
    soup = BeautifulSoup(b.page_source, 'lxml')
    total_page = soup.select_one('.page-total>span').text
    total_num = int(search(r'共\s*(\d+)\s*页', total_page).group(1))
    return total_num


def get_one_page_data(page: str):
    global succeed_num
    global total_num

    # 分割页数和次数
    p, c = page.split('.')
    # 创建浏览器,打开对应的页面
    b = Chrome(options=options)
    url = f'https://www.bilibili.com/v/anime/serial/#/all/default/0/{p}/'
    b.get(url)
    b.implicitly_wait(3)

    # 解析数据
    try:
        soup = BeautifulSoup(b.page_source, 'lxml')
        all_li = soup.select('.vd-list-cnt>ul>li')
        page_data = []
        for li in all_li:
            title = li.select_one('.title').text
            play_url = 'https:' + li.select_one('.title').attrs['href']
            play_count = li.select_one('.v-info>span:nth-child(1)>span').text
            barrage_count = li.select_one('.v-info>span:nth-child(2)>span').text
            img_url = li.select_one('.lazy-img>img').attrs['src']
            if img_url:
                img_url = 'https:' + img_url
            page_data.append([title, play_count, barrage_count, img_url, play_url])
        writer.writerows(page_data)
        print(f'--------第{page}页写入完成-----------')

        # 统计任务成功的数量
        succeed_num += 1
        if succeed_num == total_num:
            q.put('end')
    except:
        # 如果解析出错
        c = int(c) - 1
        if c != 0:
            q.put(f'{p}.{c}')
        else:
            total_num -= 1
            if total_num == succeed_num:
                q.put('end')

    b.close()


if __name__ == '__main__':
    q = Queue()

    # 0. 准备文件
    writer = csv.writer(open('files/番剧.csv', 'w', encoding='utf-8', newline=''))
    writer.writerow(['标题', '播放量', '弹幕数', '图片地址', '播放地址'])
    # 1. 获取总页数
    # total_num = get_total_page()
    total_num = 10
    succeed_num = 0

    # 2.创建线程池,添加任务
    # '页数.次数'
    pool = ThreadPoolExecutor(3)
    pool.map(get_one_page_data, [f'{x}.3' for x in range(1, total_num + 1)])
    # get_one_page_data(1)

    while True:
        data = q.get()
        if data == 'end':
            break
        pool.submit(get_one_page_data, data)

2. 多进程

一个应用程序默认只有一个进程,一个进程中默认有一个线程。
多进程指的是一个应用程序有多个进程。

如果所有的任务是相同的任务就直接用一个进程中添加多个线程的方案来解决问题。
如果所有的任务可以分成两种或者多种任务,可以每一种任务对应一个进程(根据情况,每个进程中可以有多个线程)。

如果要加工1000个玩具 - 一个进程多个线程。
如果要加工800个玩具和400个蛋糕 - 两个进程,每个进程中多个线程。

如果希望应用程序中除了主进程以外有别的进程(子进程),就需要在程序中创建进程对象。

# 导入进程类
from multiprocessing import Process, current_process
from threading import current_thread, Thread
import time

def func1(x, y):
    time.sleep(1)
    print('子进程', current_process(), current_thread())
    # 在这个函数中创建的线程就是子进程中的线程
    t2 = Thread(target=func3)
    t2.start()


def func2():
    print('func2', current_process(), current_thread())


def func3():
    print('func3', current_process(), current_thread())


if __name__ == '__main__':

    print('主进程:', current_process(), current_thread())
    # 1.创建进程对象
    p = Process(target=func1, args=(10, 20))

    # 2. 启动线程
    p.start()

    # 3. 阻塞
    p.join()
    print('=============完成================')

    t1 = Thread(target=func2)
    t1.start()

3. 多进程数据通信

3.1 进程队列

不同进程中的数据不能直接共享。在一个进程中定义的全局变量不能在另外的进程中使用。

如果想要让一个或者多个数据在不同进程中进行传递,只有一个方法:使用进程队列。

3.2 进程队列的使用

1)创建队列对象

2)添加数据

3)将队列对象以参数的形式传递到子进程对应的函数中

4)获取数据

from threading import Thread
from multiprocessing import Process, Queue
# from queue import Queue       # 导入的队列不能在多进程中使用


def func1():
    global a
    print(f'func1使用a:', a)
    a = 200
    l1.append(300)


def func2(qt: Queue):
    # global b
    # print(f'func2使用a: {b}')
    # l2.append(300)
    print('子进程:', qt.get())
    qt.put('哈哈哈!')


if __name__ == '__main__':
    a = 100
    l1 = []

    # func1在主进程中调用
    t1 = Thread(target=func1)
    t1.start()
    t1.join()
    print('主进程的主线程:', a, l1)

    # 1. 进程队列
    # 不同进程中的数据不能直接共享。在一个进程中定义的全局变量不能在另外的进程中使用
    # 如果想要让一个或者多个数据在不同进程中进行传递,只有一个方法:使用进程队列
    # 2. 进程队列的使用
    # 1)创建队列对象
    q = Queue()

    # 2) 添加数据
    b = 300
    q.put(b)

    # 3)将队列对象以参数的形式传递到子进程对应的函数中
    p1 = Process(target=func2, args=(q,))
    p1.start()
    p1.join()
    # 4)获取数据
    print('主进程:', q.get())

4. 英雄联盟皮肤

import requests, os
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Process, Queue


def get_all_hero_id():
    url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
    res = requests.get(url)
    return [x['heroId'] for x in res.json()['hero']]


# 2. 获取每个英雄所有的皮肤地址
def get_skins(hero_id):
    url = f'https://game.gtimg.cn/images/lol/act/img/js/hero/{hero_id}.js'
    res = requests.get(url)
    data = res.json()
    # 获取英雄名称
    hero_name = data['hero']['name']
    # 创建英雄名称对应的文件夹
    if not os.path.exists(f'所有英雄/{hero_name}'):
        os.mkdir(f'所有英雄/{hero_name}')
        print(f'+++{hero_name}创建成功+++')
    skins = data['skins']
    # 把获取到的英雄皮肤保存到队列中,传递到子进程中
    queue.put(skins)


def download_one_skin(name, sk_name, img_url):
    response = requests.get(img_url)
    with open(f'所有英雄/{name}/{sk_name}.jpg', 'bw') as f:
        f.write(response.content)
        print(f'---------{sk_name}下载完成-------------')


def download_hero_skin(q: Queue):
    # 在子进程中创建线程池,同时下载多个皮肤
    p_pool = ThreadPoolExecutor(20)
    while True:
        data = q.get()
        if data == 'end':
            break
        for x in data:
            main_img = x.get('mainImg')
            if not main_img:
                continue
            name = x['heroName']
            sk_name = x['name']
            p_pool.submit(download_one_skin, name, sk_name, main_img)


if __name__ == '__main__':
    # 1. 在主进程的主线程中获取所有英雄的id
    ids = get_all_hero_id()

    # 3.创建子进程完成每个英雄的皮肤下载操作
    queue = Queue()         # 传递皮肤数据
    p1 = Process(target=download_hero_skin, args=(queue, ))
    p1.start()

    # 2. 在主进程中创建线程池获取英雄的信息
    pool = ThreadPoolExecutor(20)
    pool.map(get_skins, ids[:10])
    pool.shutdown()
    queue.put('end')

5. 常见的指令操作

执行指令的工具: Windows - 命令提示符(cmd) 、Mac - 终端

  1. 运行python程序: - 运算程序的计算机必须先安装python环境

​ win: python py文件路径

​ mac: python3 py文件路径

  1. 进入文件夹: cd

    cd 文件夹相对路径、文件夹绝对路径

    注意:如果是windows操作系统,cd操作如果要跨盘需要先切盘,然后再cd

​ 切盘方法:C:、E:、D:

  1. 查看当前文件夹的内容

    ​ win: dir

    ​ Mac:ls

  2. 用指令创建虚拟环境

    第一步:找到一个用来放虚拟环境的文件夹

    第二步:通过cd指令进入到存放虚拟环境的文件夹中

    第三步:创建虚拟环境

​ python -m venv 虚拟环境名

​ python3 -m venv 虚拟环境名

第四步:激活虚拟环境

​ (mac) source 虚拟环境目录/bin/activate

​ (windows) 虚拟环境目录\ Scripts\activate.bat

第五步:退出虚拟环境

​ deactivate

  1. 常用pip指令

    ​ pip list - 查看当前环境已经安装过的所有的第三方库

    ​ pip install 第三方库名称 - 下载并且安装指定的第三方库

    ​ pip install 第三方库名称 -i 镜像地址 - 在指定的镜像地址中下载安装

    ​ pip install -r 依赖文件 - 批量安装

    ​ pip freeze > 依赖文件名 - 生成依赖文件

​ pip uninstall 第三方库名称 - 卸载指定的第三方库

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值