老司机用Python多线程爬取表情包

原创 2017年07月21日 16:28:32

多线程爬取表情包

有一个网站,叫做“斗图啦”,网址是:https://www.doutula.com/。这里面包含了许许多多的有意思的斗图图片,还蛮好玩的。有时候为了斗图要跑到这个上面来找表情,实在有点费劲。于是就产生了一个邪恶的想法,可以写个爬虫,把所有的表情都给爬下来。这个网站对于爬虫来讲算是比较友好了,他不会限制你的headers,不会限制你的访问频率(当然,作为一个有素质的爬虫工程师,爬完赶紧撤,不要把人家服务器搞垮了),不会限制你的IP地址,因此技术难度不算太高。但是有一个问题,因为这里要爬的是图片,而不是文本信息,所以采用传统的爬虫是可以完成我们的需求,但是因为是下载图片所以速度比较慢,可能要爬一两个小时都说不准。因此这里我们准备采用多线程爬虫,一下可以把爬虫的效率提高好几倍。

一、分析网站和爬虫准备工作:

构建所有页面URL列表:

这里我们要爬的页面不是“斗图啦”首页,而是最新表情页面https://www.doutula.com/photo/list/,这个页面包含了所有的表情图片,只是是按照时间来排序的而已。我们把页面滚动到最下面,可以看到这个最新表情使用的是分页,当我们点击第二页的时候,页面的URL变成了https://www.doutula.com/photo/list/?page=2,而我们再回到第一页的时候,page又变成了1,所以这个翻页的URL其实很简单,前面这一串https://www.doutula.com/photo/list/?page=都是固定的,只是后面跟的数字不一样而已。并且我们可以看到,这个最新表情总共是有869页,因此这里我们可以写个非常简单的代码,来构建一个从1到869的页面的URL列表:

# 全局变量,用来保存页面的URL的
PAGE_URL_LIST = []
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)
获取一个页面中所有的表情图片链接:

我们已经拿到了所有页面的链接,但是还没有拿到每个页面中表情的链接。经过分析,我们可以知道,其实每个页面中表情的HTML元素构成都是一样的,因此我们只需要针对一个页面进行分析,其他页面按照同样的规则,就可以拿到所有页面的表情链接了。这里我们以第一页为例,跟大家讲解。首先在页面中右键->检查->Elements,然后点击Elements最左边的那个小光标,再把鼠标放在随意一个表情上,这样下面的代码就定位到这个表情所在的代码位置了:


01.png


可以看到,这个img标签的class是等于img-responsive lazy image_dtz,然后我们再定位其他表情的img标签,发现所有的表情的img标签,他的class都是img-responsive lazy image_dtz


02.png

03.png

因此我们只要把数据从网上拉下来,然后再根据这个规则进行提取就可以了。这里我们使用了两个第三方库,一个是requests,这个库是专门用来做网络请求的。第二个库是bs4,这个库是专门用来把请求下来的数据进行分析和过滤用的,如果没有安装好这两个库的,可以使用以下代码进行安装(我使用的是python2.7的版本):

# 安装requests
pip install requests
# 安装bs4
pip install bs4
# 安装lxml解析引擎
pip install lxml

然后我们以第一个页面为例,跟大家讲解如何从页面中获取所有表情的链接:

# 导入requests库
import requests
# 从bs4中导入BeautifulSoup
from bs4 import BeautifulSoup

# 第一页的链接
url = 'https://www.doutula.com/photo/list/?page=1'
# 请求这个链接
response = requests.get(url)
# 使用返回的数据,构建一个BeautifulSoup对象
soup = BeautifulSoup(response.content,'lxml')
# 获取所有class='img-responsive lazy image_dtz'的img标签
img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
for img in img_list:
    # 因为src属性刚开始获取的是loading的图片,因此使用data-original比较靠谱
    print img['data-original']

这样我们就可以在控制台看到本页中所有的表情图片的链接就全部都打印出来了。

下载图片:

有图片链接后,还要对图片进行下载处理,这里我们以一张图片为例:http://ws2.sinaimg.cn/bmiddle/9150e4e5ly1fhpi3ysfocj205i04aa9z.jpg,来看看Python中如何轻轻松松下载一张图片:

