爬虫第九式:豆瓣电影排行榜数据抓取

温馨提示:

爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。

《刑法》第 285 条,非法获取计算机信息系统数据罪。
       违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

正文:
豆瓣电影排行榜,这个网站,是Ajax动态加载数据的网站,这样的网站有两个特点:

1】 右键 -> 查看网页源码中没有具体数据
【2】 滚动鼠标滑轮或者其他动作时加载,或者页面局部刷新

比如:腾讯招聘、小米应用商店、京东等等都是这样的网站类型
那对于这种网站的话,我们拼接URL地址、查看URL规律、正则和xpath都是没有意义的了,那只能是F12抓包找
我们进入到豆瓣电影网站,找到排行榜点进去:
在这里插入图片描述
我们先看看剧情的排行榜的电影吧:
在这里插入图片描述
然后我们按照步骤一步一步走,先看看源代码中有没有数据:
在这里插入图片描述
很显然是没有的,这个就是Ajax动态加载的特点,那我们就去F12抓包去,打开F12后先刷新页面,然后滚轮先在页面中往下走走,加载出点数据,毕竟最后还是要找URL地址规律的,然后点到XHR里面找数据包,先随便找一个吧:
在这里插入图片描述
我们看这个,哎呀这个是吗???其实不是的,因为刚才我们经过上面的步骤,滚动滑轮,加载数据,那不能就加载出一个吧,所以我们接着往下看:

我们看这个,数据显示的是数字的,这个是不是就是对应上面的什么剧情类总数、我没有看过的、可在线播放的数量
在这里插入图片描述
所以这个也不是,但是有点接近了对吧,我们接着看下一个数据包,因为不知道它页面数据的数据包是哪一个,我们只能一个一个看

这个就是了,我们看到这里面有什么国家、上映时间、电影名字和主演什么的,这个就是响应的JSON数据

在这里插入图片描述
一旦我们在Preview找到了相应的数据,我们就点开Headers进行分析,我们看这个请求方式是正常的GET请求,那URL的地址就是Preview中响应数据的地址,也就是JOSN数据地址

在这里插入图片描述

对于GET请求数据这么多,我们看下面的这个东西 Query String Parameters,这个就是查询参数的意思,这个查看参数就是上面URL地址 ? 后面的东西,就是给你格式化输出了下,然你好看一点

https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20

type=11&
interval_id=100%3A90&
action=&
start=0&
limit=20

在这里插入图片描述所以我们到现在的话,就可以发现一个问题了,就是页面是
POST请求,去找FormData
GET请求,去找Query String Parameters

那我们分析Query String Parameters里的数据规律
在这里插入图片描述
这样我们也不知道是什么,那就对比,我们找下一个数据包对比一下看看,这个
type还是11,可能是电影的类别吧;
interval_id还是100:90;
action还是没变;
start这个变了,它从上一个数据包的0,变成20了;
limit还是20也没变,这个应该是动态加载一次的电影数量吧
在这里插入图片描述

那我们在找一个数据包看看变化的规律,这个没变的还是没变,变了的还是那一个,这样看的话,这个start,下一次变得话就是60了,所以我们找到了规律就是:0、20、40、60…
在这里插入图片描述
而且其他查询参数里面也没有时间戳、也没有加密的,所以我们就直接写代码
那我们先去弄一下URL地址,就是我们之前抓到的JSON数据地址:

https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20

那我们复制到浏览器看一下是什么样的,这就是那个JSON数据,我们在页面中更好看点,因为我们之前安装的这个插件JSONView,如果不装这个的话,我们打开这个网址的时候可能是乱的,但是数据是对的,就是看着比较乱

这里丢个链接,就是我们安装的步骤,可以安装一下,按照我里面说的走就可以安装
https://blog.csdn.net/Yxh666/article/details/111300868

在这里插入图片描述
这个就是我们的html,就是在未来的某段时间,也就是一会儿写代码,会写一个这个:

	html = requests.get(url=url,headers={}).josn()

