无登录百度贴吧的帖子
基本步骤:
①分析url ②获取页面 ③提取信息 ④文本处理 ⑤写入文件
备注:
基本框架跟笔记(一)很相似,只是多了很多细节需要处理,所以这里着重描述细节的处理。
第一步:分析url
(1)像段子、贴吧这种会存在多种页数的网站,各页的url的差别在于url参数部分的值,所以爬取其它页面时需要先去页面看看控制不同页面的url的参数。所以第一步需要看看所寻找的信息的url及其特征。
(2)从url可以知道百度贴吧通过seeLZ参数控制是否只显示楼主的帖子,和通过pn参数控制当前帖子的页面。同时这意味着贴吧是通过GET方式发送请求的。
第二步:获取页面
(1)在这里百度贴吧不需要我们再添加headers信息,同时,从上一步我们知道贴吧通过GET方法发送请求,因此我们只需要使用seeLZ以及pn参数来构建url,然后使用Request方法生成请求,最后使用urlopen方式发送请求获取包含页面源码的响应对象。
(2)这一步我们使用响应对象的read()方法获取页面源码然后编码后返回。
第三步:提取信息
(1)同样地,我们需要查看源码的布局,找出我们想要提取的信息所在的地方,然后使用正则表达式模块提取我们想要的信息。
(2)由于贴吧中帖子内容包含了很多的图片,超链接,空行以及其他无关紧要的字符,因此下一步我们需要对这些字符进行处理,返回干净的文本信息。
第四步:文本提取
(1)由于获取的信息包含了图片、超链接等无关信息,因此需要去除这些信息。为了代码可重用,创建一个工具类来将字符经过一系列处理后返回。
(2)工具类必须能去除图片、超链接、多余的空行和空格、多余的标签,有需要可以同时加入空行调整信息的布局。
第五步:写入文件
(1)为了能够把有用的信息存储起来,这里我们把所有信息写入文本文件。所以需要了解python的文件操作,如打开文件,设置读写文件权限,写入文件,关闭文件等。
最后:构建程序逻辑,实现程序
有些细节地方需要注意:
①中文字符的编码问题,显示到控制台时需要先解码成utf-8,然后编码成gbk,最后输出。
②正则表达式要写对。
③文件操作。
我的程序如下:
# _*_ coding: utf-8 _*_
import urllib
import urllib2
import re
class Tool:
'文本处理工具类'
removeImg = re.compile('<img.*?>| {7}|', re.S)
removeAddr = re.compile('<a.*?>|</a>', re.S)
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
replaceTD = re.compile('<td>')
replacePara = re.compile('<p.*?>', re.S)
replaceBR = re.compile('<br><br>|<br>')
removeExtraTag = re.compile('<.*?>', re.S)
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)
return x.strip()
class BDTB:
'百度贴吧爬虫'
def __init__(self, baseUrl, seeLZ, floorTag):
self.baseURL = baseUrl
self.seeLZ = '?see_lz='+str(seeLZ)
self.tool = Tool()
self.file = None
self.floor = 1
self.defaultTitle = u'百度贴吧'
self.floorTag = floorTag
def getPage(self, pageNum):
try:
url = self.baseURL + self.seeLZ + '&pn=' + str(pageNum)
req = urllib2.Request(url)
res = urllib2.urlopen(req)
# print res.read()
return res.read().decode('utf-8')
except urllib2.URLError, 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)
result = re.search(pattern, page)
if result:
# print result.group(1)
return result.group(1).strip()
else:
print u'获取贴吧标题失败!'
return None
def getPageNum(self, page):
pattern = re.compile('<li class="l_reply_num.*?red">(.*?)</span>', re.S)
result = re.search(pattern, page)
if result:
# print result.group(1)
return result.group(1).strip()
else:
print u'获取贴吧页数失败!'
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):
if title is not None:
self.file = open(title + '.txt', 'w+')
else:
self.file = open(self.defaultTitle + '.txt', 'w+')
def writeData(self, contents):
for item in contents:
if int(self.floorTag) == 1:
floorLine = '\n' + str(self.floor) + u'----------------------------------\n'
# print floorLine
self.file.write(floorLine)
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 "该帖子共有".decode('utf-8').encode('gbk') + str(pageNum) + "页".decode('utf-8').encode('gbk')
for i in range(1, int(pageNum)+1):
print "正在写入第".decode('utf-8').encode('gbk') + str(i) + "页数据......".decode('utf-8').encode('gbk')
page = self.getPage(i)
contents = self.getContent(page)
self.writeData(contents)
except IOError, e:
print "写入异常, 原因是:" + e.message
finally:
print "写入任务成功".decode('utf-8').encode('gbk')
self.file.close()
print u"请输入帖子代号"
baseUrl = 'http://tieba.baidu.com/p/'+str(raw_input('http://tieba.baidu.com/p/'))
seeLZ = raw_input(unicode('是否获取楼主发言,是输入1,否输入0:', 'utf-8').encode('gbk'))
floorTag = raw_input(unicode('是否写入楼层信息,是输入1,否输入0:', 'utf-8').encode('gbk'))
bdtb = BDTB(baseUrl, seeLZ, floorTag)
bdtb.start()