Python网络爬虫-豆瓣电影Top250
一,选题背景:
豆瓣电影top250榜单想必大家都不陌生,上榜的电影都是经过时间的沉淀留下来比较经典,对top250榜单电影数据爬取,并对爬取的数据绘制图表进行可视化,做个简单的数据分析。
(1)数据来源:豆瓣网(https://movie.douban.com/top250?)
(2)运行环境:window10、python3.7
二, 主题页面的结构特征分析
(1) 数据爬取与处理

三,网络爬虫程序设计
from bs4 import BeautifulSoup import re import urllib.request,urllib.error import xlwt import sqlite3 from wordCloud import generateWordCloud from draw import drawBrokenLine, drawPie savepath = r"dict/豆瓣电影Top250.xls" def main(): baseurl = "https://movie.douban.com/top250?start=" #1.爬取网页 datalist = getData(baseurl) dbpath = "movie.db" #3.保存数据 saveData(datalist, savepath) # saveData2DB(datalist,dbpath) #askURL("https://movie.douban.com/top250?start=") #影片详情链接的规则 findLink = re.compile(r'<a href="(.*?)">') #创建正则表达式对象,表示规则(字符串的模式) #影片图片 findImgSrc = re.compile(r'<img.*src="(.*?)"',re.S) #re.S 让换行符包含在字符中 #影片片名 findTitle = re.compile(r'<span class="title">(.*)</span>') #影片评分 findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>') #找到评价人数 findJudge = re.compile(r'<span>(\d*)人评价</span>') #找到概况 findInq = re.compile(r'<span class="inq">(.*)</span>') #找到影片的相关内容 findBd = re.compile(r'<p class="">(.*?)</p>',re.S) #爬取网页 def getData(baseurl): datalist = [] for i in range(0, 10): url = baseurl + str(i * 25) # 网页源码 html = askURL(url) soup = BeautifulSoup(html,"html.parser") #查找符合要求的字符串,形成列表 for item in soup.find_all('div',class_="item"): # 一部电影的所有信息 data = [] item = str(item) #影片详情的链接 link = re.findall(findLink,item)[0] # re库用来通过正则表达式查找指定的字符串 data.append(link) # 添加链接 imgSrc = re.findall(findImgSrc,item)[0] data.append(imgSrc) # 添加图片 titles = re.findall(findTitle,item) # 片名可能只有一个中文名,没有外国名 if(len(titles) == 2): ctitle = titles[0] # 添加中文名 data.append(ctitle) otitle = titles[1].replace("/","") # 去掉无关的符号 data.append(otitle) # 添加外国名 else: data.append(titles[0]) data.append(' ') # 外国名字留空 rating = re.findall(findRating,item)[0] data.append(rating) # 添加评分 judgeNum = re.findall(findJudge,item)[0] data.append(judgeNum) # 提加评价人数 inq = re.findall(findInq,item) if len(inq) != 0: inq = inq[0].replace("。","") # 去掉句号 data.append(inq) # 添加概述 else: data.append(" ") # 留空 bd = re.findall(findBd,item)[0] bd = re.sub('<br(\s+)?/>(\s+)?'," ",bd) # 去掉<br/> bd = re.sub('/'," ",bd) # 替换/ data.append(bd.strip()) # 去掉前后的空格 datalist.append(data) # 把处理好的一部电影信息放入datalist return datalist #得到指定一个URL的网页内容 def askURL(url): head = { "User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36" } request = urllib.request.Request(url, headers = head) html = "" try: response = urllib.request.urlopen(request) html = response.read().decode("utf-8") #print(html) except urllib.error.URLError as e: if hasattr( e, "code" ): print( e.code ) if hasattr( e, "reason" ): print( e.reason ) return html #保存数据 def saveData(datalist,savepath): #创建workbook对象 book = xlwt.Workbook(encoding = "utf-8", style_compression = 0) #创建工作表 sheet = book.add_sheet('豆瓣电影Top250', cell_overwrite_ok=True) col = ("电影详情链接", "图片链接", "影片中文名", "影片外国名", "评分", "评价数", "概况", "相关信息") for i in range(0, 8): #列名 sheet.write(0, i, col[i]) for i in range(0,250): print("第%d条" %( i + 1 )) data = datalist[i] for j in range(0, 8): #数据 sheet.write(i + 1, j, data[j]) book.save(savepath) def saveData2DB(datalist, dbpath): init_db( dbpath ) conn = sqlite3.connect( dbpath ) cur = conn.cursor() for data in datalist: for index in range(len( data )): if index == 4 or index == 5: continue data[index] = '"'+data[index]+'"' sql = ''' insert into movie250 ( info_link,pic_link,cname,ename,score,rated,instroduction,info) values(%s)'''%",".join(data) print(sql) cur.execute(sql) conn.commit() cur.close() conn.close() def init_db(dbpath): sql = ''' create table movie250 ( id integer primary key autoincrement, info_link text, pic_link text, cname varchar, ename varchar, score numeric , rated numeric , instroduction text, info text ) ''' #创建数据表 conn = sqlite3.connect(dbpath) cursor = conn.cursor() cursor.execute(sql) conn.commit() conn.close() #当程序执行时 if __name__ == "__main__": main() #init_db("movietest.db") generateWordCloud(savepath) drawPie(savepath) drawBrokenLine(savepath)


(2) 文本分析
# -*- coding: utf-8 -*- import jieba from matplotlib import pyplot as plt from wordcloud import WordCloud from PIL import Image import numpy as np from xlrd.compdoc import FREESID import xlrd def generateWordCloud(path): wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows string = "" for item in range(sheet_nrows): if ( item == 0 ): continue string = string + sheet.cell_value(item, 6) cutRes = " ".join(jieba.cut(string)) imgArray = np.array(Image.open(r'assets/imgs/background.jpeg')) wordCloud = WordCloud( background_color = "white", mask = imgArray, font_path = r"assets/fonts/msyh.ttc", width = 3000, height = 860, ) wordCloud.generate_from_text(cutRes) # 画图 fig = plt.figure(1) plt.imshow(wordCloud) plt.axis('off') plt.show() # if __name__ == "__main__": # generateWordCloud()

(3) 可视化分析
# -*- coding: utf-8 -*- import numpy as np import matplotlib import matplotlib.mlab as mlab import matplotlib.pyplot as plt from wordcloud.wordcloud import FONT_PATH import xlrd #解决汉字乱码问题 matplotlib.rcParams['font.family'] = 'AR PL UKai CN' matplotlib.rcParams['font.sans-serif']=['AR PL UKai CN'] def drawPie(path): labels=[] X=[] wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows for item in range(sheet_nrows): if ( item == 0 ): continue X.append(sheet.cell_value(item, 4) ) labels.append(sheet.cell_value(item, 2) ) fig = plt.figure() plt.pie(X, labels = labels, autopct = '%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点) plt.title("电影评分占比") plt.show() plt.savefig("dict/PieChart.jpg") def drawBrokenLine(path): #折线图 X = [] Y = [] wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows for item in range(sheet_nrows): if ( item == 0 ): continue Y.append(sheet.cell_value(item, 5) ) X.append(sheet.cell_value(item, 2) ) plt.plot(X, sorted(Y, reverse = True), 'o-', color = 'r', label="ATT-RLSTM") plt.xlabel("电影名称") #横坐标名字 plt.ylabel("影评数量") #纵坐标名字 plt.legend(loc = "best") #图例 plt.show() # drawBrokenLine()
import pandas as pd
import numpy as np
import wordcloud as wc
import random
import matplotlib.pyplot as plt
df = pd.read_excel('豆瓣电影Top250.xls',sheet_name='豆瓣电影Top250')
Df
# 重复值处理
df = df.drop_duplicates()
# Nan处理
df = df.dropna(axis = 0)
# 删除无效行
del df['电影详情链接']
# 删除无效行
del df['图片链接']
import matplotlib.pyplot as plt
x = df['影片中文名'].head(30)
y = df['评分'].head(30)
z = df['评价数'].head(30)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False
plt.plot(x,y,'-',color = 'c',label="评分")
plt.xticks(rotation=90)
plt.legend(loc = "best")#图例
plt.title("影片综合趋势图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评分")#纵坐标名字
plt.show()
plt.plot(x,z,'-',color = 'r',label="评论数")
plt.xticks(rotation=90)
plt.legend(loc = "best")#图例
plt.title("影片综合趋势图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 柱状图
plt.bar(x,y,alpha=0.2, width=0.4, color='gray', lw=3)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.title("影片评分柱状图")
plt.xticks(rotation=90)
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评分")#纵坐标名字
plt.show()
# 柱状图
plt.bar(x,z,alpha=0.2, width=0.4, color='orange', lw=3)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.title("影片评论数柱状图")
plt.xticks(rotation=90)
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 水平图
plt.barh(x,z, alpha=0.2, height=0.4, color='b',label="评论数", lw=3)
plt.title("影片评论数量水平图")
plt.legend(loc = "best")#图例
plt.xlabel("评论数",)#横坐标名字
plt.ylabel("影片名")#纵坐标名字
plt.show()
# 水平图
plt.barh(x,y, alpha=0.2, height=0.4, color='r',label="评分", lw=3)
plt.title("影片评分水平图")
plt.legend(loc = "best")#图例
plt.xlabel("评分",)#横坐标名字
plt.ylabel("影片名")#纵坐标名字
plt.show()
# 散点图
plt.scatter(x,z,color='orange',marker='o',s=40,edgecolor='black',alpha=0.5)
plt.xticks(rotation=90)
plt.title("影片评论数散点图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 散点图
plt.scatter(x,y,color='b',marker='o',s=40,edgecolor='black',alpha=0.5)
plt.xticks(rotation=90)
plt.title("影片评分散点图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 盒图
plt.boxplot(y, # 值
vert=True, # true:纵向,false:横向
showmeans=True) # 显示均值
plt.title("影片评分盒图")
plt.show()
# 云词
import wordcloud as wc
from PIL import Image
# 定义云词画布
bk = np.array(Image.open("1.jpg"))
mask = bk
word_cloud = wc.WordCloud(
mask = mask,
background_color='white', # 词云图背景颜色,默认为白色
font_path='msyhbd.ttc', # 词云图 字体(中文需要设定为本机有的中文字体)
max_font_size=400, # 最大字体,默认为200
random_state=50, # 为每个单词返回一个PIL颜色
)
text = df['影片中文名']
DF = []
for i in text:
DF.append(i)
text = " ".join(DF)
word_cloud.generate(text)
plt.imshow(word_cloud)
plt.show()














(4) 数据持久化
爬取数据存放于excel

四,完整代码
from bs4 import BeautifulSoup import re import urllib.request,urllib.error import xlwt import sqlite3 from wordCloud import generateWordCloud from draw import drawBrokenLine, drawPie savepath = r"dict/豆瓣电影Top250.xls" def main(): baseurl = "https://movie.douban.com/top250?start=" #1.爬取网页 datalist = getData(baseurl) dbpath = "movie.db" #3.保存数据 saveData(datalist, savepath) # saveData2DB(datalist,dbpath) #askURL("https://movie.douban.com/top250?start=") #影片详情链接的规则 findLink = re.compile(r'<a href="(.*?)">') #创建正则表达式对象,表示规则(字符串的模式) #影片图片 findImgSrc = re.compile(r'<img.*src="(.*?)"',re.S) #re.S 让换行符包含在字符中 #影片片名 findTitle = re.compile(r'<span class="title">(.*)</span>') #影片评分 findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>') #找到评价人数 findJudge = re.compile(r'<span>(\d*)人评价</span>') #找到概况 findInq = re.compile(r'<span class="inq">(.*)</span>') #找到影片的相关内容 findBd = re.compile(r'<p class="">(.*?)</p>',re.S) #爬取网页 def getData(baseurl): datalist = [] for i in range(0, 10): url = baseurl + str(i * 25) # 网页源码 html = askURL(url) soup = BeautifulSoup(html,"html.parser") #查找符合要求的字符串,形成列表 for item in soup.find_all('div',class_="item"): # 一部电影的所有信息 data = [] item = str(item) #影片详情的链接 link = re.findall(findLink,item)[0] # re库用来通过正则表达式查找指定的字符串 data.append(link) # 添加链接 imgSrc = re.findall(findImgSrc,item)[0] data.append(imgSrc) # 添加图片 titles = re.findall(findTitle,item) # 片名可能只有一个中文名,没有外国名 if(len(titles) == 2): ctitle = titles[0] # 添加中文名 data.append(ctitle) otitle = titles[1].replace("/","") # 去掉无关的符号 data.append(otitle) # 添加外国名 else: data.append(titles[0]) data.append(' ') # 外国名字留空 rating = re.findall(findRating,item)[0] data.append(rating) # 添加评分 judgeNum = re.findall(findJudge,item)[0] data.append(judgeNum) # 提加评价人数 inq = re.findall(findInq,item) if len(inq) != 0: inq = inq[0].replace("。","") # 去掉句号 data.append(inq) # 添加概述 else: data.append(" ") # 留空 bd = re.findall(findBd,item)[0] bd = re.sub('<br(\s+)?/>(\s+)?'," ",bd) # 去掉<br/> bd = re.sub('/'," ",bd) # 替换/ data.append(bd.strip()) # 去掉前后的空格 datalist.append(data) # 把处理好的一部电影信息放入datalist return datalist #得到指定一个URL的网页内容 def askURL(url): head = { "User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36" } request = urllib.request.Request(url, headers = head) html = "" try: response = urllib.request.urlopen(request) html = response.read().decode("utf-8") #print(html) except urllib.error.URLError as e: if hasattr( e, "code" ): print( e.code ) if hasattr( e, "reason" ): print( e.reason ) return html #保存数据 def saveData(datalist,savepath): #创建workbook对象 book = xlwt.Workbook(encoding = "utf-8", style_compression = 0) #创建工作表 sheet = book.add_sheet('豆瓣电影Top250', cell_overwrite_ok=True) col = ("电影详情链接", "图片链接", "影片中文名", "影片外国名", "评分", "评价数", "概况", "相关信息") for i in range(0, 8): #列名 sheet.write(0, i, col[i]) for i in range(0,250): print("第%d条" %( i + 1 )) data = datalist[i] for j in range(0, 8): #数据 sheet.write(i + 1, j, data[j]) book.save(savepath) def saveData2DB(datalist, dbpath): init_db( dbpath ) conn = sqlite3.connect( dbpath ) cur = conn.cursor() for data in datalist: for index in range(len( data )): if index == 4 or index == 5: continue data[index] = '"'+data[index]+'"' sql = ''' insert into movie250 ( info_link,pic_link,cname,ename,score,rated,instroduction,info) values(%s)'''%",".join(data) print(sql) cur.execute(sql) conn.commit() cur.close() conn.close() def init_db(dbpath): sql = ''' create table movie250 ( id integer primary key autoincrement, info_link text, pic_link text, cname varchar, ename varchar, score numeric , rated numeric , instroduction text, info text ) ''' #创建数据表 conn = sqlite3.connect(dbpath) cursor = conn.cursor() cursor.execute(sql) conn.commit() conn.close() #当程序执行时 if __name__ == "__main__": main() #init_db("movietest.db") generateWordCloud(savepath) drawPie(savepath) drawBrokenLine(savepath) # -*- coding: utf-8 -*- import jieba from matplotlib import pyplot as plt from wordcloud import WordCloud from PIL import Image import numpy as np from xlrd.compdoc import FREESID import xlrd def generateWordCloud(path): wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows string = "" for item in range(sheet_nrows): if ( item == 0 ): continue string = string + sheet.cell_value(item, 6) cutRes = " ".join(jieba.cut(string)) imgArray = np.array(Image.open(r'assets/imgs/background.jpeg')) wordCloud = WordCloud( background_color = "white", mask = imgArray, font_path = r"assets/fonts/msyh.ttc", width = 3000, height = 860, ) wordCloud.generate_from_text(cutRes) # 画图 fig = plt.figure(1) plt.imshow(wordCloud) plt.axis('off') plt.show() # if __name__ == "__main__": # generateWordCloud() # -*- coding: utf-8 -*- import numpy as np import matplotlib import matplotlib.mlab as mlab import matplotlib.pyplot as plt from wordcloud.wordcloud import FONT_PATH import xlrd #解决汉字乱码问题 matplotlib.rcParams['font.family'] = 'AR PL UKai CN' matplotlib.rcParams['font.sans-serif']=['AR PL UKai CN'] def drawPie(path): labels=[] X=[] wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows for item in range(sheet_nrows): if ( item == 0 ): continue X.append(sheet.cell_value(item, 4) ) labels.append(sheet.cell_value(item, 2) ) fig = plt.figure() plt.pie(X, labels = labels, autopct = '%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点) plt.title("电影评分占比") plt.show() plt.savefig("dict/PieChart.jpg") def drawBrokenLine(path): #折线图 X = [] Y = [] wb = xlrd.open_workbook(path) #通过名字获取表格 sheet = wb.sheet_by_name('豆瓣电影Top250') # 这个`sheet`中的行数 sheet_nrows = sheet.nrows for item in range(sheet_nrows): if ( item == 0 ): continue Y.append(sheet.cell_value(item, 5) ) X.append(sheet.cell_value(item, 2) ) plt.plot(X, sorted(Y, reverse = True), 'o-', color = 'r', label="ATT-RLSTM") plt.xlabel("电影名称") #横坐标名字 plt.ylabel("影评数量") #纵坐标名字 plt.legend(loc = "best") #图例 plt.show() # drawBrokenLine()
import pandas as pd
import numpy as np
import wordcloud as wc
import random
import matplotlib.pyplot as plt
df = pd.read_excel('豆瓣电影Top250.xls',sheet_name='豆瓣电影Top250')
Df
# 重复值处理
df = df.drop_duplicates()
# Nan处理
df = df.dropna(axis = 0)
# 删除无效行
del df['电影详情链接']
# 删除无效行
del df['图片链接']
import matplotlib.pyplot as plt
x = df['影片中文名'].head(30)
y = df['评分'].head(30)
z = df['评价数'].head(30)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False
plt.plot(x,y,'-',color = 'c',label="评分")
plt.xticks(rotation=90)
plt.legend(loc = "best")#图例
plt.title("影片综合趋势图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评分")#纵坐标名字
plt.show()
plt.plot(x,z,'-',color = 'r',label="评论数")
plt.xticks(rotation=90)
plt.legend(loc = "best")#图例
plt.title("影片综合趋势图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 柱状图
plt.bar(x,y,alpha=0.2, width=0.4, color='gray', lw=3)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.title("影片评分柱状图")
plt.xticks(rotation=90)
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评分")#纵坐标名字
plt.show()
# 柱状图
plt.bar(x,z,alpha=0.2, width=0.4, color='orange', lw=3)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.title("影片评论数柱状图")
plt.xticks(rotation=90)
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 水平图
plt.barh(x,z, alpha=0.2, height=0.4, color='b',label="评论数", lw=3)
plt.title("影片评论数量水平图")
plt.legend(loc = "best")#图例
plt.xlabel("评论数",)#横坐标名字
plt.ylabel("影片名")#纵坐标名字
plt.show()
# 水平图
plt.barh(x,y, alpha=0.2, height=0.4, color='r',label="评分", lw=3)
plt.title("影片评分水平图")
plt.legend(loc = "best")#图例
plt.xlabel("评分",)#横坐标名字
plt.ylabel("影片名")#纵坐标名字
plt.show()
# 散点图
plt.scatter(x,z,color='orange',marker='o',s=40,edgecolor='black',alpha=0.5)
plt.xticks(rotation=90)
plt.title("影片评论数散点图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 散点图
plt.scatter(x,y,color='b',marker='o',s=40,edgecolor='black',alpha=0.5)
plt.xticks(rotation=90)
plt.title("影片评分散点图")
plt.xlabel("影片名",)#横坐标名字
plt.ylabel("评论数")#纵坐标名字
plt.show()
# 盒图
plt.boxplot(y, # 值
vert=True, # true:纵向,false:横向
showmeans=True) # 显示均值
plt.title("影片评分盒图")
plt.show()
# 云词
import wordcloud as wc
from PIL import Image
# 定义云词画布
bk = np.array(Image.open("1.jpg"))
mask = bk
word_cloud = wc.WordCloud(
mask = mask,
background_color='white', # 词云图背景颜色,默认为白色
font_path='msyhbd.ttc', # 词云图 字体(中文需要设定为本机有的中文字体)
max_font_size=400, # 最大字体,默认为200
random_state=50, # 为每个单词返回一个PIL颜色
)
text = df['影片中文名']
DF = []
for i in text:
DF.append(i)
text = " ".join(DF)
word_cloud.generate(text)
plt.imshow(word_cloud)
plt.show()
五,总结
通过这次作业了解了网络爬虫编写的基本套路,了解网络爬虫编写的各种陷阱,能够简单利用爬虫平台,在写的时候我就注意了爬虫的礼仪,设定了访问间隔时间,并没有遇到封锁,很幸运。可以说豆瓣简直就是爬虫新手的乐园,难度不大、但有一定挑战性通过这次练习,我对自己的水平有了大致的了解,但是还远远不够,一些相关基础知识不深刻、数据结构使用不合理、代码结构不规范等很多问题。在写代码时,发现了很多问题,这都将是我接下来努力的方向!!

浙公网安备 33010602011771号