zoukankan      html  css  js  c++  java
  • scrapy抓取豆瓣电影相关数据

    1. 任务分析及说明

    目标网站:https://movie.douban.com/tag/#/

    抓取豆瓣电影上,中国大陆地区,相关电影数据约1000条;数据包括:电影名称、导演、主演、评分、电影类型、语言、上映时间、短评top20等数据;

    1.1 Fiddler抓包要点分析:

    请求均为GET请求;拼接后的URL为是https://movie.douban.com/j/new_search_subjects?sort=U&range=0,10&tags=电影&start=0

    其中,range表示评分区间(0,10表示筛选评分在0-10之间的电影);

    tags表示分类类别(电影?剧集?);

    第一次请求默认返回20部电影相关信息,start=0;点击加载更多start=20,即每次点击一次加载更多,start增加20;

    返回数据为json格式,数据包括电影名称、导演、电影详情的URL等信息;

    从json数据中提取电影详情页的URL,访问并抓取详情信息;

    抓取电影短评时,只抓取了最前面的20条,并利用//拼接成一个字符串,数据保存为excel形式。

    2. 代码逻辑

    2.1  项目创建

    利用scrapy的基本命令创建项目、爬虫等,在此不细说,直接上命令。

    scrapy startproject DoubanMovie  # 创建项目

    cd DoubanMovie # 进入项目目录

    scrapy genspider douban douban.movie.com # 创建爬虫

    2.2 明确抓取字段

    scrapy爬虫的套路都相似,创建项目后首先明确爬取字段;其次,编写爬虫逻辑;然后,编写数据保存逻辑;最后,做一些修修补补的工作,例如添加请求头啊,注册通道呀等等。

    来到items.py文件中,明确要抓取的字段。

    # -*- coding: utf-8 -*-
    import scrapy
    
    class DoubanmoviesItem(scrapy.Item):
    
        # 电影名称
        filmtitle = scrapy.Field()
        # 电影评分
        moviemark = scrapy.Field()
        # 导演名称
        moviedirt = scrapy.Field()
        # 电影主演
        movierole = scrapy.Field()
        # 电影类型
        movietype = scrapy.Field()
        # 制片地区
        moviearea = scrapy.Field()
        # 语言类型
        movielang = scrapy.Field()
        # 上映时间
        moviedate = scrapy.Field()
        # 剧情简介
        moviesyno = scrapy.Field()
        # 电影短评
        moviecoms = scrapy.Field()
        # # 电影影评
        # movierews = scrapy.Field()

    2.3 爬虫逻辑

    明确抓取字段后,开始到spiders文件夹下的douban.py中编写爬虫逻辑。豆瓣电影返回的数据为json格式,对json格式的数据进行解析,从中提取到电影详情页的url,访问并从中提取详细信息。

    # -*- coding: utf-8 -*-
    import re
    import json
    import scrapy
    
    from DoubanMovies.items import DoubanmoviesItem
    
    class DoubanSpider(scrapy.Spider):
        name = 'douban'
        allowed_domains = ['movie.douban.com']
        # start_urls = ['http://movie.douban.com/']
    
        start = 0
    
        # 指定参数
        formdata = {
            'sort': 'U',
            'range': '0, 10',
            'tags': '电影',
            'start': '0',
            'countries': '中国大陆'  # 这里只抓取中国大陆地区,其他地区可做相应修改
        }
    
        base_url = 'https://movie.douban.com/j/new_search_subjects'
    
        def start_requests(self):
    
            # 构造初始请求url
            url = self.base_url + '?' + 'sort={}&range={}&tags={}&start={}&countries={}'.format(
                self.formdata['sort'], self.formdata['range'], self.formdata['tags'],
                self.formdata['start'], self.formdata['countries']
            )
    
            # 发起请求
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                meta={'formdata': self.formdata}
            )
    
    
        def parse(self, response):
            """
            豆瓣默认返回json格式的数据
            :param response:
            :return:
            """
            formdata = response.meta['formdata']
    
            # 将json格式的数据转化为字典
            data_list = json.loads(response.body.decode())['data']
    
            # 数据解析
            for data in data_list:
    
                # 从json数据中解析基本信息
                item = DoubanmoviesItem()
                item['filmtitle'] = data['title']
                item['moviemark'] = data['rate']
                item['moviedirt'] = ' '.join(data['directors'])
                item['movierole'] = ' '.join(data['casts'])
    
                # 拿到详情页链接,获取影评等信息
                detail_url = data['url']
                yield scrapy.Request(
                    url=detail_url,
                    callback=self.parse_detail,
                    meta={'item': item, 'formdata': formdata}  # 传入item到parse_detail,继续解析数据
                )
    
            if not self.start == 1000:  # 抓取1020条数据
                self.start += 20
                formdata = self.formdata
                formdata['start'] = str(self.start)
    
                url = self.base_url + '?' + 'sort={}&range={}&tags={}&start={}&countries={}'.format(
                formdata['sort'], formdata['range'], formdata['tags'],
                formdata['start'], formdata['countries'])
    
                yield scrapy.Request(
                    url=url,
                    callback=self.parse,
                    meta={'formdata': formdata}
                )
    
        def parse_detail(self, response):
            """
            从详情页解析其他信息
            :param response:
            :return:
            """
            formdata = response.meta['formdata']
            item = response.meta['item']
    
            item['movietype'] = '/'.join(response.xpath("//div[@id='info']/span[@property='v:genre']/text()").extract())
            item['moviearea'] = formdata['countries']
            item['movielang'] = ''.join(re.findall('<span class="pl">语言:</span>(.*?)<br/>', response.body.decode()))
            item['moviedate'] = '/'.join(response.xpath("//div[@id='info']/span[@property='v:initialReleaseDate']/text()").extract())
            item['moviesyno'] = response.xpath("//div[@id='link-report']/span[1]/text()").extract_first().strip()
    
            # 新页面解析电影短评
            coms_url = response.xpath("//div[@id='comments-section']/div[1]/h2/span/a/@href").extract_first()
            yield scrapy.Request(
                url=coms_url,
                callback=self.parse_coms,  # 在parse_coms中提取电影短评,这里只提取前20 
                meta={'item': item}
            )
    
        def parse_coms(self, response):
            """
            解析电影短评top20,将20条短评以//拼接成一个字符串
            :param response:
            :return:
            """
            item = response.meta['item']
            
            # 提取短评top20
            coms_list = response.xpath("//div[@id='comments']/div[@class='comment-item']/div[@class='comment']/p/span/text()").extract()
            item['moviecoms'] = '//'.join(coms_list)
    
            yield item

    2.4 数据保存

    编写完爬虫逻辑后,来到pipelines.py文件中编写保存数据逻辑。这里将数据保存为excel格式。

    # -*- coding: utf-8 -*-
    from openpyxl import Workbook
    
    class DoubanmoviesPipeline(object):
    
        def __init__(self):
    
            # 创建excel表格保存数据
            self.workbook = Workbook()
            self.booksheet = self.workbook.active
            self.booksheet.append(['电影名称', '评分', '导演',
                                   '主演', '电影类型', '制片地区',
                                   '语言类型', '上映时间', '剧情简介',
                                   '短评(top20)'])
    
        def process_item(self, item, spider):
    
            DATA = [
                item['filmtitle'], item['moviemark'], item['moviedirt'],
                item['movierole'], item['movietype'], item['moviearea'],
                item['movielang'], item['moviedate'], item['moviesyno'],
                item['moviecoms']]
            self.booksheet.append(DATA)
            self.workbook.save('./results.xls')
    
            return item

    2.5 其他

    1. 通道注册,包括下载中间件,pipelines等的注册,还有不遵循爬虫协议

    2. 延时处理,在settings.py文件中添加

    DOWNLOAD_DELAY = 5  # 每个请求延迟5秒

    3. 添加请求头

    在下载中间件(middlewares.py)中给每个请求添加请求头

    # -*- coding: utf-8 -*-
    from DoubanMovies.settings import USER_AGENTS as ua
    import random
    
    class DoubanmoviesDownloaderMiddleware(object):
    
        def process_request(self, request, spider):
            """
                    给每一个请求随机分配一个代理
                    :param request:
                    :param spider:
                    :return:
                    """
            user_agent = random.choice(ua)
            request.headers['User-Agent'] = user_agent

    4. 将运行命令写在main.py文件中

    from scrapy import cmdline
    
    cmdline.execute('scrapy crawl douban'.split())

    3. 完整代码

    参见:https://github.com/zInPython/DoubanMovie

  • 相关阅读:
    CodeForces 55D Beautiful numbers(数位dp+数学)
    hdu 2089 不要62(数位dp入门)
    Git版本控制
    Git初始化-添加提交以及查看状态
    linux-高并发与负载均衡-lvs-3种模型推导
    Scrapy中选择器的用法
    Scrapy命令行详解
    Scrapy框架基本用法讲解
    MaxCompute教程
    Scrapy安装报错
  • 原文地址:https://www.cnblogs.com/pythoner6833/p/10331509.html
Copyright © 2011-2022 走看看