设为首页收藏本站

Crossin的编程教室

 找回密码
 立即加入
楼主: crossin先生
打印 上一主题 下一主题

「一道大数据习题」豆瓣评论最多的三千部电影

[复制链接]
回帖奖励 21 金钱 回复本帖可获得 3 金钱奖励! 每人限 1 次

2

主题

0

好友

161

积分

注册会员

Rank: 2

11#
发表于 2013-10-25 13:06:45 |只看该作者
crossin先生 发表于 2013-10-25 11:25
去过两次厦大,今年1月份刚去了次,校园很美!

这个屏蔽是暂时的吗?如果每个tag抓一次,然后把记录保存 ...

嗯嗯,暂时的诶,目前还不清楚具体多长时间就会恢复~~这个方法应该可行,试一下~~
回复

使用道具 举报

174

主题

45

好友

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

12#
发表于 2013-10-27 16:05:32 |只看该作者
jxgx072037 发表于 2013-10-25 13:06
嗯嗯,暂时的诶,目前还不清楚具体多长时间就会恢复~~这个方法应该可行,试一下~~ ...

我发现豆瓣好像接口又改过了,现在每个tag只能抓200部,貌似是刚改的。
难道又是被我们刷爆了吗!
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

2

主题

0

好友

161

积分

注册会员

Rank: 2

13#
发表于 2013-10-27 20:41:21 |只看该作者
crossin先生 发表于 2013-10-27 16:05
我发现豆瓣好像接口又改过了,现在每个tag只能抓200部,貌似是刚改的。
难道又是被我们刷爆了吗! ...

干……我们有成为病毒制造者的潜质……
回复

使用道具 举报

174

主题

45

好友

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

14#
发表于 2013-10-28 16:57:44 |只看该作者
jxgx072037 发表于 2013-10-27 20:41
干……我们有成为病毒制造者的潜质……

好像也不是。。。它的api写着total:200,但是start=设比200大的数字也可以。然后我就抓了一个版本,待会儿放上来
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

2

主题

0

好友

161

积分

注册会员

Rank: 2

15#
发表于 2013-10-28 22:39:33 |只看该作者
本帖最后由 jxgx072037 于 2013-10-28 22:48 编辑
crossin先生 发表于 2013-10-28 16:57
好像也不是。。。它的api写着total:200,但是start=设比200大的数字也可以。然后我就抓了一个版本,待会 ...

哇,期待~~嘿嘿,我这两天也把代码更新了下~~~~另外,今晚在交大看见拉夫琴大神本人啦~~
回复

使用道具 举报

2

主题

0

好友

161

积分

注册会员

Rank: 2

16#
发表于 2013-10-28 22:47:13 |只看该作者
本帖最后由 jxgx072037 于 2013-10-28 22:50 编辑

更新日志:2013-10-28
1. 增加了步骤计时监控;
2. 每个页面完成后暂停2s再继续,躲避豆瓣的屏蔽;
3. 增加了去重;
4. 去除超过了预期的电影数目;
5. 修改了评论人数的抓取规则,初步抓取时包涵“还未上映”的情况;
6. 取消了超过3000部停止抓取的设置,改为先抓取全部标签,再去掉超过3000的部分;

更新总结:试了两次,2s/页的暂停已经可以让豆瓣开心了,不会屏蔽抓取请求……但是,出现了新的问题。每次抓取到差不多2000部电影,程序就会死机……目前猜测是电 影数量多了,所以每一次抓取后的“去重”计算量过大。 另外,真心觉得每次问题的解决方法都要比开始想象的复杂。要实现一个想法,往往会碰到之前从未考虑过的细节问题。

