首页 > 编程知识 正文

python爬虫多线程和多进程(python创建多进程)

时间:2023-05-05 15:21:31 阅读:105548 作者:4154

如果要下载大量图片,一定希望速度越快越好,所以要多任务使用。

Python支持多线程和多进程。然而,在解释器中的GIL锁导致任何Python线程执行之前,它必须首先获得GIL锁。然后,每次执行100个字节码,解释器就会自动释放GIL锁,给其他线程一个执行的机会。因此,多线程不能达到预期的效果。

当使用多个进程时,多进程是一个非常有用的库。如果是流程的创建,使用流程类;如果它很大,请使用池类。如果涉及进程间通信,还应该使用队列类和管道类。

综合考虑,选择公摊。

首先获取所有图片的URL(这一步也可以使用多进程),然后创建一个Pool,子进程从前到后读取URL,然后将图片信息保存在本地。

两个主要功能如下所示。在save _ pictures(URL)中创建一个容量为40的Pool对象(默认情况下,它与系统中的CPU内核数相同,这个值可以通过多处理. cput_count())知道),然后不断创建子进程来下载任务。P.close()表示不能再添加子进程,p.join()表示在子进程的所有任务完成之前,不会继续执行。

在save_pic(pic_url,filename)函数中,在一个进程中下载图片。使用urllib.urlretrieve (URL、文件名、reporthook、数据)函数下载。这个功能是一块一块下载的,方便。报表挂钩回调函数可以用来计算下载进度,这里不使用。具体实现方法可以看源代码。

![上传截图12 . 52 . 52 _ 787403.png.]](http://upload-images.jianshu.io/upload _ images/207122-73 A0 facdef d1b687.png?imagemog 2/自动定向/剥离|imageView2/2/w/1240)

在主函数中,时间消耗通过以下形式计算。

d1=datetime.datetime.now()

保存图片(结果)

d2=datetime.datetime.now()

这是多进程和单进程之间的简单比较。其实打开了40个进程,下载了5000张图片,总计5.56G。

#单任务,URL open:0:15336026.152427 38图片38.1 MB 42.12 KB/s

#8进程,URL检索:0:09:47.217917 38图片38.1mb66.46kb/s

#40进程,网址检索:1:43:06.125514 5.56 g943.38 kb/s

其实我们可以从Mac的活动监视器上看到实时的网络下载速度,半夜40个进程可以达到4M/s。因此,要处理这样的重复性任务,我们必须选择多任务。

然而,代码并不是一下子就这样写出来的,即使场景一点也不复杂。

第一次跑,跑了一晚上也没下载完。我查了一下文件的修改时间,发现都是第二天早上了。我猜是循环错误的某个部分导致了文件被一次又一次地下载和写入。后来发现某处多了一个循环,导致每张图片被下载了100多次。

后来发现刚开始下载很快,后来又慢得不可思议。同时还有一种现象,就是下载前创建目录,只能创建一部分,然后就停止了,也就是说下载周期停止了。后来我把创建的目录分开,防止它受到下载图片任务的影响。我怀疑之前Pool对象的创建出现了问题,导致创建了大量的子进程,然后由于通信超时和进程太多,很难进行调度。代码更改后,这个问题不再出现,所以没有进一步的探索。

再跑的时候跑完了,但是有些图片是完全下载的,有些是部分下载的,剩下的都是黑块,这种现象不是零星出现的,而是大规模出现的。有完全打不开的图片,只有1k或者2k。

我怀疑这种现象是超时造成的,因为urlretrieve没有超时设置参数,所以是通过socket.setdefaulttimeout(30)强制设置的。同时,异常处理用于处理超时时的循环操作。采用的结构如下。

而:

尝试:

.

除.之外.

.

else:

.

)

然而,这种现象并没有消除。后来,当我试图用终端重现这些照片的下载过程时,我发现会抛出其他错误。主要原因是我想下载的服务器不稳定,不小心。

就访问失败了(我怀疑是由于我的大量进程访问导致的)。所以看我的save_pic函数中是有2个except的,一个单独处理socket.timeout异常,另一个用来处理其他异常。

其实这种现象没经验的话是很难判断的,因为我设置的是30s超时,那么按照理想情况,所有正在下载的图片在文件系统的修改时间应该都在30s之内,只有下载好了的才会超过这个时间。然而实际情况那些只下载了部分的图片的修改时间也是好几分钟之前了。查看打印消息,也发现这些图片只有try部分的没有else部分的也没有except socket.timeout部分。

那为什么打印消息中没有抛出的异常信息,程序没有崩溃?我觉得由于是子进程出错,所以并不会对主进程造成影响导致。

所以说,异常处理一定要对每种场景都包括,要有对默认出错的处理方法,特别是多进程时,无声无息被kill,让人摸不着头脑。

另外还犯了一个错,就是在except中把urllib.urlretrieve也写到下面,这样导致在except中出异常时,不再能够处理异常。后续把urlretrieve只放到try中,其他的except只进行打印和计数,然后让循环来帮我重新下载。

修改完毕后基本跑通,各种异常信息都能够打印出来并处理,整体无比和谐,除了个别图片依然是1k或2k的大小,查了下网页代码,发现是bug,也就不作理会了。

总结:多任务很好用,异常处理要用心。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。