我的编程空间,编程开发者的网络收藏夹
学习永远不晚

python爬虫入门八:多进程/多线程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

python爬虫入门八:多进程/多线程

引用虫师的解释:

计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。

进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。

线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。我们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。

 

我们直接编写的爬虫程序是单线程的,在数据需求量不大时它能够满足我们的需求。

但如果数据量很大,比如要通过访问数百数千个url去爬取数据,单线程必须等待当前url访问完毕并且数据提取保存完成后才可以对下一个url进行操作,一次只能对一个url进行操作;

我们使用多线程/多进程的话,就可以实现对多个url同时进行操作。这样就能大大缩减了爬虫运行时间。

 

多线程

python提供了两组多线程接口,一是thread模块_thread,提供低等级接口;二是threading模块,在thread模块基础上进行封装,提供更容易使用的基于对象的接口,可以继承Thread对象来实现多线程。

同时,还有其他线程相关的对象,如Timer、Lock等。

在这里,我们使用threading模块实现多线程。

1. 添加线程

threading.Thread(target, args)

使用threading.Thread()新建一个线程,target是需要执行的函数,args是需要传入该函数的参数,args接受一个tuple,即使只有一个参数也需要写成(x,)形式

import threading

print(threading.active_count()) # 显示当前激活的线程数
print(threading.enumerate()) # 显示当前激活的线程
print(threading.current_thread()) # 当前运行的线程 


def thread_job():
    print('This is a thread of %s' % threading.current_thread())

def main():
    thread = threading.Thread(target=thread_job,) # 添加一个线程
    thread.start() # 开始该线程

if __name__ == '__main__':
    main()

2. 线程阻塞:join

join()的作用是调用该线程时,等待该线程完成后再继续往下运行。

join通常用于主线程与子线程之间,主线程等待子线程运行完毕后再继续执行,避免子程序和主程序同时运行,子程序还没有运行完的时候主程序就已经运行结束。

import threading
import time

# 定义一个fun,传入线程
def T1_job():
    print('T1 start\n')
    for i in range(10):
        time.sleep(0.1)
    print('T1 finish\n')
        
def T2_job():
    print('T2 start\n')
    print('T2 finish\n')
        
def main():
    thread1 = threading.Thread(target=T1_job, name='T1') # 添加线程,准备执行thread_job,命名T1
    thread2 = threading.Thread(target=T2_job, name='T2')
    
    thread1.start() # 执行该线程,没有添加join的时候,同步执行main和thread_job
    thread2.start()
    
    thread1.join() # 等待thread1完成后才进行下一步-主程序
    thread2.join() # 等待thread2完成后才进行下一步-主程序
    print('all done')

if __name__ == '__main__':
    main()

3. 信息传递:Queue队列

Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列。

Queue是一种先进先出的数据结构,一般来说读数据都从Queue头读,写数据都从Queue尾写入。

import threading
from queue import Queue

def job(l, q):
    for i in range(len(l)):
        l[i] = l[i]**2
    q.put(l) # 线程中,return获取的值无法提取,需要放入q中

def multithreading():
    q = Queue() # 队列
    threads = [] # 全部线程
    data = [[1, 2, 3], [3, 4, 5], [4,4,4], [5,5,5]]
    for i in range(4):
        # 4个线程来执行job函数
        t = threading.Thread(target=job, args=(data[i], q))
        t.start()
        threads.append(t) # 当前线程加入全部线程中
        
    # 对主线程中的每一个线程都执行join()
    for thread in threads:
        thread.join()
   
    results = [] # 保存结果
    for _ in range(4):
        results.append(q.get()) # 从q中拿出值,每次只能按顺序拿出一个值
    print(results)
    
if __name__ == '__main__':
    multithreading()

# [[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]

4. 线程锁:Lock

lock在不同线程使用同一共享内存时,能够确保线程之间互不影响。

使用lock的方法是:在每个线程执行运算修改共享内存之前执行lock.acquire()将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问;

执行运算完毕后使用lock.release()将锁打开, 保证其他的线程可以使用该共享内存。

lock.acquire()和lock.release()必须成对出现

# lock锁,当前线程运行完成后才进行下一进程
import threading

def job1():
    global A, lock
    lock.acquire() # 打开锁
    for i in range(10):
        A += 1
        time.sleep(0.2)
        print('job1', A)
    lock.release() # 关闭锁
    
