内容简介:对于日常Python爬虫由于效率问题,本次测试使用多线程和Scrapy框架来实现抓取由于IO操作不使用CPU,对于IO密集(磁盘IO/网络IO/人家交互IO)型适合用多线程,对于计算密集型:建议用多进程。但是对于python这种解释性语言带有GIL(全局解释器锁)解释器锁,同一时刻只能有一个线程在运行,遇到IO操作才会释放切换。感觉没必要多线程,但是经测试,多线程还是在很大程度能够提升效率。
一、背景
对于日常 Python 爬虫由于效率问题,本次测试使用多线程和Scrapy框架来实现抓取 斗图来 表情。
由于IO操作不使用CPU,对于IO密集(磁盘IO/网络IO/人家交互IO)型适合用多线程,对于计算密集型:建议用多进程。
- 进程:
优点:充分利用多核CPU(能够同时进行多个操作)
缺点:系统资源消耗大,重新开辟内存空间 - 线程:
优点:共享内存,IO操作可以创造出并发操作
缺点:抢占资源,请求上下文切换消耗时间
但是对于python这种解释性语言带有GIL(全局解释器锁)解释器锁,同一时刻只能有一个线程在运行,遇到IO操作才会释放切换。感觉没必要多线程,但是经测试,多线程还是在很大程度能够提升效率。
二、代码
2.1 多线程爬图
定义了10个线程去爬去每个页面的具体表情的url存放在类中的img_url_list内,然后通过10个线程从这个列表内取url进行本地图片下载。
核心代码
# 定义全局页面url列表 page_url_list = [] # 定义具体各表情图片url列表 img_url_list = [] # 定义rlock进程锁 rlock = threading.RLock() def __init__(self,page_number=10,img_dir='imgdir',thread_number=5): """ :param page_number: 抓去多少个页面,默认10 :param img_dir: 定义图片目录 :param thread_number:默认5个线程 """ self.spider_url = 'https://www.doutula.com/photo/list/?page=' self.page_number = int(page_number) self.img_dir = img_dir self.thread_num = thread_number def __add_urllist(self): """ 定义从page_url_list 爬取具体的image的url :return: """ while True: DutuSpider.rlock.acquire() if len(DutuSpider.page_url_list) == 0: DutuSpider.rlock.release() break else: page_url = DutuSpider.page_url_list.pop() DutuSpider.rlock.release() response = requests.get(page_url, headers=self.__set_header()) soup = BeautifulSoup(response.content,'lxml') sou_list = soup.find_all('img',attrs={'class':'img-responsive lazy image_dta'}) # 将获取到的具体表情图标的url保存添加进img_url_list 列表 for url_content in sou_list: DutuSpider.rlock.acquire() DutuSpider.img_url_list.append(url_content['data-original']) DutuSpider.rlock.release() def __download_img(self): """ 从image_url_list中来下载image到本地 :return: """ while True: DutuSpider.rlock.acquire() if len(DutuSpider.img_url_list) == 0: DutuSpider.rlock.release() continue else: img_url = DutuSpider.img_url_list.pop() DutuSpider.rlock.release() try: # 图片名称 img_name = img_url.split('/')[-1] # 下载图片 urllib.urlretrieve(img_url,os.path.join(self.img_dir,img_name)) print('donload img %s' % img_name) except Exception as e: pass def run(self): # 启动thread_num个进程来爬去具体的img url 链接 for th in range(self.thread_num): add_pic_t = threading.Thread(target=self.__add_urllist) add_pic_t.start() # 启动thread_num个来下载图片 for img_th in range(self.thread_num): download_t = threading.Thread(target=self.__download_img) download_t.start()
2.2 Scrapy框架爬图
利用Scrapy框架来爬取表情,items定义图片名称和每个图片的url,scrapy主文件来爬取每个图片的url来返回,piplines来进行本地文件存储。
核心代码
# items,定义img的url和name class ScrapyDoutulaiItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # 定义图片url和name img_url = scrapy.Field() img_name = scrapy.Field() # 爬虫文件 class DoutulaiSpiderSpider(scrapy.Spider): name = 'doutulai_spider' allowed_domains = ['www.doutula.com'] start_urls = ['https://www.doutula.com/photo/list/'] page = 1 def parse(self, response): content_items = ScrapyDoutulaiItem() # 解析img_url列表,拿到图片的url和,图片名称 img_url_list = response.xpath('//img[@class="img-responsive lazy image_dta"]') # page_number = response.xpath('//*[@id="pic-detail"]/div/div[3]/div[3]/ul/li[12]/a/text()').extract_first() page_number = response.xpath('//a[@class="page-link"][last()]/text()').extract_first() for img_content in img_url_list: content_items['img_url'] = img_content.xpath('./@data-original').extract_first() content_items['img_name'] = img_content.xpath('./@data-original').extract_first().split('/')[-1] print(content_items) yield content_items # 不断爬取新页面 if self.page <= page_number: self.page += 1 next_url = self.start_urls[0] + '?page=' + str(self.page) yield scrapy.Request(next_url) #pipeline下载图片 from urllib import urlretrieve from scrapy_doutulai.settings import DOWNLOAD_DIR class ScrapyDoutulaiPipeline(object): def __init__(self): """ 判断下载目录是否存在 """ if not os.path.exists(DOWNLOAD_DIR): os.makedirs(DOWNLOAD_DIR) def process_item(self, item, spider): """ 下载图片 :param item: :param spider: :return: """ try: filename = os.path.join(DOWNLOAD_DIR,item['img_name']) print(filename) urlretrieve(item['img_url'],filename) except Exception as e: pass
三、测试
测试使用2C2G centos7.4,python2.7版本,启动线程10个,爬去1000页的表情信息
3.1 多线程测试
- 启动爬虫
nohup doutulai/multithreading_spider/dutulai_spider.py &
- 查看系统负载
-
查看文件信息
3.2 Scrapy框架爬图
- 启动爬虫
nohup doutulai/scrapy_doutulai/scrapy_doutulai/main.py &
- 查看系统负载
-
查看文件信息
- 爬取的图片
四、总结
- 经测试自己写的多线程爬图,CPU使用率很高,磁盘IO很大。Scrapy默认也是10个线程,但由于自己有磁盘IO操作,CPU使用平稳。
- 虽然Python有GIL,但是在适当的场景下利用其多线程会很大程度的提升效率。之前如果单线程10分钟,利用多线程可以缩短3/2的 时间,具体需要结合线程数,磁盘与网络IO来判断。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Java多线程之Executor框架和手写简易的线程池
- Netty 和 RPC 框架线程模型分析
- 多线程、事件驱动与推荐引擎框架选型
- Java 并发编程:Executor 线程池框架简介
- Java 并发编程那些事儿(六):Executor 框架及线程池
- 不使用第三方框架编写的多线程断线续传功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。