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