前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >爬虫入门经典(十二) | 一文带你快速爬取豆瓣电影

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

作者头像
不温卜火
发布2020-10-30 10:38:55
7790
发布2020-10-30 10:38:55
举报
文章被收录于专栏:不温卜火
1
1

一、小小课堂

3
3

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

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

4
4

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

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

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

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

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

二、大体过程分析

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

  • 1. 分析获取的URL
5
5
  • 2. 单击分类信息,跳转到分类电影列表
6
6
7
7

这个页面是有多页数据加载的,当用户向下滚动右侧的滚动,加载数据,这个经过分析是ajax加载的数据,需要找到ajax请求的网址。

8
8

先找到分类,提取分类的名字和类型编号,然后再爬分类下的电影数据。

  • 2.提取数据的方法

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

三、具体细节分析

3.1 先获取整个网页的源码

代码语言:javascript
复制
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
9

3.2 获取分类

我们首先先看下网页内存在iframe没有

10
10

由于有iframe 所以不能使用xpath

下面我们先来看下我们所要爬取分类的格式

11
11

我们可以看到其格式为<a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>

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

代码语言:javascript
复制
<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
12

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

3.3 获取一页的所有数据

代码语言:javascript
复制
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
13

3.4 循环获取所有URL及内容

我们先看下地址栏的URL

14
14
15
15

URL对比

代码语言:javascript
复制
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为单位移动的。那么我们是不是能够有一个大胆的想法能否拼接这个网址,然后循环爬取内容么呢? 答案是可以的,在此博主就不多讲解了,直接给出代码

代码语言:javascript
复制
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
16

3.5 得到数据

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

17
17
代码语言:javascript
复制
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

四、完整源码

代码语言:javascript
复制
# 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
18
19
19
31
31

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


23
23

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

32
32
33
33
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/10/28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、小小课堂
  • 二、大体过程分析
  • 三、具体细节分析
    • 3.1 先获取整个网页的源码
      • 3.2 获取分类
        • 3.3 获取一页的所有数据
          • 3.4 循环获取所有URL及内容
            • 3.5 得到数据
            • 四、完整源码
            • 五、保存完成结果
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档