Python

scrapy(四) 翻页请求

2021-06-12  本文已影响0人  万事万物

很多时候,我们爬取一个网页,往往都带有下一页,我们不止要爬取当前页数据,也应该爬取下一页数据。

分页请求 首先需要找到一个网站,以77dianshi为例,
警告:此网站只是用于爬虫练手,请不要有任何不好的想法。

网站分析

进行爬虫之前首先需要了解网站结构,通过查看网站大致分析如下,上面是电影的一些信息,排名、封面,电影名等。除了最下面的列表的分页信息。 77dianshi

一页的电影信息不止这么点。

创建爬虫项目

  1. 创建scrapy项目,项目名称叫 scrapy_demo
$ scrapy startproject scrapy_demo
  1. 进入 scrapy_demo 项目中
$ cd scrapy_demo 
  1. 生成一个爬虫
    爬虫名称:movie
    爬取范围:77dianshi.com
$ scrapy genspider movie 77dianshi.com
Created spider 'movie' using template 'basic' in module:
  scrapy_demo.spiders.movie
  1. 进入movie.py 文件
import scrapy


class MovieSpider(scrapy.Spider):
    name = 'movie'
    allowed_domains = ['77dianshi.com']
    start_urls = ['http://77dianshi.com/']

    def parse(self, response):
        pass
  1. 修改 start_urls (如下)
import scrapy


class MovieSpider(scrapy.Spider):
    name = 'movie' # 项目名称
    allowed_domains = ['77dianshi.com'] # 爬取范围
    start_urls = ['http://www.77dianshi.com/kdongzuopian/'] # 爬取地址

    def parse(self, response):
        pass

网页结构分析

  1. 使用chrome xpath插件。

经过调试,所有的电影信息都用ul包围,并就放在li中。

网页结构
  1. 编写脚本
    编写parse 代码
 def parse(self, response):
        li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
        
        for li in li_list:
            print(li)

调试;保证我们的爬取没有问题

$ scrapy crawl movie 
2021-06-11 21:33:01 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-pull-left"><a class="f...'>
<Selector xpath='//ul/li' data='<li class="fed-col-sm2"><a class="fed...'>
<Selector xpath='//ul/li' data='<li class="fed-col-sm2 fed-this"><a c...'>
<Selector xpath='//ul/li' data='<li class="fed-col-sm2"><a class="fed...'>
<Selector xpath='//ul/li' data='<li class="fed-col-sm2"><a class="fed...'>
...

输入 scrapy crawl movie 后能获取以上信息,证明我们能正常获取数据就没问题了。


数据分析

可上面这样的结果并不会我们想要的数据,所以我们需要更加详细的分析每个li中的结果是什么。
单个li的数据结构如下:

<li class="fed-list-item fed-padding fed-col-xs4 fed-col-sm3 fed-col-md2 xh-highlight"><a class="fed-list-pics fed-lazy fed-part-2by3" href="/t/wumianjuexing/" data-original="https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg" style="display: block; background-image: url(&quot;https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg&quot;);"><span class="fed-list-play fed-hide-xs"></span><span class="fed-list-score fed-font-xii fed-back-green">6.0</span><span class="fed-list-remarks fed-font-xii fed-text-white fed-text-center">HD</span></a><a class="fed-list-title fed-font-xiv fed-text-center fed-text-sm-left fed-visible fed-part-eone" href="/t/wumianjuexing/">无眠觉醒</a><span class="fed-list-desc fed-font-xii fed-visible fed-part-eone fed-text-muted fed-hide-xs fed-show-sm-block">吉娜·罗德里格兹,沙米尔·安德森,詹妮弗·杰森·李,阿丽亚娜·格林布拉特,巴里·佩珀,弗兰西丝·费舍,吉尔·贝罗斯,菲恩·琼斯,塞巴斯蒂安·皮戈特,塞尔吉奥·齐奥,亚历克斯·豪斯,卢修斯·霍约斯,特洛文·海斯,肖恩·艾哈迈德,朱莉娅·迪扬,罗伯特·巴佐齐,柴·瓦拉达雷斯,凯特琳娜·塔克西亚,玛莎·格尔文,埃利亚斯·艾德拉基,Michael,Hough</span></li>

首先有两个a标签的,
第一个a标签

<a class="fed-list-pics fed-lazy fed-part-2by3" href="/t/wumianjuexing/" data-original="https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg" style="display: block; background-image: url(&quot;https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg&quot;);"><span class="fed-list-play fed-hide-xs"></span><span class="fed-list-score fed-font-xii fed-back-green">6.0</span><span class="fed-list-remarks fed-font-xii fed-text-white fed-text-center">HD</span></a>

