爬虫入门经典(十二) | 一文带你快速爬取豆瓣电影

2020-10-30 14:23发布

一、小小课堂

在古代,每当你闲暇之时,会同老友几人一起吃酒喝茶,觥筹交错,畅所欲言!而在如今,我们在无聊之时,又正值好友几人闲暇之时,可能会同去看当下最新的电影。但是如果只有我们自己一人,这时的你会怎如何做呢?

如果是我的话,我就会先自行查看豆瓣电影的评分,从中选择自己感兴趣的影片进行观看。这样不仅节省了我们的时间,更能陶冶我们的情操。同时我们还能在观影之后,查看别人给出的影评,从中给出中肯的评价,好为别人提供价值标杆。

好吧,说了那么多其实就是为了引出我们今天这篇博文所要爬取的网站——《豆瓣电影》。-。-

由于咱是技术博主,所以这些文邹邹的话咱就不写了哈。

以往的博文,大都是讲解的怎样爬取静态网页。这不,本篇博文博主专门选择了动态网页豆瓣电影进行数据采集。

所谓动态网页加载是通过js的ajax加载的数据或js算法(加密)得到的数据,并不是直接可以得到的数据结果。

豆瓣电影这个网站是通过ajax加载的数据。为什么会这样说呢?一会儿在分析网页结构的时候,博主会进行解释!

二、大体过程分析

在此,先给出豆瓣电影的URL:https://movie.douban.com/chart

  •  1. 分析获取的URL
    5

  •  2. 单击分类信息,跳转到分类电影列表
    6
    7
    这个页面是有多页数据加载的,当用户向下滚动右侧的滚动,加载数据,这个经过分析是ajax加载的数据,需要找到ajax请求的网址。
    8
    先找到分类,提取分类的名字和类型编号,然后再爬分类下的电影数据。

  •  2.提取数据的方法

ajax返回的数据是json,response.json()得到的是字典,用字典操作就可以了,当然用正则是肯定可以的。其实专门操作json的有一个模块叫jsonpath。

三、具体细节分析

3.1 先获取整个网页的源码

import requestsfrom lxml import etree

type_url = "https://movie.douban.com/chart"headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",}def parse_url(url):
    """解析url,得到html"""
    response = requests.get(url=url, headers=headers)
    return response.content.decode("utf-8")def parse_html(html):
    """解析url,得到字典"""
    etree_obj = etree.HTML(html)
    return etree_obj

content = parse_url(type_url)print(content)12345678910111213141516171819202122

9

3.2 获取分类

我们首先先看下网页内存在iframe没有
10
由于有iframe 所以不能使用xpath

下面我们先来看下我们所要爬取分类的格式
11
我们可以看到其格式为<a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>

既然xpath不能使用,那么我们就使用正则进行解析

<a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>'123

12
????,我们已经成功拿到电影的分类,下面开始尝试拿到其中一个分类中的所有数据

3.3 获取一页的所有数据

movie_url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20"response = requests.get(url=movie_url,headers=headers)content = response.json()print(content)1234

13

3.4 循环获取所有URL及内容

我们先看下地址栏的URL
14
15
URL对比

https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85%E7%89%87&type=11&interval_id=100:90&action=https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85%E7%89%87&type=11&interval_id=90:80&action=12

我们可以看到其中的interval_id是以10为单位移动的。那么我们是不是能够有一个大胆的想法能否拼接这个网址,然后循环爬取内容么呢?
答案是可以的,在此博主就不多讲解了,直接给出代码

movie_url = "https://movie.douban.com/j/chart/top_list"def get_movie(movie_type, low_score, high_score):
    """获取电影"""
    movie_type_name = movie_type[0]
    movie_type_num = movie_type[1]
    print(movie_type_num)

    i = 0
    while True:
        # 参数
        params = {
            "type": movie_type_num,
            "interval_id": "{}:{}".format(high_score, low_score),
            "action": "",
            "start": i,
            "limit": 20
        }
        # 发请求获取数据
        content = parse_json(movie_url, params)
        print(content)
        exit()def start():
    """爬虫开始"""
    low_score = int(input("输入要爬取的最低分(以5为单位),最高分默认是加10>"))
    high_score = low_score + 10

    movie_type_list = get_movie_type()
    for movie_type in movie_type_list:
        get_movie(movie_type, low_score, high_score)if __name__ == '__main__':
    start()12345678910111213141516171819202122232425262728293031323334353637

16

3.5 得到数据

