Python实战:用多线程和多进程打造高效爬虫

news2024/11/28 6:52:30

文章目录

  • 🍋引言
  • 🍋为什么要使用多线程和多进程?
  • 🍋线程的常用方法
  • 🍋线程锁(也称为互斥锁或简称锁)
  • 🍋小案例
  • 🍋实战---手办网
  • 🍋总结

🍋引言

在网络爬虫的世界里,效率是关键。为了快速地获取大量数据,我们需要运用一些高级技巧,如多线程和多进程。在本篇博客中,我们将学习如何使用Python的多线程和多进程来构建一个高效的网络爬虫,以便更快速地获取目标网站上的信息。

🍋为什么要使用多线程和多进程?

在单线程爬虫中,我们按照顺序一个个页面地下载和解析数据。这在小型网站上可能没有问题,但在处理大规模数据时会变得非常缓慢。多线程和多进程可以帮助我们同时处理多个页面,从而提高爬虫的效率。

  • 多线程:在一个进程内,多个线程可以并发执行,共享相同的内存空间。这意味着它们可以更快速地完成任务,但需要小心线程安全问题。

  • 多进程:多个进程是独立的,每个进程都有自己的内存空间。这可以避免线程安全问题,但在创建和管理进程时会产生额外的开销。

🍋线程的常用方法

  • threading.Thread(target, args=(), kwargs={}):创建一个线程对象,用于执行指定的目标函数。
    target:要执行的目标函数。
    args:目标函数的位置参数,以元组形式传递。
    kwargs:目标函数的关键字参数,以字典形式传递。

  • thread.start():启动线程,使其开始执行目标函数。

  • thread.join(timeout=None):等待线程完成执行。可选的timeout参数用于指定最长等待时间,如果超时,将继续执行主线程。

  • thread.is_alive():检查线程是否在运行。如果线程仍在执行中,返回True;否则返回False。

  • threading.current_thread():返回当前线程对象,可以用于获取当前线程的信息。

  • threading.active_count():返回当前活动线程的数量。

  • threading.enumerate():返回当前所有活动线程的列表。

  • threading.Thread.getName()和threading.Thread.setName(name):获取和设置线程的名称。

  • threading.Thread.isDaemon()和threading.Thread.setDaemon(daemonic):获取和设置线程的守护状态。守护线程在主线程结束时会被强制终止。

  • threading.Thread.ident:获取线程的唯一标识符。

  • threading.Lock():创建一个互斥锁对象,用于实现线程同步。

  • lock.acquire():获取锁,进入临界区。

  • lock.release():释放锁,退出临界区。

  • .sleep() 方法是线程对象(threading.Thread)中的一个非常常用的方法之一,它用于使线程暂停执行一段指定的时间,但是用到的库是time

🍋线程锁(也称为互斥锁或简称锁)

用于线程间的同步,主要有以下两个目的:

  • 防止竞争条件(Race Condition): 竞争条件指的是多个线程在同时访问共享资源时,由于执行顺序不确定而导致的不确定性和错误。当多个线程尝试同时修改共享数据时,可能会导致数据不一致性和错误结果。线程锁可以防止竞争条件,使得只有一个线程能够访问共享资源,其他线程需要等待锁释放。

  • 确保数据一致性: 在多线程环境下,当多个线程同时访问和修改共享数据时,可能会导致数据的不一致性。使用锁可以确保在任何时候只有一个线程能够访问和修改共享数据,从而确保数据的一致性。

这里可以举个例子:

当我们在银行取钱的时候,如果没有线程锁的保护,一大堆人同时取钱,那么余额就不好计算了。所以在多个线程同时进行存款和取款操作,但由于线程锁的保护,这些操作不会导致账户数据的混乱或错误。最后,我们打印出最终的账户余额,以确保数据一致性。

🍋小案例

这里我们通过一个小案例感受一下多线程的魅力吧~

import time
import threading
import requests
from bs4 import BeautifulSoup

urls = []
for j in range(1, 5):
    url = f'https://example.com/page{j}'
    urls.append(url)
# 存储字典
results = {}
lock = threading.Lock()
def func(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    title = soup.title.string
    # 使用线程锁确保字典操作的线程安全
    with lock:
        results[url] = title
# 创建线程列表
threads = []
start = time.time()
# 创建并启动线程
for url in urls:
    thread = threading.Thread(target=func, args=(url,))
    threads.append(thread)
    thread.start()

# 等待所有线程完成
for thread in threads:
    thread.join()
stop = time.time()
# 打印爬取结果
for url, title in results.items():
    print(f'URL: {url}, Title: {title}')
print(stop-start)

这里我们用测试网页中的title在这里插入图片描述

在这个示例中,我们定义了一个包含多个网页url的列表 urls,然后创建了多个线程来并发地爬取这些网页的标题。每个线程使用 requests 库发送请求,解析网页内容,提取标题,并将结果存储在一个共享的 results 字典中。为了确保字典操作的线程安全,我们使用了一个线程锁 lock。最后用time测试了一次时间。

🍋实战—手办网

先将之前相关爬取手办网放在下方

# 导入模块
import requests
from bs4 import BeautifulSoup
import time
import threading
# 定义url和请求头
_headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
                "Cookie": "utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105"
            }

