irpas技术客

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

大大的周 8158

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


前言

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

并且更重要的是…

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

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

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

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

下面进入正题…


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

豆瓣电影TOP250

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

<div class="grid-16-8 clearfix"> <div class="article"> <div class="opt mod">...</div> <ol> <li> <div class="item"> <div class="pic"> <em><!-- num排名 --></em> <a href=""><!-- 电影网址 --> <img src="" alt="名字"> </a> </div> <div class="info"> <div class="hd"> <a href=""><!-- 电影网址 --> <span class="title">名字</span> <span class="title">英文名</span> <span class="other">其他名字</span> </a> <span class="playable">[可播放]</span><!-- 不可播放没有该标签 --> </div> <div class="bd"> <p></p><!-- 导演 主演 上映时代 剧情类型 --> <div class="star"> <span class="rating-5"></span> <span class="rating_num" property="v:average">9.7</span><!-- 电影评分 平均分 --> <span property="v:best" content="10.0"></span> <span>xxxx人评价</span><!-- 评价人数 --> </div> <p class="quote"><!-- 一句话评价 --> <span class="inq"></span> </p> </div> </div> </div> </li> <!-- li*24 --> </ol> </div> </div>

可以看到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.观察电影网页

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

网页代码:

<div id="content"> <div class="top250"></div> <div id="dale_movie_subject_top_icon"></div> <h1> <span>名字 name</span> <span class="year">1994</span><!-- 年份 --> </h1> <div class="grid-16-8 clearfix"> <div class="article"> <div class="indent clearfix"> <div class="subjectwrap clearfix"> <div class="subject clearfix"> <div id="mainpic"> <a href=""></a><!-- 进入海报页面 --> </div> <div id="info"> <span> <span class='pl'>导演</span>: <span class='attrs'> <a href="/celebrity/1047973/" rel="v:directedBy">弗兰克·德拉邦特</a> </span> </span> <br/> <span > <span class='pl'>编剧</span>: <span class='attrs'> <a href="/celebrity/1047973/">弗兰克·德拉邦特</a> / <a href="/celebrity/1049547/">斯蒂芬·金</a> </span> </span><br/> <span class="actor"> <span class='pl'>主演</span>: <span class='attrs'> <a href="/celebrity/1054521/" rel="v:starring">蒂姆·罗宾斯</a> <a href=""></a> </span> </span> <br/> <span class="pl">类型:</span> <span property="v:genre">剧情</span> / <span property="v:genre">犯罪</span> <br/> <span class="pl">制片国家/地区:</span> 美国<br/> <span class="pl">语言:</span> 英语<br/> <span class="pl">上映日期:</span> <span property="v:initialReleaseDate" content="1994-09-10(多伦多电影节)">1994-09-10(多伦多电影节)</span> / <span property="v:initialReleaseDate" content="1994-10-14(美国)">1994-10-14(美国)</span> <br/> <span class="pl">片长:</span> <span property="v:runtime" content="142">142分钟</span> <br/> <span class="pl">又名:</span> 月黑高飞(港) / 刺激1995(台) / 地狱诺言 / 铁窗岁月 / 消香克的救赎 <br/> <span class="pl">IMDb:</span> tt0111161 <br> </div> </div> <div></div> </div> <div></div><!-- 是否看过 打分 --> <div></div><!-- 写影评 --> </div> <div></div><!-- 剧情简介 --> </div> <div class="aside"></div><!-- 侧边栏 --> <div class="extra"></div> </div> </div>

可以看到电影名 和 上映年份 在 < 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} <br/>{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()


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #豆瓣爬虫python # # #