import urllib
url = 'http://ws2.sinaimg.cn/bmiddle/9150e4e5ly1fhpi3ysfocj205i04aa9z.jpg'
urllib.urlretrieve(url,filename='test.jpg')

这样就可以下载一张图片了。

结合以上三部分内容:

以上三部分,分别对,如何构建所有页面的URL,一个页面中如何获取所有表情的链接以及下载图片的方法。接下来把这三部分结合在一起,就可以构建一个完整但效率不高的爬虫了:

# 导入requests库
import requests
# 从bs4中导入BeautifulSoup
from bs4 import BeautifulSoup
import urllib
import os

# 全局变量,用来保存页面的URL的
PAGE_URL_LIST = []
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)


for page_url in PAGE_URL_LIST:
    # 请求这个链接
    response = requests.get(page_url)
    # 使用返回的数据,构建一个BeautifulSoup对象
    soup = BeautifulSoup(response.content,'lxml')
    # 获取所有class='img-responsive lazy image_dtz'的img标签
    img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
    for img in img_list:
        # 因为src属性刚开始获取的是loading的图片,因此使用data-original比较靠谱
        src = img['data-original']
        # 有些图片是没有http的,那么要加一个http
        if not src.startswith('http'):
            src = 'http:'+ src
        # 获取图片的名称
        filename = src.split('/').pop()
        # 拼接完整的路径
        path = os.path.join('images',filename)
        urllib.urlretrieve(src,path)

以上这份代码。可以完整的运行了。但是效率不高,毕竟是在下载图片,要一个个排队下载。如果能够采用多线程,在一张图片下载的时候,就完全可以去请求其他图片,而不用继续等待了。因此效率比较高,以下将该例子改为多线程来实现。

二、多线程下载图片:

这里多线程我们使用的是Python自带的threading模块。并且我们使用了一种叫做生产者和消费者的模式,生产者专门用来从每个页面中获取表情的下载链接存储到一个全局列表中。而消费者专门从这个全局列表中提取表情链接进行下载。并且需要注意的是,在多线程中使用全局变量要用来保证数据的一致性。以下是多线程的爬虫代码(如果有看不懂的,可以看视频,讲解很仔细):

#encoding: utf-8

import urllib
import threading
from bs4 import BeautifulSoup
import requests
import os
import time

# 表情链接列表
FACE_URL_LIST = []
# 页面链接列表
PAGE_URL_LIST = []
# 构建869个页面的链接
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)

# 初始化锁
gLock = threading.Lock()

# 生产者,负责从每个页面中提取表情的url
class Producer(threading.Thread):
    def run(self):
        while len(PAGE_URL_LIST) > 0:
            # 在访问PAGE_URL_LIST的时候,要使用锁机制
            gLock.acquire()
            page_url = PAGE_URL_LIST.pop()
            # 使用完后要及时把锁给释放,方便其他线程使用
            gLock.release()
            response = requests.get(page_url)
            soup = BeautifulSoup(response.content, 'lxml')
            img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
            gLock.acquire()
            for img in img_list:
                src = img['data-original']
                if not src.startswith('http'):
                    src = 'http:'+ src
                # 把提取到的表情url,添加到FACE_URL_LIST中
                FACE_URL_LIST.append(src)
            gLock.release()
            time.sleep(0.5)

# 消费者,负责从FACE_URL_LIST提取表情链接,然后下载
class Consumer(threading.Thread):
    def run(self):
        print '%s is running' % threading.current_thread
        while True:
            # 上锁
            gLock.acquire()
            if len(FACE_URL_LIST) == 0:
                # 不管什么情况,都要释放锁
                gLock.release()
                continue
            else:
                # 从FACE_URL_LIST中提取数据
                face_url = FACE_URL_LIST.pop()
                gLock.release()
                filename = face_url.split('/')[-1]
                path = os.path.join('images', filename)
                urllib.urlretrieve(face_url, filename=path)

if __name__ == '__main__':
    # 2个生产者线程,去从页面中爬取表情链接
    for x in range(2):
        Producer().start()

    # 5个消费者线程,去从FACE_URL_LIST中提取下载链接,然后下载
    for x in range(5):
        Consumer().start()