"""
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=1
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=2
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=3
"""
start = time.time()
# 获取前五页的url
urls = []
for i in range(1,5):
    url ='https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page={}'.format(i)
    urls.append(url)
items = []
for d_url in urls:
    # 发送请求
    response = requests.get(d_url, headers=_headers)
    content = response.content.decode('utf8')
    # 实例化对象
    soup = BeautifulSoup(content, 'lxml')
    # 名称
    data = soup.find_all('ul',class_="hpoi-glyphicons-list")
    for i in data:
        data_1 = i.find_all('li')
        for j in data_1:
            data_2 = j.find_all('div',class_="hpoi-detail-grid-right")
            for k in data_2:
                title = k.find_all('a')[0].string
                changshang = k.find_all('span')[0].text[3:]
                chuhe = k.find_all('span')[1].text[3:]
                price = k.find_all('span')[2].text[3:]
                data_3 = {
                    "名称": title,
                    "厂商":changshang,
                    "出荷":chuhe,
                    "价位":price
                }
                items.append(data_3)
print(items)

stop = time.time()
print(stop-start)

运行结果如下
在这里插入图片描述

之后我们使用多线程进行实现

# 导入模块
import requests
from bs4 import BeautifulSoup
import time
import threading
# 定义url和请求头
_headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
                "Cookie": "utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105utoken=UTKbd23efb1729a444898977cf2a91381c0; JSESSIONID=9EF5C8BA4F3C3E29278A9972A946408A; Hm_lvt_05b494824003cbf80b23e846462269d1=1688387237,1688712147,1689130492,1689145041; allOrder=release; Hm_lpvt_05b494824003cbf80b23e846462269d1=1689145105"
            }

"""
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=1
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=2
https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page=3
"""
start = time.time()
# 获取前五页的url

items = []
def shouban(url):
# for d_url in urls:
    # 发送请求
    response = requests.get(url, headers=_headers)
    content = response.content.decode('utf8')
    # 实例化对象
    soup = BeautifulSoup(content, 'lxml')
    # 名称
    data = soup.find_all('ul',class_="hpoi-glyphicons-list")
    for i in data:
        data_1 = i.find_all('li')
        for j in data_1:
            data_2 = j.find_all('div',class_="hpoi-detail-grid-right")
            for k in data_2:
                title = k.find_all('a')[0].string
                changshang = k.find_all('span')[0].text[3:]
                chuhe = k.find_all('span')[1].text[3:]
                price = k.find_all('span')[2].text[3:]
                data_3 = {
                    "名称": title,
                    "厂商":changshang,
                    "出荷":chuhe,
                    "价位":price
                }
                items.append(data_3)

urls = []
t_list = []
for i in range(1,5):
    url =f'https://www.hpoi.net/hobby/all?order=release&r18=-1&workers=&view=3&category=100&page={i}'
    t1 = threading.Thread(target=shouban, args=(url,))
    t1.start()
    t_list.append(t1)

for i in t_list:
    i.join()

stop = time.time()
print(items)
print(stop-start)

运行结果如下
在这里插入图片描述
注意:可以适当添加sleep,防止被封,如果爬取大量数据,多线程表现的会更明显一点

🍋总结

通过使用多线程和多进程,我们可以显著提高网络爬虫的效率,更快地获取大量数据。然而,要小心线程安全问题和进程管理的开销。在实际项目中,还需要考虑异常处理、数据存储等更多细节,感谢看到结尾的小伙伴,感谢您的支持!

请添加图片描述

挑战与创造都是很痛苦的,但是很充实。

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

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

相关文章

【JavaSpring】Aop的通知类型,获取数据

AOP 通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置 前置通知 Pointcut("execution(void org.example.dao.BookDao.update())")private void pt() {}Before("pt()")public void befo…

数据结构与算法:排序算法(2)

目录 堆排序 使用步骤 代码实现 计数排序 适用范围 过程 代码实现 排序优化 桶排序 工作原理 代码实现 堆排序 二叉堆的特性: 1. 最大堆的堆顶是整个堆中的最大元素 2. 最小堆的堆顶是整个堆中的最小元素 以最大堆为例,如果删除一个最大堆的…

基于Java开发的数字化询价招标采购系统(SRM系统源码)

在如今商业环境中,企业的采购流程变得越来越重要。传统的采购方式可能存在诸多弊端,例如效率低下、信息不透明、易滋生腐败等。为了解决这些问题,许多企业开始转向SRM(供应商关系管理)系统。本文将详细介绍SRM数字询价…

js表单autocomplete=‘off‘失效问题