那这个html列表里面呢的字典就是我们要拿的json,我们提里面的字典,就是列表里面套了20个字典,每个字典都是这样的一个数据:
在这里插入图片描述

那我们要是提取字典里面的数据就得这样写代码:

for one_film in html:
	item = {}
	# 电影的排名
	item['rank'] = one_film['rank']

那这是一页的,我要是想获其他也的数据怎么搞,其实之前我们呢已经分析出来了,就是那个start,这个就是控制网页页数的东西,所以我们也发现了URL地址的数据,只需要改start,就行了
接下来我们正式写代码:
导入模块

import requests
import json   # 简单的话我们直接.json  但是我们最后做成全站,一定涉及到静态和动态的所以要导入json模块
from fake_useragent import UserAgent
import time
import random
import re

定义功能函数,减少重复代码

我测试了一下,网站没有检查Cookie,所以不用定义

class DoubanSpider:
    def __init__(self):
    	# json数据URL地址,type就是电影类型,start是页数,把这两个定义为{}
        self.url = 'https://movie.douban.com/j/chart/top_list?type={}&interval_id=100%3A90&action=&start={}&limit=20'

请求功能函数,需要多次发送请求

def get_html(self, url):
    headers = {'User-Agent':UserAgent().random}
    # 不要写.json,因为这个数据是有动态和静态的,要是写.json的话,只能动态用,不能静态用了,而text静态和动态都可以用
    html = requests.get(url=url,
                        headers=headers).text
    return html

解析的功能函数,提取出数据了就要解析

def parse_html(self, url):
    html = self.get_html(url=url) 
    # 动态解析数据.loads
    # html: [{},{},...,{}]
    html = json.loads(html)
    for one_film_dict in html:
        item = {}
        # 排名
        item['rank'] = one_film_dict['rank']
        # 电影名称
        item['name'] = one_film_dict['title']
        # 评分
        item['score'] = one_film_dict['score']
        # 国家
        item['regions'] = onefilm_dict['regions']
        # 电影类型
        item['types'] = onefilm_dict['types']
        # 主演
        item['actors'] = onefilm_dict['actors']
        print(item)

接下来我们写电影数量的代码,因为我们不能每次每次爬取页数的时候都去网页里面看看吧,这也不太合理,所以我们就定义一个变量来作为要爬取的电影数量,那先来我们去网页里看一下电影页数,就是这个:
在这里插入图片描述
就是这个,其实刚才我们抓包的时候也看见了,但是我们不知道是静态还是动态的,但是这种的啊,一般是动态的,就是从数据库里获取的总数,因为电影数量也会发生变化,不可能一成不变的,你要是还不相信的话自己去源代码搜索一下就知道是不是了

所以接下来我们还是抓包吧,其实上面有找到完了,我们也提了一下,现在我在给他弄下来
就是这个
在这里插入图片描述
就是那个total,中文翻译就是总计,全部的意思,那接下来我们点到Headers,点到Query String Parameters,看到里面就两个数据,一个电影类型,另一个应该是是上面的**好于100%-90%的剧情片 (731) **这个对应的,没关系不影响:

在这里插入图片描述

复制URL地址,注意这个地址跟之前的地址是不一样的,因为要是一样的话就返回电影的数据了,我们放到地址栏里回车看一下

在这里插入图片描述

这样的话,我们也获取到了电影页数的数据了,所以我们还得用到它的这个URL地址,接着写代码:

def get_total(self, user_type):
    # 获取电影总数
    # 把type设置为{},因为这样我们可以获取全排行站的类型
    total_url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(user_type)
    # json格式
    total_html = json.loads(self.get_html(total_url))

到现在有一个问题,我先要看各种类型的电影,比如:喜剧、恐怖、剧情等等,那我的 type 是不是要定义一下啊,就是弄一个字典存放起来:

# 比如
{'剧情':'11','喜剧':'24',...}

