实操案例:大量数据需要下载,线程池,进程池,协程哪种好呢?

发布时间:2024年01月20日

1. 需求

大量数据,每条数据都需要打开网站,使用Selenium进行网站操作,登录后,点击特定元素,点击下载按钮,每条数据都需要从网站上根据数据字段下载东西,打开的网站都是同一个网址。

2.线程池用法示例

当使用Selenium进行网站操作时,可以使用线程池或进程池来并发处理任务。下面是一个示例代码,演示如何使用线程池和Selenium来打开网站、登录、点击元素并下载文件:

import concurrent.futures
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def process_data(data):
    # 创建浏览器实例
    driver = webdriver.Chrome()

    # 打开网站
    driver.get("https://example.com")

    # 登录
    login_button = driver.find_element_by_id("login-button")
    login_button.click()

    username_input = driver.find_element_by_id("username")
    username_input.send_keys(data['username'])

    password_input = driver.find_element_by_id("password")
    password_input.send_keys(data['password'])

    submit_button = driver.find_element_by_id("submit-button")
    submit_button.click()

    # 等待登录成功后的元素加载完成
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "logged-in-element"))
    )

    # 点击特定元素
    element_to_click = driver.find_element_by_id("element-to-click")
    element_to_click.click()

    # 下载文件
    download_button = driver.find_element_by_id("download-button")
    download_button.click()

    # 关闭浏览器
    driver.quit()

# 假设有一个包含所有数据的列表
data_list = [
    {'username': 'user1', 'password': 'pass1'},
    {'username': 'user2', 'password': 'pass2'},
    ...
]

# 创建一个线程池
with concurrent.futures.ThreadPoolExecutor() as executor:
    # 使用线程池提交任务
    results = [executor.submit(process_data, data) for data in data_list]

    # 等待所有任务完成
    # 注意:如果有需要阻塞等待的情况,可以使用as_completed()方法逐个处理结果
    concurrent.futures.wait(results)

请注意,在使用Selenium进行网站操作时,需要根据具体情况选择合适的定位元素的方式,并确保页面中相应的元素可见和可交互。另外,根据实际需求可能需要进行异常处理和错误处理,以确保代码的稳定性和可靠性。

3.进程池用法示例

如果你需要使用Selenium来登录网站,并在每个数据字段上执行一系列动作后下载内容,可以按照以下步骤进行操作:

  1. 安装Selenium库和WebDriver。你可以使用pip命令来安装Selenium库,同时根据你要使用的浏览器,下载并配置相应的WebDriver。例如,如果你要使用Chrome浏览器,可以下载ChromeDriver,并将其添加到系统的PATH环境变量中。

  2. 导入必要的库和模块。在代码的开头,导入multiprocessingselenium和其他需要的库。

  3. 定义一个函数,用于处理每个数据字段的下载逻辑。在这个函数中,使用Selenium打开网站,执行登录操作,点击特定元素,并下载所需内容。你可以编写一个download_data函数,接收数据字段作为参数,在函数内部使用Selenium进行相关操作。请注意,为了实现并行化,每个函数都应该具有独立的WebDriver实例。

  4. 创建一个进程池,并指定进程数量。根据你的需求和系统资源,设置进程数量。

  5. 将数据字段列表划分为多个子列表,每个子列表包含一部分数据字段。

  6. 使用进程池的map()方法,将子列表中的每个数据字段传递给download_data函数进行下载。map()方法会自动将任务分配给进程池中的空闲进程,并行执行下载操作。

  7. 关闭进程池,并等待所有进程执行完毕。

下面是一个示例代码,演示了如何使用进程池、Selenium和WebDriver实现从网站上根据数据字段下载东西:

import multiprocessing
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def download_data(data):
    # 创建Chrome浏览器的选项对象
    chrome_options = Options()
    chrome_options.add_argument("--headless")  # 无头模式,不显示浏览器界面

    # 创建WebDriver对象
    driver = webdriver.Chrome(options=chrome_options)

    try:
        # 打开网页并进行登录等操作
        driver.get("http://example.com/login")
        # 在这里执行登录操作

        # 在特定元素上点击
        element = driver.find_element_by_xpath("//xpath/to/element")
        element.click()

        # 等待特定条件,确保页面加载完成
        # driver.implicitly_wait(10)  # 根据需要可以添加隐式等待

        # 在这里执行下载操作,根据数据字段获取下载链接等信息
        download_url = f"http://example.com/api/download?data={data}"
        driver.get(download_url)
        # 在这里进行下载操作

        print(f"Downloaded {data}.jpg")
    finally:
        # 关闭WebDriver对象
        driver.quit()

