python爬虫实战二之爬取百度贴吧帖子

来源:互联网 发布:网络机顶盒能收多少台 编辑:程序博客网 时间:2024/06/05 04:55

目标:

1.对百度贴吧的任意帖子进行抓取

2.指定是否只抓取楼主发帖内容

3.将抓取到的内容分析并保存到文件

1.URL格式的确定

首先,我们先观察一下百度贴吧的任意一个帖子。比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,分析一下地址

http://表示资源传输使用http协议

tieba.baidu.com 是百度的二级域名,指向百度贴吧的服务器。

/p/3138733512是服务器的某个资源,即这个帖子的地址定位符

see_lz和pn是该url的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真

所以我们可以讲url分为两部分,一部分为基础部分,一部分为参数部分。

例如上面的url我们划分基础部分是 http://tieba.baidu.com/p/3138733512,参数部分是?see_lz=1&pn=1

2.页面的抓取

熟悉了URL的格式,那就让我们用urllib2库来试着抓取页面内容吧。上一篇糗事百科我们最后改成了面向对象的编码方式,这次我们直接尝试一下,定义一个类名叫BDTB(百度贴吧),一个初始化方法,一个获取页面的方法。

其中,有些帖子我们想指定给程序是否要只看楼主,所以我们把只看楼主的参数初始化放在类的初始化上,即init方法。另外,获取页面的方法我们需要知道一个参数就是帖子页码,所以这个参数的指定我们放在该方法中。

综上,我们初步构建出基础代码如下:

import urllib.requestfrom urllib.request import urlopenimport urllib.errorimport re#百度贴吧爬虫类class BDTB:#初始化,传入基地址,是否只看楼主的参数    def __init__(self,baseurl,seeLZ):        self.baseURL=baseurl        self.seeLZ='?see_lz='+str(seeLZ)    #传入页码,获取该页帖子的代码    def getPage(self,pageNum):        try:            url=self.baseURL+self.seeLZ+'&pn'+str(pageNum)            request=urllib.request.Request(url)            response=urllib.request.urlopen(request)            print(response.read().decode('utf-8'))            return response        except urllib.error.URLError as e:            if hasattr(e,'reason'):                print(u'连接百度贴吧失败,错误原因',e.reason)                return NonebaseURL='http://tieba.baidu.com/p/3138733512'bdtb=BDTB(baseURL,1)bdtb.getPage(1)
部分运行结果:(可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为HTML代码)

  <a data-field='{&quot;un&quot;:&quot;\u660e\u4e4b\u7ffc&quot;}'alog-group="p_author" class="p_author_name j_user_card" href="/home/main?un=%E6%98%8E%E4%B9%8B%E7%BF%BC&ie=utf-8&fr=pb" target="_blank">明之翼</a>
                    
                </li>
                
                <li class="l_badge" style="display:block;">
                    <div class="p_badge">
                        <a href="/f/like/level?kw=nba&ie=utf-8&lv_t=lv_nav_intro" target="_blank" class="user_badge d_badge_bright d_badge_icon3_1" title="本吧头衔12级,经验值6206,点击进入等级头衔说明页"><div class="d_badge_title ">MVP</div><div class="d_badge_lv">12</div></a>
                    </div>

3.提取相关信息

1)提取帖子标题

关于标题的源代码:

<h3 class="core_title_txt pull-left text-overflow" title="纯原创我心中的NBA2014-2015赛季现役50大" style="width: 396px">纯原创我心中的NBA2014-2015赛季现役50大</h3>

所以我们想提取<h3>标签中的内容,同时还要指定这个class唯一,因为h3标签实在太多,正则表达式如下

<h3 class="core_title_txt .*?>(.*?)</h3>

所以我们增加一个获取标题的方法

def getTitle(self):    page=self.getPage(1)    pattern=re.compile('<h3 class="core_title_txt .*?>(.*?)</h3>',re.S)    result=re.search(pattern,page)    if result:        #print result.group(1)#测试输出        return result.group(1).strip()    else:        return None

2)提取帖子页数

同样的帖子总页数我们也可以通过分析页面中的?页来获取,所以我们的获取总页数的方法如下

#获取帖子一共有多少页def getPageNum(self,page):    pattern=re.compile('<li class="l_reply_num" .*?</span>.*?<span.*?>(.*?)</span>',re.S)    result=re.search(pattern,page)    if result:        #print result.group(1)测试输出        return result.group(1).strip()    else:        return None

3)提取正文内容

