栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

【Python】Python爬虫豆瓣电影数据并进行数据分析

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Python】Python爬虫豆瓣电影数据并进行数据分析

目录
  • 前言
  • 一、准备工作
    • 1.观察榜单网页结构
    • 2.观察电影网页
    • 3.IP代理
  • 二、开始爬取
    • 1.引入库
    • 2.获取榜单电影url
    • 3.电影信息
  • 三、可视化
    • 1.热力图
    • 2.柱状图
    • 3.饼图
    • 4.折线图
    • 5.漏斗图


前言

python爬虫爬取豆瓣电影基本上是爬虫入门必做的一个爬虫了,网上也有很多很好的教程,这篇文章写的就很没有必要,那为什么我还是要写呢,有一个很朴实的原因————期末作业。


并且更重要的是…

作为只有两个组员的小组组长,我无法继续摸鱼了…

这不是最关键的,最关键的是 要 !答 !辩 !

我老社恐了,上去答辩就跟要了我的狗命一样,不做一点记录的话,上台答辩一句话都讲不出来,我作为学生的生涯恐怕就到此结束了…

倒了这么久黑泥在不进入正题真的好吗…

下面进入正题…


一、准备工作 1.观察榜单网页结构

豆瓣电影TOP250


这个榜单能看到电影信息太少,我们要更多的电影信息就需要进入电影详细页面去获取,所以这个榜单页面我们只需要获取到每个电影的网址就好了
查看过网页代码后,找到我们需要的部分的html结构:

...
  1. 9.7 xxxx人评价

可以看到class为 pic 和 info 中的 < a > 标签中都有电影的网址,随便选一个就好,我选择获取的是pic中的。
然后这个榜单页面每一页都显示了25个电影,所以,要获取完250个电影我们需要翻页,
先翻一次页看看网址的变化。
可以看到网址从:

https://movie.douban.com/top250

变成了

https://movie.douban.com/top250?start=25&filter=

返回第一页我们可以看到第一页的网址为

https://movie.douban.com/top250?start=0&filter=

所以网址的规律就是每翻一页start的值就加25

我们只要循环十次,start的值从0开始每次加25,就可以获得所有榜单网页。

2.观察电影网页

我们再来看一下电影页面的网页结构:


网页代码:

名字 name 1994

导演: 弗兰克·德拉邦特
编剧: 弗兰克·德拉邦特 / 斯蒂芬·金
主演: 蒂姆·罗宾斯
类型: 剧情 / 犯罪
制片国家/地区: 美国
语言: 英语
上映日期: 1994-09-10(多伦多电影节) / 1994-10-14(美国)
片长: 142分钟
又名: 月黑高飞(港) / 刺激1995(台) / 地狱诺言 / 铁窗岁月 / 消香克的救赎
IMDb: tt0111161

可以看到电影名 和 上映年份 在 < h1 > 标签中 ,电影的其他信息则在 < div id=“info” > 中,只要获取就好

3.IP代理

很显然,如果爬取过于频繁就会出现以下情况:

不会返回任何数据

这时候更换ip或者等一天就可以重新获取到数据
使用代理ip:

当然也可以每次请求后加

time.sleep(random.randint(2,6))  # 暂停2~6秒的整数秒

来防止请求过多被封ip,但这样会使爬取数据变慢
当然我觉的这样好一点,减少对服务器的压力,而且免费代理ip的稳定性太差和失效快,基本就两三分钟的失效,我爬取的数据量很小,只是为了完成爬虫作业,没有必要买代理…


二、开始爬取 1.引入库
import os
import requests #发送HTTP请求
import random #
import time
from bs4 import BeautifulSoup
from lxml import etree 
import threading
from fake_useragent import UserAgent
import pandas as pd
import numpy as np #用于计算
from pyecharts import options as opts
from pyecharts.charts import Map
from pyecharts.charts import Bar
from pyecharts.charts import Pie
from pyecharts.charts import Line
from pyecharts.charts import Funnel
from pyecharts.faker import Faker

2.获取榜单电影url
ua = UserAgent(use_cache_server=False)
headers ={
    'User-Agent': ua.chrome,
    }
url = 'https://movie.douban.com/top250?start=0&filter='
request =requests.get(url,headers=headers)
print(request)

BsBOJ=BeautifulSoup(request.content,'lxml')
pic = BsBOJ.find_all(attrs={'class': 'pic'})
film_urls=[]
for x in pic:
    href = x.a.get('href')
    film_urls.append(href)
print(film_urls)

这样就可以获得一页的电影榜单url

循环十次就可以获得所有榜单电影的url了


