Pyspider批量抓取网站图片

上一次讲了Python抓取淘宝美人库,未经过任何优化,代码稳定性也没的保证,这次借助爬虫框架Pyspider实现一个较为正规的小爬虫。目的是掌握爬虫框架pyspider的用法。

来源:微博@_SiC_

Pyspider是国内一个大神编写的强大爬虫框架,详情见pyspider中文网,相比于其他爬虫框架(比如Scrapy),pyspider上手相当简单,整个过程也很快可以实现,熟练的话1小时可以做好几个网站的爬虫。
该框架将下载模块进行了封装,用户只需要关注网页内容解析即可。好,现在开始工作!
还是老规矩,按步骤来:

1、pyspider安装。

按照框架作者所言,整个框架是在linux系统下完成的,在windows系统下并没有做过测试,但考虑到python的平台兼容性,也可能完美运行,不过最好还是在linux系统下进行使用。注:python2.x、python3.x都可以使用
linux下安装很简单:执行pip install pyspider即可

2、新建项目。

看过pyspider介绍的应该知道,pyspider是webUI形式的框架,所有代码都可以在webUI里完成。创建工程项目,首先需要启动pyspider,在系统终端执行pyspider all即可,需要注意的是pyspider支持网页的动态解析,但需要phantomjs支持(本项目暂未用到),关于phantomjs就不多讲了,只要知道这是一个没有界面的浏览器就行了,用于动态网页解析。所以先要安装phantomjs再启动pyspider。启动之后就可以在localhost:5000看到pyspider的webUI了,长这样:

image.png

我的截图里面是已经创建了几个项目,第一次用的话里面应该是空的。
点击Create创建一个New Project,Project Name随便起,其他暂不用管,接着就进入编辑区:

image.png

左边相当于输出区,右边相当于代码编辑器(所有代码都可以在代码编辑区编写),左侧底部区控制左侧页面切换:enable css selector helpe是css selector帮助器,web切换到web输出界面,html切换到html输出界面,follows切换到爬取url输出界面,messages切换到代码的print界面。save按钮用来保存编辑的代码,Run用来运行代码。就本图片中代码,Run之后会出现

image.png

可以看到左侧输出区follows切换卡有内容输出,点进去

image.png

看到有index_page__START_URL__输出,点击右侧前进按钮就可以看到控制台输出(因为url报错,不用慌)

image.png

在这里Run相当于单步调试,如果代码编写好之后我们就可以回到localhost:5000可以看到项目列表多了刚才创建的项目test

image.png

点击test对应的status, 将之修改为DEBUG(或者RUNNING),再点击后面的Run,爬虫就开始全速工作了。

image.png

以上就是pyspider的简单操作内容。

3、目标网站分析。

本次我们以妹子图为目标。
我们这次的目标是爬下该网站的所有图,简单估算了一下,网站迄今为止共推出了1800+套图集,每套图集大概有50+张图片,所以有多少图片?
网站所有图入口:在这里

image.png

F12(chrome浏览器的F12打开页面审查,爬虫儿必备技能)看一下

image.png

点进一个链接看一下

image.png
来源:微博@_SiC_

页面源码直接暴露了图片链接!

4、代码实现。

还是进入localhost:5000点击刚才创建的项目名进入代码编辑页,初始代码如下:

from pyspider.libs.base_handler import *


class Handler(BaseHandler):

    # 框架配置内容
    crawl_config = {
    }

    # 框架入口函数
    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl('__START_URL__', callback=self.index_page)

    # 函数调用
    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        for each in response.doc('a[href^="http"]').items():
            self.crawl(each.attr.href, callback=self.detail_page)

    # 函数调用
    @config(priority=2)
    def detail_page(self, response):
        return {
            "url": response.url,
            "title": response.doc('title').text(),
        }

在pyspider中程序起始于on_start函数,crawl()函数调用框架的网页下载器并把下载的内容传给调用的函数(callback参数指定的函数)。那么我们首先应该给框架指定一个根url,所以在初始代码中加入:

    def __init__(self):
        self.root_url = 'http://www.mzitu.com/all/'

并修改on_start函数为:

    def on_start(self):
        self.crawl(self.root_url, callback=self.index_page)

save之后Run一下:

image.png

点进去:

image.png

