项目地址:https://github.com/Centurywang/spider_DownloadMeizituPictures/tree/master/scrapy_meizitu

注意:需要事先安装好redis数据库并开启远程访问 (方法百度)

步骤一 创建项目

# 安装 scrapy
pip install scrapy
# 安装 scrapy-redis
pip install scrapy-redis
# 安装 redis包
pip install redis
# 创建项目
scrapy startproject meizitu_spider
# 生成爬虫文件 spiders/meizi.py  (功能:上传url队列 ) 
scrapy genspider meizi mzitu.com
# 生成爬虫文件 spiders/meizi_download.py (功能:读取url队列进行爬取并下载)
scrapy genspider meiziDownload mzitu.com

步骤二 分析网页

1.百度搜索 “妹子图” 点击第一个结果

2.分析网页可以发现,前四个分类网页结构相同

3.在每一页都有大量套图,每个套图url在 id=”pins” 的 ul 标签下的 li 标签下的第一个 a 标签的 href 属性

4.下一页的url地址在 标签文字为 “下一页»” 的 a 标签的 href 属性

5.点击进入一个套图 分析网页结构

发现每张图片占一页,获取所有图片需要循环套图内所有页面

图片url在 class=”main-image” 的 div 标签下的 img 标签的 src 属性

下一页url地址在 文字为 “下一页»” 的 span 的父标签 a 的 href 属性

最后一页没有 “下一页” 而是 “下一组”,所以循环获取”下一页”就可以获得该套图的所有图片null

步骤三 编辑爬虫文件

上传需要爬取的url队列

meizi.py

# -*- coding: utf-8 -*-
import scrapy

class MeiziSpider(scrapy.Spider):
    '''功能: 获取套图url并存入redis 数据库(数据类型list)'''
    # 爬虫名 用于执行爬虫时使用
    name = 'meizi'
    # 允许爬取的网址限制
    allowed_domains = ['mzitu.com']
    # 四个分类的url地址
    start_urls = ['https://www.mzitu.com/xinggan/','https://www.mzitu.com/japan/','https://www.mzitu.com/taiwan/','https://www.mzitu.com/mm/']

   
    def parse(self, response):
        '''解析函数 
           获取套图url地址'''
        # 通过xpath语法获取每个li标签内容 (每个套图信息在不同li标签下) 
        page_img_content = response.xpath('//div[@class="postlist"]//ul//li')
        # 循环获取的li标签内容
        for img_content in page_img_content:
            # 字典 用于保存单个套图url地址
            item = {}
            # 套图url在li标签下的第一个a标签的href属性  "extract_first()" 代表提取第一个
            item['url'] = img_content.xpath('./a[1]/@href').extract_first()
            # 返回 item
            yield item

        # 获取下一页  下一页的url地址在 标签文字为 "下一页»" 的 a 标签的 href 属性
        next_page_url = response.xpath('//a[text()="下一页»"]/@href').extract_first()
        # 如果下一页地址存在
        if next_page_url is not None:
            # 请求下一页 调用parse函数
            yield scrapy.Request(
                url=next_page_url,
                callback=self.parse
            )

在settings.py文件内加入

# 启用在redis中调度存储请求队列。
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
 
# 确保所有爬行器通过redis共享相同的副本。
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

目管道序列化并将项目存储在这个redis键中。
#REDIS_ITEMS_KEY = '%(spider)s:items'
 
# 默认情况下,条目序列化器是ScrapyJSONEncoder。您可以使用任何可导入路径到可调用对象。
#REDIS_ITEMS_SERIALIZER = 'json.dumps'
 
# 指定连接到Redis的完整url
# root:用户名 password:密码 localhost:redis服务器的ip地址 6379:redis端口
REDIS_URL = 'redis://root:password@localhost:6379'

现在可以在创建的项目执行上传请求url的功能

# 进入项目目录
cd meizitu_spider
# 执行命令 
# scrapy crawl:执行爬虫
# meizi:爬虫名
scrapy crawl meizi

获取请求url并下载图片

meiziDownload.py

# -*- coding: utf-8 -*-
import scrapy
# 导入redis库
import redis

class MeiziDownloadSpider(scrapy.Spider):
    '''获取套图url'''
    # 以下同上一个meizi.py文件
    name = 'meizi_d'
    allowed_domains = ['mzitu.com']
    # 重写start_requests函数
    def start_requests(self):
        # 连接redis数据库
        # password:密码  host:redis服务器ip地址  port:端口  decode_response=True 以字符串形式返回结果
        r = redis.Redis(password='password',host='localhost',port=6379,decode_responses=True)
        # 设置循环 循环获取redis数据库内的url地址
        start_url = True
        while start_url is not None:
            # 从redis list 循环取出爬取url地址 作为开始url
            start_url = eval(r.lpop('meizi:items'))['url']
            # 发起请求,调用parse函数
            yield scrapy.Request(url=start_url,callback=self.parse)

    def parse(self, response):
        '''获取套图所有图片地址'''
        # 获取图片地址
        item['image_urls'] = response.xpath('//div[@class="main-image"]//img/@src').extract()
        item = {}
        # 获取套图名
        item['name'] = response.xpath('//div[@class="currentpath"]//text()').extract()[-1][3:]
        print(item)
        # 返回item
        yield item
        # 获取下一页url地址  下一页url地址在 文字为 "下一页»" 的 span 的父标签 a 的 href 属性
        next_page_url = response.xpath('//a[./span/text()="下一页»"]/@href').extract_first()
        # 如果下一页存在
        if next_page_url is not None:
            # 请求下一页url 调用parse函数
            yield scrapy.Request(url=next_page_url,callback=self.parse)
套图名

编写pipelines.py文件 实现下载图片功能

class ImagesrenamePipeline(ImagesPipeline):
    '''保存图片'''
    def get_media_requests(self, item, info):
        # 添加headers是因为下载图片需要headers
        headers = {
            'Referer': 'https://www.mzitu.com/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
        }
        # 循环每一张图片地址下载,若传过来的不是集合则无需循环直接yield
        for image_url in item['image_urls']:
            # meta里面的数据是从spider获取,然后通过meta传递给下面方法:file_path
            yield scrapy.Request(image_url,headers=headers, meta={'name': item['name']})

    # 重命名,若不重写这函数,图片名为哈希,就是一串乱七八糟的名字
    def file_path(self, request, response=None, info=None):
        # 提取url前面名称作为图片名。
        image_guid = request.url[-8:]
        # 接收上面meta传递过来的图片名称
        name = request.meta['name']
        # 分文件夹存储的关键:{0}对应着name;{1}对应着image_guid
        filename = u'{0}/{1}'.format(name, image_guid)
        return filename

将settings.py文件内 的ITEM_PIPELINES改为 同时加入一行 IMAGES_STORE = ‘./Picture’

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300,
    'scrapy_meizitu.pipelines.ImagesrenamePipeline':301,
}
# 该行代表的是保存图片的地址 此处代表保存在该目录下的Picture目录
IMAGES_STORE = './Picture'

注意:执行上传url队列时需要删掉ITEM_PIPELINES第二行,下载时需要增加第二行

执行下载图片命令

scrapy crawl meizi_d

此时可以看到项目目录下出现Picture目录,目录内正在保存下载图片

如需在其它机器执行爬虫,只需安装好所需的python包并将项目复制,进入项目目录并执行爬虫命令 scrapy crawl meizi_d 即可

需要注意:redis的配置需正确

到此项目完成

发表回复