教程
avatar of this article's author EarlGrey
程序猿都会爬的妹子图

Scrapy 是一个非常流行的 Python 数据抓取框架,用于抓取web站点并从页面中提取结构化的数据。它的用途广泛,可以用于数据挖掘、监测和自动化测试。

今天我们使用 Scrapy 来干一件程序猿喜闻乐见的事。

准备工作

我们使用 Python 2.7 进行开发,需要安装的库如下:

pip install -U scrapy pillow

使用 Scrapy 下载图片时默认需要使用 PIL 库,但是并没有自动安装。我们这里使用更新的 pillow 库替代。

快速设置

  1. 初始化项目
scrapy startproject mzt
cd mzt
scrapy genspider meizitu meizitu.com
  1. 修改 meizitu.py

定义 PItem 类,添加需要使用的 image_urls 、images 和 name 等属性,为下载图片做准备。

import os
import scrapy

class PItem(scrapy.Item):
    image_urls = scrapy.Field()
    images = scrapy.Field()
    name = scrapy.Field()

修改 start_urls 为初始页面, 添加 parse 用于处理列表页, 添加 parse_item 处理项目页面。

class MeizituSpider(scrapy.Spider):
    name = "meizitu"
    allowed_domains = ["meizitu.com"]
    start_urls = (
        'http://meizitu.com/a/list_1_1.html',
    )

    def parse(self, response):
        exp = u'//div[@id="wp_page_numbers"]//a[text()="下一页"]/@href'
        _next = response.xpath(exp).extract_first()
        next_page = os.path.join(os.path.dirname(response.url), _next)
        yield scrapy.FormRequest(next_page, callback=self.parse)
        for p in response.xpath('//li[@class="wp-item"]//a/@href').extract():
            yield scrapy.FormRequest(p, callback=self.parse_item)

    def parse_item(self, response):
        item = PItem()
        urls = response.xpath("//div[@id='picture']//img/@src").extract()
        name = response.xpath("//div[@id='picture']//img/@alt").extract()[0]
        item['image_urls'] = urls
        item['name'] = name.split(u',')[0]
        return item
  1. 修改 settings.py
DOWNLOAD_DELAY = 1 # 添加下载延迟配置
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1} # 添加图片下载 pipeline
IMAGES_STORE = '.' # 设置图片保存目录
  1. 运行项目:
scrapy crawl meizitu

妹子图项目运行效果1

这里的效果不是很理想,图片文件名被默认为图片 URL 的 SHA1 值。我们浏览时无法知道图片的大致内容。

我们希望能够保存为比较有意义的名称,最好是分为不同的文件夹存储。

重命名图片

如果想重命名保存文件的名称,我们需要重新定义自己的ImagePipeline。

from scrapy.contrib.pipeline.images import ImagesPipeline
from scrapy.http import Request
from scrapy.exceptions import DropItem

class MztImagesPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield Request(image_url, meta={'item': item})
            # 这里把item传过去,因为后面需要用item里面的书名和章节作为文件名

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        return item

    def file_path(self, request, response=None, info=None):
        item = request.meta['item']
        image_guid = request.url.split('/')[-1]
        filename = u'full/{0[name]}/{1}'.format(item, image_guid)
        return filename

然后修改 settings.py:

ITEM_PIPELINES = {'mzt.pipelines.MztImagesPipeline': 1}

之后重新运行项目,效果图如下:

妹子图项目运行效果2

这个网站图片太多了,由于没有开启多个线程,导致整整爬了3个多小时,最终一共下载了12000多张图片:

妹子图项目运行效果3

如果你不想自己重新运行一遍爬虫,可以考虑在微信公众号的后台回复“mzt”,会有惊喜。

参考资料

http://toutiao.com/a6300929522393841921/ http://mazih.com/post/cibuwb7bu0000rfa2m169vzv9/

上一篇
下一篇