無登錄百度貼吧的帖子
基本步驟:
①分析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()