[python]基于BeautifulSoup解析百度贴吧water_maker(谁最水)的脚本,练手打趣之作[更新0.3]
来源:互联网 发布:深入浅出软件开发 编辑:程序博客网 时间:2024/05/18 02:00
5.29
百度在第二页添加了一个标题,导致扫的时候又出错了,原来维护这样一个小东西也是不容易的啊
代码更新
5.22修复不可抗拒BUG
这是一个不知道怎么回事,发生在BeautifulSoup中调用的Python的html解析库中发生的问题。这个问题的发生具有不确定性,我重新在调试窗口解析出错的网页的时候就不会出问题。
由于这个问题并无大碍,于是我利用一次except对它进行了一次等待重试,如果还出现错误,再用一次except忽略报错
代码更新
try:#这里有可能会出现不可抗的错误——5.22 soup=BeautifulSoup(res) except: try: time.sleep(3) soup=BeautifulSoup(res) except: return [[],'nopages']这让我知道了,代码真正要投入使用,就尽量做到不出错,在有可能出错的地方使用try,并对可能产生的错误做出相应的处理。突然产生停止程序的错误会极大地降低用户体验
5.17修复BUG
1、利用‘下一页’对帖子内部进行遍历,修复了超过10页后的页数没法统计的BUG
2、tie=soup.findAll('tr',{'class':'thread_alt'}) 这句出现一个BUG只找到一半的帖子,实在是失误,感谢davidbauer1117 提出,现改为下
tie=soup.findAll('td',{'class':'thread_title'})可以找会所有帖子
3、实现了对扫过的帖子做记录防止重复扫,并每扫三页返回第一页再扫一遍防止漏扫
代码改动较多,且发现百度贴吧的个人动态处并没有把用户所有的发言全显示,害我一直以为自己扫错。。但确实不容易证实自己的脚本的正确性,写程序最无奈之处便在此。
源码更新
5.16 感谢davidbauer1117发现的BUG
1、当帖子回复页数超过9页时脚本不再继续遍历
2、寻找帖子的链接时只找到了一半的帖子
今天课实在是满一天,晚上复习了一下将要考试的数字信号处理,没能实现昨天说的更新,对关注的朋友感到抱歉,明天一定会抽出时间来把BUG修好
--------------------------------------------------
5.15 更新
改为统计从某一日期到现在的回复,而不仅仅按页数统计。
此次更新的收获:
涉及到datetime库,datetime库能方便地显示当前时间和计算时间加减
同时发现一个尚未解决的问题:由于帖子是不断被顶上去,跌下来的,所以当遍历到某一页的时候,往往会有些已经统计过的帖子跌到这个页面,从而造成重复统计;或者某些帖子升到以前统计过的页面,导致漏掉
目前想到的一个比较傻的办法,首先把所有扫过的帖子地址记录,以后每次扫帖子之前看看是否扫过。同时特别留意前面几页,多扫几遍,尽量把新往上跑的帖子都扫到。
夜深了明天还得上课,暂时没实现,明天有时间再做更新,下源码改为按日期统计
------------------------------------------------
呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
第一要发的文章就这样被网站这种恶心的格式和自动保存搞丢了,这么难编辑,又不支持恢复返回,你妹的CSDN!!!!!
这就是我第一篇在CSDN要发的文章!
第二次手打了一整面,又被吃了!!!滚!!!!直接放代码,你爱咋的咋的吧
Python作为一个强大的脚本,解析网页,爬东西是最常用的功能之一了。小可之前也曾做过一些简单的解析爬图片等,但真正接触网页解析还是在这次watermaker中。
这个脚本要实现的功能很简单,遍历百度贴吧指定页数内所有帖子,并做出基于回复量的ID的排行。(BYW:之所以选择这个题目练手,纯粹是因为最近开始水贴吧,觉得做这么个东西,很有意思。但其核心在于标签定位,定位到了之后至于抓什么东西,那只是应用问题。)
用到的HTML解析库是BeautifulSoup,一个很强大的库,利用标签特征迅速地定位到指定标签,找到所有匹配的标签。
以下eg均始于
res=urllib.urlopen('www.baidu.com').read()
p=bs4.BeautifulSoup(res)
在BeautifulSoup返回的对象中,要么是标签对象,要么是标签对象的列表形式(eg:p.findAll('a')、p.contents()、p.tr()等的返回值)
BeautifulSoup常用方法,也是本次练手中用到的功能,相信已经能够满足以后的大多需要
1、bs查找指定标签主要使用的方法
eg:要找到符合<div class='small'>的所有标签
p.findAll('div',{'class':'small'})
反复利用标签特征可以找到最终需要的标签
2、直接加标签名可以找到所有子标签
eg:找到所有<td>标签
p.td()
3、直接以字典形式,可以访问标签内对应属性的值
eg:要找到<a href='www.csdn.net'>中href的值'www.csdn.net'
p.['href']
4、要获得标签的内容,可以直接加.string
Eg:要找到<p>1235</p>中的'1235'
p.string
---------------------------------
程序流程
Input url、pages
1、打开贴吧首页(url),找到所有给定页(pages)的链接
2、对于每一页,找到所有帖子的链接
3、对于每个帖子,找到所有页的链接(“下一页”和“尾页”的链接不用算在内,且只有一页的情况要另外处理)
4、收集帖子里每一页的ID回复(注意,并非每个回复都有ID)
5、对于每个帖子,所有收集到的ID通过mangeId这个函数(manageId判断ID是否存在res,存在ID对应的值加1,不存在插入ID设初值为1)整理到主类的res属性(res为一个字典,key为ID,值为回复数)中
6、最后,对主类中的res进行排序,输出结果(这时候我才发现我犯了一个很二的错,字典没有顺序,于是亡羊补牢地做了一个ID类,ID类很简单,有name和reply两个属性,然后把res转换为一个ID类的列表,再进行排序)
小结:这个练手之作做得很顺利,一个早上就做得差不多了,关键还是比较简单,复习了一遍简单的排序算法,代码的结构层次也比较清晰易读
#! /usr/bin/env python#coding=utf-8import urllibfrom bs4 import BeautifulSoupimport datetimeimport json,stringimport timeclass idk: name='None' reply=0 def __init__(self,name='',reply=0): self.name=name self.reply=reply class TieBa: def __init__(self,url='http://tieba.baidu.com/f?kw=%CE%E4%BA%BA%B4%F3%D1%A7',count_day=1): #输入贴吧首页地址,和开始计算的时间点距现在的天数 self.url=url self.tb='http://tieba.baidu.com' self.res={} self.count_hours=count_day*24#从距现在多少小时开始起计算 self.already_read_tiezi=[]#记录已经扫过的帖子,防止帖子变动引起的重复统计 #以下为debug,查看某ID是否有重复计算 # self.count=0#debug # self.record=[]#debug def FindTieHref(self,url): res=urllib.urlopen(url).read() soup=BeautifulSoup(res) # tie=soup.findAll('tr',{'class':'thread_alt'}) 这句出现一个BUG只找到一半的帖子,实在是失误,感谢davidbauer1117 提出,现改为下 tie=soup.findAll('td',{'class':'thread_title'}) href=[] for i in tie: a=i.find('a') hreft=a['href']#在每一个标签中再细找 # print a.string.encode('gbk')#debug-输出每个帖子的名字 # print hreft#debug if len(hreft)>40: continue href.append(hreft) return href def count_idIntiezi_allpages(self,tiezi='http://'):#遍历指定帖子内所有的页 #改为寻找‘下一页’链接遍历 res_t=self.count_idIntiezi(tiezi=tiezi) res=res_t[0] while res_t[1]!='nopages': res_t=self.count_idIntiezi(tiezi=self.tb+res_t[1])#返回该页的ID和下一页地址 res+=res_t[0] return res #以下为直接将帖子中给的链接记录并遍历。这样会使得无法遍历超过十页的帖子 ''' soup=BeautifulSoup(urllib.urlopen(tiezi).read()) pages=soup.find('li',{'class':'l_pager'}) if pages is None:#如果只有一页 return self.count_idIntiezi(tiezi=tiezi) else: page_list=pages.findAll('a') res=self.count_idIntiezi(tiezi=tiezi) res_t=[] for i in page_list: res_t.append(i)#所有页链接放入res_t容器中 for i in res_t[:-2]:#count每页的id,重复也算在内。最后把列表都相加连在一起 print 'in the tiezi '+i.string+'th page.........is reading' res+=self.count_idIntiezi(tiezi=self.tb+i['href']) #之所以放入容器,还是为了计数方便,过掉后面两页,否则可以采用下面这种方法 for i in page_list: print 'in the tiezi '+i.string+'th page.........is reading' res+=self.count_idIntiezi(tiezi=self.tb+i['href']) return res ''' def count_idIntiezi(self,tiezi='http://tieba.baidu.com/p/1583478764'): res=urllib.urlopen(tiezi).read() try: soup=BeautifulSoup(res) except: try: time.sleep(3) soup=BeautifulSoup(res) except: return [[],'nopages'] #找出下一页的链接 #这里虽然可以使用递归,但还是避免了使用递归,改为在上层做循环 nextpage='' pages=soup.find('li',{'class':'l_pager'}) if pages is None:#如果只有一页 nextpage='nopages' else: page_list=pages.findAll('a') if page_list[-2].string.encode('utf8')!='下一页':#如果不存在‘下一页’这几个字,即终结 nextpage='nopages' else: nextpage=page_list[-2]['href'] print page_list[-2].string+nextpage[-2:]#debug ''' if self.judge_time(0,soup):#判断是否为当天的帖子,不是则跳过 return [] ''' #还是判断是否为指定时间点之后的回复比较好 post=soup.findAll('div',{'class':'l_post'}) idk=[] for i in post: try: date=json.loads(i.div['data-field'])['content']['date'] if self.jTime(date)==False: continue name=i.find('a',{'class':'p_author_name'}).string ''' #debug 查看某个ID的回复记录,看是否有重复计算 if name.encode('utf8')=='爱情小小猫': self.count+=1 print name.encode('utf8') print date+'.............count='+str(self.count) if date in self.record: print 'here maybe an error' time.sleep(10) else: self.record.append(date) ''' except AttributeError,NoneType:#如果找到的ID名字无法识别(可能为IP回复) name='no name' idk.append(name) return [idk,nextpage] def manage_id(self,idlist=[]): for i in idlist: if self.res.has_key(i): #如果存在id,则加1,否则创建id self.res[i]=self.res[i]+1 else: self.res[i]=1 def count_idInAll(self,pages=50,begin_time=''): b=0 tag=0 while b<pages:#遍历贴吧每一页 #每扫三页再对第一页重复扫,以防帖子漏掉 if (b+1)%3!=0 or tag==1: tag=0 url=self.url+'&pn='+str(b*50) b+=1 print str(b)+'th page is reading-----------------------------'+url else: tag=1 url=self.url print 'return to page 1 to check if there is new tiezi' href=self.FindTieHref(url=url) for tiezi in href:#遍历每一页中的每个帖子 if tiezi in self.already_read_tiezi:#判断帖子是否扫过 print tiezi+'.............is repeated' continue else: self.already_read_tiezi.append(tiezi) print "tiezi.........url="+self.tb+tiezi idlist_t=self.count_idIntiezi_allpages(tiezi=self.tb+tiezi)#遍历每个帖子中的每个回复 try: if len(idlist_t)==0: print 'this tiezi does not exist ID required' continue print 'in the '+str(b)+'th page '+idlist_t[0].encode('gbk')+"'s tiezi.........successed" except: print 'here an error id************************'+str(len(idlist_t)) self.manage_id(idlist=idlist_t) ''' #debug---检测每个帖子后是否发生回复量异常 for i in self.res: if self.res[i]<10 or i.encode('utf8')!='爱情小小猫': continue print i.encode('gbk')+'.............'+str(self.res[i]) ''' return self.res def rangeId(self,res=[]): idlist_all=[] for i in res:#把id全转换成ID类放进容器 idlist_all.append(idk(i,res[i])) ranged_idlist=[] print str(len(idlist_all))+'........idlist_all' for i in idlist_all:#来吧……复习一个数据结构,插入排序 if len(ranged_idlist)==0:# ranged_idlist.append(i) elif len(ranged_idlist)==1: print 'here' if ranged_idlist[0].reply>=i.reply: ranged_idlist.append(i) else: ranged_idlist.insert(0,i) else: k=0 for l in ranged_idlist: if l.reply>i.reply: k+=1 if k==len(ranged_idlist)-1: ranged_idlist.append(i) continue ranged_idlist.insert(k,i) break return ranged_idlist ''' @staticmethod def judge_time(hours=0,soup='bs'):#接收时间差,和一个已经做成soup的网页 res=datetime.datetime.now()+datetime.timedelta(hours=hours) #从soup中找到时间 ppost=soup.find('div',{'class':'p_post'}) date=json.loads(ppost['data-field'])['content']['date'] day=string.atoi(date[8:10]) month=string.atoi(date[5:7]) year=string.atoi(date[:4]) if day!=res.day: return False elif month!=res.month: return False elif year!=res.year: return False else: return True ''' def jTime(self,date=''):#只负责判断给定结构的时间是否符合要求 res=datetime.datetime.now()-datetime.timedelta(hours=self.count_hours)#获取指定起始日期 day=string.atoi(date[8:10]) month=string.atoi(date[5:7]) year=string.atoi(date[:4]) #只实现判断到月 if day<res.day and month<=res.month: return False elif day>res.day and month<res.month: return False elif day==res.day and month<res.month: return False elif year!=res.year: return False # elif year!=res.year: # return False else: return True def countWaterMaker(self,pages=1): idlist=self.count_idInAll(pages=pages) return self.rangeId(idlist)if __name__=='__main__': a=TieBa(count_day=2) k=a.countWaterMaker(pages=10) c=0 print 'water-maker top 30' for i in k: if c==30: break print str(c)+' th is '+i.name+'...........................'+str(i.reply) c+=1 ''' k=a.count_idInAll(pages=1) for i in k: print i.encode('gbk') print k[i] '''
- [python]基于BeautifulSoup解析百度贴吧water_maker(谁最水)的脚本,练手打趣之作[更新0.3]
- python解析html之BeautifulSoup
- Python爬虫urllib笔记(四)之使用BeautifulSoup爬取百度贴吧
- 基于树莓派的智能寝室终端(Python练手)1
- 基于树莓派的智能寝室终端(Python练手)2
- 基于树莓派的智能寝室终端(Python练手)4
- 百度贴吧爬虫【练手】
- 【Python爬虫系列】内容解析之BeautifulSoup
- Python爬虫之BeautifulSoup库函数解析
- 基于BeautifulSoup解析的网页爬虫实现
- 【Python】 html解析BeautifulSoup
- Python爬虫实例(2)-用BeautifulSoup爬取一个网页上的多张照片(以百度贴吧为例)
- python网络爬虫与信息采取之解析网页(二)---BeautifulSoup库的find()和find_all()
- python网络爬虫与信息采取之解析网页(三)---- BeautifulSoup库的导航树实例
- 毕业设计-基于深度神经网络的语音关键词检出系统-使用python脚本作词频统计-TIMIT
- 毕业设计-基于深度神经网络的语音关键词检出系统-使用python脚本作词频统计-Librispeech
- BeautifulSoup简单爬取百度贴吧
- 解析百度搜索结果页面的python脚本(Linux/Win都可以运行)
- android,内存优化详解 .
- nodejs系列提纲
- ASP.NET状态管理(视图状态ViewState)
- 第二章:黑客与画家
- 关于 eclipse的一切
- [python]基于BeautifulSoup解析百度贴吧water_maker(谁最水)的脚本,练手打趣之作[更新0.3]
- exp/expdp中query参数的使用
- 编写程序强制用户通过SSL访问网站
- A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be avail
- c2-3-main-frm_10255-10300渲染日志
- Ubuntu 下常用网站连接地址说明
- 在win7环境下利用cuteftp无法登陆Linux的解决办法
- 位图排序
- Project——多线程断点续传下载