3.电影信息
film_url='https://movie.douban.com/subject/1291561/'
request =requests.get(film_url,headers=headers,timeout=10)
request.encoding = 'utf-8'
film_info=[]
BsBOJ=BeautifulSoup(request.text,'html.parser')
#排名
rank = BsBOJ.find(attrs={'class': 'top250-no'}).text.split('.')[1]
#电影名
film_name = BsBOJ.find(attrs={'property': 'v:itemreviewed'}).text.split(' ')[0] #split 将中英文从空格处分开
#导演
director = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[1].split(':')[1].split('/')
#编剧
scriptwriter = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[2].split(':')[1].split('/')
#主演
actor = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[3].split(':')[1].split('/')
#类型
filmtype = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[4].split(':')[1].split('/') #
#制片国家/地区
area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[1].split('/') #
#语言
language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/') #
#上映日期
initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/')).split('(')[0] #
#片长
runtime = BsBOJ.find(attrs={'property': 'v:runtime'}).text #
#评分(平均分)
rating_num = BsBOJ.find(attrs={'property': 'v:average'}).text #
#五星百分比
stars5_rating_per = BsBOJ.find(attrs={'class': 'rating_per'}).text #
#评价人数
rating_people = BsBOJ.find(attrs={'property': 'v:votes'}).text #
film_info=[rank,film_name,director,scriptwriter,actor,filmtype,area,language,initialReleaseDate,runtime,rating_num,stars5_rating_per,rating_people]
print(film_info)

这时候发现了问题:

第二个数据的国家,语言和上映时间错了,打开网页看看


很显然有些电影会有官方网站这一栏,有些则没有
而第202个电影因为是纪录片,没有编剧和主演

那么我们的代码就需要改一下。
用最简单的方法,加个判断就好

#制片国家/地区
area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[1].split('/') #
#语言
language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/') #
#上映日期
initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/')).split('(')[0] #

改为:

if BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[0] == '官方网站':
    #制片国家/地区
    area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/')#
    #语言
    language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/') #
    #上映日期
    initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[8].split(':')[1].split('/')).split('(')[0] #
elif rank == 202:
     #编剧
     scriptwriter = '无'
     #主演
     actor = '无'
     #类型
     filmtype = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[2].split(':')[1].split('/') #
     #制片国家/地区
     area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[3].split(':')[1].split('/')#
     #语言
     language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[4].split(':')[1].split('/') #
     #上映日期
     initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[1].split('/')).split('(')[0] 
                
else:

    #制片国家/地区
    area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[1].split('/')#
    #语言
    language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/') #
    #上映日期
    initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/')).split('(')[0] #

现在就没有问题了

加个循环 :

for i in range(len(film_urls)):
            href=film_urls[i]
            #print(href)
            time.sleep(random.randint(2,7))
            r = requests.get(href,headers=headers,timeout=10)
            r.encoding = 'utf-8'
            BsBOJ=BeautifulSoup(r.text,'html.parser')
            #排名
            rank = BsBOJ.find(attrs={'class': 'top250-no'}).text.split('.')[1]
            #电影名
            film_name = BsBOJ.find(attrs={'property': 'v:itemreviewed'}).text.split(' ')[0] #split 将中英文从空格处分开
            #导演
            director = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[1].split(':')[1].split('/')
            #编剧
            scriptwriter = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[2].split(':')[1].split('/')
            #主演
            actor = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[3].split(':')[1].split('/')
            #类型
            filmtype = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[4].split(':')[1].split('/') #
            if BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[0] == '官方网站':
                #制片国家/地区
                area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/')#
                #语言
                language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/') #
                #上映日期
                initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[8].split(':')[1].split('/')).split('(')[0] #
            else:

                #制片国家/地区
                area = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[5].split(':')[1].split('/')#
                #语言
                language = BsBOJ.find(attrs={'id': 'info'}).text.split('n')[6].split(':')[1].split('/') #
                #上映日期
                initialReleaseDate = min(BsBOJ.find(attrs={'id': 'info'}).text.split('n')[7].split(':')[1].split('/')).split('(')[0] #
            #片长
            runtime = BsBOJ.find(attrs={'property': 'v:runtime'}).text #
            #评分(平均分)
            rating_num = BsBOJ.find(attrs={'property': 'v:average'}).text #
            #五星百分比
            stars5_rating_per = BsBOJ.find(attrs={'class': 'rating_per'}).text #
            #评价人数
            rating_people = BsBOJ.find(attrs={'property': 'v:votes'}).text #
            film_info=[rank,film_name,director,scriptwriter,actor,filmtype,area,language,initialReleaseDate,runtime,rating_num,stars5_rating_per,rating_people]
            print(film_info)

这样就可以获得所有的电影数据了。

然后每次循环保存数据:

