Scrapy爬虫实战项目【003】 - 抓取360图解电影

爬取360图解电影

原创

目的:使用Scrapy爬取360图解电影,保存至MONGODB数据库,并将图集按电影名称分类保存至本地

目标网址:http://image.so.com/z?ch=video

分析/知识点:

  1. 爬取难度:
    a. 入门级,电影索引页/详情页都是返回json数据结果;
    b. 图片分类保存:需要对内置ImagesPipeline进行继承后改写几个方法;

实际步骤:

  1. 创建Scrapy项目/tujiemovie360(spider)
Terminal: > scrapy startproject tujiemovie360
Terminal: > scrapy genspider tujiemovie image.so.com/z?ch=video   
  1. 配置settings.py文件
# MONGODB配置
MONGO_URI = 'localhost'
MONGO_DB = 'maoyan_movie'

# 图集保存默认路径
IMAGES_STORE = './movies/'

# 不遵守爬取机器人协议
ROBOTSTXT_OBEY = False

# 设置headers
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
}

# 启用ImagePipeline/MongoPipeline
ITEM_PIPELINES = {
    'tujiemovie360.pipelines.ImagePipeline': 300,
    'tujiemovie360.pipelines.MongoPipeline': 301,
}
  1. 编写items.py文件
    注:此处创建了电影图集索引Item/详情页Item
# 图集IndexItem
class IndexItem(Item):
    id = Field()            # 电影图集id,重要
    actor = Field()         # 演员
    director = Field()      # 导演
    group_title = Field()   # 电影名称
    total_count = Field()   # 电影图集图片总数


# 图集DetailItem
class DetailItem(Item):
    id = Field()            # 电影图集id,重要
    imageid = Field()       # 图片ID
    group_id = Field()      # 图集ID
    pic_title = Field()     # 电影图集名称
    pic_url = Field()       # 图片url(命名升序, gm_1/gm_2...)
    qhimg_url = Field()     # 图片url(高清) pic_url对应的是960x540分辨率的图片
  1. 编写pipelines.py文件
    a) MongoPipeline:根据官方例子改写。根据Item中所含字段不同,分别予以处理
# !! 更新MONGODB,使用UPDATE方法,查重功能
    def process_item(self, item, spider):
        # 保存movie_index信息
        if 'group_title' in item.fields:
            self.db['movie_index'].update({'id': item['id']}, {'$set': item}, upsert=True)
        # 保存movie_detail信息(图集)
        elif 'imageid' in item.fields:
            self.db['movie_detail'].update({'imageid': item['imageid']}, {'$set': item}, upsert=True)
        return item
        

b) ImagePipeline:
i. 根据获取的图集中每张图片的url(qhimg_url),重新生成Request进行请求下载

# 重写ImagesPipeline类的此方法
    # 发送图片下载请求
    def get_media_requests(self, item, info):
        yield Request(item['qhimg_url'])

ii. 获取文件名(下载时)

# 每张图片文件名(下载时的文件名,后续还要改名)
def file_path(self, request, response=None, info=None):
    url = request.url
    file_name = url.split('/')[-1]
    return file_name

iii. 图片默认下载路径为:IMAGES_STORE/full/;
下载完成后,根据图集名称创建对应文件夹,并将对应图片全部转移至相对应文件夹中,使用内置库shutil.move(src, des)完成此项功能

