美文网首页
scrapy(五) 爬取详情页

scrapy(五) 爬取详情页

作者: 万事万物 | 来源:发表于2021-06-13 08:11 被阅读0次

    前言

    本章内容会在scrapy(四) 翻页请求文章的基础上继续编写(人太懒,从头开始太累了)。有时间的朋友可以去看看这边文章,自认为写得还算详细,希望得到大家的认可。


    继续

    回到正题,昨晚完成了电影的列表爬取,但是单个电影信息并不会都展示到列表中,此时我们需要读取单个电影的详细信息了。

    如图:我们还需要获取该电影的导演是谁,分类,地区,年份等信息。

    scrapy(四) 翻页请求文章中有和大家着重介绍过scrapy.Request,其中有个callback参数。这个参数可以指定交由哪个parse函数做处理。所以我们需要自定义parse函数。

    网页分析

    进行爬虫之前,分析网页的结构是很重要的,我们了解了网页结构之后,才能知道想要的数据存放在哪里。

    通过xpath插件调试,最终确认我们想要的详情信息都在ul标签

    详情页结构分析

    自定义parse函数

    1. 获取详情页的url
       # 详情页的href
       item["details_href"]=li.xpath("./a[1]/@href").extract_first()
       # 拼接详情页的url
       details_url= host_name+item["details_href"]
    
    1. 自定义parse函数
    # 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
        def details_parse(self, response):
            li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
    
            for li in li_list:
                print(li)
    
    1. 调用自定义parse函数
        yield scrapy.Request(url=details_url,callback=self.details_parse)
    
    1. 完整代码
    import scrapy
    
    
    class MovieSpider(scrapy.Spider):
        name = 'movie' # 项目名称
        allowed_domains = ['77dianshi.com'] # 爬取范围
        start_urls = ['http://www.77dianshi.com/kdongzuopian/'] # 爬取地址
    
        def parse(self, response):
            li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
    
            #网址
            host_name="http://www.77dianshi.com"
            
            for li in li_list:
                item={} # 用于封装数据
                # 获取第一个 a 标签
                a1=li.xpath("./a[1]")
                # 封面
                item["cover"]=li.xpath("./a[1]/@data-original").extract_first()
                # 详情页的href
                item["details_href"]=li.xpath("./a[1]/@href").extract_first()
                # 评分
                item["score"]=a1.xpath("./span[2]/text()").extract_first()
                # HD
                item["hd"]=a1.xpath("./span[3]/text()").extract_first()
                # 电影名称
                item["movie_name"]=li.xpath("./a[2]/text()").extract_first()
                # 演员名单
                item["cast_list"]=li.xpath("./span/text()").extract_first()
    
                #请求详情页
                details_url= host_name+item["details_href"]
                yield scrapy.Request(url=details_url,callback=self.details_parse)
               
    
            # 获取下页的url
            next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
            next_url=host_name+next_href
            
            #获取当前页的url
            current_url=response.url
    
            #为了方便查看进度,打印当前url地址
            print("已经爬取了到了:",current_url,"下页地址:",next_url)
    
            # 通过判断是否以next_href为结尾,来判断是否是最后一页
            if not current_url.endswith(next_href):
                # url:下一页的url地址
                # callback:需要交由那个parse方法处理(可以自定义),因为下一页的数据结构,和当前页的数据一样,所以处理方式都是一样的。若不一样,那么需要自定义
               yield scrapy.Request(url=next_url,callback=self.parse)
            else:
                print("爬取完毕")
    
        # 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
        def details_parse(self, response):
            li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
    
            for li in li_list:
                print(li) # 测试是否爬取成功
       
    
    1. 启动爬虫
      获取以下信息,表示xpath没有问题,并且details_parse函数是没有问题的
    $ scrapy crawl movie
    已经爬取了到了: http://www.77dianshi.com/kdongzuopian/ 下页地址: http://www.77dianshi.com/kdongzuopian-2/
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-col-md6 f...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-col-md6 f...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
    <Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-hide fed-...'>
    

    解析

    接下里的工作就是对li标签里的内容进行解析了。

    <ul class="fed-part-rows"><li class="fed-col-xs12 fed-col-md6 fed-part-eone"><span class="fed-text-muted">主演:</span><a href="/q/-%E6%A2%81%E5%AE%B6%E8%BE%89------------/" target="_blank">梁家辉</a>&nbsp;<a href="/q/-%E5%88%98%E5%98%89%E7%8E%B2------------/" target="_blank">刘嘉玲</a>&nbsp;<a href="/q/-%E9%83%91%E8%A3%95%E7%8E%B2------------/" target="_blank">郑裕玲</a>&nbsp;<a href="/q/-%E5%BC%A0%E5%9D%9A%E5%BA%AD------------/" target="_blank">张坚庭</a>&nbsp;<a href="/q/-%E5%90%B4%E5%AE%B6%E4%B8%BD------------/" target="_blank">吴家丽</a>&nbsp;</li><li class="fed-col-xs12 fed-col-md6 fed-part-eone"><span class="fed-text-muted">导演:</span><a href="/q/-----%E5%BC%A0%E5%9D%9A%E5%BA%AD--------/" target="_blank">张坚庭</a>&nbsp;</li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">分类:</span><a href="/kdongzuopian/" target="_blank">动作</a>,<a href="/q/------------1990%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">1990动作片</a>&nbsp;<a href="/q/------------%E9%A6%99%E6%B8%AF%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">香港动作片</a>&nbsp;<a href="/q/------------%E7%B2%A4%E8%AF%AD%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">粤语动作片</a>&nbsp;<a href="/q/------------%E5%8F%A4%E6%83%91%E5%A4%A7%E5%BE%8B%E5%B8%88-/" target="_blank">古惑大律师</a>&nbsp;<a href="/q/------------%E6%AD%A6%E6%89%93%E7%89%87-/" target="_blank">武打片</a>&nbsp;<a href="/q/------------%E5%8A%A8%E4%BD%9C%E7%94%B5%E5%BD%B1-/" target="_blank">动作电影</a>&nbsp;<a href="/q/------------1990%E9%A6%99%E6%B8%AF%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">1990香港动作片</a>&nbsp;</li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">地区:</span><a href="/q/--%E9%A6%99%E6%B8%AF-----------/" target="_blank">香港</a>&nbsp;</li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">年份:</span><a href="/q/-------------1990/" target="_blank">1990</a>&nbsp;</li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">更新:</span>06.11</li><li class="fed-col-xs12 fed-hide fed-show-md-block"><div class="fed-part-esan"><span class="fed-text-muted">简介:</span>已改邪归正浪子陈子辉(梁家辉饰)临去外地工作前与女友(刘嘉玲饰)话别。村长之子根与女友莲丛林追逐玩耍,根色欲薰心使莲受重伤,根慌忙而逃。辉与玲话别后经过丛林时被绊倒。原来莲昏迷时,辉欲施救之际被村民</div></li></ul>
    

    经过分析,发现共有7个 li标签

    • 第一个li 存放着演员信息
    • 第二个li 存放着导演姓名
    • 第三个li 存放着电影分类
    • 第四个li 存放着地区
    • 第五个li 存放着年份
    • 第六个li 存放着更新时间
    • 第七个li 存放着电影简介

    编写代码:

        # 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
        def details_parse(self, response):
            print(response.url)
    
            li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
    
            #定义一个字典,用于存放爬取的数据
            detail_item={}
    
            # 经过分析,使用for循环的方式,好像并不是很合理
    
            # 演员列表
            detail_item["star_list"]=li_list[0].xpath("./a/text()").extract()
            # 导演
            detail_item["director_name"]=li_list[1].xpath("./a/text()").extract_first()
            # 分类
            detail_item["type"]=li_list[2].xpath("./a/text()").extract()
            # 地区
            detail_item["region"]=li_list[3].xpath("./a/text()").extract_first()
            # 年份
            detail_item["year"]=li_list[4].xpath("./a/text()").extract_first()
            # 更新时间
            detail_item["update_time"]=li_list[5].xpath("./text()").extract_first()
            # 简介
            detail_item["introduction"]=li_list[6].xpath("./div/text()").extract_first()
    
            print(detail_item)
    

    爬取结果:这里注意的是,采用的是异步的方式进行爬取,数据结果并不是按照电影列表的顺序获取的。爬取的数据如下,数据很多,只展示了其中一部分。

    $ scrapy crawl movie
    http://www.77dianshi.com/t/xuedou/
    {'star_list': ['朴熙顺', '晋久', '高昌锡'], 'director_name': '朴勋政', 'type': ['动作', '2010动作', '韩国动作', '韩语动作', '血斗', '动作', '2010韩国动作', '动作血斗'], 'region': '韩国', 'year': '2010', 'update_time': '06.04', 'introduction': '以朝鲜时代作为背景,讲述了光海君11年间,朝鲜军队帮助明朝反抗满清却遭大败,战争中幸存的3人在随后与追杀的清军展开殊死血战的故事。'}
    http://www.77dianshi.com/t/mowang2011/
    {'star_list': ['沙鲁克·汗', '卡琳娜·卡普尔', '阿俊·拉姆鲍', 'Tom', 'Wu', 'Shahana', 'Goswami', '萨蒂什·沙阿', '达里普·塔西尔'], 'director_name': '安布哈雅·辛哈', 'type': ['动作', '2011动作', '美国', '印度动作', '英语', '北印 
    度语动作', '魔王2011', '动作', '2011美国', '印度动作', '动作魔王2011'], 'region': '美国', 'year': '2011', 'update_time': '06.04', 'introduction': '夏卡(沙鲁克·罕ShahrukhKhan饰)是一位出色的游戏程序设计师,而他的儿子帕蒂则是 
    一个不折不扣的游戏迷。为了增进父子之间的感情,夏卡创造出了一个名为RaOne的反派游戏角色,这个角色不仅有着'}
    ...
    

    此时还没有结束,details_parse 的数据结果是 parse的详情,他们应该是一块的。也就是说,detail_item 的数据应该存放到 item 里。
    那么如何将 detail_item 存放到 parse 函数的 item 字典中呢?在 scrapy.Request 中还有一个参数meta,它可以用于传递一些参数给另一个parse 函数。所以我们可以可将 item 作为参数传递到 details_parse 中。这样,detail_item 就可以存放到 item 中,最后进行返回了。

    修改亿点点代码

    1. 传递 item
      #请求详情页
      details_url= host_name+item["details_href"]
      yield scrapy.Request(url=details_url,callback=self.details_parse,meta={"item":item})
    

    2.在 details_parse 中接收 item

        def details_parse(self, response):
            # 获取item
            item=response.meta["item"]
    
    1. 将 detail_item 存入到 time中(依旧在 details_parse 函数中操作)
      # 将detail_item 存放到item
      item["detail_item"]=detail_item
            
      yield item #这里使用 yield 主要用于将 item 的数据结果推送到 ScrapyDemoPipeline 中
    
    1. 输出结果
    $ scrapy crawl movie
    http://www.77dianshi.com/t/fenglingcao2011/
    模拟将数据入库: {'cover': 'https://img.huishij.com/upload/vod/20210604-1/83de12082acb1c0f9589eceb8f66fdd1.jpg', 'details_href': '/t/fenglingcao2011/', 'score': '4.0', 'hd': 'HD', 'movie_name': '风铃草2011', 'cast_list': '伊 
    万·格罗德尔,Jessie,Wiseman,Tyler,Dawson,丽贝卡·布兰德斯,文森特·格拉肖,Zack,Kraus,Keghan,Hurst,Alexandra,Boylan,Bradshaw,Pruitt,Brian,Thomas,Evans,Britta,Jacobellis,Ceaser,Flores,Chris,Snyder,Dan,Dulle,Jon,Huck,乔尔·霍奇', 'detail_item': {'star_list': ['伊万·格罗德尔', 'Jessie', 'Wiseman', 'Tyler', 'Dawson', '丽贝卡·布兰德斯', '文森特·格拉肖', 'Zack', 'Kraus', 'Keghan', 'Hurst', 'Alexandra', 'Boylan', 'Bradshaw', 'Pruitt', 'Brian', 'Thomas', 'Evans', 'Britta', 'Jacobellis', 'Ceaser', 'Flores', 'Chris', 'Snyder', 'Dan', 'Dulle', 'Jon', 'Huck', '乔尔·霍奇'], 'director_name': '伊万·格罗德尔', 'type': ['动作', '2011动作', '美国动作', '英语动作', '风铃草2011', '动作', '2011美国动作', '动作风铃草2011'], 'region': '美国', 'year': '2011', 'update_time': '06.04', 'introduction': '两个好朋友改装了一辆带着火焰喷射器的死亡战车,为即将到来的全球灾难做准备。而后,他们遇见了一个充满魅力的姑娘,开启了一 
    段奇妙的冒险旅程。但是,这个女人破坏自然的行为可能为他们招来灾祸……花絮·本片参'}}
    ....
    

    movie.py 完整代码

    import scrapy
    from scrapy import item
    
    
    class MovieSpider(scrapy.Spider):
        name = 'movie' # 项目名称
        allowed_domains = ['77dianshi.com'] # 爬取范围
        start_urls = ['http://www.77dianshi.com/kdongzuopian/'] # 爬取地址
    
        def parse(self, response):
            li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
    
            #网址
            host_name="http://www.77dianshi.com"
            
            for li in li_list:
                item={} # 用于封装数据
                # 获取第一个 a 标签
                a1=li.xpath("./a[1]")
                # 封面
                item["cover"]=li.xpath("./a[1]/@data-original").extract_first()
                # 详情页的href
                item["details_href"]=li.xpath("./a[1]/@href").extract_first()
                # 评分
                item["score"]=a1.xpath("./span[2]/text()").extract_first()
                # HD
                item["hd"]=a1.xpath("./span[3]/text()").extract_first()
                # 电影名称
                item["movie_name"]=li.xpath("./a[2]/text()").extract_first()
                # 演员名单
                item["cast_list"]=li.xpath("./span/text()").extract_first()
                #请求详情页
                details_url= host_name+item["details_href"]
                yield scrapy.Request(url=details_url,callback=self.details_parse,meta={"item":item})
               
    
            # 获取下页的url
            next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
            next_url=host_name+next_href
            
            #获取当前页的url
            current_url=response.url
    
            # 通过判断是否以next_href为结尾,来判断是否是最后一页
            if not current_url.endswith(next_href):
                # url:下一页的url地址
                # callback:需要交由那个parse方法处理(可以自定义),因为下一页的数据结构,和当前页的数据一样,所以处理方式都是一样的。若不一样,那么需要自定义
               yield scrapy.Request(url=next_url,callback=self.parse)
            else:
                print("爬取完毕")
    
        # 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
        def details_parse(self, response):
            # 获取item
            item=response.meta["item"]
    
            print(response.url)
    
            li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
    
            #定义一个字典,用于存放爬取的数据
            detail_item={}
    
            # 经过分析,使用for循环的方式,好像并不是很合理
    
            # 演员列表
            detail_item["star_list"]=li_list[0].xpath("./a/text()").extract()
            # 导演
            detail_item["director_name"]=li_list[1].xpath("./a/text()").extract_first()
            # 分类
            detail_item["type"]=li_list[2].xpath("./a/text()").extract()
            # 地区
            detail_item["region"]=li_list[3].xpath("./a/text()").extract_first()
            # 年份
            detail_item["year"]=li_list[4].xpath("./a/text()").extract_first()
            # 更新时间
            detail_item["update_time"]=li_list[5].xpath("./text()").extract_first()
            # 简介
            detail_item["introduction"]=li_list[6].xpath("./div/text()").extract_first()
    
            # 将detail_item 存放到item
            item["detail_item"]=detail_item
            
            yield item #这里使用 yield 主要用于将 item 的数据结果推送到 ScrapyDemoPipeline 中
               
    

    pipelines.py 代码

    
    class ScrapyDemoPipeline:
        def process_item(self, item, spider):
            #获取item 数据
            print("模拟将数据入库:",item)
    

    完结

    爬取详情页的demo就写完了,写代码没花多少时间,写博客却花了我一个多小时,真的太苦逼了。scrapy框架用起来很简单,很多东西都是大佬帮我们做好了,我们直接用就行了。使用scrapy框架写爬虫是一件很好玩的事情,大家千万不要用爬虫做什么坏事。我写这些作用主要用于对scrapy的学习总结,能够帮助你我很荣幸。等到以后对scrapy框架越来越熟悉,我将重新整理前三篇内容。

    相关文章

      网友评论

          本文标题:scrapy(五) 爬取详情页

          本文链接:https://www.haomeiwen.com/subject/epvbeltx.html