Scrapy 是一个非常流行的 Python 数据抓取框架,用于抓取web站点并从页面中提取结构化的数据。它的用途广泛,可以用于数据挖掘、监测和自动化测试。
今天我们使用 Scrapy 来干一件程序猿喜闻乐见的事。
我们使用 Python 2.7 进行开发,需要安装的库如下:
pip install -U scrapy pillow
使用 Scrapy 下载图片时默认需要使用 PIL 库,但是并没有自动安装。我们这里使用更新的 pillow 库替代。
scrapy startproject mzt cd mzt scrapy genspider meizitu meizitu.com
定义 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
DOWNLOAD_DELAY = 1 # 添加下载延迟配置 ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1} # 添加图片下载 pipeline IMAGES_STORE = '.' # 设置图片保存目录
scrapy crawl meizitu
这里的效果不是很理想,图片文件名被默认为图片 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}
之后重新运行项目,效果图如下:
这个网站图片太多了,由于没有开启多个线程,导致整整爬了3个多小时,最终一共下载了12000多张图片:
如果你不想自己重新运行一遍爬虫, 可以考虑在微信公众号的后台回复“mzt” ,会有惊喜。
http://toutiao.com/a6300929522393841921/ http://mazih.com/post/cibuwb7bu0000rfa2m169vzv9/