众所周知。。。。autocomplete是Html5中的新属性,有‘off’,on’两个属性。作用是点击输入框时,会打开或者关闭提示信息。 部分浏览器也会出现失效的情况(emmmm…,毕竟是html5新增的,有点bug也正常哈)。 …

初识Java 10-1 集合

目录 泛型和类型安全的集合 基本概念 添加一组元素 打印集合 List Iterator(迭代器) 本笔记参考自: 《On Java 中文版》 在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着&am…

Vue3 - 实现动态获取菜单路由和按钮权限控制指令

GitHub Demo 地址 在线预览 前言 关于动态获取路由已在这里给出方案 Vue - vue-admin-template模板项目改造:动态获取菜单路由 这里是在此基础上升级成vue3和ts,数据和网络请求是通过mock实现的 具体代码请看demo!!! 本地权限控制,具体是通过…

关于若依(ruoyi)前端,f12跟踪失效的问题处理

1、根据作者反馈,使用了vite-plugin-vue-setup-extend该插件; 2、参考作者指导,我采用了去掉这个插件的方法; 具体操作: (1)找到package.json,去掉该插件; (2&#xff…

新的小伙伴加入,开始系统更新分享了

近几个月一直有一个好消息未跟大家分享,就是我们有新的小伙伴加入了,帅就不必说了,关键是对电控的理解那可不是一般的强,工程经验丰富,学术能力也是一等一的。我们有幸在一个公司工作,跟着一个企业导师学习…

10个值得关注的学习网站,知乎超30万人收藏,什么资源都可找到!

hi,大家好我是技术苟,每周准时上线为你带来实用黑科技!由于公众号改版,现在的公众号消息已经不再按照时间顺序排送了。因此小伙伴们就很容易错过精彩内容。喜欢黑科技的小伙伴,可以将黑科技百科公众号设为标星&#xf…

Webstorm怎么导入插件

Webstorm怎么导入插件: 1.点击“File”,选择“Settings” 2.选择“Plugins” 3.如下图所示继续操作 4.选择想要导入的插件

【C++/Python】Windows用Swig实现C++调用Python(史上最简单详细,80岁看了都会操作)

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化【获取源码商业合作】 👉荣__誉👈:阿里云博客专家博主、5…

自动化测试:yaml结合ddt实现数据驱动!

在pythonunittestseleniumddt的框架中,数据驱动常见有以下几种方式实现: Csv/txtExcelYAML 本文主要给大家介绍测试数据存储在YAML文件中的使用场景。首先先来简单介绍一下YAML。 1. 什么是YAML 一种标记语言类似YAML,它实质上是一种通用…

方案:浅析AI视频分析与视频监控技术的工厂车间智能化监管方案

一、方案背景 工厂生产车间一般是从原材料到成品的流水作业,有大量器械和物料。为保障车间财产安全并提高生产效率,需要进行全面的监管。在生产制造流水线的关键工序中,不仅有作业过程监管需求,同时,也存在生产发生异…

全网最全知识图谱讲解!

什么是知识图谱 知识图谱标准化白皮书定义:知识图谱(Knowledge Graph)以结构化的形式描述客观世界中概念、实体及其关系,将互联网的信息表达成更接近人类认知世界的形式,提供了一种更好地组织、管理和理解互联网海量信…

Jmeter怎么实现接口关联?

用于接口测试时,后一个接口经常需要用到前一次接口返回的结果,应该如何获取前一次请求的结果值,应用于后一个接口呢,拿一个登录的例子来说明如何获取。 1、打开jmeter,新建一个测试计划,在测试计划里新建一…

分享基于SringBoot足球训练俱乐部系统Python训练打卡系统(源码+调试+lw)

💕💕作者:计算机源码社 💕💕个人简介:本人七年开发经验,擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等,大家有这一块的问题可以一起交流! 💕&…

SMS--短信服务

1 短信服务介绍 短信服务(Short Message Service)是阿里云为用户提供的一种通信服务的能力。 2 短信服务使用 接下来,我们使用短信验证码功能来演示短信服务的使用。流程如下: 2.1 准备工作 2.1.1 实名认证 https://help.aliyun.com/document_detail…

智能箱式浪涌保护器综合行业解决方案

智能箱式浪涌保护器是一种集成了多种功能的浪涌保护装置,它可以对电力系统、通信系统、计算机系统、工业控制系统等设备提供有效的防雷和过电压保护。本文将详细介绍智能箱式浪涌保护器的作用和原理,以及在不同行业中的应用方案,并参考相关的…

【Hash表】两数之和-力扣 1 题

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

【国产32位mcu】电动车控制芯片CS32F031C8T6的应用

近年来,随着“新国标”的落地,双轮电动车在智能化、强性能、安全性等方面不断演进,带动了新一轮的换车高峰。电动车控制器作为双轮电动车的核心部件,迎来新的增长。 芯海科技32位MCU CS32F031C8T6,作为电动车控制器的…