zoukankan      html  css  js  c++  java
  • Python多线程爬图&Scrapy框架爬图

    一、背景

    对于日常Python爬虫由于效率问题,本次测试使用多线程和Scrapy框架来实现抓取斗图啦表情。
    由于IO操作不使用CPU,对于IO密集(磁盘IO/网络IO/人机交互IO)型适合用多线程,对于计算密集型:建议用多进程。

    • 进程:
      优点:充分利用多核CPU(能够同时进行多个操作)
      缺点:系统资源消耗大,重新开辟内存空间
    • 线程:
      优点:共享内存,IO操作可以创造出并发操作
      缺点:抢占资源,请求上下文切换消耗时间

    但是对于python这种解释性语言带有GIL(全局解释器锁)解释器锁,同一时刻只能有一个线程在运行,遇到IO操作才会释放切换。感觉没必要多线程,但是经测试,多线程还是在很大程度能够提升效率。

    二、代码

    git地址
    Python多线程爬图&Scrapy框架爬图

    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框架爬图

      3.3 持久化存储在OSS上

      最终配合阿里云OSS的API来将图片持久化存储在对象存储内。
      整体image下载地址:图片压缩包

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

    四、总结

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

    最后说一件重要的事,只怕有同学错过。云计算现在白菜价了,2018年各大云计算厂商竞相降价,云服务器低至300元/年。现在不管是学习linux系统,运行长时间工作的爬虫,上线个人App或小程序,搭建网站做个人站长,都是时候上云服务了,错过这一波可能你就掉队。这里整理了一份云计算优惠活动产品列表,点击拿走不谢!


    本文转自:http://blog.51cto.com/kaliarch/2162411

  • 相关阅读:
    HDU
    HDU
    A. Reorder the Array
    A. New Building for SIS Codeforce
    HUD Is It A Tree?!!!!!)
    博客园申请博客批准
    一起学CC3200之CRC校验
    新安装CCS 编译问题Process_begin :createProcess
    一起学CC3200之开发环境简介(2)烧录程序
    新安装CCS 后编译出现问题:gmake:No rule to make target clean
  • 原文地址:https://www.cnblogs.com/jebysun/p/9521906.html
Copyright © 2011-2022 走看看