【Python爬虫(35)】解锁Python多进程爬虫:高效数据抓取秘籍

news/2025/2/22 16:43:25

【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取,还涉及数据处理与分析。无论是新手小白还是进阶开发者,都能从中汲取知识,助力掌握爬虫核心技能,开拓技术视野。

目录

  • 一、引言
  • 二、多进程爬虫基础
  • 三、使用多进程实现高性能爬虫
  • 四、利用进程池进行任务分配与管理
    • 4.1 进程池概念与原理
    • 4.2 multiprocessing.Pool 的使用
    • 4.3 任务分配策略
  • 五、处理多进程爬虫中的数据共享与同步问题
    • 5.1 数据共享需求
    • 5.2 Manager 对象介绍
    • 5.3 使用 Manager 实现数据共享
    • 5.4 数据同步与锁机制
  • 六、案例实战:多进程爬虫抓取网页数据
    • 6.1 案例背景与目标
    • 6.2 爬虫实现步骤
    • 6.3 结果分析与优化
  • 七、总结与展望


一、引言

爬虫开发中,随着数据量的不断增大和爬取任务的日益复杂,提升爬虫效率成为了关键挑战。传统的单进程爬虫在面对大量数据时,由于其按顺序依次处理任务的特性,效率较低,耗时较长。而多进程爬虫的出现,为解决这一问题提供了有效的途径。

多进程爬虫利用操作系统的多进程机制,允许多个进程同时执行爬取任务。每个进程独立运行,拥有自己的内存空间和系统资源,这使得它们能够并行处理不同的爬取任务。例如,在爬取一个包含大量网页的网站时,单进程爬虫需要逐个下载和解析网页,而多进程爬虫可以将这些网页分配给多个进程同时进行处理,大大缩短了整体的爬取时间。

多进程爬虫不仅能够提高爬取速度,还能有效避免 Python 全局解释器锁(GIL)的限制。在 Python 中,GIL 会导致同一时间只有一个线程能执行 Python 字节码,这在 CPU 密集型任务中会严重影响效率。而多进程爬虫每个进程都有自己的 Python 解释器和内存空间,不受 GIL 的影响,能够充分利用多核 CPU 的优势,实现真正的并行计算,从而显著提升爬虫的性能。因此,掌握多进程爬虫的实现方法,对于提高爬虫开发效率和应对复杂的数据爬取需求具有重要意义。

二、多进程爬虫基础

2.1 多进程爬虫优势

多进程爬虫在数据抓取领域展现出显著优势,这些优势主要源于其对系统资源的高效利用和独特的执行机制。

从 CPU 资源利用角度来看,多进程爬虫能够充分利用多核 CPU 的强大计算能力。现代计算机的 CPU 通常具备多个核心,而多进程爬虫可以将不同的爬取任务分配到各个核心上并行执行。以一个拥有 4 核 CPU 的计算机为例,当进行大规模网页爬取时,单进程爬虫只能在一个核心上依次处理每个网页,而多进程爬虫则可以同时启动 4 个进程,每个进程负责处理一部分网页,使得 CPU 的每个核心都能得到充分利用,大大提高了整体的爬取效率。这种并行处理方式与单进程爬虫的串行处理形成鲜明对比,就如同一条单车道和多条并行车道,在相同时间内,多车道能够容纳更多车辆通行,多进程爬虫也能处理更多的爬取任务。

在爬取速度提升方面,多进程爬虫优势明显。传统单进程爬虫在处理大量网页时,需要逐个完成每个网页的请求、下载和解析,这一过程中,网络请求的等待时间和数据处理时间会累加,导致整体爬取速度缓慢。而多进程爬虫通过并行执行多个任务,能够同时发起多个网络请求,在等待一个请求响应的过程中,其他进程可以继续执行其他任务,减少了等待时间的浪费。例如,在爬取一个包含 1000 个网页的网站时,单进程爬虫可能需要数小时才能完成,而多进程爬虫通过合理分配任务,能够在短短几十分钟甚至更短时间内完成,大大缩短了数据获取的时间周期,使我们能够更快地获取所需数据进行后续分析和处理。

