最近看到一篇这样的帖子,学习一下。
目的:抓取百度贴吧某一篇帖子的内容(包括帖子名称,页数,及内容),并对内容进行筛选处理。
一、URL格式的确定:
我们观察一下百度贴吧的任意一个帖子:https://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,分析一下这个地址:
1)http:// 代表资源传输使用http协议
2)tieba.baidu.com是百度的二级域名,指向百度贴吧的服务器
3)/p/3138733512是服务器摸个资源,即这个帖子的地址定位符
4)see_lz和pn是该URL的两个参数,分别代表了只看楼主(1代表为真,0为假)和帖子页码。
我们把URL分为基础部分和参数部分,这个URL的基础部分为https://tieba.baidu.com/p/3138733512,参数部分为?see_lz=1&pn=1
二、抓取网页的html
# -*- coding: gbk -*- #我用utf-8显示不出中文,不知道怎么回事
import urllib
import urllib2
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 = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read() #返回页面的html
except urllib2.URLError,e:
#当urlopen不能够处理一个request的时候,会产生一个URLError,URLError通常在没有网络连接,或者服务器不存在的情况下产生,这种情况下,异常通常带有“reason”属性,它是一个tuple,包含了一个错误号和一个错误信息。
if hasattr(e,"reason"):
# hasattr(object,name)判断一个对象是否有name属性或name方法,返回BOOL值;有name特性返回True,否则返回False
print "链接错误,原因" ,e.reason
return None
baseURL = "http://tieba.baidu.com/p/3138733512"
bdtb = BDTB(baseURL,1)
三、提取相关信息
1)提取帖子标题:
def getTitle(self):
page = self.getPage(1)
reg = '<title>(.*?)</title>'
pattern = re.compile(reg)
result = re.search(pattern,page)
if result:
print result.group(1)
#group(0)和group()就是匹配正则表达式整体结果,group(1)列出第一个括号匹配部分,group(2)列出第二个括号匹配部分,group(3)列出第三个括号匹配部分,注意这里如果不添加group()就不会输出结果,所有我猜想应该是re.search()所必须的。
else:
print "error"
2)提取帖子页数:
def getPageNum(self):
page = self.getPage(1)
reg = r'<li class="l_reply_num" style="margin-left:8px" >'
reg += '<span class="red" style="margin-right:3px">(.*)'
reg += '</span>.*?<span class="red">(.*?)</span>(.*?)</li>'
pattern = re.compile(reg)
result = re.search(pattern,page)
if result:
print result.group(2)
else:
print None
3)提取正文内容:
def getSentance(self):
page= self.getPage(1)
reg = r'<div id="post_content_.*?" class="d_post_content j_d_post_content ">'
reg += '[\s]*(.*?)</div>'
pattern = re.compile(reg)
items = re.findall(pattern,page)
for item in items:
print item
这个时候运行程序
baseURL = "http://tieba.baidu.com/p/3138733512"
bdtb = BDTB(baseURL,1)
bdtb.getSentance()
就会发现输出来一大锥内容,还包含图片代码什么乱七八糟的,所以下面就要进行这节课最有意思的一部分,就是剔除杂乱信息——处理页面标签。
4)处理页面标签:**********************************
我们把标签的处理写到单独一个类里边,这样更方便以后的处理;在类中定义了一个方便,用来替换要处理的标签,我们用re.sub()方法来替换要处理的标签。
class Tool:
removeImg = re.compile("<img.*?>") #去除img标签,也就是图片部分
removeAddr = re.compile("<a.*?>|</a>") #删除超链接标签
#这里说一下,在html中,超链接的标签是:<a href="URL">超链</a>
replaceLine = re.compile("<tr>|<div>|</div>|</p>") #把换行的标签换为\n
replaceTD = re.compile("<td>") #把表格制表<td>替换为\t
replaceBR = re.compile("<br><br>|<br>") #把换行符或双换行符替换为\n。<br>是分行
replacePara = re.compile('<p.*?>') # 把段落开头替换为\n加空两格,这里好像用不上
removeExist = re.compile("<.*?>") #将其余标签剔除
def replace(self,x):
x = re.sub(self.removeImg,"",x) #用re.sub()方法替换上面正则表达式对应的段落中的html。
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\n",x)
x = re.sub(self.replaceTD,"\t",x)
x = re.sub(self.replaceBR,"\n",x)
x = re.sub(self.replacePara,"\n ",x)
x = re.sub(self.removeExist,"",x)
# re.sub(pattern,replacement,string),pattern是正则表达式中的模式字符串,replacement是要被替换进去的字符串,string是要被替换一部分的整个字符串。
return x.strip() #strip()将字符串前后空格删除
说明一下,这里是给出了几种替换方法,根据实际需要选择相应的替换方法,当然自己也可以随意选择其他方法替换。
这样的话,整体代码就可写为:
class Tool:
removeImg = re.compile("<img.*?>")
removeAddr = re.compile("<a.*?>|</a>") #正则表达式中的竖杠(|)是或者的意思。
replaceLine = re.compile("<tr>|<div>|</div>|</p>")
replaceTD = re.compile("<td>")
replaceBR = re.compile("<br><br>|<br>")
replacePara = re.compile('<p.*?>')
removeExist = 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.replaceBR,"\n",x)
x = re.sub(self.replacePara,"\n ",x)
x = re.sub(self.removeExist,"",x)
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 = urllib2.Request(url)
response = urllib2.urlopen(request)
#print response.read()
return response.read()
except urllib2.URLError,e:
if hasattr(e,"reason"):
print "链接错误,原因" ,e.reason
return None
def getTitle(self):
page = self.getPage(1)
reg = '<title>(.*?)</title>'
pattern = re.compile(reg)
result = re.search(pattern,page)
if result:
print result.group(1)
else:
print "error"
def getPageNum(self):
page = self.getPage(1)
reg = r'<li class="l_reply_num" style="margin-left:8px" >'
reg += '<span class="red" style="margin-right:3px">(.*)'
reg += '</span>.*?<span class="red">(.*?)</span>(.*?)</li>'
pattern = re.compile(reg)
result = re.search(pattern,page)
if result:
print result.group(2)
else:
print None
def getSentance(self):
page= self.getPage(1)
reg = r'<div id="post_content_.*?" class="d_post_content j_d_post_content ">'
reg += '[\s]*(.*?)</div>'
pattern = re.compile(reg)
items = re.findall(pattern,page)
for item in items:
print self.tool.replace(item)
5)手动添加楼层并存入txt文件:
我们把函数getSentance()改一下:
def getSentance(self):
page= self.getPage(1)
reg = r'<div id="post_content_.*?" class="d_post_content j_d_post_content ">'
reg += '[\s]*(.*?)</div>'
pattern = re.compile(reg)
items = re.findall(pattern,page)
floor = 1
contents = []
fileName = open("nba.txt",'w')
for item in items:
floorLine = "\n\n" + str(floor) + "----------------------------\n"
content = self.tool.replace(item)
contents.append(floorLine)
contents.append(content)
floor += 1
for content in contents:
fileName.write(content)
baseURL = "http://tieba.baidu.com/p/3138733512"
bdtb = BDTB(baseURL,1)
bdtb.getSentance()
这样就可以运行程序了。
其实这个程序还存在一些问题:
1)写入文件应该再单独编写一个函数
2)文件的标题应该把getTitle()的结果加上,但是因为getTitle()的结果是汉语,网上给了一些方法,我的python2.7版本试了几次都没成功,所以暂时存疑。