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