代码如下:
  1. # -*- coding: utf-8 -*-
  2. import urllib2
  3. import re
  4. import time

  5. #抓取豆瓣电影标签
  6. f=urllib2.urlopen('http://movie.douban.com/tag/?view=cloud').read()
  7. n1=f.find('007')
  8. n2=f.find('宗教')        #n1是第一个标签,n2是第二个标签,利用这两个标签定位标签们在f中的位置
  9. f1=f[(n1-4):(n2+10)]    #去掉标签旁边的杂质
  10. f2=re.findall('\>\S{1,}?\<',f1)         #初步抓取标签
  11. movie_tags=[]   #movie_tags为电影标签
  12. for n in f2:
  13.     n=n[1:-1]
  14.     movie_tags.append(n)

  15. #抓取每个标签打开后的网页地址
  16. class Movie_list:
  17.     def __init__(self):
  18.         self.url1='http://movie.douban.com/tag/'
  19.     def request_open(self,n1,n2): #n1是标签list中的序号,n2是页码
  20.         self.url2='?start='+str(n2*20)+'&type=T'
  21.         self.page1=urllib2.urlopen(self.url1+movie_tags[n1]+self.url2).read()
  22.         return self.page1 #点击标签打开后的页面地址
  23.         
  24.    

  25. #抓取电影列表的页码数
  26. class Next_page:
  27.     def __init__(self):
  28.         self.url1='http://movie.douban.com/tag/'
  29.     def np(self,n1):
  30.         self.page1=urllib2.urlopen(self.url1+movie_tags[n1]).read()
  31.         self.url2=re.findall('amp.*\d{1,2}',self.page1)
  32.         if self.url2:
  33.             self.num2=self.url2[-1][13:]
  34.             if int(self.num2)<50 or int(self.num2)==50:
  35.                 return int(self.num2)
  36.             else:
  37.                 return 50       #电影列表页数超过50页则只扫描前50页
  38.         else:
  39.             return 1

  40. movie_list=Movie_list()
  41. movie=[]


  42. #抓取电影信息(名字,评价人数,排名,地址)
  43. class Movie_info:
  44.     def __init__(self):
  45.         pass
  46.     def m(self,n1,n2):
  47.     # 抓取每部电影的电影名称
  48.         self.movie_name2=[]
  49.         self.movie_name1=re.findall('title="\S{1,}?"',movie_list.request_open(n1,n2))
  50.         for x in range(len(self.movie_name1)-1):
  51.             self.movie_name2.append(self.movie_name1[x][7:-1])
  52.    
  53.     # 抓取每部电影的评价人数
  54.         self.movie_comment2=[]
  55.         self.movie_comment1=re.findall('span class="pl.*评价|span class="pl.*上映',movie_list.request_open(n1,n2))
  56.         for s in self.movie_comment1:
  57.             if re.findall('\d{1,}',s):
  58.                 self.movie_comment2.append(re.findall('\d{1,}',s)[0])
  59.             else:
  60.                 self.movie_comment2.append('0')

  61.     # 抓取每部电影的评分
  62.         self.movie_rating2=[]
  63.         self.movie_rating1=re.findall('class="star clearfix[\s\S]*?pl',movie_list.request_open(n1,n2))
  64.         for s in self.movie_rating1:
  65.             self.p2=re.findall('\d\.\d',s)
  66.             if self.p2:
  67.                 self.movie_rating2.append(self.p2[0])
  68.             else:
  69.                 self.movie_rating2.append('没有评分')   

  70.     # 抓取每部电影的链接
  71.         self.movie_url2=[]
  72.         self.n=0
  73.         self.movie_url1=re.findall('http://movie.douban.com/subject/\d{1,10}',movie_list.request_open(n1,n2))
  74.         for i in range(len(self.movie_url1)/2):
  75.             self.movie_url2.append(self.movie_url1[2*self.n])
  76.             self.n+=1   

  77. # 把每部电影的4个信息合并成一个list--self.dic,再依次存到movie这个大list中
  78.         for i in range(len(self.movie_name2)):
  79.             self.dic=[]                                        #用self.dic暂时存储电影信息
  80.             self.dic.append(self.movie_name2[i])               #名字
  81.             self.dic.append(self.movie_comment2[i])            #评价人数
  82.             self.dic.append(self.movie_rating2[i])             #评分
  83.             self.dic.append(self.movie_url2[i])                #地址
  84.             if int(self.dic[1])>25000:    #评价人数少于25000的直接放弃
  85.                 movie.append(self.dic)
  86.                 if len(movie)>2:
  87.                     for i in range(len(movie)-1):              #去重
  88.                         if movie[-1][0]==movie[i][0]:   #用电影名字判定是否重复
  89.                             del movie[-1]
  90.                             break
  91.                 print '%d/3000'%(len(movie))      #打印进度
  92.             else:
  93.                 continue

  94. next_page=Next_page()
  95. movie_info=Movie_info()


  96. #执行,开始抓取
  97. for x in range(len(movie_tags)):          #x代表标签在movie_tags这个list中的位置
  98.     print "正在抓取标签“%s”中的电影"%(movie_tags[x])
  99.     starttime2=time.time()
  100.     for i in range(next_page.np(x)):       #i代表正在抓取当前标签的第i页
  101.         print "开始抓取第%d页,抓取进度:"%(i+1)
  102.         starttime2=time.time()
  103.         movie_info.m(x,i)
  104.         endtime2=time.time()
  105.         print "抓取第%d页完毕,用时%.2fs"%(i+1,endtime2-starttime2)     #输出抓取每个页面所花费的时间
  106.         time.sleep(2)
  107.     endtime2=time.time()
  108.     print "抓取“%s”标签完毕,用时%.2fs\n"%(movie_tags[x],endtime2-starttime2)   #输出抓取每个标签所花费的时间


  109. #删除超过3000部的电影
  110. if len(movie)>3000:
  111.     for i in range(len(movie)-3000):
  112.         del movie[-i]

  113. #排序
  114. def comment(s):
  115.     return int(s[1])
  116. starttime4=time.time()
  117. print '开始排序……'
  118. movie.sort(key = comment, reverse=True)
  119. endtime4=time.time()
  120. print '排序完毕,共耗时%.2f'%(endtime4-starttime4)

  121. #写到html文件里面
  122. f=file('Douban_movies.html','w')
  123. f.write('<!DOCTYPE html>\n<html>\n<head>\n')
  124. f.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n')
  125. f.write('</head>\n\n<body>\n')
  126. f.write('<h1>豆瓣电影榜单</h1>'+' '+'<h3>按评价人数排名,共3000部</h3>')  #标题
  127. s=1     #s是电影序号
  128. for i in movie:
  129.     f.write('<p>'+str(s)+'. '+'<a href="'+i[3]+'">'+i[0]+'</a>'+',共'+i[1]+'人评价,'+'得分:'+i[2]+'分;'+'\n')
  130.     s+=1
  131. f.write('</body>')
  132. f.close()

  133. print '完成!请查看html文件,获取豆瓣电影榜单。'











  134.         
