一、背景

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

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

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

二、代码

git地址

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 &
  • 查看系统负载

  • 查看文件信息

  • 爬取的图片

    3.3 持久化存储在OSS上

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


四、总结

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

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


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

Python多线程爬图&Scrapy框架爬图的更多相关文章

  1. 使用scrapy框架爬取自己的博文(2)

    之前写了一篇用scrapy框架爬取自己博文的博客,后来发现对于中文的处理一直有问题- - 显示的时候 [u'python\u4e0b\u722c\u67d0\u4e2a\u7f51\u9875\u76 ...

  2. Python网络爬虫之Scrapy框架(CrawlSpider)

    目录 Python网络爬虫之Scrapy框架(CrawlSpider) CrawlSpider使用 爬取糗事百科糗图板块的所有页码数据 Python网络爬虫之Scrapy框架(CrawlSpider) ...

  3. Python逆向爬虫之scrapy框架,非常详细

    爬虫系列目录 目录 Python逆向爬虫之scrapy框架,非常详细 一.爬虫入门 1.1 定义需求 1.2 需求分析 1.2.1 下载某个页面上所有的图片 1.2.2 分页 1.2.3 进行下载图片 ...

  4. Python爬虫进阶之Scrapy框架安装配置

    Python爬虫进阶之Scrapy框架安装配置 初级的爬虫我们利用urllib和urllib2库以及正则表达式就可以完成了,不过还有更加强大的工具,爬虫框架Scrapy,这安装过程也是煞费苦心哪,在此 ...

  5. scrapy框架爬取糗妹妹网站妹子图分类的所有图片

    爬取所有图片,一个页面的图片建一个文件夹.难点,图片中有不少.gif图片,需要重写下载规则, 创建scrapy项目 scrapy startproject qiumeimei 创建爬虫应用 cd qi ...

  6. Python使用Scrapy框架爬取数据存入CSV文件(Python爬虫实战4)

    1. Scrapy框架 Scrapy是python下实现爬虫功能的框架,能够将数据解析.数据处理.数据存储合为一体功能的爬虫框架. 2. Scrapy安装 1. 安装依赖包 yum install g ...

  7. 基于python的scrapy框架爬取豆瓣电影及其可视化

    1.Scrapy框架介绍 主要介绍,spiders,engine,scheduler,downloader,Item pipeline scrapy常见命令如下: 对应在scrapy文件中有,自己增加 ...

  8. python爬虫---scrapy框架爬取图片,scrapy手动发送请求,发送post请求,提升爬取效率,请求传参(meta),五大核心组件,中间件

    # settings 配置 UA USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, l ...

  9. 爬虫入门(四)——Scrapy框架入门:使用Scrapy框架爬取全书网小说数据

    为了入门scrapy框架,昨天写了一个爬取静态小说网站的小程序 下面我们尝试爬取全书网中网游动漫类小说的书籍信息. 一.准备阶段 明确一下爬虫页面分析的思路: 对于书籍列表页:我们需要知道打开单本书籍 ...

随机推荐

  1. SD卡 驱动层测速

    init_timer(&timer1); timer1.function = test_time; timer1.data = ; timer1.expires = jiffies + mse ...

  2. [52ABP系列] - 002、模板项目配合代码生成器开发

    前言 本篇的主要内容是 52ABP SPA模板如何配合52ABP代码生成器开发项目 如果不了解 52ABP 项目请先看我的第一篇文章 [52ABP系列] - 001.SPA免费项目模版搭建教程 话不多 ...

  3. [Java][Android] 多线程同步-主线程等待全部子线程完毕案例

    有时候我们会遇到这种问题:做一个大的事情能够被分解为做一系列相似的小的事情,而小的事情无非就是參数上有可能不同样而已! 此时,假设不使用线程,我们势必会浪费许多的时间来完毕整个大的事情.而使用线程的话 ...

  4. cmd启动Oracle服务和监听服务

    启动数据库服务 net start oracleserviceorcl 启动数据库监听 lsnrctl start

  5. 使用mpxj读取MSPrjoect

    import java.util.ArrayList; import java.util.Calendar; import java.util.Hashtable; import java.util. ...

  6. 服务器端将复合json对象传回前端

    前端接收后端传过来的JSON对象,对前端来说,传过来的确实就是JSON对象:但后端,类型则灵活得多,可以是IList<>等类型,当然也可以是newtonsoft的JObject类型.反正在 ...

  7. easyUI的treegrid添加节点(append)时间过长,设置等待(wait)遮罩效果

    如题所述,在treegrid中,一次添加多个节点时,时间很长,但easyUI并无显示等待信息,让人以为是陷入了死循环,或者死机了,用户体验很差. 本来,treegrid(或者datagrid)有所谓的 ...

  8. makefile 参数

    GNU Make make是负责从项目的源代码中生成最终可执行文件和其他非源代码文件的工具. make命令本身可带有四种参数:标志.宏定义.描述文件名和目标文件名. 其标准形式为:make [flag ...

  9. C的结构体函数

    #include<stdio.h> #include<string.h> struct Test { int age; ]; double score; }std1; //结构 ...

  10. IIs+php 最精简的环境配置

    一,安装IIS 1,打开控制面板->程序和功能->打开或关闭windows功能->Internet 信息服务 1>选 中web管理工具 2>选 中万维网服务 1>应 ...