此外,多进程爬虫还具有更好的稳定性和容错性。由于每个进程独立运行,拥有自己的内存空间和系统资源,一个进程在爬取过程中出现异常(如网络连接中断、目标网站反爬虫机制导致的请求失败等),不会影响其他进程的正常运行。这就好比一个团队中,每个成员独立完成自己的任务,即使其中一个成员遇到问题,其他成员仍能继续推进工作,保证整个项目的进度。相比之下,单进程爬虫一旦出现异常,整个爬取任务就会中断,需要重新启动,大大降低了工作效率。

2.2 多进程模块 multiprocessing

在 Python 中,multiprocessing模块是实现多进程编程的核心工具,为开发者提供了丰富且强大的功能,用于创建、管理和控制多个进程。

multiprocessing模块的设计目标是让多进程编程变得简单直观,它基于操作系统的进程管理机制,封装了底层的系统调用,使得开发者可以通过 Python 代码轻松地创建和操作进程。例如,通过Process类,我们可以像创建普通对象一样创建一个新的进程,并指定该进程要执行的任务函数。在创建进程时,只需要传入目标函数和相关参数,就可以启动一个独立运行的进程,该进程会在单独的内存空间中执行指定的任务,与主进程和其他进程相互独立。

这个模块提供了多种进程间通信和同步的机制,以满足不同场景下的需求。其中,队列(Queue)是一种常用的进程间通信方式,它可以在不同进程之间安全地传递数据。一个进程可以将数据放入队列中,而另一个进程则可以从队列中获取数据,就像一个数据中转站,确保数据在不同进程之间有序传递。管道(Pipe)则提供了一种双向通信的机制,两个进程可以通过管道进行直接的消息传递,实现更灵活的数据交互。

同步机制方面,multiprocessing模块提供了锁(Lock)、信号量(Semaphore)和事件(Event)等原语。锁用于确保在同一时刻只有一个进程可以访问共享资源,避免数据竞争和不一致的问题。比如,当多个进程需要访问同一个文件进行写入操作时,通过锁机制可以保证每次只有一个进程能够成功写入,防止数据混乱。信号量则可以控制同时访问某个资源的进程数量,适用于需要限制资源访问并发度的场景。事件则用于进程之间的同步和协调,一个进程可以通过设置事件来通知其他进程某个特定的条件已经满足,从而触发其他进程的相应操作。

multiprocessing模块还提供了进程池(Pool)的功能,它可以方便地管理一组进程,并自动分配任务给这些进程。通过进程池,我们可以一次性提交多个任务,由进程池中的进程并行执行这些任务,无需手动创建和管理每个进程的生命周期。这在处理大量重复性任务时非常高效,大大简化了多进程编程的复杂度,提高了开发效率。

三、使用多进程实现高性能爬虫

3.1 多进程爬虫架构设计

多进程爬虫的架构设计是实现高效数据爬取的关键,它涉及到任务的合理分配、数据的有效爬取以及各进程之间的协同工作。

在任务分配环节,通常由一个主进程负责统筹全局。主进程会将待爬取的任务(如一系列网页的 URL 列表)划分成多个子任务,然后将这些子任务分配给不同的子进程。这就好比一个大型建筑项目,项目经理(主进程)将整个工程拆分成多个小项目,如地基建设、主体结构搭建、内部装修等,然后分配给不同的施工小组(子进程)。这种任务分配方式能够充分利用多进程的并行处理能力,提高爬取效率。

数据爬取部分,每个子进程独立执行分配到的任务。子进程会根据给定的 URL 发起 HTTP 请求,获取网页内容。在这个过程中,每个子进程都拥有自己独立的网络连接和数据处理环境,不受其他进程的干扰。例如,当爬取一个电商网站的商品信息时,一个子进程负责爬取服装类商品页面,另一个子进程负责爬取电子产品类商品页面,它们可以同时进行数据请求和解析,大大缩短了整体的爬取时间。