head=['rank','film_name','director','scriptwriter','actor','filmtype','area','language','initialReleaseDate','runtime','rating_num','stars5_rating_per','rating_people']
#df2.to_csv('douban_top250_test.csv', mode='a', header=head, index=None)
current_path = os.path.dirname('__file__')
if film_info[0] == '1' :
    df2.to_csv(current_path+'douban_top250_test.csv', mode='a', header=head, index=None)
    print(f"top{film_info[0]}爬取完成")
else:
    df2.to_csv(current_path+'douban_top250_test.csv', mode='a', header=False, index=None)
    print(f"top{film_info[0]}爬取完成")


生成了一个文件里面保存着数据

这样基本就完成了数据的爬取。

三、可视化

引入文件:

data=pd.read_csv('douban_top250_final.csv')
data

1.热力图
value = Country_value#[38, 18, 9, 5, 5, 5, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1] 
attr = Country_attr#['United States', 'China', 'United Kingdom', 'France', 'Italy', 'Janpan', 'New Zealand', 'Korea', 'Swizetland', 'India', 'Poland', 'Australia', 'Mexico', 'Cyprus', 'Qatar', 'Lebanon']
data = []
for index in range(len(attr)):
    city_ionfo=[attr[index],value[index]]
    data.append(city_ionfo)
print(data)
m = (
    Map()
    .add("世界地图",data, "world")
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="top250电影制片国家"),
        visualmap_opts=opts.VisualMapOpts(max_=40),
 
    )
    #.render(path='热力图.html')
)
 
#os.system("热力图.html")
m.render_notebook()

2.柱状图

各个国家的电影的数量

x_choose=Country_attr
y_values=Country_value
print(type(Country_value))
b= (
    Bar()
    .add_xaxis(x_choose)
    .add_yaxis("电影数量", y_values)
    .set_global_opts(
        title_opts=opts.TitleOpts(title="top250电影制片国家", subtitle="数量"),
        brush_opts=opts.BrushOpts(),
    )
    #.render(path='柱状图.html')
)
b.render_notebook()

然后是电影评价人数的柱状图

#print(data['rating_people'])
#print(data['rating_people'].sort_values(ascending=False))
#print(data['film_name'][3])
rating_attr = []
rating_value = []
p_lists = data['rating_people'].sort_values(ascending=False)
#print(p_lists[0])

for i in range(20):
    print(data['film_name'][p_lists.index[i]])
    rating_attr.append(data['film_name'][p_lists.index[i]])
    print(p_lists[i])
    rating_value.append(int(p_lists[p_lists.index[i]]))
#print(rating_attr)
#print(rating_value)
x_choose=rating_attr
y_values=rating_value
print(type(Country_value))
b2= (
    Bar()
    .add_xaxis(x_choose)
    .add_yaxis("评价人数", y_values)
    .set_global_opts(
        title_opts=opts.TitleOpts(title="top250电影评价人数", subtitle="数量"),
        brush_opts=opts.BrushOpts(),
    )
    #.render(path='柱状图-评价人数.html')
)
b2.render_notebook()

3.饼图
x_data = Country_attr
y_data = Country_value
data = []
for index in range(len(x_data)):
    Country_ionfo=[x_data[index],y_data[index]]
    data.append(Country_ionfo)


p = (
    Pie()
    .add(
        "",
        data,
        radius=["40%", "55%"],
        label_opts=opts.LabelOpts(
            position="outside",
            formatter="{a|{a}}{abg|}n{hr|}n {b|{b}: }{c}  {per|{d}%}  ",
            background_color="#eee",
            border_color="#aaa",
            border_width=1,
            border_radius=4,
            rich={
                "a": {"color": "#999", "lineHeight": 22, "align": "center"},
                "abg": {
                    "backgroundColor": "#e3e3e3",
                    "width": "100%",
                    "align": "right",
                    "height": 22,
                    "borderRadius": [4, 4, 0, 0],
                },
                "hr": {
                    "borderColor": "#aaa",
                    "width": "100%",
                    "borderWidth": 0.5,
                    "height": 0,
                },
                "b": {"fontSize": 16, "lineHeight": 33},
                "per": {
                    "color": "#eee",
                    "backgroundColor": "#334455",
                    "padding": [2, 4],
                    "borderRadius": 2,
                },
            },
        ),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="Pie-top250电影制片国家"))
    #.render(path='饼图.html')
)
p.render_notebook()


电影类型的饼图:

f_type_lists = []
for i in range(len(data['filmtype'])):
    for j in range(len(data['filmtype'][i].split(','))):
        f_type_lists.append(data['filmtype'][i].split(',')[j].split("'")[1].split("'")[0])