if __name__ == '__main__':
    # 假设有一组数据字段需要下载东西
    data_list = ['data1', 'data2', 'data3', 'data4', 'data5']

    # 创建进程池,根据 CPU 核心数量决定进程数量
    num_processes = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=num_processes)

    # 将数据字段列表划分为子列表
    chunk_size = len(data_list) // num_processes
    data_chunks = [data_list[i:i+chunk_size] for i in range(0, len(data_list), chunk_size)]

    # 使用进程池并行下载数据
    pool.map(download_data, data_chunks)

    # 关闭进程池
    pool.close()
    pool.join()

在这个示例中,我们使用了Selenium和WebDriver来实现对网站的操作。首先,我们导入了multiprocessingselenium库,并创建了一个download_data函数,其中包含了使用Selenium进行登录、点击特定元素和下载内容的逻辑。

download_data函数中,我们创建了一个Chrome浏览器的选项对象chrome_options,并设置了无头模式,即不显示浏览器界面。然后,创建了一个WebDriver对象driver,通过webdriver.Chrome()方法传入选项对象来创建Chrome浏览器的WebDriver实例。

download_data函数的主体部分,我们使用WebDriver对象执行了一系列操作,包括打开网页、登录、点击特定元素和下载内容。最后,我们关闭了WebDriver对象以释放资源。

在主程序部分,我们创建了一个进程池,并根据CPU核心数量设置进程数量。然后,将数据字段列表划分为多个子列表,并使用进程池的map()方法将每个子列表中的数据字段传递给download_data函数进行下载。

最后,我们关闭进程池,并等待所有进程执行完毕。

4.用协程示例

使用协程(Coroutine)可以实现异步任务的并发执行,以提高程序的效率。下面是一个使用协程演示的示例代码,其中包含了使用Selenium登录网站并下载内容的逻辑:

import asyncio
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

async def download_data(data):
    # 创建Chrome浏览器的选项对象
    chrome_options = Options()
    chrome_options.add_argument("--headless")  # 无头模式,不显示浏览器界面

    # 创建WebDriver对象
    driver = webdriver.Chrome(options=chrome_options)

    try:
        # 打开网页并进行登录等操作
        driver.get("http://example.com/login")
        # 在这里执行登录操作

        # 在特定元素上点击
        element = driver.find_element_by_xpath("//xpath/to/element")
        element.click()

        # 等待特定条件,确保页面加载完成
        # driver.implicitly_wait(10)  # 根据需要可以添加隐式等待

        # 在这里执行下载操作,根据数据字段获取下载链接等信息
        download_url = f"http://example.com/api/download?data={data}"
        driver.get(download_url)
        # 在这里进行下载操作

        print(f"Downloaded {data}.jpg")
    finally:
        # 关闭WebDriver对象
        driver.quit()

async def main():
    # 假设有一组数据字段需要下载东西
    data_list = ['data1', 'data2', 'data3', 'data4', 'data5']

    tasks = []
    for data in data_list:
        task = asyncio.create_task(download_data(data))
        tasks.append(task)

    await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())

在这个示例中,我们使用了asyncio库来创建协程和管理异步任务。首先,我们定义了一个download_data协程函数,其中包含了使用Selenium进行登录、点击特定元素和下载内容的逻辑。

download_data协程函数中,我们创建了一个Chrome浏览器的选项对象chrome_options,并设置了无头模式,即不显示浏览器界面。然后,创建了一个WebDriver对象driver,通过webdriver.Chrome()方法传入选项对象来创建Chrome浏览器的WebDriver实例。

download_data协程函数的主体部分,我们使用WebDriver对象执行了一系列操作,包括打开网页、登录、点击特定元素和下载内容。最后,我们关闭了WebDriver对象以释放资源。

在主程序部分,我们定义了一个main协程函数作为程序的入口点。在main协程函数中,我们创建了一组协程任务,并将每个任务添加到一个任务列表中。然后,使用asyncio.gather()函数并行执行所有的协程任务。

最后,我们使用asyncio.run()函数来运行main协程函数,启动整个协程任务的执行。

请注意,这只是一个示例代码,用于演示如何使用协程和Selenium实现异步登录和下载任务。你需要根据实际需求和网站接口的特点来编写适合自己的协程函数,并在其中处理登录、点击和下载操作的逻辑。同时,还可以根据实际情况对代码进行优化和调整,以提高异步任务的执行效率和性能。另外,如果你要使用其他浏览器,可以相应地更改选项对象和WebDriver的创建方式。

5.三种方式各有什么特点