如果想这样的话,我们就是要获取每个电影类型的数值了,那我们只能是在这个页面获取
在这里插入图片描述
因为这里是总的,我们点进去抓包就能看到的,但是这样太麻烦了,我们为了节省时间就是直接去看看,是抓包还是直接提,一般来说这种的不会是动态加载的,所以我们直接源代码搜一下,就可以看到了:
在这里插入图片描述
果不其然的有,就是静态的,那我们就提取呗,我们看看用什么提取,re还是xpath,xpath的话,还得写表达式,我就用正则了,正则直接**(.*?)**就行了呗:

<span><a href=.*?type_name=(.*?)&type=(.*?)&

那我们就要发请求了,注意: 要向我们提取类型的地址发请求, https://movie.douban.com/chart
因为,别的地址只能找到一个类型,这个是所有的类型的,所以要发向这个地址发请求,再说,别的地址你发请求,要么没有,要么只有一个,那我们写正则提那个就没有意义了吧

正则解析提取数据

    regex = '<span><a href=.*?type_name=(.*?)&type=(.*?)&'
    pattern = re.compile(regex, re.S)
    r_list = pattern.findall(html)

此时获取的type是这样的:

r_list: [('剧情','11'),('喜剧':'24'),....]

我们存的是字典,直接遍历出来存入字典:

# 定义空字典
film_type_dict = {}
for r in r_list:
	# 取出列表里的数据
    film_type_dict[r[0]] = r[1]

最后我们从字典中吧所有的key提取出来,当作爬取电影类的提示,我们就直接遍历就好了;

menu = ''
for key in film_type_dict:
	# | 分隔作用
    menu += key + '|'
print(menu)

最后我们拼接地址就好了:

# 获取到type的key
user_type = film_type_dict[choice]
# total: 电影总数
total = self.get_total(user_type)
for start in range(0, total, 20):
    page_url = self.url.format(user_type, start)
    self.parse_html(url=page_url)

最后奉上全部代码:

import requests
import json
from fake_useragent import UserAgent
import time
import random
import re

class DoubanSpider:
    def __init__(self):
        self.url = 'https://movie.douban.com/j/chart/top_list?type={}&interval_id=100%3A90&action=&start={}&limit=20'

    def get_html(self, url):
        """请求功能函数"""
        headers = {'User-Agent':UserAgent().random}
        html = requests.get(url=url,
                            headers=headers).text

        return html

    def parse_html(self, url):
        html = self.get_html(url=url)
        # html: [{},{},...,{}]
        html = json.loads(html)
        for one_film_dict in html:
            item = {}
            item['rank'] = one_film_dict['rank']
            item['name'] = one_film_dict['title']
            item['score'] = one_film_dict['score']
            print(item)

    def get_total(self, user_type):
        """获取电影总数"""
        total_url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(user_type)
        total_html = self.get_html(total_url)
        total_html = json.loads(total_html)

        return total_html['total']

    def get_film_type_dict(self):
        """获取所有类别的大字典"""
        # 获取主页响应内容
        url = 'https://movie.douban.com/chart'
        html = self.get_html(url)
        # 正则解析提取数据
        regex = '<span><a href=.*?type_name=(.*?)&type=(.*?)&'
        pattern = re.compile(regex, re.S)
        # r_list: [('剧情','11'),('喜剧':'24'),....]
        r_list = pattern.findall(html)
        film_type_dict = {}
        for r in r_list:
            film_type_dict[r[0]] = r[1]

        return film_type_dict

    def run(self):
        # {'剧情':'11','喜剧':'24',...}
        film_type_dict = self.get_film_type_dict()
        menu = ''
        for key in film_type_dict:
            menu += key + '|'
        print(menu)

        choice = input('请输入电影类别:')
        # 获取到type的值
        user_type = film_type_dict[choice]
        # total: 电影总数
        total = self.get_total(user_type)
        for start in range(0, total, 20):
            page_url = self.url.format(user_type, start)
            self.parse_html(url=page_url)
            # 控制自己
            time.sleep(random.uniform(0, 1))


if __name__ == '__main__':
    spider = DoubanSpider()
    spider.run()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨旭华 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值