def getContent(self,page):    pattern=re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)    items=re.findall(pattern,page)    for item in items:        print(item)
运行部分结果:

很多媒体都在每赛季之前给球员排个名,我也有这个癖好…………,我会尽量理性的分析球队地位,个人能力等因素,评出我心目中的下赛季50大现役球员,这个50大是指预估他本赛季在篮球场上对球队的影响力……不是过去的荣誉什么的,所以难免有一定的主观性……如果把你喜欢的球星排低了,欢迎理性讨论!<img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=557ae4d4fadcd100cd9cf829428947be/a9d6277f9e2f0708468564d9eb24b899a801f263.jpg" pic_ext="jpeg"  pic_type="0" width="339" height="510"><br><br><br><br>状元维金斯镇楼<br>P.S 1 我每天都至少更新一个,不TJ。<br>      2 今年的新秀我就不考虑了,没上赛季参照
            <img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=cb6ab1f8708b4710ce2ffdc4f3ccc3b2/06381f30e924b899d8ca30e16c061d950b7bf671.jpg" pic_ext="jpeg"  pic_type="0" width="339" height="510"><br><br><br><br>50 惊喜新人王 <a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivS12AkS11WcjnMQsTddE2yXZInIi4k8KEu5449mWp1SxBADVCHPuUFSTGH+WZuV+ecUBG6CY6mAz/Zq1mzxbFxzAG+4Cm4FSU0="  class="ps_cb" target="_blank" onclick="$.stats.track(0, 'nlp_ps_word',{obj_name:'迈卡威'});$.stats.track('Pb_content_wordner','ps_callback_statics')">迈卡威</a><br>上赛季数据<br>篮板 6.2  助攻 6.3  抢断 1.9 盖帽  0.6 失误 3.5 犯规  3  得分 16.7<br><br><br>    


可以看到,结果中还有一大片换行符和图片符,既然这样,我们就要对这些文本进行处理,把各种各样复杂标签给它剔除掉,还原精华内容,把文本处理写成一个方法也可以,不过为了实现更好的代码架构,我们可以考虑把标签等的处理写成一个类。

那就向他叫做tool(工具类),里面定义了一个方法,叫replace,是替换各种标签的,在类中定义了几个正则表达式,主要利用了re.sub方法对文本进行匹配后然后替换。具体的思路可以看注释。

import urllib.requestfrom urllib.request import urlopenimport urllib.errorimport re#处理页面标签类class Tool:    #去除img标签,7位长空格    removeImg=re.compile('<img .*?>| {7}|')    #删除超链接标签    removeAddr=re.compile('<a.*?>|</a>')    #把换行的标签换为\n    replaceLine=re.compile('<tr>|<div>|</div>|</p>')    #将表格制表<td>替换为\t    replaceTD=re.compile('<td>')    #将段落开头换为\n加空两格    replacePara=re.compile('<p.*?>')    #将换行符或双换行符替换为\n    replaceBR=re.compile('<br><br>|<br>')    #将其余标签剔除    removeExtraTag=re.compile('<.*?>')    def replace(self,x):        x=re.sub(self.removeImg,'',x)        x=re.sub(self.removeAddr,'',x)        x=re.sub(self.replaceLine,'\n',x)        x=re.sub(self.replaceTD,'\t',x)        x=re.sub(self.replacePara,'\n  ',x)        x=re.sub(self.replaceBR,'\n',x)        x=re.sub(self.removeExtraTag,'',x)        #strip()将前后多余的内容删除        return x.strip()# 百度贴吧爬虫类class BDTB:#初始化,传入基地址,是否只看楼主的参数    def __init__(self,baseurl,seeLZ):        self.baseURL=baseurl        self.seeLZ='?see_lz='+str(seeLZ)        self.tool=Tool()    #传入页码,获取该页帖子的代码    def getPage(self,pageNum):        try:            url=self.baseURL+self.seeLZ+'&pn'+str(pageNum)            request=urllib.request.Request(url)            response=urllib.request.urlopen(request)            # print(response.read().decode('utf-8'))            return response.read().decode('utf-8')        except urllib.error.URLError as e:            if hasattr(e,'reason'):                print(u'连接百度贴吧失败,错误原因',e.reason)                return None    def getTitle(self):        page=self.getPage(1)        pattern=re.compile('<h3 class="core_title_txt .*?>(.*?)</h3>',re.S)        #re.S整体匹配        result=re.search(pattern,page)        if result:            #print result.group(1)#测试输出            return result.group(1).strip()        else:            return None    #获取帖子一共有多少页    def getPageNum(self,page):        pattern=re.compile('<li class="l_reply_num" .*?</span>.*?<span.*?>(.*?)</span>',re.S)        result=re.search(pattern,page)        if result:            #print result.group(1)测试输出            return result.group(1).strip()        else:            return None    def getContent(self,page):        pattern=re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)        items=re.findall(pattern,page)        # for item in items:        #     print(item)        print (self.tool.replace(items[1]))baseURL='http://tieba.baidu.com/p/3138733512'bdtb=BDTB(baseURL,1)bdtb.getContent(bdtb.getPage(1))
Tool类看的不是特别懂,尤其是7位长空格是什么意思?做不到像原作者一样那么棒的匹配替换