写在最后:

本教程采用多线程来完成表情的爬取,可以让爬取效率高出很多倍。Python的多线程虽然有GIL全局解释器锁,但在网络IO处理这一块表现还是很好的,不用在一个地方一直等待。以上这个例子就很好的说明了多线程的好处。另外,如果你对Python和爬虫或者框架有相关的兴趣,那么可以加入QQ群:526929231一起学习和讨论哦(本教程视频可以入群找我索取)。也可以关注本公众号,每天都有最新的干货哦~~


一个用Python实现的多入口全网爬的多线程爬虫的实现

最近做计网大作业,要求实现一个爬虫抓取网络上关于人工智能的有关信息,于是就自己diy了一个,然后丢到服务器上跑,勉勉强强能达到十万级的数据量,也算能交差了。 下面就把实现过程记录一下,一来可以做个笔记...
  • Youthjack
  • Youthjack
  • 2016年12月07日 22:44
  • 4354

Python 爬虫多线程爬取美女图片保存到本地

Wanning 我们不是生产者,我们只是搬运工 资源来至于qiubaichengren ,代码基于Python 3.5.2 友情提醒:血气方刚的骚年。请谨慎 阅图 !!! 谨慎 阅图 !!! ...
  • cch1024
  • cch1024
  • 2017年01月18日 15:24
  • 2516

scrapy自动多网页爬取CrawlSpider类(五)

一.目的。 自动多网页爬取,这里引出CrawlSpider类,使用更简单方式实现自动爬取。 二.热身。 1.CrawlSpider (1)概念与作用: 它是Spide...
  • u013378306
  • u013378306
  • 2016年11月03日 17:07
  • 2433

python使用多线程爬取表情包

使用多线程爬取数据可以显著提高效率 编辑环境:pycharm 目标:爬取表情包库的所有表情包 首先在同目录下建一个images文件夹 代码如下 #coding:ut...
  • xiaobai12123
  • xiaobai12123
  • 2017年11月28日 23:01
  • 48

python 爬虫及multiprocessing包——多线程爬取并解析百度贴吧某贴小trick

preface:看极客学院关于xpath的视频时,偶然看到可以用multiprocessing进行多线程爬取网页,只有小段代码,故先贴出来。mark下。 coding: #!/usr/bin/env...
  • u010454729
  • u010454729
  • 2015年11月10日 22:00
  • 1117

爬取妹子图(python):爬虫(bs+rq)+ gevent多线程

爬取妹子图:爬虫(bs+rq)+ gevent多线程简介我观察爬取妹子图算是爬虫专业户必做的事情之一,所以我也做了一个,结果是有3.45GB,49847张图。Beautiful Soup + Requ...
  • sileixinhua
  • sileixinhua
  • 2017年07月15日 19:51
  • 4119

Python 爬取163整个网站的图片 多线程 深度控制 相对路径处理 请求间隔控制 正则匹配

# coding:utf-8 import urllib import urllib2 import re import time import threading import socket imp...
  • chenyun19890626
  • chenyun19890626
  • 2017年02月08日 11:39
  • 274

Python 爬虫第三步 -- 多线程爬虫爬取当当网书籍信息

XPath 的安装以及使用1 . XPath 的介绍刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊。其实我个人...
  • Hwaphon
  • Hwaphon
  • 2016年08月23日 06:57
  • 3266

Python网络爬虫(二):多线程爬取小说

这里我们爬取的小说是网站:笔趣阁,其中一本小说:一念永恒。 (一)准备阶段 1、网站URL:http://www.biqukan.com/1_1094/ 2、浏览器:搜狗 3、我们打开网址,找...
  • mmayanshuo
  • mmayanshuo
  • 2018年01月01日 14:33
  • 94

Python爬虫爬取百度贴吧多线程版

XPath提取内容//定位根节点/ 往下层寻找提取文本内容:/text()提取属性内容 : /@XXXX常规匹配#-*-coding:utf8-*- from lxml import etree ht...
  • jethai
  • jethai
  • 2016年08月28日 14:08
  • 231
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:老司机用Python多线程爬取表情包
举报原因:
原因补充:

(最多只允许输入30个字)