下面的代码可以抓取豆瓣及IMDB上的电影信息,由于每段代码使用的数据源自上一段代码输出的数据,所以需要按顺序执行。
step1_getDoubanMovies.py
1 # -*- coding: utf-8 -*- 2 ''' 3 该脚本得到豆瓣上所有电影的如下信息: 4 "rate": "7.5", 5 "cover_x": 2000, 6 "is_beetle_subject": false, 7 "title": "鬼乡", 8 "url": "https://movie.douban.com/subject/26322928/", 9 "playable": false, 10 "cover": "https://img3.doubanio.com/view/movie_poster_cover/lpst/public/p2226663805.jpg", 11 "id": "26322928", 12 "cover_y": 2820, 13 "is_new": false 14 并保存为csv格式和js格式,但是未去重 15 ''' 16 import requests #此处采用requests方法得到网页响应 17 import json 18 import time 19 from pandas import DataFrame 20 21 def getTagData(tag): 22 data_oneTag=[] #待添加数据列表 23 page_start=0 #起始页 24 data_oneSubject=[1] #为了冷启动,使data_oneSubject不为空 25 #设置代理 26 #proxies={ "http": "http://115.159.96.136:1080"} 27 #设置User-Agent 28 headers={"User-Agent":'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'} 29 #当data_oneSubject不为空,也就是始终可以从网页中获取内容时,不停循环 30 while(data_oneSubject!=[]): 31 #通过修改tag和page_start来不断的修改网址 32 url='https://movie.douban.com/j/search_subjects?type=movie&tag='+tag+'&sort=recommend&page_limit=20&page_start='+str(page_start) 33 resp=requests.get(url,headers=headers) 34 #resp=requests.get(url,proxies=proxies,headers=headers) #发出获取内容请求,得到的resp.text为json字符串 35 data_oneSubject=json.loads(resp.text) #将json字符串resp.text转换成Python形式,得到的data_oneSubject整体为一个键为'subjects'的长度为1的字典 36 data_oneSubject=data_oneSubject['subjects'] #取出data_oneSubject字典中键'subjects'对应的值,为20个字典 37 data_oneTag+=data_oneSubject #将data_oneSubject添加到data_oneTag数据中 38 page_start+=20 #起始页增加20 39 time.sleep(1) #为了避免请求的太频繁被封掉IP,所以每次循环都要暂停一秒 40 return data_oneTag #返回标签为tag时所有获得的数据 41 42 data_allTag=[] #待添加所有标签的数据集列表 43 moviesNum=0 #所有标签下的电影总数,该数字没有消除重复项 44 for tag in ['热门','最新','经典','可播放','豆瓣高分','冷门佳片','华语','欧美','韩国','日本','动作','喜剧','爱情','科幻','悬疑','恐怖','治愈']: 45 data_oneTag=getTagData(tag) #针对每个标签调用getTagData函数 46 data_allTag+=data_oneTag #将每个标签下的得到的数据加入到data_allTag数据集中,最终的data_allTag为数据list数据结构,每一项为一个字典 47 moviesNum+=len(data_oneTag) #计算所有标签下电影的总数(包含重复项) 48 print tag+':',len(data_oneTag) #打印出各个标签下得到的电影数 49 50 print '电影总数为:',moviesNum #打印出总电影数,该数字没有消除重复项 51 for i in range(5): 52 print data_allTag[i]['title']+' '+data_allTag[i]['rate'] #打印出data_allTag的前10项,只输出键'title'和'rate'对应的值 53 54 df=DataFrame(data_allTag) 55 56 #title列为unicode编码格式,在将数据输出为csv格式时会出现编码问题,所以下面三行将title列数据转换为utf-8格式 57 title=df['title'] 58 title=title.map(lambda x:x.encode('utf-8')) 59 df['title']=title 60 61 #将结果中rate列的数字转换为float格式 62 rate=df['rate'] #将df的rate列取出,格式为Series 63 rate=rate.map(float) #将rate列中的数值转换为float格式 64 df['rate']=rate #将df中的rate列替换为float格式的rate列 65 66 #将DataFrame格式的结果数据写入csv格式的文件中 67 df.to_csv('doubanMovies.csv',index=False,header=True) 68 69 # #将数据结构为list的data_allTag转换成json格式后保存到doubanMovie.js文件中 70 # try: 71 # f1=open('doubanMovies.js','w') 72 # f1.write(json.dumps(data_allTag)) #将list数据结构的data_allTag写入json文件 73 # except: 74 # print '写入js文件未成功!' 75 # finally: 76 # f1.close() #注意关闭文件流
step2_getScore.py
1 # -*- coding: utf-8 -*- 2 ''' 3 该脚本利用step1_doubanMovies.py脚本中得到的csv格式文件进行处理 4 对豆瓣电影进行了去重 5 并由rate列得到了score列 6 ''' 7 import pandas as pd 8 df=pd.read_csv('doubanMovies.csv') 9 movies_unique=df.drop_duplicates() #去重 10 movies_unique.to_csv('doubanMovies_unique.csv',index=False,header=True) #将最终的数据输出 11 print len(movies_unique) 12 13 def getScore(i): 14 if i>=0 and i<6.0: 15 return 0 16 elif i>=6.0 and i<6.5: 17 return 1 18 elif i>=6.5 and i<7.0: 19 return 2 20 elif i>=7.0 and i<7.5: 21 return 3 22 elif i>=7.5 and i<8.0: 23 return 4 24 elif i>=8.0 and i<=10.0: 25 return 5 26 else: 27 return None 28 29 rate=movies_unique['rate'] #取出rate列 30 score=rate.map(getScore) #对rate列应用getScore函数,得到score列 31 movies_unique['score']=score #将score列添加到movies_unique 32 movies_unique.to_csv('doubanMovies_score.csv',index=False,header=True) #将最终的数据输出
step3_getInfoOfOneMovie.py
1 # -*- coding: utf-8 -*- 2 ''' 3 该段代码通过step2_getScore.py得到的doubanMovies_score.csv文件中的每个豆瓣电影的网址 4 获取每个豆瓣电影的页面内容信息 5 得到directors导演、leadingRoles主演、releaseDate上映日期 6 alterNames又名、IMDBurl对应的IMDB链接等信息 7 ''' 8 9 from getInfoOfOneMovie_functions import * #从getInfoOfOneMovie_functions脚本中引入自己定义的函数 10 doubanMovies_score=pd.read_csv('doubanMovies_score.csv') 11 allUrls=doubanMovies_score['url'] 12 13 movieInfo=DataFrame({'directors':[],'leadingRoles':[],'releaseDate':[],'alterNames':[],'IMDBurl':[]}) 14 csvFileName='doubanMovies_scoreInfoAdded.csv' #包含豆瓣电影对应的IMDB网址的数据文件 15 errorStartPoint=1 #为了方便做错误标记,增加该参数,表示上一次运行到哪部电影出错 16 unknownError=DataFrame({'directors':['unknownError'],'leadingRoles':['unknownError'],'releaseDate':['unknownError'],'alterNames':['unknownError'],'IMDBurl':['unknownError']}) #出现严重错误时添加该字符串,为了方便添加,所以使用DataFrame格式 17 for i in range(errorStartPoint-1,len(allUrls)): 18 try: 19 movieInfo=appendOne(movieInfo,str(allUrls[i])) 20 print len(movieInfo)+errorStartPoint-1 21 except: 22 movieInfo=pd.concat([movieInfo,unknownError]) 23 print len(movieInfo)+errorStartPoint-1,'with unknownError added' 24 finally: 25 movieInfo.to_csv(csvFileName, index=False, encoding='utf-8')
step4_getIMDBRate.py
1 # -*- coding: utf-8 -*- 2 ''' 3 该段代码可以通过step3_getInfoOfOneMovie.py得到的 4 doubanMovies_scoreInfoAdded.csv文件中的IMDBurl 5 定位到每个豆瓣电影对应的IMDB链接 6 然后解析IMDB链接的内容,得到每个电影在IMDB上的IMDBRate评分 7 和numOfPeopleWhoRate打分人数 8 ''' 9 import pandas as pd 10 import urllib2 11 import time 12 import lxml.html 13 from pandas import DataFrame 14 doubanMovie_info=pd.read_csv('doubanMovies_scoreInfoAdded.csv') #取出数据结构为DataFrame的doubanMovies_info 15 IMDBurl=doubanMovie_info['IMDBurl'] #IMDBurl为一个Series 16 IMDBRateList=[] #初始化IMDBRateList,就是所有的IMDB评分的一个列表 17 numOfPeopleWhoRateList=[] 18 19 def getDoc(url): 20 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'} 21 request = urllib2.Request(url, headers=headers) # 发送请求 22 response = urllib2.urlopen(request) # 获得响应 23 time.sleep(1) 24 content = response.read() # 获取网页内容 25 doc = lxml.html.fromstring(content) # 可能是将网页以xml格式解析 26 return doc 27 28 #函数:获得IMDB评分 29 def getIMDBRate(doc,oneIMDBurl): 30 #匹配IMDB评分的xpath路径 31 tempList=doc.xpath('//*[@id="title-overview-widget"]/div[2]/div[2]/div/div[1]/div[1]/div[1]/strong/span/text()') 32 return float(tempList[0]) #返回的是float格式的数据 33 34 #函数:将类似于'123,456'的字符串转换为int数据类型123456 35 #因为得到的评分人数的格式为'123,456' 36 def toInt(numWithDot): 37 temp1=numWithDot.split(',') #首先将字符串以‘,’分割成list 38 temp2='' #初始化一个字符串 39 for i in range(len(temp1)): #通过循环,将temp1中的各个元素项合并成一个字符串 40 temp2+=temp1[i] 41 temp2=int(temp2) #将合并后的数字字符串转换成int格式 42 return temp2 43 44 #函数:得到评分人数 45 def getNumOfPeopleWhoRate(doc,oneIMDBurl): 46 #匹配评分人数的xpath路径 47 tempList=doc.xpath('//*[@id="title-overview-widget"]/div[2]/div[2]/div/div[1]/div[1]/a/span/text()') 48 temp=str(tempList[0]) #得到人数,此时为str数据类型 49 numOfPeopleWhoRate=toInt(temp) #用前面定义的函数转换数据为int类型 50 return numOfPeopleWhoRate #返回int类型的评分人数 51 52 num=1 #没有IMDB链接的电影个数,用作统计 53 startPoint=1 #为了在出错时,从错误的地方重新开始运行,设置了该错误点参数 54 for i in range(startPoint-1,len(IMDBurl)): 55 print i+1 #打印出当前位置,作为标记 56 try: 57 #由于有些电影没有IMDB链接,所以要进行判断 58 if IMDBurl[i]!='-': #如果有IMDB链接 59 doc=getDoc(IMDBurl[i]) #得到xml格式数据 60 #得到IMDB评分 61 IMDBRate=getIMDBRate(doc,IMDBurl[i]) 62 IMDBRateList.append(IMDBRate) #将得到的IMDB评分加入IMDBRateList中 63 #得到评分人数 64 numOfPeopleWhoRate=getNumOfPeopleWhoRate(doc,IMDBurl[i]) 65 numOfPeopleWhoRateList.append(numOfPeopleWhoRate) #将评分人数加入列表 66 else: #如果没有IMDB链接,将两个list列表中都加入'-'表示没有值 67 print 'Movie:',i+1,'Without IMDBurl,No.',num #打印出没有IMDB链接的电影相关信息 68 num=num+1 #没有IMDB链接的电影数加1 69 IMDBRateList.append('-') 70 numOfPeopleWhoRateList.append('-') 71 except: #发生未知错误时,将两个list列表中加入'unknownError',表示出现未知错误 72 print 'unknownError happened!' 73 IMDBRateList.append('unknownError') 74 numOfPeopleWhoRateList.append('unknownError') 75 finally: 76 #将两个list列表转换成DataFrame格式,方便往csv格式文件中添加 77 IMDBRate=DataFrame({'IMDBRate':IMDBRateList,'numOfPeopleWhoRate':numOfPeopleWhoRateList}) #将IMDBRateList转换为DataFrame格式,方便加入其他数据中 78 #将DataFrame格式的结果添加到csv格式文件中 79 IMDBRate.to_csv('IMDBRate.csv',index=False,encoding='utf-8')
step5_final.py
1 # -*- coding: utf-8 -*- 2 from pandas import DataFrame 3 import pandas as pd 4 5 def getScore(i): 6 if i>=0 and i<6.0: 7 return 0 8 elif i>=6.0 and i<6.5: 9 return 1 10 elif i>=6.5 and i<7.0: 11 return 2 12 elif i>=7.0 and i<7.5: 13 return 3 14 elif i>=7.5 and i<8.0: 15 return 4 16 elif i>=8.0 and i<=10.0: 17 return 5 18 else: 19 return None 20 ################################################### 21 ##将IMDB评分转换为5分制 22 #df=pd.read_csv('doubanMovies_IMDB.csv') 23 #score_IMDB=[] 24 #for i in range(len(df)): 25 # if df.ix[i,'IMDBRate']!='No Rating' and df.ix[i,'IMDBRate']!='-': 26 # score_IMDB.append(getScore(float(df.ix[i,'IMDBRate']))) 27 # else: 28 # score_IMDB.append(df.ix[i,'IMDBRate']) 29 # 30 #df['score_IMDB']=score_IMDB 31 #df.to_csv('doubanMovies_IMDBScore.csv') 32 ################################################### 33 34 ############################################################################ 35 #将豆瓣和IMDB的rate合并,并配置权重 36 #如果没有相应的IMDB链接,或者有IMDB链接,但是没有评分 37 #则合并后的rate就采用豆瓣的rate 38 #最后将合并后的rate转化为5分制 39 df=pd.read_csv('doubanMovies_IMDBScore.csv') 40 weight_douban=0.5 #豆瓣的rate的权重值 41 weight_IMDB=1-weight_douban #IMDB的rate的权重值 42 rate_doubanAndIMDB=[] #初始化合并后的rate列表 43 score_final=[] #初始化最终的评分列表 44 45 #得到豆瓣rate和IMDBRate加权后rate_doubanAndIMDB列表 46 for i in range(len(df)): 47 df.ix[i,'rate']=float(df.ix[i,'rate']) #为防止出错,再加这么一句 48 if df.ix[i,'IMDBRate']!='No Rating' and df.ix[i,'IMDBRate']!='-': 49 df.ix[i,'IMDBRate']=float(df.ix[i,'IMDBRate']) #将数据集中的IMDBRate数据转换为float格式 50 #将rate和IMDBRate进行加权 51 temp=weight_douban*(df.ix[i,'rate'])+weight_IMDB*(df.ix[i,'IMDBRate']) 52 #将加权后的rate值加入到 rate_doubanAndIMDB列表中 53 rate_doubanAndIMDB.append(temp) 54 else: 55 #如果没有IMDB链接或IMDB没有评分,则直接使用豆瓣的rate值 56 rate_doubanAndIMDB.append(df.ix[i,'rate']) 57 58 #利用加权后的rate值得到最终的分数值score_final列表 59 for i in range(len(df)): 60 score_final.append(getScore(rate_doubanAndIMDB[i])) 61 62 df['rate_doubanAndIMDB']=rate_doubanAndIMDB 63 df['score_final']=score_final 64 df.to_csv('test.csv',index=False) 65 ############################################################################
getInfoOfOneMovie_functions.py
1 #-*- coding:utf-8 -*- 2 ''' 3 该段代码定义了若干getInfoOfOneMovie.py中用到的函数 4 前面6个函数都在第7个函数也就是appendOne中调用 5 ''' 6 import lxml.html 7 import time 8 from pandas import DataFrame 9 import urllib2 10 import pandas as pd 11 import re 12 13 #得到网页的xml格式数据内容 14 def getDoc(url_oneMovie): 15 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'} 16 request = urllib2.Request(url_oneMovie, headers=headers) # 发送请求 17 response = urllib2.urlopen(request) # 获得响应 18 time.sleep(1) 19 content = response.read() # 获取网页内容 20 doc = lxml.html.fromstring(content) # 可能是将网页以xml格式解析 21 return doc 22 23 #通过xml数据得到导演信息 24 def getDirectors(doc,url_oneMovie): 25 directors=doc.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') 26 #将列表中的每一项先转换成unicode,再转换成utf-8格式 27 for i in range(len(directors)): 28 directors[i]=unicode(directors[i]).encode('utf-8') 29 return directors #返回的是list 30 31 #通过xml数据得到主演信息 32 def getLeadingRoles(doc,url_oneMovie): 33 leadingRoles=doc.xpath('//*[@id="info"]/span[3]/span[2]/a/text()') 34 #有些有些豆瓣电影信息里没有编剧这一项,所以主演会在第二条信息里。 35 #所以需要判断得到的主演列表是否为空 36 #将列表中的每一项先转换成unicode,再转换成utf-8格式 37 if leadingRoles==[]: 38 leadingRoles=doc.xpath('//*[@id="info"]/span[2]/span[2]/a/text()') 39 for i in range(len(leadingRoles)): 40 leadingRoles[i]=unicode(leadingRoles[i]).encode('utf-8') 41 return leadingRoles #返回的是list 42 43 #通过xml数据得到上映日期信息 44 def getReleaseDate(doc,url_oneMovie): 45 releaseDate=doc.xpath('//*[@id="info"]/span/text()') #得到的是一个list,其中有一个元素是上映日期 46 #将列表中的每一项先转换成unicode,再转换成utf-8格式 47 for i in range(len(releaseDate)): 48 releaseDate[i]=unicode(releaseDate[i]).encode('utf-8') 49 temp=re.compile(r'\d*-\d*-\d*') 50 for i in range(len(releaseDate)): 51 if re.findall(temp,releaseDate[i])!=[]: #findall返回的是能匹配到的list,所以用是否为[]进行判断 52 return releaseDate[i] #返回的是str 53 54 #通过xml数据得到又名信息 55 def getAlterNames(doc,url_oneMovie): 56 tempList=doc.xpath('//*[@id="info"]/text()') #得到的是一个list,最后一个非空的元素就是又名 57 #将列表中的每一项先转换成unicode,再转换成utf-8格式 58 for i in range(len(tempList)): 59 tempList[i]=unicode(tempList[i]).encode('utf-8') 60 #取出‘又名’的名字字符串 61 temp=re.compile(r'\S') #匹配非空字符串的正则表达式 62 for i in range(len(tempList)): #取出非空字符串,最后一个才是‘又名’ 63 if re.findall(temp,tempList[i])!=[]: #如果匹配的结果非空 64 alterNames=tempList[i] #由于不停的循环,找到的最后一个非空的字符串才是’又名‘ 65 return alterNames #返回‘又名’,格式为str 66 67 #通过xml数据得到IMDB链接信息 68 def getIMDBurl(doc,url_oneMovie): 69 xpathList=doc.xpath('//*[@id="info"]/a/text()') 70 #将列表中的每一项先转换成unicode,再转换成utf-8格式 71 #有些豆瓣电影没有对应的IMDB网址,所以要判断一下是否为[] 72 if xpathList!=[]: 73 for i in range(len(xpathList)): 74 xpathList[i]=unicode(xpathList[i]).encode('utf-8') 75 #取出通过//*[@id="info"]/a得到的形如‘tt12345678’的IMDBurl的ID 76 #有三种情况: 77 #1、xpathList长度为1,第一项为tt12345678 78 #2、xpathList长度为1,第一项为豆瓣内容专题 79 #3、xpathList长度为2,第二项为tt12345678 80 #此处采用正则表达式 81 temp=re.compile(r'tt\d{3,}') 82 for i in range(len(xpathList)): 83 tempList=re.findall(temp,xpathList[i]) 84 if tempList!=[]: 85 IMDBurlID=tempList[0] #得到形如‘tt12345678’的IMDBurl的ID或者None 86 IMDBurl='http://www.imdb.com/title/' + IMDBurlID + '/' #将ID转换成IMDB网址 87 return IMDBurl #返回IMDB网址 88 else: 89 return '-' 90 91 def appendOne(movieInfo,url_oneMovie): 92 doc=getDoc(url_oneMovie) 93 directors=getDirectors(doc,url_oneMovie) #得到的是若干导演的一个list 94 leadingRoles=getLeadingRoles(doc,url_oneMovie) #得到的是若干主演的一个list 95 releaseDate=getReleaseDate(doc,url_oneMovie) #得到的是str字符串 96 alterNames=getAlterNames(doc,url_oneMovie) #得到的是str字符串 97 IMDBurl=getIMDBurl(doc,url_oneMovie) #得到的str格式的IMDB网址 98 tempDf=DataFrame({'directors':[directors],'leadingRoles':[leadingRoles],'releaseDate':[releaseDate],'alterNames':[alterNames],'IMDBurl':[IMDBurl]}) 99 movieInfo=pd.concat([movieInfo,tempDf]) 100 return movieInfo 101 102
发表于
2016-05-16 17:54 PistonType 阅读(
...) 评论(
...) 编辑 收藏