在实际运行过程中,各进程之间需要进行必要的通信和协调。比如,子进程在爬取过程中可能会遇到一些问题,如网络连接超时、目标网站反爬虫机制导致的请求失败等,这时子进程需要将这些异常信息反馈给主进程,以便主进程进行相应的处理,如重新分配任务、调整爬取策略等。主进程也需要监控各个子进程的运行状态,确保整个爬虫系统的稳定运行。

3.2 代码实现示例

下面通过一个具体的代码示例,展示如何使用multiprocessing模块创建多进程爬虫,实现对一系列网页的爬取。假设我们要爬取一个包含多个页面的小说网站,获取每个页面的章节标题和内容。

python">import requests
from multiprocessing import Process, Queue
import re


# 定义爬取函数
def crawler(url_queue, result_queue):
    while True:
        # 从任务队列中获取URL
        url = url_queue.get()
        if url is None:
            break
        try:
            # 发送HTTP请求获取网页内容
            response = requests.get(url, timeout=10)
            response.raise_for_status()
            html = response.text

            # 使用正则表达式提取章节标题和内容
            title_pattern = re.compile(r'<h1 class="chapter-title">(.*?)</h1>')
            content_pattern = re.compile(r'<div class="chapter-content">(.*?)</div>', re.S)
            title = re.search(title_pattern, html).group(1)
            content = re.search(content_pattern, html).group(1).strip()

            # 将结果放入结果队列
            result_queue.put((title, content))
        except Exception as e:
            print(f"爬取 {url} 失败: {e}")


if __name__ == '__main__':
    # 待爬取的URL列表
    base_url = "https://example.com/novel/chapter_{}"
    urls = [base_url.format(i) for i in range(1, 11)]

    # 创建任务队列和结果队列
    task_queue = Queue()
    result_queue = Queue()

    # 将URL放入任务队列
    for url in urls:
        task_queue.put(url)

    # 创建并启动多个进程
    num_processes = 4
    processes = []
    for _ in range(num_processes):
        p = Process(target=crawler, args=(task_queue, result_queue))
        p.start()
        processes.append(p)

    # 等待所有任务完成
    for _ in range(num_processes):
        task_queue.put(None)
    for p in processes:
        p.join()

    # 从结果队列中获取并处理结果
    while not result_queue.empty():
        title, content = result_queue.get()
        print(f"章节标题: {title}")
        print(f"章节内容: {content}")
        print("-" * 80)

在上述代码中,首先定义了crawler函数,该函数从任务队列中获取 URL,发送 HTTP 请求获取网页内容,并使用正则表达式提取章节标题和内容,最后将结果放入结果队列。在主程序中,创建了任务队列和结果队列,并将待爬取的 URL 放入任务队列。然后,通过循环创建并启动了 4 个进程,每个进程都执行crawler函数。在所有任务完成后,从结果队列中获取并打印每个章节的标题和内容。通过这种方式,利用多进程实现了对小说网站多个页面的高效爬取。

四、利用进程池进行任务分配与管理

4.1 进程池概念与原理

进程池是一种在多进程编程中用于管理和复用进程的机制,它通过预先创建一组固定数量的进程,避免了频繁创建和销毁进程所带来的开销,从而提高了系统资源的利用率和任务执行的效率。

从原理上讲,进程池在初始化时会创建一定数量的进程,这些进程被放置在一个 “池子” 中,处于空闲状态并等待接收任务。当有新的任务到来时,进程池管理器会从空闲进程列表中选择一个进程来执行该任务。任务通常以某种数据结构(如任务队列)的形式存储,管理器会从队列中取出任务,并将其分配给选中的进程。被选中的进程会执行分配的任务,在任务执行完成后,进程会将结果返回给进程池管理器,然后重新回到空闲状态,等待下一个任务的到来。

