scrapy(五) 爬取详情页

前言

本章内容会在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框架越来越熟悉,我将重新整理前三篇内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容