部分运行结果如下:

50 惊喜新人王 迈卡威
上赛季数据
篮板 6.2  助攻 6.3  抢断 1.9 盖帽  0.6 失误 3.5 犯规  3  得分 16.7

新赛季第50位,我给上赛季的新人王迈卡威。 上赛季迈卡威在彻底重建的76人中迅速掌握了球队,一开始就三双搞定了热火赢得了万千眼球。后来也屡屡有经验的表现,新秀赛季就拿过三双的球员不多,迈卡威现在可以说在76人站稳了脚跟。
作为上赛季弱队的老大,迈卡威刷出了不错的数据,但我们静下心来看一看他,还是发现他有很多问题。

4)替换楼层

至于这个问题,我感觉直接提取楼层没什么必要呀,因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。

所以可以尝试下面的方法:

(1)每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。

(2)试着重新编写一个楼层,按照顺序,设置一个变量吗,每打印出一个变量结果加一,打印出这个变量当做楼层

修改getContent方法如下:

def getContent(self,page):    pattern=re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)    items=re.findall(pattern,page)    floor=1    for item in items:        print(floor,u'楼..................................................\n')        print (self.tool.replace(item))        floor+=1部分运行结果:9 楼..................................................开更今天的了!10 楼..................................................47 神塔的绝唱  保罗加索尔上赛季数据篮板9.7  助攻3.4  抢断0.5  盖帽1.5  失误2.4  犯规2.1  得分17.4 作为上赛季湖人唯一一个明星级别的球员(以效率20左右看,科比也没达到)加索尔真心是尽力了,但是将要年满35岁的他还有多少油呢?就不得而知了!先来看看优点,作为NBA技术流内线的代表,加索尔策应,篮板,背筐,面框都是非常全能的表现,在进攻端,依然是内线做中轴的非常优秀的选择,防守端利用身高臂长,也能在禁区内有一定的威慑力。而且技术流的内线一般下滑比较慢,上赛季加索尔场均才13分本赛季恢复到了上上赛季的接近17+10的水平,不知道加索尔下一站还会不会是湖人,但是他依然可以用他自己的表现,帮助一支需要内线的球队发挥自身的能力。然后来看看问题所在,加索尔的命中率只有很一般的48%了,哥们你是内线啊!加索尔三分能力本来就不佳但受困于身体对抗能力的下降,加索尔的进攻离篮下越来越远,现在居然连续几个赛季开始丢三分了,但是28%的命中率就凄惨了。只要还在看湖人比赛的人就知道,加索尔现在的问题是身体机能下降,身体很多时候无法支持技术动作的完成,篮下终结力已经从顶级内线变成了一般内线,中距离也是逐年下滑,只是和一般的四号位球员差不多!遇到身体强硬的内线防守人几乎没有出手几乎,而且横移速度也在下滑,加之35岁的年龄,以及球队地位的不稳固(估计在哪一队都很难是当家球星了)我只能把他排在这个位置,不知道明年是否还能在50大之内呢?11 楼..................................................

4.写入文件

最后便是写入文件的过程,过程很简单,就几句话的代码,主要是利用了以下两句:

file = open(“tb.txt”,”w”)

file.writelines(obj)

5.完善代码

