一、背景

对于日常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. Windows系统下查看已共享的文件夹的方法

    电脑使用时间比较长,共享过好几次文件夹,现在想取消共享,但共享文件夹的路径隐藏得太深,要怎么样才能快速找到共享文件夹的实际路径呢?” “其实现在单位常见的方法都是用网上邻居的共享文件夹,共享的文件夹也 ...

  2. js下载

    下载用ajax不好使,得用表单提交的方式 download:function(url,paramObj){ var doc = document; //使用一个隐藏的form表单执行提交,没有则创建 ...

  3. poj 2318 TOYS &amp; poj 2398 Toy Storage (叉积)

    链接:poj 2318 题意:有一个矩形盒子,盒子里有一些木块线段.而且这些线段坐标是依照顺序给出的. 有n条线段,把盒子分层了n+1个区域,然后有m个玩具.这m个玩具的坐标是已知的,问最后每一个区域 ...

  4. 本地调用jni之VC++无法导入问题

    事实上非常easy,无法导入头文件就自己新建呗 1. 首先编写java代码 class Vrv { public native void printVersion(); static { System ...

  5. MySQl 子查询,左右连接,多表连接学习笔记

    1.子查询是指在还有一个查询语句中的SELECT子句.   例句:   SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);   当中, ...

  6. poj1151==codevs 3044 矩形面积求并

    Atlantis Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 21511   Accepted: 8110 Descrip ...

  7. 关于编译(javac),import,package的再理解

    1.若我们在A.java中用到了类B,当我们仅仅用 javac A.java 编译A时,编译器也会去寻找B,若类B依然是源文件,也会自动编译它.在使用javac和java命令时,有一个参数选项 -ve ...

  8. 最大heap

    1 什么是最大heap 最大heap是一棵完全二叉树.每棵子树的根比它的两棵子树上的节点都要大. 2 建堆的过程 function max_heaptify(A): for (i = n/2向下取整: ...

  9. Codeforces Round #311 (Div. 2)C. Arthur and Table

    C. Arthur and Table time limit per test 1 second memory limit per test 256 megabytes input standard ...

  10. FileStream StreamWriter StreamReader BinaryReader

    FileStream vs/differences StreamWriter? http://stackoverflow.com/questions/4963667/filestream-vs-dif ...