以一个简单的文件处理任务为例,假设有 100 个文件需要进行处理,如果不使用进程池,每处理一个文件都需要创建一个新的进程,处理完成后再销毁该进程。在这个过程中,创建和销毁进程的系统调用会消耗大量的时间和资源,导致整体处理效率低下。而使用进程池时,我们可以预先创建 5 个进程放入进程池中。当有文件处理任务时,进程池管理器会依次将文件分配给这 5 个进程中的空闲进程进行处理。当一个进程完成一个文件的处理后,它会立即从任务队列中获取下一个文件继续处理,而不是被销毁。这样,通过复用这 5 个进程,大大减少了进程创建和销毁的开销,提高了文件处理的效率。

4.2 multiprocessing.Pool 的使用

在 Python 的multiprocessing模块中,Pool类提供了方便的进程池管理功能,它包含了多个常用方法,用于实现高效的任务分配和结果获取。

map方法是Pool类中较为常用的方法之一,它的使用方式类似于 Python 内置的map函数。map方法接受两个主要参数:一个是要执行的函数,另一个是可迭代对象。它会将可迭代对象中的每个元素依次作为参数传递给指定的函数,并在进程池中并行执行这些函数调用,最后返回一个包含所有函数执行结果的列表。例如,我们要计算 1 到 10 的平方,可以使用以下代码:

python">from multiprocessing import Pool


def square(x):
    return x * x


if __name__ == '__main__':
    with Pool(processes=4) as pool:
        results = pool.map(square, range(1, 11))
    print(results)

在上述代码中,创建了一个包含 4 个进程的进程池,然后使用map方法将square函数应用到range(1, 11)中的每个元素上,最终返回一个包含 1 到 10 的平方的列表。

apply_async方法则用于异步执行函数,它不会阻塞主进程的执行。当调用apply_async时,它会立即返回一个AsyncResult对象,我们可以通过这个对象来获取任务的执行结果、检查任务是否完成等。apply_async方法接受要执行的函数、函数的参数(以元组形式传递)以及可选的回调函数和错误回调函数。回调函数会在任务成功完成时被调用,错误回调函数则会在任务执行过程中出现异常时被调用。例如:

python">from multiprocessing import Pool


def multiply(x, y):
    return x * y


def print_result(result):
    print(f"结果是: {result}")


def handle_error(error):
    print(f"发生错误: {error}")


if __name__ == '__main__':
    with Pool(processes=4) as pool:
        async_result = pool.apply_async(multiply, (3, 5), callback=print_result, error_callback=handle_error)

在这个例子中,使用apply_async方法异步执行multiply函数,传入参数 3 和 5。当任务完成时,会调用print_result函数打印结果;如果任务执行过程中出现错误,会调用handle_error函数处理错误。

4.3 任务分配策略

合理的任务分配策略对于充分发挥多进程爬虫的优势、提升整体效率至关重要。在分配任务时,需要综合考虑任务的特点、系统资源的状况以及进程的负载情况等因素。

对于任务的复杂度和耗时差异,我们可以采用不同的分配策略。如果任务的复杂度和耗时较为均匀,采用轮询策略是一种简单有效的方法。轮询策略就是按照顺序依次将任务分配给进程池中的每个进程,保证每个进程都能均衡地承担任务量。例如,有 10 个网页爬取任务,每个网页的结构相似,数据量相近,爬取和解析所需的时间大致相同,这时可以将这 10 个任务依次分配给进程池中的 5 个进程,每个进程负责 2 个任务,这样可以充分利用每个进程的资源,避免某个进程负载过高,而其他进程闲置的情况。

当任务的复杂度和耗时差异较大时,优先分配策略更为合适。这种策略会根据任务的预计耗时或重要性进行排序,将耗时较长或更为重要的任务优先分配给资源较为充足或处理能力较强的进程。比如,在爬取一个包含多种类型数据的网站时,有些页面需要进行复杂的 JavaScript 渲染才能获取完整的数据,而有些页面则可以直接通过简单的 HTTP 请求获取,前者的爬取和解析耗时会远远超过后者。在这种情况下,我们可以将需要复杂渲染的页面任务优先分配给配置较高、处理能力较强的进程,以确保整个爬虫系统的效率和稳定性。同时,对于重要性较高的数据,如关键业务数据或时效性强的数据,也应优先分配给可靠的进程进行爬取,以保证数据的及时获取和处理。

