如何突破豆瓣python 爬虫豆瓣电影限制频率

比特之理 & 豆瓣爬虫杂记|とある東雲研究所の分所
とある東雲研究所の分所
& 豆瓣爬虫杂记
豆瓣爬虫杂记
手头上有一个便宜的VPS,一直没怎么用,除了用来跑程序和VPN外。然后最近觉得没什么程序在上面跑很对不起它,就没事找事地想让它去爬点东西,但是也没想到什么好爬的,就愉快地决定让它去爬一些豆瓣的信息;
初步计划先让它爬一下豆瓣的书的信息和豆瓣用户关注被关注的关系网;这里随便写一下爬虫的杂记,作为我最近的存在感。。。
豆瓣的书啊,电影啊,音乐啊那些条目有一点很讨厌,就是他们的URL的编排,都是这种形式的:
http://[type]./subject/[id]/
type可以是"book",或者"music"或者"movie";
但是id完全没有什么规律(大概没有吧),不仅仅是说你无法从id中判断出这个条目是书还是电影还是音乐;而且就算你要找书的URL,你也不知道这些id是服从什么规律的;比如id=10000可能是一本书,但是10001可能对应的type就变成了music,也有可能是404。。。
如果我想爬下都把上面所有的书,我从/subject/1/ 开始扫描起的话,预计会花上几个月的时间,得到很多404,而且不知道什么时候是个尽头。。【另外movie和music占了更多的id】
最后没办法,找了一下,决定从这个页面下手:
/tag/?view=type
这些是热门的tag,但是好像不是全部,虽然觉得差不多了,估计不包含在这里面的都是一些冷门的书,热门的话基本一定会出现在这几百个tag里面的【大概。。】。
所以我的计划是,从每个标签爬进去,一页一页爬下书的id,构建一个列表,里面全是有效的书id,也只有id而已;而且可以顺便分好类,在一个文件里面记录下属于某个tag的所有书本的id;
这个文件我爬好了,放在的BookId.txt文件中;
这个过程大概花费了6,7个小时,爬下来统计了一下,去重之后8万本不到,和网上传说的10万本比,少了一些,但是估计热门书籍都没漏(吧)。。
def GetRE(content,regexp):
return re.findall(regexp, content)
def GetContent(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 5.1)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/33.0. Safari/537.36'}
req = urllib2.Request(url = url,headers = headers);
while True:
content = urllib2.urlopen(req).read();
time.sleep(5)
if flag == 1:
def GetAllTagList():
获取全部分类列表/tag/
content = GetContent(&/tag/&)
regexp = r&&td&&a href=\&./(.+)\&&\1&;
return GetRE(content,regexp);
def GetBookWithTag(tag):
获取书本信息/tag/:tag?start=0&type=T
startid = 0;
TotalList = [];
while True:
url = &/tag/&+tag+
&?start=&+str(startid)+&&type=T&
content = GetContent(url)
cut_idx = content.find('block5 movie_show');
#截断右边栏的推荐书目
if cut_idx &= 0:
content = content[:cut_idx]
#提取书本ID
regexp = r&/subject/(\d+)/\&&;
BookIdList = list(set(GetRE(content,regexp)))
if len(BookIdList) != 0:
TotalList += BookIdList
startid += 20;
time.sleep(2)
return list(set(TotalList))
最后爬取书的详细内容的时候再去抓取别的信息。
有了书本ID,我开始还准备傻傻地去爬下每个/subject/[id]/ 的页面源码的,但是发现豆瓣开放了,翻看了一下,发现只要请求:
/v2/book/[id]
就可以返回书本信息的json;
这个过程大概vps爬了一周,一边爬一边写到文件里面以便后期分析(其实主要是为了防止中途崩溃可以断点续“爬”);
主要爬取了书的名字,评分,评分人数,出版社,价钱,页数,出版时间这几个信息;
放在了的book_detail.txt里面,如果有人想自行分析,可以拿去研究研究。。【我猜没有】
class Book:
def __init__(self,JsonInfo):
if JsonInfo.has_key('title'):
self.Name = JsonInfo['title'];
if JsonInfo.has_key('rating') and
JsonInfo['rating'].has_key('average'):
self.Rate = JsonInfo['rating']['average']
if JsonInfo.has_key('rating') and
JsonInfo['rating'].has_key('numRaters'):
self.RateNum = JsonInfo['rating']['numRaters']
if JsonInfo.has_key('pages'):
self.Page = JsonInfo['pages']
if JsonInfo.has_key('price'):
self.Price = JsonInfo['price']
if JsonInfo.has_key('pubdate'):
self.Pubdate = JsonInfo['pubdate']
if JsonInfo.has_key('publisher'):
self.Publisher = JsonInfo['publisher']
if JsonInfo.has_key('id'):
self.Id = JsonInfo['id']
Name = None
Rate = None
RateNum = None
Page = None
Price = None
Pubdate = N
Publisher = N
def getBookInfo(id):
根据API来获取书本信息
url = '/v2/book/'+
content = GetContent(url)
JsonInfo = json.loads(content)
book = Book(JsonInfo);
return book
def WriteItem(fh,book):
fh.write('%s\n'%book.Name);
fh.write('\tId:%s\n'%book.Id);
fh.write('\tRate:%s\n'%book.Rate);
fh.write('\tRate Number:%d\n'%book.RateNum);
fh.write('\tPrice:%s\n'%book.Price);
fh.write('\tPublish Date:%s\n'%book.Pubdate);
fh.write('\tPublisher:%s\n\n'%book.Publisher);
def GetAllIdOfBook():
taglist = GetAllTagList();
f = open('BookId.txt','w');
for tag in taglist:
f.write('%s\n'%(tag))
tag = urllib.quote(tag)
BookIdList = GetBookWithTag(tag)
for BookId in BookIdList:
f.write('\t%s\n'%(BookId))
f.close();
def scrawlAllBook():
BookIdList = [];
IDListFile = open('BookId.txt','r')
for line in IDListFile:
id = GetRE(line,r'^\t(\d+)$');
if id == []:
BookIdList.append(id[0])
IDListFile.close()
BookIdList = list(set(BookIdList))
file_h = open('book_detail.txt','a+')
for bookid in BookIdList:
BookInfo = getBookInfo(bookid);
print BookInfo.Id,BookInfo.Name
WriteItem(file_h,BookInfo)
time.sleep(2)
然后用Mathematica写了几行代码分析了一下评分分布(去掉0分的后),嗯。。挺合理的:
之后找了一下评分大于⑨分,评价人数大于1000的那些书:
{name[[#]],rate[[#]],ratenum[[#]]}&/@Select[Range@n,rate[[#]]&9&&ratenum[[#]]&1000&];
SortBy[bestbook, #[[2]] &] // Reverse // Column
壮哉大灌篮,可能有人觉得评价人数才1000出头,不具备代表性;
下面这个列表是分数大于⑨,评价人数大于10000的书,有几本我最近想看一下了。。
{灌篮高手(31),9.5,25388}
{红楼梦,9.5,88089}
{海贼王,9.5,23581}
{史记,9.5,10742}
{机器猫哆啦A梦23,9.4,27123}
{冰与火之歌(卷一),9.3,16767}
{撒哈拉的故事,9.3,12019}
{撒哈拉的故事,9.3,10855}
{撒哈拉的故事,9.3,10613}
{飘(上下),9.3,61462}
{一九八四,9.3,17514}
{活着,9.3,13208}
{一九八四·动物农场,9.2,21619}
{七龍珠 34,9.2,21676}
{子不语3,9.2,11463}
{福尔摩斯探案全集(上中下),9.2,42553}
{三国演义(全二册),9.2,47330}
{撒哈拉的故事,9.2,32557}
{百年孤独,9.2,42867}
{动物农场,9.2,21107}
{小王子,9.2,10321}
{三体Ⅲ,9.2,38614}
{三体Ⅱ,9.2,37147}
{NANA(1-13),9.1,15788}
{明朝那些事儿(1-9),9.1,23841}
{子不语(1,2),9.1,14904}
{機器娃娃(1),9.1,10684}
{小王子(中英文对照本),9.1,10163}
{平凡的世界(全三部),9.1,70350}
{经济学原理(上下),9.1,10032}
{天龙八部(全五册),9.1,45739}
{安徒生童话故事集,9.1,31924}
{中国历代政治得失,9.1,16435}
{肖申克的救赎,9.1,28744}
{父与子全集,9.1,14587}
{动物庄园,9.1,11173}
{小王子,9.1,10024}
{活着,9.1,92948}
{活着,9.1,64119}
{围城,9.1,12139}
豆瓣登陆验证码
在爬用户关注关系的时候遇到的第一个问题就是模拟登陆,有人说模拟登陆很简单啊:
loginurl = '/accounts/login'
cookie = cookielib.CookieJar()
urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
params = {
&form_email&:EMAIL,
&form_password&:PASSWORD,
&source&:&index_nav&
response=opener.open(loginurl, urllib.urlencode(params))
但是,豆瓣比较麻烦的一点是,短时间内登陆几次左右你再登陆,就要你输入验证码了。。
一开始我觉得没什么办法,然后就正则表达式检测是否有验证码,如果检测到了,就手动保存到本地,然后等待用户识别输入,但是这样在VPS上弄很麻烦的样子,要开ftp软件把图片拿回本地看一下才可以。。
然后呢?前几天看知道宇创的的时候,在很下面看到了:
验证码破解
然后就去谷歌了一下这货,发现,诶,好像很不错的样子诶,虽然是一个用于OCR的项目,但是google自动补全的关键字有验证码诶,而且不少人都是用它来做验证码的,我还以为捡到好东西了。。。然后看了一下自带的demo,它识别的是这个:
然后找了一下别人用它来做验证码识别的,比如。。。。。
识别你奶奶个腿啊,你敢不敢背景复杂点?!
然后我查了一下,也自己试了一下,这个pytesser工具还真是太他喵的大爷了。。你必须要把图片处理到非常非常干净,一丝半点噪声都没有,它才会好好工作。。
比如说我处理成这个样子了:
识别出来的结果还是错的,因为只要是一小点,它就会觉得,哦,这个可以是,或者.或者:或者\之类的;而且会被识别成哪个完全不知道,完全看大爷它的心情。。它甚至无视位置,在奇怪的位置出现的噪点它会理解成多行文本。。
但是有什么办法,想要自动识别,就(暂时)只能借助这个了。。
爬了一些验证码图片下来,发现豆娘的还不算太坑,主字体都是黑色的(这点最重要),而且英文笔画宽度大概有3~6个像素左右,背景是简单的带有一点明暗变化和椒盐噪声(黑白噪声):
当然也有一些特别坑爹的。。。
想了半天,发现多想无益,动手测试一下才是王道。。。【注:pytesser这玩意儿依赖于PIL库】
首先先二值化,把黑色的部分拿出来,然后要去掉尽可能多的背景,但是一开始我算是自己把自己坑了,没想太多,直接把图片转成灰度图,准备一个阈值解决:
def Binarize(img,threshold):
img = img.convert('L')
table = [0]*threshold+[1]*(256-threshold)
return img.point(table,'1').convert('L')
这个问题我是很后来才注意到的,这个效果必然很差,原因在于我用的是系统自带的彩色图像转灰度图的函数Image.convert('L'),但是我真正想要的二值化效果是把黑色变成黑色,其他颜色变成白色,但是这个做法会把蓝色,红色也变成黑色。。
比如之前的那个验证码
就会变成:
很无奈,还是乖乖写一个二值化函数吧,要让RGB三个通道的灰度值都小于阈值才行:
def Binarize(img,threshold):
img_G = Image.new('L',img.size,1)
w,d = img.size
imgdata,graydata = img.load(),img_G.load()
for x in range(w):
for y in range(d):
graydata[x,y] = 255*(imgdata[x,y][0]&threshold or
imgdata[x,y][1]&threshold or
imgdata[x,y][2]&threshold)
return img_G
这时候就变成了:
其中操作的时候虽然这些黑色的文字像素值都在(10,10,10)附近,但是不敢把阈值设太低,虽然这么做可以进一步去除噪点,但是同时会带来麻烦;一来阈值太低会把字符笔画变得更细;二来,容易出现断点,要是把w变成了vv,pytesser这货肯定识别不了。。【唉,感觉用pytesser就像在照顾熊孩子一样。。】
二值化是为了下一步,之后只要消除掉噪点,变成这个就行了:
去噪点是在不把笔画变细或者弄断的前提下进行的,所以自带的那些图形滤波函数我就不敢用了,首先我不清楚内部的变换核函数是高斯还是什么的(懒得看源码),虽然这并不重要。。最重要的是这种滤波方式绝对绝对会把笔画变细的;【显然的嘛,低通必然带来模糊化,而二值图模糊化的后果就是少的那部分(黑色)更少。】
以前用OPENCV做图像处理的时候,对于这种去噪,想都不用想,就是形态学处理一下马上就可以了,腐蚀一下然后再膨胀一下!
但是。。。我翻遍了PIL的函数库和GOOGLE了半天,愣是没有找到这两个函数。。【想了想,以后python还是不用PIL了,乖乖回去用OPENCV吧】
没办法,又不想自己实现(虽然实现并不难,有空写一个),但是我想到了另一个更简单的方法,直接直接查找每个黑色块的连通区域面积,把面积小于某个值的全部去掉就是了:
#返回跟(x,y)连通的黑色区域的每个点的坐标
def scrap_img(imgdata,dst,width,heigth,x,y):
findlist = []
waitlist = [(x,y)]
while waitlist != []:
cur = waitlist.pop(0)
for (i,j) in [(cur[0]+1,cur[1]),
(cur[0]-1,cur[1]),
(cur[0],cur[1]+1),
(cur[0],cur[1]-1)]:
i & 0 or i&=width or j&0 or j&=heigth:
if imgdata[i,j] == dst:
if not (i,j) in findlist:
findlist.append((i,j));
if not (i,j) in waitlist:
waitlist.append((i,j));
return findlist
def m_filter(img):
imgdata = img.load()
w,h = img.size
for x in range(w):
for y in range(h):
if imgdata[x,y] == 0:
scraplist = scrap_img(imgdata,0,w,h,x,y);
for p in scraplist:
imgdata[p[0],p[1]] =
255 - 254* (len(scraplist) & 30)
imgdata[x,y] = 255 - 254*(len(scraplist)&30)
其中上面第28-29行用了个小伎俩,对于每一个黑点(像素值为0),找到连通区域后,如果连通区域像素点少于特定的值,那么直接在原图中把这些点变成白色(像素值为255),否则变成1,这样就直接省掉一个变量,而且避免重复计算。
总结起来步骤就是:
处理到这个份上,大爷它表示总算可以识别了。。
不过这种方法太过简单粗暴了,识别率其实很低,但是不要紧,我们的目的不是要做一个高识别率的验证码破解程序,而是成功登陆豆瓣,只要识别成功一次就可以了,所以我们可以一个While语句不断地尝试,识别成功就可以了;
最后自动登陆代码就变成这个样子。。。
def logindouban():
loginurl = '/accounts/login'
cookie = cookielib.CookieJar()
urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
params = {
&form_email&:EMAIL,
&form_password&:PASSWORD,
&source&:&index_nav&
response=opener.open(loginurl, urllib.urlencode(params))
if response.geturl() ==
&/accounts/login&:
html=response.read()
#如果需要验证码
imgurl=re.search('&img id=&captcha_image& src=&(.+?)&
alt=&captcha& class=&captcha_image&/&',html)
while imgurl:
url = imgurl.group(1)
urllib.urlretrieve(url, 'captcha.jpg')
captcha = re.search('&input type=&hidden&
name=&captcha-id& value=&(.+?)&/&' ,html)
if captcha:
params[&captcha-solution&] =
captchaRecognition.recognition('./captcha.jpg');
params[&captcha-id&] = captcha.group(1)
params[&user_login&] = &登录&
response =
opener.open(loginurl, urllib.urlencode(params))
if response.geturl() == '/':
opener.open(loginurl,urllib.urlencode(params))
html=response.read()
imgurl=re.search('&img id=&captcha_image&
src=&(.+?)& alt=&captcha&
class=&captcha_image&/&', html)
return opener
效率还好,登陆只要两三秒就可以了。。
嘛,用户关系网还没开始爬(豆瓣用户数和书的数目可不是一个数量级的),大概。。这篇博文就先这样吧。。
后话:什么时候等我突破了迅雷娘的那个验证码,之前那个URL获取器就可以变成全自动了!!!
不过这个看起来好像很麻烦的样子。。还是有空自己搞一个模式识别的方法吧。。桌上一直放着本这方面的书没怎么看。。
本文内容遵从CC版权协议,转载请注明出自
我勒个去。。太久没打理网站,Wordpress版本太低,被黑了都不知道。。。感谢白帽子。。
Categories
► (22)
► (2)
► (5)
► (4)
► (6)
► (28)
► (2)
► (1)
► (12)
► (3)
► (1)
► (2)
► (6)
► (48)
► (11)
► (4)
► (9)
► (5)
► (5)
► (2)
► (1)
► (1)
► (34)
► (16)
► (2)
▼ (41)
► (3)
► (7)
► (5)
► (3)
► (6)
▼ (4)
► (2)
► (10)
► (20)
► (14)
► (4)
collapsItems['collapsCat-150:2'] = '';
collapsItems['collapsCat-160:2'] = '';
collapsItems['collapsCat-154:2'] = '';
collapsItems['collapsCat-159:2'] = '';
collapsItems['collapsCat-2:2'] = '► (4)
► (2)
► (5)
► (4)
► (6)
collapsItems['collapsCat-98:2'] = '';
collapsItems['collapsCat-131:2'] = '';
collapsItems['collapsCat-164:2'] = '';
collapsItems['collapsCat-96:2'] = '';
collapsItems['collapsCat-163:2'] = '';
collapsItems['collapsCat-122:2'] = '';
collapsItems['collapsCat-97:2'] = '';
collapsItems['collapsCat-157:2'] = '';
collapsItems['collapsCat-3:2'] = '► (1)
► (2)
► (1)
► (12)
► (3)
► (1)
► (2)
► (6)
collapsItems['collapsCat-144:2'] = '';
collapsItems['collapsCat-151:2'] = '';
collapsItems['collapsCat-147:2'] = '';
collapsItems['collapsCat-152:2'] = '';
collapsItems['collapsCat-149:2'] = '';
collapsItems['collapsCat-106:2'] = '';
collapsItems['collapsCat-127:2'] = '';
collapsItems['collapsCat-155:2'] = '';
collapsItems['collapsCat-99:2'] = '';
collapsItems['collapsCat-4:2'] = '► (10)
► (11)
► (4)
► (9)
► (5)
► (5)
► (2)
► (1)
► (1)
collapsItems['collapsCat-95:2'] = '';
collapsItems['collapsCat-103:2'] = '';
collapsItems['collapsCat-94:2'] = '';
collapsItems['collapsCat-5:2'] = '► (16)
► (16)
► (2)
collapsItems['collapsCat-153:2'] = '';
collapsItems['collapsCat-120:2'] = '';
collapsItems['collapsCat-162:2'] = '';
collapsItems['collapsCat-104:2'] = '';
collapsItems['collapsCat-148:2'] = '';
collapsItems['collapsCat-139:2'] = '';
collapsItems['collapsCat-121:2'] = '';
collapsItems['collapsCat-100:2'] = '';
collapsItems['collapsCat-6:2'] = '► (3)
► (7)
► (5)
► (3)
► (6)
▼ (4)
► (2)
► (10)
collapsItems['collapsCat-102:2'] = '';
collapsItems['collapsCat-145:2'] = '';
collapsItems['collapsCat-101:2'] = '';
collapsItems['collapsCat-7:2'] = '► (2)
► (14)
► (4)
Collapse Functions, version 2.0
*--------------------------------------------------------------------------*/
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g,"");
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i
本博客所有作品遵从CC版权协议,采用进行许可。我用爬虫抓了豆瓣16万电影条目,如果把数据外泄_香港电影吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:392,030贴子:
我用爬虫抓了豆瓣16万电影条目,如果把数据外泄收藏
会不会违法
青岛高防服务器,高防BGP服务器,双线服务器租用/托管!
凭本事偷的数据,为什么会违法
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或&实战——豆瓣爬虫
实战——豆瓣爬虫
视频太卡?试试切换线路
本课时介绍 Scrapy 并讲解如何配置 Scrapy 的运行环境。
本课时介绍使用 Scrapy 生成 Project 并爬取简单的网页。
本课时讲解 Scrapy 生成的 Project 中各个文件的作用。
本课时介绍使用 Scrapy 爬取豆瓣电影 Top250 并生成 csv 文件。
只有成为VIP会员才能提问&回复,快吧!如果你还没有账号你可以一个账号。
添加新技术问题
课程 [实战——豆瓣爬虫]
中已存在问题
添加新技术问题
问题描述越详细,被解答的速度越快
有新回答时请邮件提醒我
着急,拜托快点
不急,慢慢解决
关联课程 [实战——豆瓣爬虫]
服务热线:400-678-8266【已解决】nodejs豆瓣小组爬虫分析与解决。 - CNode技术社区
积分: 1460
前某不知名公司项目总监,现某不知名电商平台产品总监,全能打杂。
豆瓣小组地址:
(采用 request、http.get全部是403错误)
(ALL)具体小组地址:
例如:分页地址
request、http.get,还是TM403错误)
该小组下的具体主题地址:
request、http.get,吐血还是403错误)
大伙有没有相应的解决方案呢?demo 等等之类的,这TM的全是403错,这是为毛呢!!
电影的那个爬虫我看过了,测试了是可以爬的。为毛小组爬不了呢?
我也看了不少Python写的爬虫都有爬小组的。为毛nodeJS不行呢?
采用 request.get({url:‘.’,headers:{‘User-Agent’: ‘request’},funaction(err,req,data){})
那怕是取页面里的一个字符,也要下载整个页面
懂前端的人都知道,dom树没构建之前,不可能用jquery去取元素,唯一的办法就是用正则表达式
兄台,能否讲明白点呢,是指用正则表达式可以做到吗?
抓取响应方法啊
思路大概是 status==‘200’
握手握到一半就END就行了。
var jsonreq = http.request(opt, function (serverFeedback) {
if (serverFeedback.statusCode == 200) {
var x='';
serverFeedback.setEncoding('utf8');
serverFeedback.on('data',function(data){x +=});
serverFeedback.on('end',function(){req.session.json=JSON.parse(x);eval(point)});
req.session.json='error';
jsonreq.write(data);
jsonreq.end();
拿这个拆吧
怎么解决的?
常规的解决,模拟用户登录
在请求头部中加东西headers:{‘User-Agent’: ‘request’}
request.get({url:‘.’,headers:{‘User-Agent’: ‘request’},funaction(err,req,data){})
已经解决,谢谢。
为何不直接通过API爬,这样还要处理dom多麻烦
豆瓣小组有API吗?
关键在于要取小组内主题页面里面的内容。而不是小组标签,目前豆瓣有提供这个API接口?
我只是提供了一个无关痛痒的api举个例子而已,其它的接口显然都有
话说你在哪里找到的。我都没有找到。去官方看了。居然没有小组的API
CNode 社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。
服务器赞助商为
,存储赞助商为
,由提供应用性能服务。
新手搭建 Node.js 服务器,推荐使用无需备案的

我要回帖

更多关于 豆瓣电台爬虫 的文章

 

随机推荐