爬取豆瓣电影信息,再将豆瓣信息写入csv文件和mongodb数据库,再进行数据分析
爬取豆瓣电影信息
分析网站
首先我们先进入到电影网站首页https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0
但是这不是我所要的网址,我想要获得电影信息的网址,按f12看到动态网页代码,
我进行网页分析,得出,这个才是我想要的网址,我在把这个网址复制一个新的网页上查看
这里可以看到20条信息,但是有些看起来有点乱,可以用python来读取,就可以看到详细的信息
import requests import json url = 'https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0' request = requests.get(url=url).text js = json.loads(request) print(js)
通过这串代码就可以看到网址的信息,这里的信息我想获得的有评分、电影名、电影链接、图片链接、电影id,但是这里只有20条电影信息,如果想要获取全部,只要把链接https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0的page_limit=20改成2000就行了。
获取每个电影的网址,进而获取剩余信息
因为上面已经获得了电影网站,所以就进去查看网页源代码
可以看到这里有我想要获得的信息,导演、演员、上映时间、电影时长、电影类型、生产的国家,但是有些信息还是获取不到,比如说生产电影的国家,还有些电影的电影时长还是获取不到,这都是我不断尝试之后发现的
我发现了这里的网址https://movie.douban.com/j/subject_abstract?subject_id=33384987,有我们所要获取的电影信息
比如说导演、演员、电影时长、生产国家,然后我知道这些信息在哪里,就可以进入正题了。
访问电影总网址
import requests from bs4 import BeautifulSoup import re import json import pandas as pd from pymongo import MongoClient class Douban(object): #首先定义一个init方法,用来访问电影的主网址 def __init__(self): url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=time&page_limit=2000&page_start=0" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"} movie_url = requests.get(url=url, headers=headers).text` self.js = json.loads(movie_url)
这里用面向对象的方式编写,也可以不用,但是面向对象编写代码可以更容易修改
获取剩余电影信息
def movie_data(self): movie_data = [] for i in self.js['subjects']: movie_dict = {} url = i['url'] movie_dict["movie_url"] = url movie_dict["title"] = i['title'] print(i['title']) movie_dict["score"] = i['rate'] movie_dict["img"] = i['cover'] movie_dict["movie_id"] = i['id'] #调用下面的方法,来获取电影部分信息,然后在加入电影总的信息当中 movie_dict_2 = self.movie_url(url,i['id']) movie_dict.update(movie_dict_2) #再把这些信息加入一个全新列表当中 movie_data.append(movie_dict)
首先定义一个空字典,用于存取电影信息,然后定义一个空列表,把这些电影信息的字典写入列表中,为的是以后更容易把数据写入csv文件和mongodb数据库。
这里有一个调用方法,就是调用self.movie_url(url,i[‘id’]),因为这里数据还没全,所以要访问每个电影的网址来获取剩余的信息,然后把剩余的信息加入电影总信息中,也就是movie_dict字典中,然后再植入movie_data列表中
def movie_url(self,url,movie_id): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"} movie_url = requests.get(url=url, headers=headers).text soup = BeautifulSoup(movie_url) info_list = soup.find('div', id='info') type = info_list.find_all('span', property="v:genre") #先定义一个空字典 movie_dict = {} type_list = [] for j in type: type_list.append(j.text) #把电影类型加入字典中 movie_dict['type'] = "、".join(type_list) #因为有些电影的源代码有些不同,所以用异常处理来pass这些电影,这种电影极少出现 try: #获取电影的上映时间 relerse_time = info_list.find('span', property="v:initialReleaseDate").text release_time = re.findall(r"\d+\.?\d*", relerse_time) movie_dict['release_time'] = "-".join(release_time) country_url = "https://movie.douban.com/j/subject_abstract?subject_id={}".format(movie_id) get_country = requests.get(url=country_url).text js_country = json.loads(get_country) #获取电影的制作国家 movie_dict['country'] = js_country['subject']['region'] play_time = country_url['subject']['duration'] play_time = re.findall(r"\d+\.?\d*", play_time) #获取电影的播放时间 movie_dict['play_time'] = int("".join(play_time)) data = info_list.find_all('span') result = [span.get_text() for span in data] #获取电影的导演和演员 movie_dict['director'] = result[2] movie_dict['actor'] = result[8] except: pass #然后返回这些字典信息 return movie_dict
这里的方法有两个参数,一个是电影网站和电影id,电影网站是用来获取电影类型、电影上映时间、电影生产国家,用电影id结合上面所说的网址https://movie.douban.com/j/subject_abstract?subject_id=33384987来获取演员、导演、电影时长。
用电影网站获取是用bs4,而另外一个是用json库的方法获取。然后返回这些信息字典,上面调用这个方法就可以获取剩余的电影信息,然后在结合成一个字典再加入列表中。
这里用异常处理,防止部分电影的网站结构不同,虽然极少,也有可能会出问题,而这些出问题的电影就不录入了。
写入csv文件
def csv_write(self): data = self.movie_dataframe() pd.DataFrame.to_csv(data,"I:/crack/DATA/movie_data.csv",encoding="utf_8_sig") print("写入成功")
我先把总数据列表转化成dataframe数据类型,然后再写入csv文件,这里用的编码格式是utf_8_sig,这个编码格式可以更好的转化中文字符。
写入mongodb数据库
def mongodb_write(self): data = self.movie_dataframe() data_columns = list(data.columns) # 首先连接数据库 client = MongoClient(host="127.0.0.1", port=27017) # 连接数据库中的某个集合,这里如果原本没有这个数据库或者是集合,就会自动创建 collection = client['test_python']['movie_data'] i = 0 for j in list(data.values): l = list(j) data_list = {"_id":i,data_columns[0]: l[0], data_columns[1]: l[1], data_columns[2]: l[2], data_columns[3]: l[3], data_columns[4]: l[4], data_columns[5]: l[5], data_columns[6]: l[6],data_columns[7]:l[7]} collection.insert_one(data_list) i +=1 print("写入成功")
这里用的写入mongodb数据库的方法有些复杂,如果嫌麻烦也可以用导入csv文件的方式导入数据库
调用类对象
Movie = Douban() Movie.csv_write() Movie.mongodb_write()
源代码
''' 豆瓣信息爬虫,将爬取的信息写入mongodb和csv文件中 ''' import requests from bs4 import BeautifulSoup import re import json import pandas as pd from pymongo import MongoClient #用面向对象的方式编写 class Douban(object): #首先定义一个init方法,用来访问电影的主网址 def __init__(self): url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=time&page_limit=2000&page_start=0" url_2 = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E6%9C%80%E6%96%B0&page_limit=2000&page_start=0" url_3 = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%BB%8F%E5%85%B8&sort=time&page_limit=2000&page_start=0" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"} movie_url = requests.get(url=url, headers=headers).text movie_url_2 = requests.get(url=url_2,headers=headers).text movie_url_3 = requests.get(url=url_3,headers=headers).text print(movie_url_3) self.js_3 = json.loads(movie_url_3) self.js_2 = json.loads(movie_url_2) self.js = json.loads(movie_url) #再定义一个方法用来获取电影的数据 def movie_data(self): movie_data = [] for i in self.js['subjects']: movie_dict = {} url = i['url'] movie_dict["movie_url"] = url movie_dict["title"] = i['title'] print(i['title']) movie_dict["score"] = i['rate'] movie_dict["img"] = i['cover'] movie_dict["movie_id"] = i['id'] #调用下面的方法,来获取电影部分信息,然后在加入电影总的信息当中 movie_dict_2 = self.movie_url(url,i['id']) movie_dict.update(movie_dict_2) #再把这些信息加入一个全新列表当中 movie_data.append(movie_dict) for j in self.js_2['subjects']: movie_dict_3 = {} movie_dict_3["movie_url"] = j['url'] movie_dict_3["title"] = j['title'] print(j['title']) movie_dict_3["score"] = j['rate'] movie_dict_3["img"] = j['cover'] movie_dict_3["movie_id"] = j['id'] # 调用下面的方法,来获取电影部分信息,然后在加入电影总的信息当中 movie_dict_4 = self.movie_url(j['url'], j['id']) movie_dict_3.update(movie_dict_4) if movie_dict_3 in movie_data: print("已有这个数据") else: movie_data.append(movie_dict_3) for z in self.js_3['subjects']: movie_dict_5 = {} movie_dict_5["movie_url"] = z['url'] movie_dict_5["title"] = z['title'] print(z['title']) movie_dict_5["score"] = z['rate'] movie_dict_5["img"] = z['cover'] movie_dict_5["movie_id"] = z['id'] # 调用下面的方法,来获取电影部分信息,然后在加入电影总的信息当中 movie_dict_6 = self.movie_url(z['url'], z['id']) movie_dict_5.update(movie_dict_6) if movie_dict_5 in movie_data: print("已有这个数据") else: movie_data.append(movie_dict_5) return movie_data #定义一个方法来获取电影URL,然后在获取剩下的电影信息 def movie_url(self,url,movie_id): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"} movie_url = requests.get(url=url, headers=headers).text soup = BeautifulSoup(movie_url) info_list = soup.find('div', id='info') type = info_list.find_all('span', property="v:genre") #先定义一个空字典 movie_dict = {} type_list = [] for j in type: type_list.append(j.text) #把电影类型加入字典中 movie_dict['type'] = "、".join(type_list) #因为有些电影的源代码有些不同,所以用异常处理来pass这些电影,这种电影极少出现 try: #获取电影的上映时间 relerse_time = info_list.find('span', property="v:initialReleaseDate").text release_time = re.findall(r"\d+\.?\d*", relerse_time) movie_dict['release_time'] = "-".join(release_time) country_url = "https://movie.douban.com/j/subject_abstract?subject_id={}".format(movie_id) get_country = requests.get(url=country_url).text js_country = json.loads(get_country) #获取电影的制作国家 movie_dict['country'] = js_country['subject']['region'] play_time = country_url['subject']['duration'] play_time = re.findall(r"\d+\.?\d*", play_time) #获取电影的播放时间 movie_dict['play_time'] = int("".join(play_time)) data = info_list.find_all('span') result = [span.get_text() for span in data] #获取电影的导演和演员 movie_dict['director'] = result[2] movie_dict['actor'] = result[8] except: pass #然后返回这些字典信息 return movie_dict #定义一个方法来把这些数据转化成DataFrame数据类型 def movie_dataframe(self): movie_data = self.movie_data() movie_dataframe = pd.DataFrame(movie_data) movie_dataframe.index.name = "id" return movie_dataframe #定义一个方法来写入csv文件 def csv_write(self): data = self.movie_dataframe() pd.DataFrame.to_csv(data,"I:/crack/DATA/movie_data.csv",encoding="utf_8_sig") print("写入成功") return data #定义一个方法来写入mongodb数据库 def mongodb_write(self): data = self.movie_dataframe() data_columns = list(data.columns) # 首先连接数据库 client = MongoClient(host="127.0.0.1", port=27017) # 连接数据库中的某个集合,这里如果原本没有这个数据库或者是集合,就会自动创建 collection = client['test_python']['movie_data'] i = 0 for j in list(data.values): l = list(j) data_list = {"_id":i,data_columns[0]: l[0], data_columns[1]: l[1], data_columns[2]: l[2], data_columns[3]: l[3], data_columns[4]: l[4], data_columns[5]: l[5], data_columns[6]: l[6],data_columns[7]:l[7]} collection.insert_one(data_list) i +=1 print("写入成功") Movie = Douban() Movie.csv_write() Movie.mongodb_write()
这里的源代码有我上面所说的有些不同,因为这里用了三个网站的电影数量来写入的,所以有些复杂。
数据分析
这里所要说的是读取mongodb数据库的内容,然后再进行数据分析
读取数据库
import numpy as np import pandas as pd import matplotlib.pyplot as plt from pymongo import MongoClient pd.set_option('display.max_columns', 10000) pd.set_option('display.width', 200) pd.set_option('display.max_colwidth', 1000) #连接数据库,读取数据 class Douban(object): def __init__(self): client = MongoClient(host='localhost',port=27017) self.collection = client['test_python']['movie_data'] data = self.collection.find() movie_data = pd.DataFrame(data) self.movie_data=movie_data.set_index("_id")
set_option方法是用来更好的显现数据的,里面的值可以修改然后达到你最想要的效果
这里还是用面向对象的方式编写的,我获得了数据,然后把_id字段的内容设置为索引了。
统计每个季度上映电影数量的变化情况
def release_date(self): movie_data = self.movie_data release_date = movie_data['release_time'] #将上映时间转化为时间序列,再进行重采样 movie_data['release_date'] = release_date = pd.to_datetime(release_date) movie_data = movie_data.set_index("release_date") #对时间进行降采样,再进行求和,统计每个季度电影上映的数量 movie_data = movie_data.resample('Q',closed='right',label='right').count().to_period('M') #绘制折线图显示 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=80) x = movie_data.index y = movie_data['img'].tolist() plt.plot(range(len(x)),y) plt.title("近年来每个季度电影上映的数量统计") gird_list = [i*10 for i in range((max(movie_data['img'].tolist()) - min(movie_data['img'].tolist()))//9)] plt.grid() plt.yticks(gird_list) plt.xticks(range(len(x)),x) plt.savefig("I:/crack/img/近年来每个季度电影上映的数量统计.png") plt.show()
因为要用到时间,所以就要用pandas的时间序列。
首先把上映时间那一列提取出来,然后转化为时间序列,然后在设为索引,然后再进行重采样,只显示年月。
然后就可以进行画图了,如果不懂matplotlib就上matplotlib官网查看怎么写。
注意:图片存储地址,要选择好,不然会出错
不同类型的电影统计
这里要分析不同种类的电影数量,因为很多电影的类型不只一个,但是我们要把每个电影的每种类型都分出来,这里的数据出来处决于你上面存储的电影类型的数据格式是什么,如果是列表,就要用到mongodb的unwind方法,这里我存储的类型是字符串,所以我不是使用unwind方法。
def movie_type(self): type = self.movie_data['type'] type_list = type.tolist() type_list_2 = [] for i in type_list: list = i.split("、") for j in list: type_list_2.append(j) type_array = pd.DataFrame(type_list_2,columns=['type']) type_count = type_array['type'].value_counts() y = type_count.values[:-1] x = type_count.index[:-1] #绘图 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=100) rect = plt.bar(range(len(x)),y) plt.xticks(range(len(x)),x) plt.title("不同类型的电影分类统计") plt.grid() for r in rect: height = r.get_height() plt.text(r.get_x() + r.get_width()/2,1.02*height,height,fontsize=18,ha='center') plt.savefig("I:/crack/img/不同类型的电影分类统计.png") plt.show()
这里我是用split的方法把字符串转化为列表,然后定义一个空列表,把所有类型都存储在一个列表中,然后再转化为dataframe数据类型,再用分类汇总的方法,就可以开始画图了,如果不懂画柱状图就参看我前面的博客,我前面有一篇是专门讲柱状图的。
注意:图片存储地址,要选择好,不然会出错
统计每个国家生产的电影数量
def movie_country(self): data = self.movie_data country = data['country'].value_counts() x = country.index y = country.values #绘图 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=100) rect = plt.bar(range(len(x)),y) plt.xticks(range(len(x)),x,rotation=45) plt.title("不同国家生产的电影汇总") for rects in rect: height = rects.get_height() plt.text(rects.get_x() + rects.get_width()/2,1.02*height,height,fontsize=18,ha='center') plt.savefig("I:/crack/img/不同国家生产的电影汇总.png") plt.show()
这里的方法很简单就不细讲了。
源代码
''' 从mongodb数据库中读取豆瓣数据,进行数据分析 creat on May 15,2019 @Athor 小明 ''' import numpy as np import pandas as pd import matplotlib.pyplot as plt from pymongo import MongoClient pd.set_option('display.max_columns', 10000) pd.set_option('display.width', 200) pd.set_option('display.max_colwidth', 1000) #连接数据库,读取数据 class Douban(object): def __init__(self): client = MongoClient(host='localhost',port=27017) self.collection = client['test_python']['movie_data'] data = self.collection.find() movie_data = pd.DataFrame(data) self.movie_data=movie_data.set_index("_id")def release_date(self): movie_data = self.movie_data release_date = movie_data['release_time'] #将上映时间转化为时间序列,再进行重采样 movie_data['release_date'] = release_date = pd.to_datetime(release_date) movie_data = movie_data.set_index("release_date") #对时间进行降采样,再进行求和,统计每个季度电影上映的数量 movie_data = movie_data.resample('Q',closed='right',label='right').count().to_period('M') #绘制折线图显示 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=80) x = movie_data.index y = movie_data['img'].tolist() plt.plot(range(len(x)),y) plt.title("近年来每个季度电影上映的数量统计") gird_list = [i*10 for i in range((max(movie_data['img'].tolist()) - min(movie_data['img'].tolist()))//9)] plt.grid() plt.yticks(gird_list) plt.xticks(range(len(x)),x) # plt.savefig("I:/crack/img/近年来每个季度电影上映的数量统计.png") # plt.show() def movie_type(self): type = self.movie_data['type'] type_list = type.tolist() type_list_2 = [] for i in type_list: list = i.split("、") for j in list: type_list_2.append(j) type_array = pd.DataFrame(type_list_2,columns=['type']) type_count = type_array['type'].value_counts() y = type_count.values[:-1] x = type_count.index[:-1] #绘图 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=100) rect = plt.bar(range(len(x)),y) plt.xticks(range(len(x)),x) plt.title("不同类型的电影分类统计") plt.grid() for r in rect: height = r.get_height() plt.text(r.get_x() + r.get_width()/2,1.02*height,height,fontsize=18,ha='center') # plt.savefig("I:/crack/img/不同类型的电影分类统计.png") # plt.show() def movie_country(self): data = self.movie_data country = data['country'].value_counts() x = country.index y = country.values #绘图 plt.rcParams['font.family'] = 'SimHei' plt.figure(figsize=(20,10),dpi=100) rect = plt.bar(range(len(x)),y) plt.xticks(range(len(x)),x,rotation=45) plt.title("不同国家生产的电影汇总") for rects in rect: height = rects.get_height() plt.text(rects.get_x() + rects.get_width()/2,1.02*height,height,fontsize=18,ha='center') plt.savefig("I:/crack/img/不同国家生产的电影汇总.png") plt.show() movie = Douban() movie.movie_country()
这篇博客还有什么不懂的地方,可以添加我的QQ:1693490575
- 【转】爬取豆瓣电影top250提取电影分类进行数据分析
- 基于Spark和Hive进行的豆瓣电影数据分析
- 利用Python进行数据分析 —— CSV文件
- 写入数据java将数据写入到csv文件
- ArrayList 与HashSet的比较,及应用反射读取properties配置文件中的数据进行实例化再调用,以及类加载器的使用;还有HashCode的分析,及导致内存泄露,内存溢出的原因之一
- java将数据写入csv文件,从csv文件中读取数据
- java将数据写入到csv文件
- 利用python进行数据分析-数据加载、存储与文件格式2
- Hive实战:将xml文件处理为txt文件,并用Hive进行微博数据分析
- Matplotlib加载csv数据文件进行可视化
- 将大量有规律txt文本数据转换成xml格式,在导入excel,生成excel文件,在导入spass中,对数据进行分析
- [python和大数据-1]利用爬虫登录知乎进行BFS搜索抓取用户信息本地mysql分析【PART1】
- 学生信息键盘输入成绩并反转比较器进行降序排列存入TreeSet写入文件示例
- selenium-Java-使用csv文件进行数据驱动-中文乱码
- 【python数据分析】CSV文件数据读写
- Java从CSV文件中读取数据和写入
- python爬虫-爬取爱情公寓电影(2018)豆瓣短评并数据分析
- 创建一个csv文件,并写入数据
- Java从.CSV文件中读取数据和写入
- apue第六章 系统数据文件和信息(详细分析shadow的加密方法)