python从网址爬图片协程_【Python】协程爬虫实现断点续爬与分布式爬虫原理举例...

Python协程爬虫实现断点续爬与分布式爬虫原理举例

具体网站我已经去除,该代码仅作编程技术的学习交流

前言

之前几次都是在写多线程或者是多进程的爬虫,其实Python里最强的还是协程爬虫,因为对于这类I/O密集型任务,用多进程就是杀鸡用牛刀,太消耗系统资源了,而Python里的多线程又有个GIL(全局锁),这导致Python的多线程其实是一个“假的”多线程,所以协程的优势就体现出来了,协程也叫微线程,会在I/O阻塞时自动将任务转向其它协程和主线程,并且消耗的资源极小。如果我们开3000个线程,普通电脑很有可能蓝屏,但我们即使开10000个协程,电脑也没什么感觉,效果却可以和10000个线程相媲美,因此,其实协程才是最适合爬虫的。

先给你们看看协程的速度,(单线程时只有几百K/s),这个站点50多g图片单线程要爬46个小时才能爬完,协程只需要一个半小时。

NowYz8.png

断点续爬

1.我们知道,像这种资源很多的网站,有的地方爬的时候会出现一些莫名其妙的错误,因此我们需要用try来执行所有的requests.get(),当出错时重复执行5次,直到成功,如下:

try:

rsp = requests.get(url, headers=headers, timeout=5)

print(rsp.status_code)

with open(img_path+img_name, 'wb') as f:

f.write(rsp.content)

except requests.exceptions.Timeout:

print('超时')

try:

for i in range(5):

rsp = requests.get(url, headers=headers, timeout=5)

if rsp.status_code == 200:

with open(img_path + img_name, 'wb') as f:

f.write(rsp.content)

break

except:

print('出错')

pass

2.但即使这样,也并不能保证那重复执行的5次中就能有一次成功,而且这么庞大的数据量,有时候无法一次爬完,中途程序如果中断,又得重爬岂不是得不偿失?因此我们引入了断点续爬,断点续爬有很多种方式,我这里用的是最稳健的一种方法,那就是引入mongo数据库,如下图:

NowEPx.png

我们可以看出,首先我们把所有要爬取的页面链接和页面里的图片链接都存储在了mongo数据库里,url字段指页面链接,img_urls字段指那一页的所有图片链接,title是标题,也是后面给不同图片下载到不同文件夹分类的依据,最重要的是used字段,这个字段的值直接控制着我们这一页图片是否爬取完毕,爬取完毕我们就更新其值为true,每次我们都查询used值为false的字段来爬取就可以实现断点续爬了,下图更详细:

NowkI1.png

3.既然我们所有的需要爬的信息,和断点续爬的方法都实现了,那么我们就要开始构建下载图片的函数了:

def Download(url, path_to):

img_path = path+'/'+path_to+'/'

if not os.path.exists(img_path):

os.mkdir(img_path)

img_name = url.split('/')[-1]

try:

rsp = requests.get(url, headers=headers, timeout=5)

print(rsp.status_code)

with open(img_path+img_name, 'wb') as f:

f.write(rsp.content)

except requests.exceptions.Timeout:

print('超时')

try:

for i in range(5):

rsp = requests.get(url, headers=headers, timeout=5)

if rsp.status_code == 200:

with open(img_path + img_name, 'wb') as f:

f.write(rsp.content)

break

except:

print('出错')

pass

def Spider(item):

path_to = item['title']

img_urls = item['img_urls']

g2 = []

try:

for url in img_urls:

Download(url, path_to)

"""g2.append(gevent.spawn(Download, url, path_to))

gevent.joinall(g2)"""

item['used'] = True

print('下载完毕', item)

collection.update({'id': item['id']}, item)

except:

pass

4.函数构造完毕后我们设置程序入点,开始执行!

if __name__ == '__main__':

if not os.path.exists(path):

os.mkdir(path)

needed_all = collection.find({'used': False})

g = []

for item in needed_all:

g.append(gevent.spawn(Spider, item))

gevent.joinall(g)

这样我们不管什么时候中断,第4行代码只拿used字段为false的记录,而爬取完毕的记录的used字段已经全部被更新为true了,所以我们实现了断点续爬。

5.以下为本次程序导入的包和全局变量

from gevent import monkey; monkey.patch_all()

import pymongo, requests, gevent, os

path = 'G:/储藏室/图片/'

client = pymongo.MongoClient(host='localhost', port=27017)

db = client['picture']

collection = db['数据库名']

headers = {

'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'

}

6.总结

至此,我们的断点续爬就已经完成了!这也是协程全部的完整源码,一行都没有少的,数据在数据库里,别人网站50多G的图片数据,我怕违规,所以数据库没发出来,但我们主要也是交流思路方法,所以我觉得没有数据库就显得并不重要了。

分布式爬虫

1.什么是分布式爬虫

前面我们所讲的,不论是多线程还是多进程还是协程,全部都是在一台电脑上完成的,而分布式爬虫则是将多台主机组合起来,共同完成一个爬取任务,这将大大提高爬取的效率。

2.分布式爬虫实现的难点

有人这时会想,我连多线程之间的数据共享都搞不清楚,你让我在多台电脑里共享数据?这也是给很多新手劝退的地方,但其实并没有你想的那么复杂。

3.分布式爬虫实现的方法

我们知道,多线程之间共享数据,要么用线程锁,要么用队列Queue,多进程共享数据,需要用到多进程模块下特有的Queue,那多台电脑之间共享数据也当然有其独特的方法。

就拿我刚才断点续爬举的例子,如果我们把数据库假设在一台公网服务器上,供其它所有主机连接,然后把used字段的值设为1、2、3。1表示还未爬取的,2表示正在爬取的,3表示爬取完毕的,每台主机拿走数据库里的一条记录时,我们把used值更新为2,如果爬取失败,再将used值更新回1,成功就把used值更新为3,每次我们只拿used值为1的记录不就可以实现共享数据加断点续爬了吗?

4.总结

当然,这并不是唯一的方法,但这是很容易理解的方法,真正的分布式爬虫需要考虑的比我所讲的其实还是要多一些的,但总的原理就是这样,欢迎大家评论交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值