一、背景

对于日常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. 中高级前端应该必会,js实现事件委托代理、切换样式、元素获取相对于文档位置等

    1.介绍 随着组件开发大流行,现在三大框架已经基本占领了整个前端. 这时候,我们要是引入一个 jq 是不是先得你的项目非常臃肿,jq 也很不适合. 这个时候,你就需要来增加你 js 的功底. 2.各种 ...

  2. APPLE STORE

    直接在设置中,使用查看APPLE ID是无法更改的,现在必须要有所在区域的信用卡信息,支付方式无法像以前一样选择“无”. 查询后发现,有人说icloud3.0,即这个旧版的可以进行更改,于是下载. 但 ...

  3. UML基础与应用总结

          敲响一段键盘的乐响曲,一段路程留下一些足迹.       UML.是Unified-Modeling-Language的缩写. 首先要明白知道它是一种可视化的建模语言.   什么是UML基 ...

  4. UG如何把语言改成中文,UG如何把界面语言改成中文

    1 高级系统设置,高级,新建一个用户变量(变量名为lang,变量值为chs)   2 高级系统设置,高级,环境变量,系统变量中,查看变量名为UGII_LANG的值是否为simpl_chinese,如果 ...

  5. Maven具体解释之仓库------本地仓库、远程仓库

    在Maven中,不论什么一个依赖.插件或者项目构建的输出.都能够称之为构件. Maven在某个统一的位置存储全部项目的共享的构件.这个统一的位置.我们就称之为仓库.(仓库就是存放依赖和插件的地方) 不 ...

  6. MYSQL入库常用PHP函数

    addslashes   addslashes() 函数在指定的预定义字符前添加反斜杠.这些字符是单引号(').双引号(").反斜线(\)与NUL(NULL字符).   stripslash ...

  7. UVa 11582 Colossal Fibonacci Numbers! 【大数幂取模】

    题目链接:Uva 11582 [vjudge] watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fil ...

  8. javascript总结03

  9. 利用Theme自定义Activity进入退出动画

    有没有觉得Activity的默认动画太快了或者太难看了.. 我原来使用Activity.overridePendingTransition来自定义Activity的进入动画,却发现没法定义退出的动画. ...

  10. Python Tricks(十九)—— switch 的实现

    python 原生语法不支持 switch,体现了 Python 大道至简的设计思路,有时为了避免啰嗦的 if elif等判断语句,我们可以用字典来代替 switch 的各分支,也即建立表达式和操作的 ...