data-original:存储了电影封面
三个span标签:第一个并没有数据,第二span标签存有评分,第三个span标签存有 HD 的字样(不清楚是什么意思,知道的朋友告诉我一下)。

def parse(self, response):
        li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            #//封面
            item["cover"]=li.xpath("./a[1]/@data-original").extract_first()
            # 评分
            item["score"]=a1.xpath("./span[2]/text()").extract_first()
            # HD
            item["hd"]=a1.xpath("./span[3]/text()").extract_first()
            print(item)

第一个a标签结果如下:

$ scrapy crawl movie --nolog
{'cover': 'https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg', 'score': '6.0', 'hd': 'HD'}
{'cover': 'http://ae01.alicdn.com/kf/Ua7d5187008104f1b8da8d322d3e160ffv.jpg', 'score': '5.0', 'hd': '共12集,完结'}
{'cover': 'https://img.huishij.com/upload/vod/20210609-1/dba912b28f71400c85e98a75ec193aaa.jpg', 'score': '6.0', 'hd': 'HD'}
{'cover': 'http://ae01.alicdn.com/kf/U3dece43d297848f2b5dd58ff0e070eb2D.jpg', 'score': '9.0', 'hd': 'HD'}
{'cover': 'https://img.huishij.com/upload/vod/20210608-1/47c74371cbdf5bb97f02d7c0f06d7cd2.jpg', 'score': '3.0', 'hd': 'HD'}
{'cover': 'https://img.huishij.com/upload/vod/20210607-1/df394b70390b62a5358aeed3163f3131.jpg', 'score': '10.0', 'hd': 'HD'}
{'cover': 'https://img.huishij.com/upload/vod/20210606-1/86dcf8922c009db93ae28458bc51125f.jpg', 'score': '4.0', 'hd': 'HD'}
...

第二个a标签存放了封面名称,最后一个span标签存放了演员名单信息,并不会很多,这里就一块处理了。

<a class="fed-list-title fed-font-xiv fed-text-center fed-text-sm-left fed-visible fed-part-eone" href="/t/wumianjuexing/">无眠觉醒</a><span class="fed-list-desc fed-font-xii fed-visible fed-part-eone fed-text-muted fed-hide-xs fed-show-sm-block">吉娜·罗德里格兹,沙米尔·安德森,詹妮弗·杰森·李,阿丽亚娜·格林布拉特,巴里·佩珀,弗兰西丝·费舍,吉尔·贝罗斯,菲恩·琼斯,塞巴斯蒂安·皮戈特,塞尔吉奥·齐奥,亚历克斯·豪斯,卢修斯·霍约斯,特洛文·海斯,肖恩·艾哈迈德,朱莉娅·迪扬,罗伯特·巴佐齐,柴·瓦拉达雷斯,凯特琳娜·塔克西亚,玛莎·格尔文,埃利亚斯·艾德拉基,Michael,Hough</span>

编写程序

def parse(self, response):
        li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()
            print(item)

最终数据结果

