一、摘要
该数据来自豆瓣,本选题应用Python网络爬虫方法。
二、选题背景:
为一些片荒的影迷快速找出高分优质电影,省去寻找电影或观看劣质爆米花电影的时间
三、过程及代码:
1.爬取豆瓣高分电影
1 import re 2 import requests 3 from bs4 import BeautifulSoup 4 import pandas as pd 5 def get_movies(link): 6 headers = { # 这是请求头 7 \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \' 8 \'Chrome/83.0.4103.116 Safari/537.36 \' 9 10 } 11 movie_names=[] 12 movie_types=[] 13 movie_dis=[] 14 movie_grade=[] 15 movie_addr=[] 16 movie_actor=[] 17 movie_director=[] 18 movie_year=[] 19 r=requests.get(link,headers=headers,timeout=10) 20 soup=BeautifulSoup(r.text,"lxml") 21 22 for each in soup.find_all(\'div\',class_=\'abstract\'): 23 a=each.text 24 #.匹配任意字符,除了换行符 25 tp = re.search(r\'类型: (.*)\',a) 26 if tp==None: 27 movie_types.append(" ") 28 else: 29 movie_types.append(tp.group(1)) 30 actor = re.search(r\'主演: (.*)\',a) 31 if actor==None: 32 movie_actor.append(" ") 33 else: 34 movie_actor.append(actor.group(1)) 35 director = re.search(r\'导演: (.*)\',a) 36 if director==None: 37 movie_director.append(" ") 38 else: 39 movie_director.append(director.group(1)) 40 addr = re.search(r\'制片国家/地区: (.*)\',a) 41 if addr==None: 42 movie_addr.append(" ") 43 else: 44 movie_addr.append(addr.group(1)) 45 year=re.search(r\'年份: (.*)\',a) 46 if year==None: 47 movie_year.append(" ") 48 else: 49 year_str=year.group(1) 50 sj=int(year_str[:2])+1 51 nd=year_str[2]+\'0\' 52 movie_year.append(str(sj)+\'世纪\'+nd+\'年代\') 53 #查询所有class=title的div 54 div_list=soup.find_all(\'div\',class_=\'title\') 55 for each in div_list: 56 movie_name=each.a.text.strip() # 在div中,a标签的text的内容就是中文电影名称 57 movie_names.append(movie_name) 58 for each in soup.find_all(\'div\',class_=\'rating\'): 59 a=each.text.split(\'\n\') #在div中,第三个span的text的内容就是评价人数 60 #获取字符串中的数字 61 x=re.sub("\D","",a[3]) 62 movie_dis.append(int(x)) 63 movie_grade.append(float(a[2])) 64 return movie_names,movie_types,movie_dis,movie_grade,movie_addr,movie_actor,movie_director,movie_year
用for 循环读取每页的数据并写入dataframe
1 movies=get_movies("https://www.douban.com/doulist/240962/") 2 movies_1=pd.DataFrame({\'电影名称\':movies[0],\'电影类型\':movies[1],\'导演\':movies[6],\'主演\':movies[5],\'评价人数\':movies[2],\'评分\':movies[3],\'国家/地区\':movies[4],\'年代\':movies[7]}) 3 for i in range(1,18): 4 #总共18页,一页25个 5 link="https://www.douban.com/doulist/240962/?start="+str(i*25) 6 movies=get_movies(link) 7 movies_1=movies_1.append(pd.DataFrame({\'电影名称\':movies[0],\'电影类型\':movies[1],\'导演\':movies[6],\'主演\':movies[5],\'评价人数\':movies[2],\'评分\':movies[3],\'国家/地区\':movies[4],\'年代\':movies[7]}),ignore_index=True) 8 all_movies=movies_1
总共读取三个网页的电影信息,操作类似
整合所有数据后的到
2.数据分析
首先把年份的列名改成year
data.rename(columns={\'年代\':\'year\'},inplace=True)
统计每个年代上榜影片数量,并用plotly绘制柱状图
首先要导入相关库
1 import plotly.express as px 2 import plotly.graph_objects as go
1 n=data.groupby(\'year\').count() 2 x=list(n.index) 3 y=list(n[\'评分\']) 4 fig = go.Figure() 5 fig.add_trace(go.Bar( 6 x=x, 7 y=y, 8 ))
plotly可以用以下方法下载图片
import plotly.io as pio pio.write_image(fig, \'1.png\')
3.评分和评价人数
这里需要标准化数据,使他们落在一个区间
1 x=data.groupby(\'year\')[\'评价人数\',\'评分\'].mean() 2 s=(x-x.mean())/x.std() 3 fig = px.line(s) 4 fig.show()
20世纪早期的电影观看人数较少,但评分普遍较高,了20世纪中期之后的电影观看人数开始增加,但评分却略显平庸,到了本世纪,评分才有了上涨的趋势。
4.统计所有上榜电影
首先将电影类型按照 ‘/’ 分割,并去空格,转化成list 的目的是使用collections.Counter
先导入
1 from collections import Counter 2 types=data[\'电影类型\'].str.split(\'/\') 3 l=[] 4 tl=[] 5 for i in range(783): 6 l.extend(types[i]) 7 for i in l: 8 tl.append(i.strip()) 9 c = Counter(tl) 10 k=list(c.keys()) 11 v=list(c.values()) 12 count=pd.DataFrame({\'type\':k,\'c\':v}) 13 count.sort_values(by=\'c\') 14 total=count
绘制饼图
fig = px.pie(total, values=\'c\', names=\'type\') fig.show()
5.每个年代电影类型占比的统计
再按照之前的方法进行统计
并计算出每个年代各个类型出现的概率,画出柱状图
其中x是所有电影类型的列表
y是该年代各个电影类型的占比
1 import plotly.graph_objects as go 2 3 fig = go.Figure() 4 fig.add_trace(go.Bar( 5 x=k, 6 y=get_ep(l20_20,k), 7 name=\'20世纪20年代\', 8 )) 9 fig.add_trace(go.Bar( 10 x=k, 11 y=get_ep(l20_30,k), 12 name=\'20世纪30年代\', 13 )) 14 fig.add_trace(go.Bar( 15 x=k, 16 y=get_ep(l20_40,k), 17 name=\'20世纪40年代\', 18 )) 19 fig.add_trace(go.Bar( 20 x=k, 21 y=get_ep(l20_50,k), 22 name=\'20世纪50年代\', 23 )) 24 fig.add_trace(go.Bar( 25 x=k, 26 y=get_ep(l20_60,k), 27 name=\'20世纪60年代\', 28 )) 29 fig.add_trace(go.Bar( 30 x=k, 31 y=get_ep(l20_70,k), 32 name=\'20世纪70年代\', 33 )) 34 fig.add_trace(go.Bar( 35 x=k, 36 y=get_ep(l20_80,k), 37 name=\'20世纪80年代\', 38 )) 39 fig.add_trace(go.Bar( 40 x=k, 41 y=get_ep(l20_90,k), 42 name=\'20世纪90年代\', 43 )) 44 fig.add_trace(go.Bar( 45 x=k, 46 y=get_ep(l21_00,k), 47 name=\'21世纪00年代\', 48 )) 49 fig.add_trace(go.Bar( 50 x=k, 51 y=get_ep(l21_10,k), 52 name=\'21世纪10年代\', 53 )) 54 fig.show()
你可以详细查看其中的类
四、完整代码:
1 import re 2 import requests 3 from bs4 import BeautifulSoup 4 import pandas as pd 5 def get_movies(link): 6 headers = { # 这是请求头 7 \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \' 8 \'Chrome/83.0.4103.116 Safari/537.36 \' 9 10 } 11 movie_names=[] 12 13 movie_types=[] 14 15 movie_dis=[] 16 17 movie_grade=[] 18 19 movie_addr=[] 20 21 movie_actor=[] 22 23 movie_director=[] 24 25 movie_year=[] 26 27 r=requests.get(link,headers=headers,timeout=10) 28 29 soup=BeautifulSoup(r.text,"lxml") 30 31 for each in soup.find_all(\'div\',class_=\'abstract\'): 32 a=each.text 33 #.匹配任意字符,除了换行符 34 35 tp = re.search(r\'类型: (.*)\',a) 36 if tp==None: 37 movie_types.append(" ") 38 39 else: 40 movie_types.append(tp.group(1)) 41 actor = re.search(r\'主演: (.*)\',a) 42 43 if actor==None: 44 movie_actor.append(" ") 45 46 else: 47 movie_actor.append(actor.group(1)) 48 director = re.search(r\'导演: (.*)\',a) 49 50 if director==None: 51 movie_director.append(" ") 52 53 else: 54 movie_director.append(director.group(1)) 55 addr = re.search(r\'制片国家/地区: (.*)\',a) 56 57 if addr==None: 58 movie_addr.append(" ") 59 60 else: 61 movie_addr.append(addr.group(1)) 62 year=re.search(r\'年份: (.*)\',a) 63 64 if year==None: 65 movie_year.append(" ") 66 67 else: 68 year_str=year.group(1) 69 sj=int(year_str[:2])+1 70 nd=year_str[2]+\'0\' 71 movie_year.append(str(sj)+\'世纪\'+nd+\'年代\') 72 #查询所有class=title的div 73 74 div_list=soup.find_all(\'div\',class_=\'title\') 75 for each in div_list: 76 movie_name=each.a.text.strip() # 在div中,a标签的text的内容就是中文电影名称 77 78 movie_names.append(movie_name) 79 for each in soup.find_all(\'div\',class_=\'rating\'): 80 a=each.text.split(\'\n\') #在div中,第三个span的text的内容就是评价人数 81 82 #获取字符串中的数字 83 x=re.sub("\D","",a[3]) 84 85 movie_dis.append(int(x)) 86 87 movie_grade.append(float(a[2])) 88 89 return movie_names,movie_types,movie_dis,movie_grade,movie_addr,movie_actor,movie_director,movie_year 90 91 movies=get_movies("https://www.douban.com/doulist/240962/") 92 movies_1=pd.DataFrame({\'电影名称\':movies[0],\'电影类型\':movies[1],\'导演\':movies[6],\'主演\':movies[5],\'评价人数\':movies[2],\'评分\':movies[3],\'国家/地区\':movies[4],\'年代\':movies[7]}) 93 94 for i in range(1,18): 95 #总共18页,一页25个 96 97 link="https://www.douban.com/doulist/240962/?start="+str(i*25) 98 99 movies=get_movies(link) 100 101 movies_1=movies_1.append(pd.DataFrame({\'电影名称\':movies[0],\'电影类型\':movies[1],\'导演\':movies[6],\'主演\':movies[5],\'评价人数\':movies[2],\'评分\':movies[3],\'国家/地区\':movies[4],\'年代\':movies[7]}),ignore_index=True) 102 all_movies=movies_1 103 104 data.rename(columns={\'年代\':\'year\'},inplace=True) 105 106 import plotly.express as px 107 import plotly.graph_objects as go 108 109 110 n=data.groupby(\'year\').count() 111 x=list(n.index) 112 y=list(n[\'评分\']) 113 fig = go.Figure() 114 fig.add_trace(go.Bar( 115 x=x, 116 y=y, 117 )) 118 119 import plotly.io as pio 120 pio.write_image(fig, \'1.png\') 121 122 123 x=data.groupby(\'year\')[\'评价人数\',\'评分\'].mean() 124 s=(x-x.mean())/x.std() 125 fig = px.line(s) 126 fig.show() 127 128 from collections import Counter 129 130 types=data[\'电影类型\'].str.split(\'/\') 131 132 l=[] 133 134 tl=[] 135 136 for i in range(783): 137 l.extend(types[i]) 138 139 for i in l: 140 tl.append(i.strip()) 141 142 c = Counter(tl) 143 k=list(c.keys()) 144 v=list(c.values()) 145 146 count=pd.DataFrame({\'type\':k,\'c\':v}) 147 count.sort_values(by=\'c\') 148 total=count 149 150 fig = px.pie(total, values=\'c\', names=\'type\') 151 fig.show() 152 153 data_y=data[data.year==year] 154 155 import plotly.graph_objects as go 156 157 fig = go.Figure() 158 fig.add_trace(go.Bar( 159 x=k, 160 y=get_ep(l20_20,k), 161 name=\'20世纪20年代\', 162 )) 163 164 fig.add_trace(go.Bar( 165 x=k, 166 y=get_ep(l20_30,k), 167 name=\'20世纪30年代\', 168 )) 169 170 fig.add_trace(go.Bar( 171 x=k, 172 y=get_ep(l20_40,k), 173 name=\'20世纪40年代\', 174 )) 175 176 fig.add_trace(go.Bar( 177 x=k, 178 y=get_ep(l20_50,k), 179 name=\'20世纪50年代\', 180 )) 181 182 fig.add_trace(go.Bar( 183 x=k, 184 y=get_ep(l20_60,k), 185 name=\'20世纪60年代\', 186 )) 187 188 fig.add_trace(go.Bar( 189 x=k, 190 y=get_ep(l20_70,k), 191 name=\'20世纪70年代\', 192 )) 193 194 fig.add_trace(go.Bar( 195 x=k, 196 y=get_ep(l20_80,k), 197 name=\'20世纪80年代\', 198 )) 199 200 fig.add_trace(go.Bar( 201 x=k, 202 y=get_ep(l20_90,k), 203 name=\'20世纪90年代\', 204 )) 205 206 fig.add_trace(go.Bar( 207 x=k, 208 y=get_ep(l21_00,k), 209 name=\'21世纪00年代\', 210 )) 211 212 fig.add_trace(go.Bar( 213 x=k, 214 y=get_ep(l21_10,k), 215 name=\'21世纪10年代\', 216 )) 217 fig.show()
五、总结:
1.以评分高优先的方式爬取电影数据
2.分析高分电影的年代分布情况
3.爬取各个年代电影的评分和评价人数
4.统计上榜电影的类型占比