美文网首页
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