def job2():
    global A, lock
    lock.acquire() # 打开锁
    for i in range(10):
        A += 10
        time.sleep(0.2)
        print('job2', A)
    lock.release() # 关闭锁
    
if __name__ == '__main__':
    lock = threading.Lock() # lock锁
    A = 0
    t1 = threading.Thread(target=job1)
    t2 = threading.Thread(target=job2)
    t1.start()
    t2.start()

将上述代码中的lock.acquire()和lock.release()四行代码注释后运行,就是不加锁的情况,这时候输出结果都是混乱的。而加锁后,输出结果正常。

5. 线程池

线程池有几种方法可以实现,这里我们使用multiprocessing.dummy库。

from multiprocessing.dummy import Pool as ThreadPool # 线程池
import threading

def job(i):
    print(i, '\n', threading.current_thread())
    
if __name__ == '__main__':
    pool = ThreadPool(4) # 创建一个包含4个线程的线程池
    pool.map(job, range(12))
    pool.close() # 关闭线程池的写入
    pool.join() # 阻塞,保证子线程运行完毕后再继续主进程 

 

多进程

多进程multiprocessing和多线程threading类似,都是用在python中进行并行计算的,而多进程则是为了弥补python在多线程中的劣势而出现的。

multiprocessing是使用计算机的多核进行运算,它可以避免多线程中GIL的影响。

python使用multiprocessing模块实现多进程,用法和threading基本一致。

1. 添加进程

multiprocessing.Process(target, args)

使用multiprocessing.Process新建一个进程,target是需要执行的函数,args是需要传入该函数的参数,args接受一个tuple,即使只有一个参数也需要写成(x,)形式

import multiprocessing as mp

def job(a,d):
    print('aaaaa')

if __name__=='__main__':
    p1 = mp.Process(target=job,args=(1,2)) # 添加一个进程
    p1.start()
    p1.join()

2. 信息传递:Queue队列

多进程中的Queue使用同多线程一致,同样为先进先出

多进程可以直接从multiprocessing.Queue()导入Queue队列。

import multiprocessing as mp

def job(q):
    res=0
    for i in range(1000):
        res+=i+i**2+i**3
    q.put(res)    # 将值放入队列