场景:
大量数据,每条数据都需要打开网站,登录后,点击特定元素,点击下载按钮,每条数据都需要从网站上根据数据字段下载东西,打开的网站都是同一个网址,用了Selenium登录。
分析:
对于上面的场景,协程是处理性能更好的选择。下面是对线程池、进程池和协程进行比较的原因:

  1. 线程池:使用线程池可以并发执行多个任务,但是由于Python中的全局解释器锁(GIL)的存在,同一时间只有一个线程可以执行Python字节码,因此多线程在CPU密集型任务中无法充分利用多核处理器的优势。在网页自动化测试中,Selenium是与浏览器通信的过程,受限于浏览器性能,主要消耗在IO等待上,因此线程池在这种场景下并不能显著提高性能。

  2. 进程池:使用进程池可以充分利用多核处理器的优势,每个进程都有独立的Python解释器和GIL,可以并行执行任务。然而,在网页自动化测试中,每个进程都需要启动一个浏览器实例,浏览器的资源消耗较大,如果同时启动大量的浏览器进程,会导致系统资源的消耗增加,可能会影响性能和稳定性。

  3. 协程:协程是一种轻量级的并发处理方式,协程通过避免任务切换的开销和利用IO等待时的空闲时间,可以高效地进行任务调度和并发执行。在网页自动化测试中,使用协程可以在一个浏览器实例中执行多个任务,减少了浏览器实例的启动和销毁开销。同时,协程的切换开销较小,可以更好地利用CPU资源和IO等待时间,提高程序的性能。

综上所述,对于上述场景,协程是性能更好的处理方式。它能够高效地利用浏览器资源、减少任务切换开销,并在IO等待时充分利用空闲时间,实现高效的并发执行。通过使用协程,可以提高程序的性能,并有效地管理大量数据的并发处理。

6.其他方法的拓展

除了线程池、进程池和协程,还有其他处理大量数据并发执行任务的方法,例如使用异步IO库aiohttpasyncio

下面是一个使用aiohttpasyncio库处理大量数据并发下载的示例代码:

import asyncio
import aiohttp

async def download_data(session, data):
    download_url = f"http://example.com/api/download?data={data}"
    async with session.get(download_url) as response:
        content = await response.read()
        with open(f"{data}.jpg", "wb") as file:
            file.write(content)
        print(f"Downloaded {data}.jpg")

async def main():
    data_list = ['data1', 'data2', 'data3', 'data4', 'data5']

    async with aiohttp.ClientSession() as session:
        tasks = [download_data(session, data) for data in data_list]
        await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())

在这个示例中,我们使用了aiohttp库来进行异步的HTTP请求,asyncio库用于管理异步任务的执行。

首先,我们定义了一个download_data协程函数,接受一个session对象和数据字段作为参数。在协程函数中,我们使用session对象发送HTTP GET请求获取下载内容,并将其保存到文件中。

在主程序部分,我们定义了一个main协程函数作为程序的入口点。在main协程函数中,我们创建了一个ClientSession对象session,用于发送HTTP请求。然后,我们创建了一组协程任务tasks,每个任务调用download_data函数并传入session对象和数据字段。最后,使用asyncio.gather()函数并行执行所有的协程任务。

通过使用aiohttpasyncio库,我们可以实现高效的异步HTTP请求和并发下载任务。这种方法可以充分利用IO等待时间,提高程序的性能,同时也减少了浏览器实例的启动和销毁开销。

7.IO密集型任务 和 CPU密集型

IO密集型任务 和 CPU密集型

IO密集型任务是指任务主要涉及输入/输出(IO)操作的任务。这类任务的特点是需要频繁地进行文件读写、网络通信、数据库访问等IO操作,而实际的计算量相对较小。

常见的IO密集型任务包括:

  • 网络爬虫:需要从网页上抓取数据并保存到本地或数据库中。
  • 文件处理:需要读取、写入或处理大量文件的任务,例如日志分析、文件压缩/解压缩等。
  • 数据库操作:需要频繁地进行数据库的读取、写入、更新或查询等操作。
  • 图片/视频处理:需要对大量图片或视频进行读取、处理、保存等操作。
  • 网络通信:需要与远程服务器进行频繁的通信、请求和响应操作。

在这些任务中,主要的时间消耗在等待IO操作的完成,而不是CPU的计算。因此,使用线程池可以充分利用CPU的多线程能力,在等待IO操作的同时,可以切换执行其他线程的任务,提高程序的整体效率。

需要注意的是,如果任务主要是CPU密集型的,即任务需要进行大量的计算操作而不涉及太多的IO操作,那么使用多进程或其他并行计算的方式可能更加合适,因为Python中的全局解释器锁(GIL)会限制多线程程序在多核CPU上的性能提升。