因为初始代码在调用函数index_page里做了过滤,所以输出区看到一堆url链接,我们发现其中就包含了我们需要的链接(每套图集对应的链接,例如http://www.mzitu.com/108003),但也包含了一些无用链接(如http://www.mzitu.com/xinggan/),怎么办呢?当然是修改默认的过滤规则咯,我们对套图链接稍加分析就可以看出它们的特点:"http://www.mzitu.com/数字",利用此特点就可以轻松地将它们从中筛选出来,因为筛选规则简单,在这里我们就选择正则表达式来筛选:
修改index_page函数为(注意头部导入正则库import re):

def index_page(self, response):
        for each in response.doc('a[href^="http"]').items():
            if re.match('http://www.mzitu.com/\d.*$', each.attr.href):
                self.crawl(each.attr.href, callback=self.detail_page)

再次saveRun,不出意外就可以得到正确的套图url了。
以上我们已经拿到了所有套图的链接,简不简单!
接下来要做的是进入每个套图链接并获取图片,网页下载的部分框架已经帮我们实现了,在index_page函数的self.crawl(each.attr.href, callback=self.detail_page)调用里,这句调用告诉框架:你去下载each.attr.href(刚才分析的套图链接)网页内容,并调用detail_page函数。
接下来修改detail_page函数,这个函数拿到的response就是套图链接网页的内容,我们从左侧输出区随便点进一个套图看一看(切换到web视图):

image.png

这个web视图不太友好,不过也没关系,能看就行!可以看到进入套图链接后已经可以看到图片了,那么我们应该怎么在对应的网页源码中定位该图片url呢?很简单,就用框架提供的css selector帮助器,保持web选项卡不变,点击enable css selector helper即来到帮助模式,可以发现鼠标停留的页面元素被选中,单击该页面元素就可以在源码中定位到该页面元素的位置,so,我们点击图片:

image.png

注意看,页面上方出现了该元素的标签定位,好了,定位也完成了,开始修改detial_page函数:

    def detail_page(self, response):
        for each in response.doc('').items():
            print(each.attr.src)
        return {
            "url": response.url,
            "title": response.doc('title').text(),
        }

此时,代码中筛选规则是空的response.doc('').items(),接下来就是见证奇迹的时候,将编辑光标停留在response.doc('停在这里').items(),然后鼠标单击刚才css selector生成的标签后面的->按钮,Duang!筛选规则已经插入代码中了,看起来像这样:

    def detail_page(self, response):
        for each in response.doc('p img').items():  # 筛选规则已经插入
            print(each.attr.src)
        return {
            "url": response.url,
            "title": response.doc('title').text(),
        }

saveRun

image.png

看到没,灰色输出区已经输出了图片的url,接下来是不是要收到碗里来了呢?还差一步!细心的你可能就发现了,这只是保存了每套图里的第一张图,后面的呢?每套图不是50+张么?那我们就找啊,发现了,在这里

image.png

套图页面通过图片下方的页码来切换图片,那简单,有两种方案:
1、我们直接拿到页码链接,然后遍历即可;
2、我们拿下一页按钮的链接,然后遍历。
这两种方案都是OK的,这里我们用方案2,实践发现如果我们还用css selector来定位下一页链接会出问题,因为在本页面中css selector选择的是下一页所在文本的位置,而链接所在的标签属于文本标签的父标签,同时页码和下一页是并列关系,单纯用css selector并不能从中定位到唯一的下一页的链接,那我们就F12查看源码,发现

image.png

那么根据css 选择器规则不难得出筛选规则:.pagenavi > a:last-child,啥?不了解css选择器?回家等死吧!(CSS选择器传送门)。
完善detail_page函数:

    def detail_page(self, response):
        for each in response.doc('p img').items():
            print(each.attr.src)
        for each in response.doc('.pagenavi > a:last-child').items():
            # 根据拿到的下一页url继续调用detail_page函数,获取下一页的图片和下一页中的下一页
            self.crawl(each.attr.href, callback=self.detail_page)
        return {
            "url": response.url,
            "title": response.doc('title').text(),
        }

saveRun,至此,所有url都可以获取到了。接下来想干什么?随便你,存数据库、down到本地,没问题!以保存本地为例给出代码实现。图片url拿到了,怎么保存本地?当然是访问url,并保存获取的response了,保存本地的代码我就不实现了,这里放上从别处找来的源码,给封装成一个Deal类了(作者:静觅),以后可以随时用,很方便:

import os
 
class Deal:
    def __init__(self):
        self.path = DIR_PATH
        if not self.path.endswith('/'):
            self.path = self.path + '/'
        if not os.path.exists(self.path):
            os.makedirs(self.path)
 
    # 这个是建文件夹的
    def mkDir(self, path):
        path = path.strip()
        dir_path = self.path + path
        exists = os.path.exists(dir_path)
        if not exists:
            os.makedirs(dir_path)
            return dir_path
        else:
            return dir_path
 
    # 这个是存图片的
    def saveImg(self, content, path):
        f = open(path, 'wb')
        f.write(content)
        f.close()

     # 这个是存文本的
    def saveBrief(self, content, dir_path, name):
        file_name = dir_path + "/" + name + ".txt"
        f = open(file_name, "w+")
        f.write(content.encode('utf-8'))
 
    # 这个是获取文件后缀的
    def getExtension(self, url):
        extension = url.split('.')[-1]
        return extension

以上,所有内容!
完整源码

注:关于图片防盗链的不用我解释了,网上一搜就知道怎么回事

来源:微博@_SiC_

参考资料:
Python爬虫进阶四之PySpider的用法
小白爬虫第一弹之抓取妹子图
pyspider
CSS选择器参考手册

上一节:Python抓取淘宝美人库

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 138,635评论 20 591
  • 旅行者们在严冬的山林里迷路,掉下溪流,行李都被冲走,拾了枯枝却点不起火,正一筹莫展的时候,忽见远远有只狐狸,穿过树...
    洞庭府君阅读 398评论 0 7
  • 西安之行,我参观了永泰公主墓,行走在墓道中,观看彩绘陶俑、骑马俑、三彩马等随葬品,欣赏壁画,再加上讲解员详细...
    不解柒缘阅读 55评论 1 0
  • 意外总会在平平常常的日子里向一个人发起突然袭击,昨日晚上,一位亲人就以这种突然的方式永远地告别了我们,因脑溢血猝死...
    浮生小姿阅读 87评论 18 12