if __name__=='__main__':
    q = mp.Queue() # Queue队列
    p1 = mp.Process(target=job,args=(q,))
    p2 = mp.Process(target=job,args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    res1 = q.get() # 从队列中取出值
    res2 = q.get() # 从队列中取出值
    print(res1, res2)

3. 进程池

import multiprocessing as mp

def job(x):
    return x*x

def multicore():
    pool = mp.Pool() # 定义一个进程池
    res = pool.map(job, range(100))
    print(res)

if __name__=='__main__':
    multicore()

 关于进程池的更多信息请跳转至:

 4. 共享内存

一般的变量在进程之间是没法进行通讯的,multiprocessing 给我们提供了 Value 和 Array 模块,他们可以在不通的进程中共同使用。

Value()和Array()都接受两个参数,第一个为数据类型,第二个是传入的数。

Value()可以接受传入单个数值,Array()可以接受传入一个一维数组

import multiprocessing as mp

value1 = mp.Value('i', 0) # value接受单个数值,i表示一个带符号的整型
array = mp.Array('i', [1, 2, 3, 4]) # Array接受一个一维数组

array2 = mp.Array('i', [[1,2], [2,3]]) # 传入一个二维数组错误,传入参数非一维数组

数据类型如下:

Type code
C Type Python Type Minimum size in bytes
'b' signed char int 1
'B' unsigned char int 1
'u' py_UNICODE Unicode character 2
'h' signed short int 2
'H' unsigned short int 2
'i' signed int int 2
'I' unsigned int int 2
'l' signed long int 4
'L' unsigned long int 4
'q' signed long long int 8
'Q' unsigned long long int 8
'f' float float 4
'd'
double
float 
8

5. 进程锁

进程锁同线程锁使用方法一致,lock在不同进程使用同一共享内存时,能够确保进程之间互不影响。

使用lock的方法是:在每个进程执行运算修改共享内存之前执行lock.acquire()将共享内存上锁, 确保当前进程执行时,内存不会被其他进程访问;

执行运算完毕后使用lock.release()将锁打开, 保证其他的进程可以使用该共享内存。

lock.acquire()和lock.release()必须成对出现。 

import multiprocessing as mp

def job(v, num, l):
    l.acquire() # 锁住
    for _ in range(5):
        time.sleep(0.1) 
        v.value += num # 获取共享内存
        print(v.value)
    l.release() # 释放

def multicore():
    l = mp.Lock() # 定义一个进程锁
    v = mp.Value('i', 0) # 定义共享内存
    p1 = mp.Process(target=job, args=(v,1,l)) # 需要将lock传入
    p2 = mp.Process(target=job, args=(v,3,l)) 
    p1.start()
    p2.start()
    p1.join()
    p2.join()

if __name__ == '__main__':
    multicore()

 

1. 结论

CPU密集型代码(各种循环处理、计算等等):使用多进程

IO密集型代码(文件处理、网络爬虫等):使用多线程

2. 解释

多线程和多进程的理解可以类比于公路。

假设当前公路均为单行道,并且出于安全考虑,一个车道只能同时行驶一辆汽车,一条公路只有一名驾驶员。只有一名指挥者进行集中调度,驾驶员获取到了指挥者的调度信息才会驾驶。

单线程是只有一条公路而且是单车道,只能同时行驶一辆汽车;

多线程是只有一条公路,但是是多车道,可以同时行驶多辆汽车;

多进程是有很多条公路,每条公路可能是单车道也可能是多车道,同样可以同时行驶多辆汽车。

 

因为GIL的存在,python中的多线程其实在同一时间只能运行一个线程,就像一名驾驶员只能同时驾驶一辆汽车。四线程类比于一条四车道的公路,但是驾驶员可以从驾驶车道A上的汽车切换至驾驶车道B上的汽车,驾驶员切换的速度够快的话,看起来就像是这条公路上的四辆汽车都在同时行驶。指挥者发布的命令只需要跨越车道就能传递给驾驶员,命令传输的时间损耗相对较小。所以对于多线程,我们希望指挥者可以比较频繁发布命令,驾驶员获取到命令后能够很快就完成然后切换到下一个车道继续执行命令,这样看起来就像是驾驶员同时驾驶四辆汽车了。所以对于IO密集型代码,推荐使用多线程。

而对于多进程来说,每条公路都有一名驾驶员,四线程类比于四条公路,则四名驾驶员可以同时驾驶四辆汽车。但指挥者发布的命令需要跨越公路才能传递给驾驶员,命令传输的时间损耗相对较大。所以对于多进程,我们希望指挥者发布一次命令后驾驶员可以执行较长时间,这样就不必把时间过多花费在信息传输上。所以对于CPU密集型代码,推荐使用多进程。 

 

1. python的多线程中的join的作用

2. python队列Queue

3. Python多线程(2)——线程同步机制

4. 莫烦PYTHON-Threading多线程

5. Python 多进程锁 多进程共享内存

6. python学习笔记——多进程中共享内存Value & Array

7. 莫烦PYTHON-multiprocessing多进程

8. python 之 多进程

9. Python多进程 

10. Python 使用multiprocessing 特别耗内存

11. 廖雪峰-进程和线程 

12. python 多线程,详细教程,线程同步,线程加锁,ThreadPoolExecutor

13. 多进程 multiprocessing 多线程Threading 线程池和进程池concurrent.futures

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

python爬虫入门八:多进程/多线程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

python爬虫入门八:多进程/多线程

引用虫师的解释:计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记
2023-01-30

Python多线程、异步+多进程爬虫实现代码

安装Tornado 省事点可以直接用grequests库,下面用的是tornado的异步client。 异步用到了tornado,根据官方文档的例子修改得到一个简单的异步爬虫类。可以参考下最新的文档学习下。 pip install tor
2022-06-04

Python+多线程+队列爬虫

Python+多线程+队列,爬虫例子# -*- coding: utf-8-*-import urllib2import urllibimport jsonimport timeimport datetimeimport threading
2023-01-31

python爬虫中多线程和多进程的示例分析

小编给大家分享一下python爬虫中多线程和多进程的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象的脚本语言,其最初的设计是用于
2023-06-14

Python 爬虫学习笔记之多线程爬虫

XPath 的安装以及使用 1 . XPath 的介绍 刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊。其实我个人认为学习一下正则表达式是大有益处的,
2022-06-04

爬虫学习之第四章爬虫进阶之多线程爬虫

有些时候,比如下载图片,因为下载图片是一个耗时的操作。如果采用之前那种同步的方式下载。那效率肯会特别慢。这时候我们就可以考虑使用多线程的方式来下载图片。多线程介绍:多线程是为了同步完成多项任务,通过提高资源使用效率来提高系统的效率。线程是在
2023-01-31

多线程爬虫介绍

一个进程里只有一个线程,我们称之为单线程爬虫。单线程爬虫每次只访问一个页面,不能充分利用电脑的网络带宽。一个页面最多也就几百KB,所以爬虫在爬取一个页面的时候,多出来的网速就浪费掉了。而如果我们可以让爬虫同时访问10个页面,就相当于我们的爬
2023-01-30

Python多线程爬虫简单示例

python是支持多线程的,主要是通过thread和threading这两个模块来实现的。thread模块是比较底层的模块,threading模块是对thread做了一些包装的,可以更加方便的使用。 虽然python的多线程受GIL限制,并
2022-06-04

Python 爬虫 多进程清洗代理

1 import requests 2 from lxml import etree 3 import time 4 import multiprocessing 5 6 def get_all_proxy(queue): 7 u
2023-01-30

Python怎么实现selenium多线程爬虫

要在Python中实现Selenium多线程爬虫,你可以按照以下步骤进行操作:导入必要的库:from selenium import webdriverfrom threading import Thread创建一个继承自`Thread`
2023-10-24

Python爬虫入门教程 15-100

石家庄政民互动数据爬取-写在前面今天,咱抓取一个网站,这个网站呢,涉及的内容就是 网友留言和回复,特别简单,但是网站是gov的。网址为http://www.sjz.gov.cn/col/1490066682000/index.html首先声
2023-01-30

Python爬虫入门教程 19-100

从今天开始的几篇文章,我将就国内目前比较主流的一些在线学习平台数据进行抓取,如果时间充足的情况下,会对他们进行一些简单的分析,好了,平台大概有51CTO学院,CSDN学院,网易云课堂,慕课网等平台,数据统一抓取到mongodb里面,如果对上
2023-01-30

Python爬虫入门教程 29-100

1. 手机APP数据----写在前面继续练习pyspider的使用,最近搜索了一些这个框架的一些使用技巧,发现文档竟然挺难理解的,不过使用起来暂时没有障碍,估摸着,要在写个5篇左右关于这个框架的教程。今天教程中增加了图片的处理,你可以重点学
2023-01-30

Python爬虫入门教程 32-100

1. B站博人传评论数据爬取简介今天想了半天不知道抓啥,去B站看跳舞的小姐姐,忽然看到了评论,那就抓取一下B站的评论数据,视频动画那么多,也不知道抓取哪个,选了一个博人传跟火影相关的,抓取看看。网址: https://www.bilibil
2023-01-30

Python爬虫入门教程 38-100

爬前叨叨今天要爬取一下正规大学名单,这些名单是教育部公布具有招生资格的高校名单,除了这些学校以外,其他招生的单位,其所招学生的学籍、发放的毕业证书国家均不予承认,也就是俗称的野鸡大学!网址是 https://daxue.eol.cn/min
2023-01-30

Python爬虫入门教程 59-100

图片比对昨天的博客已经将图片存储到了本地,今天要做的第一件事情,就是需要在两张图片中进行比对,将图片缺口定位出来缺口图片完整图片计算缺口坐标对比两张图片的所有RBG像素点,得到不一样像素点的x值,即要移动的距离 def get_dis
2023-01-31

Python爬虫入门教程 61-100

python3爬虫遇到了反爬当你兴冲冲的打开一个网页,发现里面的资源好棒,能批量下载就好了,然后感谢写个爬虫down一下,结果,一顿操作之后,发现网站竟然有反爬措施,尴尬了。接下来的几篇文章,我们研究一下各种反爬虫套路,当然互联网没有100
2023-01-31

Python爬虫入门教程 10-100

图虫网-写在前面经历了一顿噼里啪啦的操作之后,终于我把博客写到了第10篇,后面,慢慢的会涉及到更多的爬虫模块,有人问scrapy 啥时候开始用,这个我预计要在30篇以后了吧,后面的套路依旧慢节奏的,所以莫着急了,100篇呢,预计4~5个月写
2023-01-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录