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

  大家好,我是不温卜火,是一名计算机学院大数据专业大三的学生,昵称来源于成语—不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!暂时只在csdn这一个平台进行更新,博客主页:https://buwenbuhuo.blog.csdn.net/
1

PS:由于现在越来越多的人未经本人同意直接爬取博主本人文章,博主在此特别声明:未经本人允许,禁止转载!!!


2


推荐

31
  ♥各位如果想要交流的话,可以加下QQ交流群:974178910,里面有各种你想要的学习资料。♥

  ♥欢迎大家关注公众号【不温卜火】,关注公众号即可以提前阅读又可以获取各种干货哦,同时公众号每满1024及1024倍数则会抽奖赠送机械键盘一份+IT书籍1份哟~♥
30

一、小小课堂

3

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

如果是我的话,我就会先自行查看豆瓣电影的评分,从中选择自己感兴趣的影片进行观看。这样不仅节省了我们的时间,更能陶冶我们的情操。同时我们还能在观影之后,查看别人给出的影评,从中给出中肯的评价,好为别人提供价值标杆。
4
好吧,说了那么多其实就是为了引出我们今天这篇博文所要爬取的网站——《豆瓣电影》。-。-

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

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

所谓动态网页加载是通过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 requests
from 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)

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>'

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)

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=

我们可以看到其中的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()

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 += 20

四、完整源码

# 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 requests
import time
import re
import random
import csv
from 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 += 20

def 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)

五、保存完成结果

18
1931

美好的日子总是短暂的,虽然还想继续与大家畅谈,但是本篇博文到此已经结束了,如果还嫌不够过瘾,不用担心,我们下篇见!


23

  好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
  如果我的博客对你有帮助、如果你喜欢我的博客内容,请“点赞” “评论”“收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
  码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注我哦!

32
33

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页