CPU密集型任务是指任务主要涉及大量的计算操作,而与输入/输出(IO)操作的频率相比较低的任务。这类任务的特点是需要进行复杂的数学运算、逻辑判断、数据处理等操作,而对于IO操作的需求较少。

常见的CPU密集型任务包括:

  • 数据分析/科学计算:需要对大量数据进行数学运算、统计分析、机器学习等复杂的计算操作。
  • 图像/视频处理:需要对图像或视频进行复杂的图像处理、特征提取、模式识别等操作。
  • 加密/解密操作:需要进行复杂的数据加密、解密或哈希运算等操作。
  • 数值模拟/仿真:需要进行大规模的数值计算和模拟实验。

在这些任务中,主要的时间消耗在于CPU的计算操作,而IO操作相对较少。因此,对于CPU密集型任务,使用多进程、多线程或并行计算的方式可以充分利用多核CPU的性能,从而提高程序的整体效率。

需要注意的是,在Python中,由于全局解释器锁(GIL)的存在,多线程程序在CPU密集型任务上可能无法充分利用多核CPU的性能。因此,如果任务是纯粹的CPU密集型任务,并且需要充分利用多核CPU,那么使用多进程或其他并行计算的方式可能更加合适。

8. 协程的实操用法

需求:
大量数据,每条数据都需要打开网站,登录后,点击特定元素,点击下载按钮,每条数据都需要从网站上根据数据字段下载东西,打开的网站都是同一个网址。用了Selenium登录,
分析:
在这种场景下,可以使用aiohttpasyncio结合pyppeteer来实现协程并发处理网站操作和下载任务。pyppeteer是一个基于asyncio的无界面浏览器控制工具,可以模拟用户操作浏览器。

首先,确保已经安装了aiohttppyppeteer库。可以使用以下命令进行安装:

pip install aiohttp pyppeteer

下面是一个使用aiohttpasynciopyppeteer实现的示例代码:

import asyncio
import aiohttp
from pyppeteer import launch

async def login_and_download(session, data):
    login_url = "http://example.com/login"  # 登录页面URL
    download_url = f"http://example.com/download?data={data}"  # 下载页面URL

    # 打开浏览器
    browser = await launch(headless=True)
    page = await browser.newPage()

    # 登录
    await page.goto(login_url)
    # 填写用户名和密码
    await page.type("#username", "your_username")
    await page.type("#password", "your_password")
    # 提交登录表单
    await asyncio.sleep(1)  # 等待页面加载完全,根据实际情况调整等待时间
    await page.click("#login-button")

    # 等待登录成功
    await page.waitForNavigation()

    # 跳转到下载页面
    await page.goto(download_url)

    # 执行下载操作
    await asyncio.sleep(1)  # 等待页面加载完全,根据实际情况调整等待时间
    await page.click("#download-button")

    # 等待下载完成
    await asyncio.sleep(5)  # 等待下载完成,根据实际情况调整等待时间

    # 关闭浏览器
    await browser.close()

    print(f"Downloaded {data}.jpg")

async def main():
    data_list = ['data1', 'data2', 'data3', 'data4', 'data5']

    async with aiohttp.ClientSession() as session:
        tasks = [login_and_download(session, data) for data in data_list]
        await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())

在这个示例中,我们使用pyppeteer库来控制无界面浏览器进行网站登录和下载操作。

login_and_download协程函数中,我们首先定义了登录页面URL和下载页面URL。然后,使用pyppeteer库打开一个无界面浏览器,并创建一个新的页面。接下来,在页面上执行登录操作,包括填写用户名和密码,并点击登录按钮。登录成功后,跳转到下载页面,并执行下载操作,例如点击下载按钮。最后,关闭浏览器。

在主程序部分,我们定义了一个main协程函数作为程序的入口点。在main协程函数中,我们创建了一个ClientSession对象session,用于发送HTTP请求。然后,我们创建了一组协程任务tasks,每个任务调用login_and_download函数并传入session对象和数据字段。最后,使用asyncio.gather()函数并行执行所有的协程任务。

需要注意的是,根据实际情况,可能需要等待页面加载完全、表单提交成功和下载完成等操作。代码中使用await asyncio.sleep()来进行等待,可以根据实际情况调整等待时间。另外,需要根据实际需求对代码进行优化和调整,以适应大量数据的并发处理。

这种方法可以实现高效的并发登录和下载任务,通过使用aiohttpasynciopyppeteer库,可以同时处理多个网站操作和下载任务,提高程序的性能和效率。

文章来源:https://blog.csdn.net/m0_57021623/article/details/135588810
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。