还可以根据系统资源的动态变化来调整任务分配策略。当系统检测到某个进程的负载较低时,可以动态地将更多的任务分配给该进程,以充分利用系统资源;而当某个进程的负载过高时,则减少对其任务分配,避免其因过载而出现性能下降甚至崩溃的情况。通过这种动态调整的策略,可以使整个多进程爬虫系统在不同的工作负载下都能保持较高的效率和稳定性。

五、处理多进程爬虫中的数据共享与同步问题

5.1 数据共享需求

多进程爬虫的实际应用中,数据共享是一个常见且关键的需求。当多个进程同时执行爬取任务时,往往需要共享某些数据,以实现更高效的协作和更准确的结果处理。

以爬取电商网站商品信息为例,假设我们需要爬取多个店铺的商品价格、销量等数据,并对这些数据进行汇总分析。在这个过程中,不同的进程可能负责爬取不同店铺的信息,但它们都需要将爬取到的数据存储到一个共享的数据库或数据结构中,以便后续统一分析。例如,一个进程爬取了某店铺的手机商品信息,另一个进程爬取了该店铺的电脑商品信息,它们都需要将各自获取的数据共享给其他进程,以便进行综合分析,如计算该店铺的电子产品总销量、平均价格等。

再比如,在爬取新闻网站时,可能需要多个进程同时爬取不同板块的新闻内容。为了避免重复爬取,这些进程需要共享一个已爬取 URL 的列表。当一个进程准备爬取某个 URL 时,它首先会检查共享列表中是否已经存在该 URL,如果存在则跳过,从而提高爬取效率,减少不必要的网络请求和资源浪费。

此外,在一些需要实时更新数据的场景中,数据共享也至关重要。比如,爬取股票市场的实时数据,多个进程分别获取不同股票的价格、成交量等信息,它们需要将这些实时数据共享给一个数据处理模块,以便及时分析股票市场的动态,为投资者提供准确的决策依据。

5.2 Manager 对象介绍

在 Python 的multiprocessing模块中,Manager对象是实现多进程间数据共享的核心工具,它通过提供一种基于服务器进程的机制,有效地解决了多进程环境下数据共享的难题。

Manager对象本质上是一个管理共享对象的服务器进程。当我们创建一个Manager对象时,实际上是启动了一个新的进程,这个进程负责管理和维护共享的数据。它就像是一个数据仓库的管理员,所有需要共享的数据都由它来统一管理和分配。其他进程通过代理对象来访问共享数据,这些代理对象就像是仓库管理员发放的通行证,每个进程凭借代理对象可以安全地访问和修改共享数据。

Manager对象支持多种数据类型的共享,包括字典(dict)、列表(list)、队列(Queue)等。以共享字典为例,多个进程可以同时对共享字典进行读写操作。当一个进程向共享字典中添加一个键值对时,其他进程可以立即看到这个变化,就像多个用户同时编辑一个在线文档一样,每个人的修改都能实时同步给其他用户。

多进程爬虫中,Manager对象的作用尤为显著。它可以用来共享爬取任务的状态信息,如每个进程的爬取进度、已完成的任务数量等。通过共享这些信息,主进程可以实时监控整个爬虫系统的运行状态,及时发现并处理可能出现的问题,如某个进程长时间无响应或出现异常等。同时,Manager对象还可以用于共享爬取到的数据,确保各个进程获取的数据一致性,方便后续的数据处理和分析。

5.3 使用 Manager 实现数据共享

以下通过一个具体的代码示例,展示如何使用Manager对象创建共享字典和列表,并在多进程中进行数据共享。假设我们要爬取多个网页的标题和链接,并将这些信息存储在共享的数据结构中。

