下面的代碼可以抓取豆瓣及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