#print(f_type_lists)
df_f_type = pd.Dataframe(f_type_lists)
df_f_type.value_counts()
x_f_type_data = []
y_f_type_data = []
for i in range(len(df_f_type.value_counts())):
    #print(lists.index[i])
    x_f_type_data.append(df_f_type.value_counts().index[i][0])
    #print(lists[i])
    y_f_type_data.append(int(df_f_type.value_counts()[i]))
    data_pair = [list(z) for z in zip(x_f_type_data, y_f_type_data)]
data_pair.sort(key=lambda x: x[1])

p2=(
    Pie(init_opts=opts.InitOpts(width="1600px", height="800px", bg_color="#2c343c"))
    .add(
        series_name="类型",
        data_pair=data_pair,
        rosetype="radius",
        radius="90%",
        center=["50%", "50%"],
        label_opts=opts.LabelOpts(is_show=False, position="center"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="电影类型",
            pos_left="center",
            pos_top="20",
            title_textstyle_opts=opts.TextStyleOpts(color="#fff"),
        ),
        legend_opts=opts.LegendOpts(is_show=False),
    )
    .set_series_opts(
        tooltip_opts=opts.TooltipOpts(
            trigger="item", formatter="{a} 
{b}: {c} ({d}%)" ), label_opts=opts.LabelOpts(color="rgba(255, 255, 255, 0.3)"), ) #.render("饼图-电影类型.html") ) p2.render_notebook()

4.折线图

电影上映年份:

#data['initialReleaseDate'].sort_values().value_counts()
#data['initialReleaseDate']
print(len(data['initialReleaseDate'].value_counts().sort_index()))
lists = data['initialReleaseDate'].value_counts().sort_index()
x_year_data = []
y_year_data = []
for i in range(len(lists)):
    #print(lists.index[i])
    x_year_data.append(lists.index[i])
    #print(lists[i])
    y_year_data.append(int(lists[i]))
print(x_year_data)
print(y_year_data)
year_data = []
for i in range(len(data['initialReleaseDate'].sort_values())):
    year_data.append(data['initialReleaseDate'].sort_values()[i].split('-')[0])
#print(year_data)
data['year'] =year_data
lists_n = data['year'].value_counts().sort_index()
lists_n=lists_n[0:-1]
lists_n[-5]=lists_n[-5] + 1
x_year_data_n = []
y_year_data_n = []
for i in range(len(lists_n)):
    #print(lists_n.index[i])
    x_year_data_n.append(lists_n.index[i])
    #print(lists_n[i])
    y_year_data_n.append(int(lists_n[i]))
#print(x_year_data_n)
#print(y_year_data_n)
l=(
    Line()
    .add_xaxis(xaxis_data=x_year_data_n)
    .add_yaxis(
        "电影上映年份",
        y_axis=y_year_data_n,
        linestyle_opts=opts.LineStyleOpts(width=2),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="Line-top250电影年份"),
        xaxis_opts=opts.AxisOpts(name="x"),
        yaxis_opts=opts.AxisOpts(
            type_="log",
            name="y",
            splitline_opts=opts.SplitLineOpts(is_show=True),
            is_scale=True,
        ),
    )
    #.render(path="折线图-年份.html")
)
l.render_notebook()

5.漏斗图
p_name =[]
for i in range(len(data['actor'])):
    #print(data['actor'][i])
    for j in range(len(data['actor'][i].split(','))):
        #print(data['actor'][i].split(','))
            if j == len(data['actor'][i].split(',')) - 1:
                p_name.append(data['actor'][i].split(',')[j].split("'")[1].split("'")[0])
                #print(data['actor'][0].split(',')[z].split("'")[1])
            else:
                p_name.append(data['actor'][i].split(',')[j].split("'")[1])
                #print(data['actor'][0].split(',')[z].split("'")[1].split("'")[0])
#print(p_name)
df_p = pd.Dataframe(p_name)
df_p.value_counts()
print(df_p.value_counts())
p_attr = []
p_value = []
for i in range(20):
    #print(lists.index[i])
    p_attr.append(df_p.value_counts().index[i][0])
    #print(lists[i])
    p_value.append(int(df_p.value_counts()[i]))
#print(p_attr)
#print(p_attr[0])
#print(p_value)
x_data = p_attr
y_data = p_value
p_data = []
for index in range(len(x_data)):
    p_ionfo=[x_data[index],y_data[index]]
    p_data.append(p_ionfo)
f = (
    Funnel()
    .add(
        "参演数",
        p_data,
        sort_="ascending",
        label_opts=opts.LabelOpts(position="inside"),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="top250电影参演数"))
    #.render(path="漏斗图-演员.html")
)
f.render_notebook()

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/664134.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号