现在对代码进行优化重构,在一些地方添加必要的打印信息,整理如下:
import urllib.requestfrom urllib.request import urlopenimport urllib.errorimport re#处理页面标签类class Tool:    #去除img标签,7位长空格    removeImg=re.compile('<img .*?>| {7}|')    #删除超链接标签    removeAddr=re.compile('<a.*?>|</a>')    #把换行的标签换为\n    replaceLine=re.compile('<tr>|<div>|</div>|</p>')    #将表格制表<td>替换为\t    replaceTD=re.compile('<td>')    #将段落开头换为\n加空两格    replacePara=re.compile('<p.*?>')    #将换行符或双换行符替换为\n    replaceBR=re.compile('<br><br>|<br>')    #将其余标签剔除    removeExtraTag=re.compile('<.*?>')    def replace(self,x):        x=re.sub(self.removeImg,'',x)        x=re.sub(self.removeAddr,'',x)        x=re.sub(self.replaceLine,'\n',x)        x=re.sub(self.replaceTD,'\t',x)        x=re.sub(self.replacePara,'\n  ',x)        x=re.sub(self.replaceBR,'\n',x)        x=re.sub(self.removeExtraTag,'',x)        #strip()将前后多余的内容删除        return x.strip()# 百度贴吧爬虫类class BDTB:#初始化,传入基地址,是否只看楼主的参数    def __init__(self,baseurl,seeLZ,floorTag):        #base链接地址        self.baseURL=baseurl        #是否只看楼主        self.seeLZ='?see_lz='+str(seeLZ)        #HTML标签剔除工具类对象        self.tool=Tool()        #全局file变量,文件写入操作对象        self.file=None        #楼层标号,初始为1        self.floor=1        #默认标题,如果没有成功获取到标题的话则会用这个标题        self.defaultTitle=u'百度贴吧'        #是否写入楼分隔符的标记        self.floorTag=floorTag    #传入页码,获取该页帖子的代码    def getPage(self,pageNum):        try:            url=self.baseURL+self.seeLZ+'&pn'+str(pageNum)            request=urllib.request.Request(url)            response=urllib.request.urlopen(request)            # print(response.read().decode('utf-8'))            return response.read().decode('utf-8')        except urllib.error.URLError as e:            if hasattr(e,'reason'):                print(u'连接百度贴吧失败,错误原因',e.reason)                return None    def getTitle(self,page):        pattern=re.compile('<h3 class="core_title_txt .*?>(.*?)</h3>',re.S)        #re.S整体匹配        result=re.search(pattern,page)        if result:            #print result.group(1)#测试输出            return result.group(1).strip()        else:            return None    #获取帖子一共有多少页    def getPageNum(self,page):        pattern=re.compile('<li class="l_reply_num" .*?</span>.*?<span.*?>(.*?)</span>',re.S)        result=re.search(pattern,page)        if result:            #print result.group(1)测试输出            return result.group(1).strip()        else:            return None    #获取每一楼层的内容    def getContent(self,page):        pattern=re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)        items=re.findall(pattern,page)        contents=[]        for item in items:            #将文本进行去除标签处理,同时在前后加入换行符            content='\n'+self.tool.replace(item)+'\n'            contents.append(content.encode('utf-8'))        return contents    def setFileTitle(self,title):        #如果标题不是为None,即成功获取到标题        if title is not None:            self.file=open(title+'.txt','wb')        else:            self.file=open(self.defaultTitle+'.txt','wb')    def writeData(self,contents):        #向文件写入每一楼的信息        for item in contents:            if self.floorTag=='1':                #楼之间的分隔符                floorLine='\n'+str(self.floor)+u'.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................\n'                self.file.write(floorLine.encode())            self.file.write(item)            self.floor+=1    def start(self):        indexPage=self.getPage(1)        pageNum=self.getPageNum(indexPage)        title=self.getTitle(indexPage)        self.setFileTitle(title)        if pageNum==None:            print('URL已失效,请重试')            return        try:            print('该帖子共有'+str(pageNum)+'页')            for i in range(1,int(pageNum)+1):                print('正在写入第'+str(i)+'页数据')                page=self.getPage(i)                contents=self.getContent(page)                self.writeData(contents)            #出现写入异常        except IOError as e:            print('写入异常,原因'+e.message)        finally:            print('写入任务完成')print(u'请输入帖子代号')baseURL='http://tieba.baidu.com/p/'+str(input(u'http://tieba.baidu.com/p/'))seeLZ=input('是否只获取楼主发言,是输入1,否输入0\n')floorTag=input('是否写入楼层信息,是输入1,否输入0\n')bdtb=BDTB(baseURL,seeLZ,floorTag)bdtb.start()部分运行结果显示: 
1.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................AJ群星镇楼。2.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................几下拉杆来着?我数不清楚。3.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................酋长?假动作不错,不过我在天上等着你。4.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................其实90年代无防守,别喷我,我先承认这是我PS的好吗?5.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................同样的进攻路线,你真不知道他到底会做什么。
部分参考:
静觅 » Python爬虫实战四之抓取淘宝MM照片 





0 0
原创粉丝点击