python">from multiprocessing import Process, Manager
import requests
from bs4 import BeautifulSoup


# 定义爬取函数
def crawler(url, shared_dict, shared_list):
    try:
        response = requests.get(url)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')

        # 获取网页标题
        title = soup.title.string if soup.title else '无标题'

        # 获取所有链接
        links = [link.get('href') for link in soup.find_all('a')]

        # 将结果存入共享字典和列表
        shared_dict[url] = title
        shared_list.extend(links)
    except Exception as e:
        print(f"爬取 {url} 失败: {e}")


if __name__ == '__main__':
    # 创建Manager对象
    manager = Manager()

    # 创建共享字典和列表
    shared_dict = manager.dict()
    shared_list = manager.list()

    # 待爬取的URL列表
    urls = ['https://example.com', 'https://example.net', 'https://example.org']

    # 创建并启动多个进程
    processes = []
    for url in urls:
        p = Process(target=crawler, args=(url, shared_dict, shared_list))
        p.start()
        processes.append(p)

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

    # 打印共享字典和列表
    print("共享字典:")
    for url, title in shared_dict.items():
        print(f"{url}: {title}")

    print("\n共享列表:")
    for link in shared_list:
        print(link)

在上述代码中,首先创建了一个Manager对象,然后使用该对象创建了共享字典shared_dict和共享列表shared_list。在crawler函数中,每个进程对给定的 URL 进行爬取,获取网页标题和链接,并将这些信息分别存入共享字典和列表中。最后,在主进程中打印共享字典和列表的内容,展示了多进程间数据共享的效果。

5.4 数据同步与锁机制

多进程爬虫中,当多个进程同时访问和修改共享数据时,可能会出现数据不一致的问题,这就需要引入锁机制来确保数据的同步和一致性。

数据不一致问题通常是由于多个进程同时对共享数据进行读写操作引起的。例如,当两个进程同时读取共享字典中的某个值,然后各自对其进行修改并写回时,可能会导致最终的结果并不是我们期望的。假设共享字典中存储了某个商品的销量,一个进程读取销量为 100,另一个进程也读取销量为 100,然后第一个进程将销量增加 10,第二个进程将销量增加 20。如果没有合适的同步机制,最终共享字典中的销量可能是 110(第二个进程的修改覆盖了第一个进程的修改),而不是正确的 130。

为了解决这个问题,multiprocessing模块提供了锁(Lock)机制。锁就像是一个房间的钥匙,当一个进程获取到锁时,就相当于拿到了进入房间(访问共享数据)的权限,其他进程必须等待该进程释放锁后才能进入。在 Python 代码中,使用锁非常简单。例如:

python">from multiprocessing import Process, Manager, Lock


def update_shared_data(shared_dict, lock):
    with lock:
        # 获取共享字典中的数据
        value = shared_dict.get('count', 0)
        # 修改数据
        value += 1
        # 写回共享字典
        shared_dict['count'] = value


if __name__ == '__main__':
    manager = Manager()
    shared_dict = manager.dict()
    shared_dict['count'] = 0

    lock = Lock()

    processes = []
    for _ in range(10):
        p = Process(target=update_shared_data, args=(shared_dict, lock))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

    print(f"最终的计数值: {shared_dict['count']}")

在上述代码中,定义了update_shared_data函数,该函数用于更新共享字典中的数据。在函数内部,使用with lock语句来获取锁,确保在修改共享数据时,其他进程无法同时访问。这样,无论有多少个进程同时调用这个函数,都能保证共享数据的一致性。在主程序中,创建了 10 个进程来调用update_shared_data函数,最终打印出正确的计数值。通过这种方式,锁机制有效地解决了多进程爬虫中数据同步的问题,保证了数据的准确性和完整性。

六、案例实战:多进程爬虫抓取网页数据

6.1 案例背景与目标

