Python爬蟲實踐筆記(二)

無登錄百度貼吧的帖子

基本步驟:
①分析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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章