至于此部分只是单纯的提取JSON中的数据,在此就不多解释了
17

def get_movie(movie_type, low_score, high_score):
    """获取电影"""
    movie = {
        "title": "",  # 电影名称
        "actors": "",  # 主演
        "release_date": "",  # 上映日期
        "regions": "",  # 上映地
        "types": "",  # 类型
        "score": "",  # 评分
        "vote_count": "",  # 评论数
        "url": "",  # url
    }

    movie_type_name = movie_type[0]
    movie_type_num = movie_type[1]

    i = 0
    while True:
        # 参数
        params = {
            "type": movie_type_num,
            "interval_id": "{}:{}".format(high_score, low_score),
            "action": "",
            "start": i,
            "limit": 20
        }
        # 发请求获取数据
        data_list = parse_json(movie_url, params)
        # 判断循环退出
        if not data_list:
            break
        # 循环
        for data in data_list:
            movie["title"] = data["title"]
            movie["actors"] = data["actors"]
            movie["release_date"] = data["release_date"]
            movie["regions"] = data["regions"]  
            movie["types"] = data["types"]     
            movie["score"] = data["score"]     
            movie["vote_count"] = data["vote_count"]  
            movie["url"] = data["url"]         
            save(movie)

        i += 20123456789101112131415161718192021222324252627282930313233343536373839404142434445

四、完整源码

# encoding: utf-8'''
  @author 李华鑫
  @create 2020-10-09 8:27
  Mycsdn:https://buwenbuhuo.blog.csdn.net/
  @contact: 459804692@qq.com
  @software: Pycharm
  @file: 豆瓣电影.py
  @Version:1.0
  
'''import requestsimport timeimport reimport randomimport csvfrom lxml import etree

type_url = "https://movie.douban.com/chart"movie_url = "https://movie.douban.com/j/chart/top_list"headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",}def parse_html(url, params={}):
    """解析url,得到html"""
    response = requests.get(url=url, headers=headers, params=params)
    return response.content.decode("utf-8")def parse_json(url, params={}):
    """解析url,得到字典"""
    response = requests.get(url=url, headers=headers, params=params)
    return response.json()def get_movie_type():
    """获取电影分类"""
    content = parse_html(type_url)
    return re.findall(r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>', content)def get_movie(movie_type, low_score, high_score):
    """获取电影"""
    movie = {
        "title": "",  # 电影名称
        "actors": "",  # 主演
        "release_date": "",  # 上映日期
        "regions": "",  # 上映地
        "types": "",  # 类型
        "score": "",  # 评分
        "vote_count": "",  # 评论数
        "url": "",  # url
    }

    movie_type_name = movie_type[0]
    movie_type_num = movie_type[1]

    i = 0
    while True:
        # 参数
        params = {
            "type": movie_type_num,
            "interval_id": "{}:{}".format(high_score, low_score),
            "action": "",
            "start": i,
            "limit": 20
        }
        # 发请求获取数据
        data_list = parse_json(movie_url, params)
        # 判断循环退出
        if not data_list:
            break
        # 循环
        for data in data_list:
            movie["title"] = data["title"]
            movie["actors"] = data["actors"]
            movie["release_date"] = data["release_date"]
            movie["regions"] = data["regions"]  # 国家
            movie["types"] = data["types"]     # 类型
            movie["score"] = data["score"]     # 评分
            movie["vote_count"] = data["vote_count"]  # 评论条数
            movie["url"] = data["url"]         # url
            save(movie)

        i += 20def save(item):
    """将数据保存到csv中"""
    with open("./豆瓣电影.csv", "a", encoding="utf-8") as file:
        writer = csv.writer(file)
        writer.writerow(item.values())def start():
    """爬虫开始"""
    low_score = int(input("输入要爬取的最低分(以5为单位),最高分默认是加10>"))
    high_score = low_score + 10

    movie_type_list = get_movie_type()
    for movie_type in movie_type_list:
        print("{}爬取中...".format(movie_type[0]))
        get_movie(movie_type, low_score, high_score)if __name__ == '__main__':
    start()#  测试代码:# content = parse_url(type_url)# # 由于有iframe 所以不能使用xpath# print(re.findall(r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>',content))### """# <a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>## r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>'# """## response = requests.get(url=movie_url,headers=headers)# content = response.json()# print(content)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126

五、保存完成结果

18
19

转载自:CSDN   作者:不温卜火

原文链接:https://buwenbuhuo.blog.csdn.net/article/details/109329736