在互联网信息爆炸的时代,数据成为了宝贵的资源。本案例旨在通过多进程爬虫技术,从一个热门的科技资讯网站(假设为https://technews.com)抓取最新的文章信息,包括文章标题、发布时间、作者以及文章内容。这些信息对于研究科技行业动态、分析热门技术趋势以及进行内容聚合等具有重要价值。通过本案例,我们将深入了解多进程爬虫在实际应用中的实现方法和优势,以及如何处理可能遇到的各种问题。

6.2 爬虫实现步骤

  1. URL 获取:首先,需要确定要爬取的页面范围。可以通过分析网站的结构,获取文章列表页面的 URL 规律。例如,该科技资讯网站的文章列表页面可能遵循https://technews.com/articles?page={page_number}的格式,其中{page_number}为页码。通过循环生成不同页码的 URL,构建待爬取的 URL 列表。
python">base_url = "https://technews.com/articles?page={}"
urls = [base_url.format(i) for i in range(1, 11)]
  1. 任务分配:使用multiprocessing.Pool创建进程池,并将爬取任务分配给进程池中的进程。每个进程负责处理一个 URL 的爬取和解析任务。
python">from multiprocessing import Pool


def crawl_article(url):
    # 这里是爬取和解析文章的具体代码
    pass


with Pool(processes=4) as pool:
    pool.map(crawl_article, urls)
  1. 数据爬取与解析:在crawl_article函数中,使用requests库发送 HTTP 请求获取网页内容,并使用BeautifulSoup库进行 HTML 解析,提取文章的标题、发布时间、作者和内容等信息。
python">import requests
from bs4 import BeautifulSoup


def crawl_article(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取文章标题
        title = soup.find('h1', class_='article-title').text.strip()

        # 提取发布时间
        publish_time = soup.find('span', class_='publish-time').text.strip()

        # 提取作者
        author = soup.find('span', class_='author').text.strip()

        # 提取文章内容
        content = soup.find('div', class_='article-content').text.strip()

        return {
            'title': title,
            'publish_time': publish_time,
            'author': author,
            'content': content
        }
    except Exception as e:
        print(f"爬取 {url} 失败: {e}")
  1. 数据共享与存储:使用Manager对象创建共享列表,将每个进程爬取到的文章信息存储到共享列表中。最后,将共享列表中的数据保存到本地文件或数据库中。
python">from multiprocessing import Manager


def save_data(shared_data):
    # 将数据保存到本地文件或数据库的代码
    pass


if __name__ == '__main__':
    manager = Manager()
    shared_data = manager.list()

    with Pool(processes=4) as pool:
        results = pool.map(crawl_article, urls)
        for result in results:
            if result:
                shared_data.append(result)

    save_data(shared_data)

6.3 结果分析与优化

在爬取完成后,对爬取结果进行分析。可以统计爬取到的文章数量,检查是否存在重复数据或数据缺失的情况。如果发现部分文章内容爬取不完整,可能是由于网站采用了 JavaScript 动态加载技术,需要使用 Selenium 等工具模拟浏览器行为进行爬取。

从效率方面来看,如果爬取速度较慢,可以进一步优化任务分配策略,根据文章页面的大小和复杂度动态调整进程的任务量。同时,合理设置请求头和请求间隔,避免触发网站的反爬虫机制。此外,还可以考虑使用分布式爬虫框架,如 Scrapy - Redis,将爬取任务分布到多个节点上执行,进一步提升爬取效率和稳定性。通过不断地分析和优化,使多进程爬虫能够更高效、稳定地获取所需数据。

七、总结与展望

多进程爬虫通过并行处理任务,充分利用多核 CPU 资源,显著提升了数据爬取的效率和速度,为应对大规模数据爬取任务提供了强大的解决方案。在实现过程中,合理的架构设计、任务分配策略以及对数据共享和同步问题的有效处理是关键。multiprocessing模块提供了丰富的工具和方法,使得多进程爬虫的开发变得相对便捷。

随着互联网数据量的持续增长和数据需求的日益多样化,多进程爬虫在未来的爬虫领域将发挥更为重要的作用。在大数据分析、人工智能训练数据采集等场景中,对高效、稳定的数据爬取需求将不断增加,多进程爬虫能够满足这些需求,为后续的数据处理和分析提供坚实的数据基础。

未来,多进程爬虫技术有望与人工智能、机器学习等领域进一步融合。例如,利用机器学习算法动态调整任务分配策略,根据目标网站的反爬虫机制自动优化爬取策略,实现更加智能化、自适应的爬虫系统。同时,随着分布式计算技术的发展,多进程爬虫可能会与分布式爬虫相结合,充分利用集群计算资源,进一步提升爬取大规模数据的能力,以应对更加复杂和多样化的数据采集任务,为各行业的发展提供更有力的数据支持。


http://www.niftyadmin.cn/n/5862533.html

相关文章

Windows 系统下,使用 PyTorch 的 DataLoader 时,如果 num_workers 参数设置为大于 0 的值,报错

在 Windows 系统下&#xff0c;使用 PyTorch 的 DataLoader 时&#xff0c;如果 num_workers 参数设置为大于 0 的值&#xff0c;可能会遇到以下错误&#xff1a; RuntimeError: An attempt has been made to start a new process before thecurrent process has finished its…

现场可以通过手机或者pad实时拍照上传到大屏幕的照片墙现场大屏电子照片墙功能

现场可以通过手机或者pad实时拍照上传到大屏幕的照片墙现场大屏电子照片墙功能&#xff0c;每个人都可以通过手机实时拍照上传到大屏幕上,同时还可以发布留言内容&#xff0c;屏幕上会同步滚动播放展示所有人的照片和留言。相比校传统的照片直播功能更加灵活方便&#xff0c;而…

什么是事务?并发事务引发的问题?什么是MVCC?

文章目录 什么是事务&#xff1f;并发事务引发的问题&#xff1f;什么是MVCC&#xff1f;1.事务的四大特性2.并发事务下产生的问题&#xff1a;脏读、不可重复读、幻读3.如何应对并发事务引发的问题&#xff1f;4.什么是MVCC&#xff1f;5.可见性规则&#xff1f;参考资料 什么…

两相四线步进电机的步距角为什么是1.8度

机缘 在CSDN查了好多文章&#xff0c;发现都是用公式来解释1.8的步距角&#xff08;Q&#xff1d;360&#xff0f;MZ&#xff09;&#xff0c;因为转子是50齿&#xff0c;4拍一个循环&#xff0c;所以θ360度/&#xff08;50x4&#xff09;1.8度。估计第一次接触步进电机的什么…

SPRING10_SPRING的生命周期流程图

经过前面使用三大后置处理器BeanPostProcessor、BeanFactoryPostProcessor、InitializingBean对创建Bean流程中的干扰,梳理出SPRING的生命周期流程图如下

科普mfc100.dll丢失怎么办?有没有简单的方法修复mfc100.dll文件

当电脑频繁弹窗提示“mfc100.dll丢失”或应用程序突然闪退时&#xff0c;这个看似普通的系统文件已成为影响用户体验的核心痛点。作为微软基础类库&#xff08;MFC&#xff09;的核心组件&#xff0c;mfc100.dll直接关联着Visual Studio 2010开发的大量软件运行命脉。从工业设计…

VUE3+TS+element-plus项目从0开始入门 - 创建项目、认识基本结构

文章目录 写在前面1、创建vue3项目npm create vuelatestnpm i 2、项目结构.vscodevue3结构a、项目树结构b、package.jsonc、tsconfig.jsond、index.htmld、srce、main.tsf、App.vue 写在前面 开前请自行下载vs code、node.js, 在vs code里面安装Vue - Official插件。本文使用的…

Rpc导读

手写Rpc框架 - 导读 git仓库-all-rpc GTIEE&#xff1a;https://gitee.com/quercus-sp204/all-rpc 【参考源码 yrpc】 1. Rpc概念 RPC 即远程过程调用&#xff08;Remote Procedure Call&#xff09; &#xff0c;就是通过网络从远程计算机程序上请求服务。 本地调用抽象&…