复制代码
回复

使用道具 举报

174

主题

45

好友

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

17#
发表于 2013-10-28 23:31:40 |只看该作者
jxgx072037 发表于 2013-10-28 22:39
哇,期待~~嘿嘿,我这两天也把代码更新了下~~~~另外,今晚在交大看见拉夫琴大神本人啦~~ ...

我偷了个懒,直接拿了豆瓣api的search,然后把几个分类标签扔进去,“爱情”和“剧情”分别抓了一两万,后来觉得没必要,其他标签都只抓了前几百个,去重排序。
而且为了进一步偷懒,我没用评价数(这个有点不符合题目最初要求了),用了search结果列表里直接附带的collect_count,也就是“看过”的人数。这个数和评价数是相关的,所以不会差太多。就不用进一步去请求每部电影的数据了。

在抓的时候,当发现电影已经在列表中,就不保存了。
为了让列表不要太大,我按看过数在10万以上、1万以上、1千以上、1千以下分了四个文件。
写文件的时候用了a模式,抓到一部写一部,即使中途断掉也无所谓,改一下初始位置继续抓。

我这个方法不是很严谨,没有用“评价”数,也不能保证所有影片都抓到。不过误差应该不会很大。另外结果里好像还是有重复的,可能中间哪里有漏洞。

PS:你今天说不定也看到我本人了。我也在现场。

top3k.html

465.97 KB, 下载次数: 51

get_movie.py

2.42 KB, 下载次数: 38

#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

2

主题

0

好友

161

积分

注册会员

Rank: 2

18#
发表于 2013-10-29 18:20:54 来自手机 |只看该作者
本帖最后由 jxgx072037 于 2013-10-29 18:26 编辑
crossin先生 发表于 2013-10-28 23:31
我偷了个懒,直接拿了豆瓣api的search,然后把几个分类标签扔进去,“爱情”和“剧情”分别抓了一两万, ...


嚓,我昨天只在群上喊了一下,忘记直接问你了。。我是群里的Qiao
回复

使用道具 举报

174

主题

45

好友

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

19#
发表于 2013-10-29 20:38:57 |只看该作者
jxgx072037 发表于 2013-10-29 18:20
嚓,我昨天只在群上喊了一下,忘记直接问你了。。我是群里的Qiao

我回来后看到了
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

2

主题

0

好友

161

积分

注册会员

Rank: 2

20#
发表于 2013-11-4 00:26:08 |只看该作者
这周荒废了……没有练习,我先去看看豆瓣API咋用
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即加入

QQ|手机版|Archiver|Crossin的编程教室 ( 苏ICP备15063769号  

GMT+8, 2024-5-18 22:32 , Processed in 0.029426 second(s), 24 queries .

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部