# 重写item_completed方法
# 将下载的文件保存到不同的目录中
def item_completed(self, results, item, info):
    image_path = [x['path'] for ok, x in results if ok]
    if not image_path:
        raise DropItem('Image Downloaded Failed')

    # 定义图集目录保存路径
    movie_dir = '%s%s' % (self.img_store, item['pic_title'])
    # 目录不存在则创建目录
    if not os.path.exists(movie_dir):
        os.mkdir(movie_dir)

    # 将文件从默认下路路径移动到指定路径下,同时变更文件名(gm_1~X)
    shutil.move(self.img_store + image_path[0], movie_dir + '/' + item['pic_url'].split('/')[-1])

    return item
  1. 编写spiders > tujiemovie.py文件
    注意:
    a) 在解析完索引页(parse_index)后,需要分别生成对具体图集的Request请求。此时需要注意sn的上限值计算(total_count // 60 + 1)
def parse_index(self, response):
    '''
    解析索引页
    '''
    ...(略)
    # 请求每部电影图集详情页
    id = movie.get('id')
    total_count = movie.get('total_count')
    for sn in range(0, total_count // 60 + 1):
        yield Request(url=self.detail_url.format(id=id, sn=sn * 60), callback=self.parse_detail,
                      dont_filter=True)

b) 完整代码如下:

import json

from scrapy import Spider, Request
from tujiemovie360.items import IndexItem, DetailItem


class TujiemovieSpider(Spider):
    name = 'tujiemovie'
    allowed_domains = ['image.so.com/z?ch=video']
    start_urls = ['http://image.so.com/z?ch=video/']

    # 索引页
    index_url = 'http://image.so.com/zj?ch=video&sn={sn}&listtype=new&temp=1'

    # 详情页
    detail_url = 'http://image.so.com/zvj?ch=video&id={id}&pn=60&sn={sn}'

    # 重写
    def start_requests(self):
        # 索引页请求
        for sn in range(0, 1):  # 修改上限值,可以把整个图解电影全爬下来
            yield Request(url=self.index_url.format(sn=sn * 30), callback=self.parse_index, dont_filter=True)

    def parse_index(self, response):
        '''
        解析索引页
        '''
        results = json.loads(response.text)

        if 'list' in results.keys():
            for movie in results.get('list'):
                item = IndexItem()
                item['id'] = movie.get('id')
                item['group_title'] = movie.get('group_title')
                item['total_count'] = movie.get('total_count')
                item['actor'] = movie.get('actor')
                item['director'] = movie.get('director')
                yield item

                # 请求每部电影图集详情页
                id = movie.get('id')
                total_count = movie.get('total_count')
                for sn in range(0, total_count // 60 + 1):
                    yield Request(url=self.detail_url.format(id=id, sn=sn * 60), callback=self.parse_detail,
                                  dont_filter=True)

    def parse_detail(self, response):
        '''
        解析电影图集详情页(每张图片)
        '''
        results = json.loads(response.text)

        if 'list' in results.keys():
            for pic in results.get('list'):
                item = DetailItem()
                item['id'] = pic.get('group_id')  # 电影图集id
                item['imageid'] = pic.get('imageid')
                item['pic_url'] = pic.get('pic_url')
                item['pic_title'] = pic.get('pic_title')
                item['qhimg_url'] = pic.get('qhimg_url')
                yield item

6. 运行结果

temp-1.png

temp-2.png

小结

  1. 入门级项目,难度略微有所提升;
  2. 改写Scrapy内置ImagesPipeline,实现对图片的分类保存;
    *目前使用的是python内置库shutil/os,图片下载完成后再进行分类整理。但这样做的效率相对较低,需要进一步改进成在下载时就创建对应文件夹并保存。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 12,653评论 4 46
  • 前一篇文章介绍了很多关于scrapy的进阶知识,不过说归说,只有在实际应用中才能真正用到这些知识。所以这篇文章就来...
    乐百川阅读 2,297评论 0 7
  • 爬取360摄影美图 参考来源:《Python3网络爬虫开发实战》 第497页 作者:崔庆才 目的:使用Scrap...
    akiraakito0514阅读 813评论 0 2
  • 1 安装Scrapy Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘...
    superzhan阅读 1,421评论 0 11
  • 悠悠岁月,已过十八;回首当春,伤痕累累。 下雨了,一个人,一张床,一张桌子,一把椅子,一杯水,陈简朴素地已停在了黑...
    大学微传媒阅读 195评论 1 1