$ scrapy crawl movie --nolog
{'cover': 'https://img.huishij.com/upload/vod/20210610-1/ba6cb9b6c0161b3e46406a154fdec464.jpg', 'score': '6.0', 'hd': 'HD', 'movie_name': '无眠觉醒', 'cast_list': '吉娜·罗德里格兹,沙米尔·安德森,詹妮弗·杰森·李,阿丽亚娜·格林布
拉特,巴里·佩珀,弗兰西丝·费舍,吉尔·贝罗斯,菲恩·琼斯,塞巴斯蒂安·皮戈特,塞尔吉奥·齐奥,亚历克斯·豪斯,卢修斯·霍约斯,特洛文·海斯,肖恩·艾哈迈德,朱莉娅·迪扬,罗伯特·巴佐齐,柴·瓦拉达雷斯,凯特琳娜·塔克西亚,玛莎·格尔文,埃利亚斯·艾德拉基,Michael,Hough'}
{'cover': 'http://ae01.alicdn.com/kf/Ua7d5187008104f1b8da8d322d3e160ffv.jpg', 'score': '5.0', 'hd': '共12集,完结', 'movie_name': '无间道', 'cast_list': '刘德华,梁朝伟,黄秋生,曾志伟,郑秀文,陈慧琳,陈冠希,余文乐,杜汶泽,林家栋,萧
亚轩'}
{'cover': 'https://img.huishij.com/upload/vod/20210609-1/dba912b28f71400c85e98a75ec193aaa.jpg', 'score': '6.0', 'hd': 'HD', 'movie_name': '贼世至尊1999', 'cast_list': 'Alec,Baldwin,Andre,Braugher,Michael,Jai,White'}
{'cover': 'http://ae01.alicdn.com/kf/U3dece43d297848f2b5dd58ff0e070eb2D.jpg', 'score': '9.0', 'hd': 'HD', 'movie_name': '少林小子', 'cast_list': '李连杰,黄秋燕,潘清福,于承惠,于海,胡坚强'}
{'cover': 'https://img.huishij.com/upload/vod/20210608-1/47c74371cbdf5bb97f02d7c0f06d7cd2.jpg', 'score': '3.0', 'hd': 'HD', 'movie_name': '填字遊戲事件簿:致命謎題', 'cast_list': 'Lacey,Chabert,John,Kapelos,Brennan,Elliott'} 
{'cover': 'https://img.huishij.com/upload/vod/20210607-1/df394b70390b62a5358aeed3163f3131.jpg', 'score': '10.0', 'hd': 'HD', 'movie_name': '半狼传说', 'cast_list': '子剑,吴宣萱,任天野'}
{'cover': 'https://img.huishij.com/upload/vod/20210606-1/86dcf8922c009db93ae28458bc51125f.jpg', 'score': '4.0', 'hd': 'HD', 'movie_name': '国家安全2010', 'cast_list': '内详'}
{'cover': 'https://img.huishij.com/upload/vod/20210605-1/e9e7b4fff221c92464b3786fa3340dee.jpg', 'score': '7.0', 'hd': 'HD', 'movie_name': '卡拉鹰', 'cast_list': 'Ricardo,Darín,Martina,Gusman'}
{'cover': 'https://img.huishij.com/upload/vod/20210605-1/558ca1cc9423a9c08ff8e65c42acfef2.jpg', 'score': '6.0', 'hd': 'HD', 'movie_name': '拳神2010', 'cast_list': 'Mahesh,Babu,Anushka,Shetty'}
{'cover': 'https://img.huishij.com/upload/vod/20210605-1/9a82882e959add4063b6bd7b7ebfb0fb.jpg', 'score': '8.0', 'hd': 'HD', 'movie_name': '三日危情', 'cast_list': '罗素·克劳,伊丽莎白·班克斯,泰·辛普金斯,奥利维亚·王尔德,连姆·尼
森,乔纳森·塔克,布莱恩·丹内利,瑞秋·迪肯,连尼·詹姆斯,杰森·贝盖,詹姆斯·兰索恩,莫兰·阿提艾斯,艾莎·辛德斯,丹尼尔·斯特恩'}
{'cover': 'https://img.huishij.com/upload/vod/20200627-1/b90ab5f7cf189c8556d9947971c2581b.jpg', 'score': '5.0', 'hd': 'HD', 'movie_name': '翻转', 'cast_list': '帕克·波西,迈克尔·拉帕波特,布鲁斯·邓恩,迈克尔·库立兹,保罗·莱维斯克
'}
...

这样我们就爬取了当前整页的电影信息
movie.py的完整代码如下:

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')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()

            print(item)


获取下页的地址

爬取完第一页的数据之后,如何爬取下一页的数据呢?我们只需要获取下一页的url地址即可。


image.png

使用xpath工具分析,获取包含下页文本内容a标签href信息

获取下一页href

点击下页:url是这样的:http://www.77dianshi.com/kdongzuopian-2/ ,也就是说将获取下页的href地址与http://www.77dianshi.com进行拼接即可。

编写程序,获取下页地址

def parse(self, response):
        li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()

        # 获取下页的url
        next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()

        next_url="http://www.77dianshi.com"+next_href
        print(next_url)

结果如下:

$ scrapy crawl movie --nolog
http://www.77dianshi.com/kdongzuopian-2/

获取到下页的url之后,还需要思考一个问题,若没有下一页了肯定会报错,这不是我们想看到的。所以下页的href不会都可能存在,所以得做好判断,防止报错。

我们看看最后一页是的什么样子 最后一页

从上面看出,下页的href都会存在,只不过最后一页的href就是当前页的href。

判断是否为最后一页

    def parse(self, response):
        li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()

        # 获取下页的url
        next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
        next_url="http://www.77dianshi.com"+next_href
        
        #获取当前页的url
        current_url=response.url
        # 通过判断是否以next_href为结尾,来判断是否是最后一页
        if not current_url.endswith(next_href):
            print("最后一页")
        else:
            print("不是最后一页")

爬取下页的电影信息

通过上面的准备工作,完成了当前页的数据爬取,下页url地址获取及最后一页的判断。接下来我们将使用scrapy来爬取下一页的数据。

止住:先别着急,为了保证爬取数据更稳定,我们需要修改一些参数。
settings.py配置:

DOWNLOAD_DELAY = 3 # 设置爬取延长时间,这里设置为三秒。爬取过快,可能会导致IP被封
ROBOTSTXT_OBEY = False # 我称这个为撕毁**君子协议**
# 自定义USER_AGENT
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36'

完整的代码

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')
        
        for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()

            print(item)

        # 获取下页的url
        next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
        next_url="http://www.77dianshi.com"+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("爬取完毕")

爬取结果

...
{'cover': 'http://ae01.alicdn.com/kf/U5f88cacadfe24a4894ed96f43676d1b1i.jpg', 'score': '4.0', 'hd': 'BD高清', 'movie_name': '血姬传', 'cast_list': '任娇,王铭,陈超良,张星阑'}
{'cover': 'https://cdn1.mh-pic.com/upload/vod/2019-10-05/157026432110.jpg', 'score': '7.0', 'hd': 'HD', 'movie_name': '钟离凰', 'cast_list': '岳辛,陈希郡,赵子络,何索,于歆童,李佳蔚'}
{'cover': 'https://cdn1.mh-pic.com/upload/vod/2019-10-05/15702721910.jpg', 'score': '7.0', 'hd': 'BD高清', 'movie_name': '半条命3:特种兵之战', 'cast_list': '楚镇,郭云飞,靳美玲'}
已经爬取了到了: http://www.77dianshi.com/kdongzuopian-17/ 下页地址: http://www.77dianshi.com/kdongzuopian-18/
{'cover': 'http://ae01.alicdn.com/kf/Uf774144860144247bbea9d4861c1e7a04.jpg', 'score': '5.0', 'hd': 'HD', 'movie_name': '看见我和你', 'cast_list': '杜海涛,刘璇,吴昕,唐禹哲,王雯雯,汤镇业,李健仁,陈志雄,孙丹丹,郑伊涵'}
{'cover': 'http://ae01.alicdn.com/kf/U7402fe10e7564417b3443a322e3f7dd3O.jpg', 'score': '8.0', 'hd': 'HD', 'movie_name': '分歧者2:绝地反击', 'cast_list': '谢琳·伍德蕾,提奥·詹姆斯,凯特·温丝莱特,奥克塔维亚·斯宾瑟'}
{'cover': 'http://ae01.alicdn.com/kf/U6a124ba5fdcd4cf5b99f133e16f012000.jpg', 'score': '7.0', 'hd': 'HD', 'movie_name': '分歧者:异类觉醒', 'cast_list': '谢琳·伍德蕾,提奥·詹姆斯,艾什莉·贾德,杰·科特尼'}
{'cover': 'http://ae01.alicdn.com/kf/U0c95ac9b8d964a24b8da02686bd971dbI.jpg', 'score': '7.0', 'hd': 'HD', 'movie_name': '杀人者唐斩', 'cast_list': '张丰毅,关之琳,莫少聪,张光北'}
....

目前爬取到了前18页数据:
已经爬取了到了: http://www.77dianshi.com/kdongzuopian-17/ 下页地址: http://www.77dianshi.com/kdongzuopian-18/


总结:

scrapy.Request 能构建一个requests,同时指定提取数据的callback函数。

scrapy.Request知识点:
scrapy.Request(url,callbock,method='GET',headers,body,cookies,meta,dont_filter=False)

scrapy.Request常用参数:


最后一点说明:
对于以后的数据存档操作(保存到本地磁盘或存储到数据库中)我们都应该在 movie.py 这个文件中操作。我们应该把数据交给pipelines.py来做。
需要怎么做呢?需要使用 yield 关键字。
具体操作如下:

  1. 需要将 print(item) 改成 yield item。
for li in li_list:
            item={} # 用于封装数据
            # 获取第一个 a 标签
            a1=li.xpath("./a[1]")
            # 封面
            item["cover"]=li.xpath("./a[1]/@data-original").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()

            #print(item) 
            yield item
  1. 修改 settings.py 文件
    将 ITEM_PIPELINES 注释打开
  2. 进入 pipelines.py 文件中
    获取item数据,模拟入库操作
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter

class ScrapyDemoPipeline:
    def process_item(self, item, spider):

        #获取item 数据
        print("模拟将数据入库:",item) #这里的打印和movie.py 里打印是一样的。

完结...

上一篇 下一篇

猜你喜欢

热点阅读