Python多线程爬图&Scrapy框架爬图

栏目: 编程工具 · 发布时间: 5年前

内容简介:对于日常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 &
  • 查看系统负载
    Python多线程爬图&Scrapy框架爬图 Python多线程爬图&Scrapy框架爬图
  • 查看文件信息

    Python多线程爬图&Scrapy框架爬图

    3.2 Scrapy框架爬图

  • 启动爬虫
    nohup doutulai/scrapy_doutulai/scrapy_doutulai/main.py &
  • 查看系统负载
    Python多线程爬图&Scrapy框架爬图 Python多线程爬图&Scrapy框架爬图
  • 查看文件信息

    Python多线程爬图&Scrapy框架爬图
  • 爬取的图片
    Python多线程爬图&Scrapy框架爬图

四、总结

  • 经测试自己写的多线程爬图,CPU使用率很高,磁盘IO很大。Scrapy默认也是10个线程,但由于自己有磁盘IO操作,CPU使用平稳。
  • 虽然Python有GIL,但是在适当的场景下利用其多线程会很大程度的提升效率。之前如果单线程10分钟,利用多线程可以缩短3/2的 时间,具体需要结合线程数,磁盘与网络IO来判断。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据结构与问题求解

数据结构与问题求解

韦斯 / 清华大学出版社 / 2011-8 / 89.50元

《数据结构与问题求解(Java语言版)(第4版)》是专为计算机科学专业的两个学期课程而设计的,从介绍什么足数据结构开始,继而对高级数据结构与算法进行分析。《数据结构与问题求解(Java语言版)(第4版)》以独特的方式,清晰地将每种数据结构的接口与其实现分离开来,即将如何使用数据结构与如何对数据结构编程相分离。《数据结构与问题求解(Java语言版)(第4版)》从抽象思维和问题求解的角度出发,为数据结......一起来看看 《数据结构与问题求解》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具