目标:
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.request from urllib.request import urlopen import urllib.error import 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 None baseURL='http://tieba.baidu.com/p/3138733512' bdtb=BDTB(baseURL,1) bdtb.getPage(1)部分运行结果:(可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为HTML代码)
<a data-field='{"un":"\u660e\u4e4b\u7ffc"}'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" οnclick="$