大家好,上次我們實驗了爬取了糗事百科的段子,那么這次我們來嘗試一下爬取百度貼吧的帖子。與上一篇不同的是,這次我們需要用到文件的相關操作。
## 本篇目標
1.對百度貼吧的任意帖子進行抓取
2.指定是否只抓取樓主發帖內容
3.將抓取到的內容分析并保存到文件
## 1.URL格式的確定
首先,我們先觀察一下百度貼吧的任意一個帖子。
比如:[http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1](http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1),這是一個關于NBA50大的盤點,分析一下這個地址。
~~~
http://??代表資源傳輸使用http協議
tieba.baidu.com 是百度的二級域名,指向百度貼吧的服務器。
/p/3138733512 是服務器某個資源,即這個帖子的地址定位符
see_lz和pn是該URL的兩個參數,分別代表了只看樓主和帖子頁碼,等于1表示該條件為真
~~~
所以我們可以把URL分為兩部分,一部分為基礎部分,一部分為參數部分。
例如,上面的URL我們劃分基礎部分是?[http://tieba.baidu.com/p/3138733512](http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1),參數部分是?[?see_lz=1&pn=1](http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1)
## 2.頁面的抓取
熟悉了URL的格式,那就讓我們用urllib2庫來試著抓取頁面內容吧。上一篇糗事百科我們最后改成了[面向對象]的編碼方式,這次我們直接嘗試一下,定義一個類名叫BDTB(百度貼吧),一個初始化方法,一個獲取頁面的方法。
其中,有些帖子我們想指定給程序是否要只看樓主,所以我們把只看樓主的參數初始化放在類的初始化上,即init方法。另外,獲取頁面的方法我們需要知道一個參數就是帖子頁碼,所以這個參數的指定我們放在該方法中。
綜上,我們初步構建出基礎代碼如下:
~~~
__author__ = 'CQC'
# -*- coding: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)
????????????print response.read()
????????????return response
????????except urllib2.URLError, e:
????????????if hasattr(e,"reason"):
????????????????print u"連接百度貼吧失敗,錯誤原因",e.reason
????????????????return None
baseURL = 'http://tieba.baidu.com/p/3138733512'
bdtb = BDTB(baseURL,1)
bdtb.getPage(1)
~~~
運行代碼,我們可以看到屏幕上打印出了這個帖子第一頁樓主發言的所有內容,形式為HTML代碼。
[](http://qiniu.cuiqingcai.com/wp-content/uploads/2015/02/20150219162232.jpg)
## 3.提取相關信息
### 1)提取帖子標題
首先,讓我們提取帖子的標題。
在瀏覽器中審查元素,或者按F12,查看頁面源代碼,我們找到標題所在的代碼段,可以發現這個標題的HTML代碼是
~~~
h1 title="純原創我心中的NBA2014-2015賽季現役50大" style="width: 396px">純原創我心中的NBA2014-2015賽季現役50大/h1>
~~~
所以我們想提取標簽中的內容,同時還要指定這個class確定唯一,因為h1標簽實在太多啦。
正則表達式如下
~~~
h1 class="core_title_txt.*?>(.*?)/h1>
~~~
所以,我們增加一個獲取頁面標題的方法
~~~
#獲取帖子標題
def getTitle(self):
????page = self.getPage(1)
????pattern = re.compile('(.*?)',re.S)
????result = re.search(pattern,page)
????if result:
????????#print result.group(1)??#測試輸出
????????return result.group(1).strip()
????else:
????????return None
~~~
### 2)提取帖子頁數
同樣地,帖子總頁數我們也可以通過分析頁面中的共?頁來獲取。所以我們的獲取總頁數的方法如下
~~~
#獲取帖子一共有多少頁
def getPageNum(self):
????page = self.getPage(1)
????pattern = re.compile('.*?(.*?)',re.S)
????result = re.search(pattern,page)
????if result:
????????#print result.group(1)??#測試輸出
????????return result.group(1).strip()
????else:
????????return None
~~~
### 3)提取正文內容
審查元素,我們可以看到百度貼吧每一層樓的主要內容都在標簽里面,所以我們可以寫如下的正則表達式
~~~
div id="post_content_.*?>(.*?)/div>
~~~
相應地,獲取頁面所有樓層數據的方法可以寫成如下方法
~~~
#獲取每一層樓的內容,傳入頁面內容
def getContent(self,page):
????pattern = re.compile('(.*?)',re.S)
????items = re.findall(pattern,page)
????for item in items:
????????print item
~~~
好,我們運行一下結果看一下
[](http://qiniu.cuiqingcai.com/wp-content/uploads/2015/02/20150219235120.jpg)
真是醉了,還有一大片換行符和圖片符,好口怕!既然這樣,我們就要對這些文本進行處理,把各種各樣復雜的標簽給它剔除掉,還原精華內容,把文本處理寫成一個方法也可以,不過為了實現更好的代碼架構和代碼重用,我們可以考慮把標簽等的處理寫作一個類。
那我們就叫它Tool(工具類吧),里面定義了一個方法,叫replace,是替換各種標簽的。在類中定義了幾個正則表達式,主要利用了re.sub方法對文本進行匹配后然后替換。具體的思路已經寫到注釋中,大家可以看一下這個類
~~~
import re
#處理頁面標簽類
class Tool:
????#去除img標簽,7位長空格
????removeImg = re.compile('| {7}|')
????#刪除超鏈接標簽
????removeAddr = re.compile('|')
????#把換行的標簽換為\n
????replaceLine = re.compile('|||')
????#將表格制表替換為\t
????replaceTD= re.compile('')
????#把段落開頭換為\n加空兩格
????replacePara = re.compile('')
????#將換行符或雙換行符替換為\n
????replaceBR = re.compile('|')
????#將其余標簽剔除
????removeExtraTag = 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.replacePara,"\n????",x)
????????x = re.sub(self.replaceBR,"\n",x)
????????x = re.sub(self.removeExtraTag,"",x)
????????#strip()將前后多余內容刪除
????????return x.strip()
~~~
在使用時,我們只需要初始化一下這個類,然后調用replace方法即可。
現在整體代碼是如下這樣子的,現在我的代碼是寫到這樣子的
~~~
import re
#處理頁面標簽類
class Tool:
????#去除img標簽,7位長空格
????removeImg = re.compile('| {7}|')
????#刪除超鏈接標簽
????removeAddr = re.compile('|')
????#把換行的標簽換為\n
????replaceLine = re.compile('|||')
????#將表格制表替換為\t
????replaceTD= re.compile('')
????#把段落開頭換為\n加空兩格
????replacePara = re.compile('')
????#將換行符或雙換行符替換為\n
????replaceBR = re.compile('|')
????#將其余標簽剔除
????removeExtraTag = 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.replacePara,"\n????",x)
????????x = re.sub(self.replaceBR,"\n",x)
????????x = re.sub(self.removeExtraTag,"",x)
????????#strip()將前后多余內容刪除
????????return x.strip()
~~~
我們嘗試一下,重新再看一下效果,這下經過處理之后應該就沒問題了,是不是感覺好酸爽!
[](http://qiniu.cuiqingcai.com/wp-content/uploads/2015/02/20150220000103.jpg)
### 4)替換樓層
至于這個問題,我感覺直接提取樓層沒什么必要呀,因為只看樓主的話,有些樓層的編號是間隔的,所以我們得到的樓層序號是不連續的,這樣我們保存下來也沒什么用。
所以可以嘗試下面的方法:
> 1.每打印輸出一段樓層,寫入一行橫線來間隔,或者換行符也好。
>
> 2.試著重新編一個樓層,按照順序,設置一個變量,每打印出一個結果變量加一,打印出這個變量當做樓層。
這里我們嘗試一下吧,看看效果怎樣
把getContent方法修改如下
~~~
#獲取每一層樓的內容,傳入頁面內容
def getContent(self,page):
????pattern = re.compile('(.*?)',re.S)
????items = re.findall(pattern,page)
????floor = 1
????for item in items:
????????print floor,u"樓------------------------------------------------------------------------------------------------------------------------------------\n"
????????print self.tool.replace(item)
????????floor += 1
~~~
運行一下看看效果
[](http://qiniu.cuiqingcai.com/wp-content/uploads/2015/02/20150220000947.jpg)
嘿嘿,效果還不錯吧,感覺真酸爽!接下來我們完善一下,然后寫入文件
## 4.寫入文件
最后便是寫入文件的過程,過程很簡單,就幾句話的代碼而已,主要是利用了以下兩句
> file = open(“tb.txt”,”w”)
>
> file.writelines(obj)
這里不再贅述,稍后直接貼上完善之后的代碼。
## 5.完善代碼
現在我們對代碼進行優化,重構,在一些地方添加必要的打印信息,整理如下
~~~
__author__ = 'CQC'
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
#處理頁面標簽類
class Tool:
????#去除img標簽,7位長空格
????removeImg = re.compile('| {7}|')
????#刪除超鏈接標簽
????removeAddr = re.compile('|')
????#把換行的標簽換為\n
????replaceLine = re.compile('|||')
????#將表格制表替換為\t
????replaceTD= re.compile('')
????#把段落開頭換為\n加空兩格
????replacePara = re.compile('')
????#將換行符或雙換行符替換為\n
????replaceBR = re.compile('|')
????#將其余標簽剔除
????removeExtraTag = 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.replacePara,"\n????",x)
????????x = re.sub(self.replaceBR,"\n",x)
????????x = re.sub(self.removeExtraTag,"",x)
????????#strip()將前后多余內容刪除
????????return x.strip()
#百度貼吧爬蟲類
class BDTB:
????#初始化,傳入基地址,是否只看樓主的參數
????def __init__(self,baseUrl,seeLZ,floorTag):
????????#base鏈接地址
????????self.baseURL = baseUrl
????????#是否只看樓主
????????self.seeLZ = '?see_lz='+str(seeLZ)
????????#HTML標簽剔除工具類對象
????????self.tool = Tool()
????????#全局file變量,文件寫入操作對象
????????self.file = None
????????#樓層標號,初始為1
????????self.floor = 1
????????#默認的標題,如果沒有成功獲取到標題的話則會用這個標題
????????self.defaultTitle = u"百度貼吧"
????????#是否寫入樓分隔符的標記
????????self.floorTag = floorTag
????#傳入頁碼,獲取該頁帖子的代碼
????def getPage(self,pageNum):
????????try:
????????????#構建URL
????????????url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
????????????request = urllib2.Request(url)
????????????response = urllib2.urlopen(request)
????????????#返回UTF-8格式編碼內容
????????????return response.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('(.*?)',re.S)
????????result = re.search(pattern,page)
????????if result:
????????????#如果存在,則返回標題
????????????return result.group(1).strip()
????????else:
????????????return None
????#獲取帖子一共有多少頁
????def getPageNum(self,page):
????????#獲取帖子頁數的正則表達式
????????pattern = re.compile('.*?(.*?)',re.S)
????????result = re.search(pattern,page)
????????if result:
????????????return result.group(1).strip()
????????else:
????????????return None
????#獲取每一層樓的內容,傳入頁面內容
????def getContent(self,page):
????????#匹配所有樓層的內容
????????pattern = re.compile('(.*?)',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):
????????#如果標題不是為None,即成功獲取到標題
????????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 self.floorTag == '1':
????????????????#樓之間的分隔符
????????????????floorLine = "\n" + str(self.floor) + u"-----------------------------------------------------------------------------------------\n"
????????????????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 "該帖子共有" + str(pageNum) + "頁"
????????????for i in range(1,int(pageNum)+1):
????????????????print "正在寫入第" + str(i) + "頁數據"
????????????????page = self.getPage(i)
????????????????contents = self.getContent(page)
????????????????self.writeData(contents)
????????#出現寫入異常
????????except IOError,e:
????????????print "寫入異常,原因" + e.message
????????finally:
????????????print "寫入任務完成"
print u"請輸入帖子代號"
baseURL = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))
seeLZ = raw_input("是否只獲取樓主發言,是輸入1,否輸入0\n")
floorTag = raw_input("是否寫入樓層信息,是輸入1,否輸入0\n")
bdtb = BDTB(baseURL,seeLZ,floorTag)
bdtb.start()
~~~
現在程序[演示]如下
[](http://qiniu.cuiqingcai.com/wp-content/uploads/2015/02/20150220012351.jpg)
完成之后,可以查看一下當前目錄下多了一個以該帖子[命名]的txt文件,內容便是帖子的所有數據。
抓貼吧,就是這么簡單和任性!
- Python爬蟲入門
- (1):綜述
- (2):爬蟲基礎了解
- (3):Urllib庫的基本使用
- (4):Urllib庫的高級用法
- (5):URLError異常處理
- (6):Cookie的使用
- (7):正則表達式
- (8):Beautiful Soup的用法
- Python爬蟲進階
- Python爬蟲進階一之爬蟲框架概述
- Python爬蟲進階二之PySpider框架安裝配置
- Python爬蟲進階三之Scrapy框架安裝配置
- Python爬蟲進階四之PySpider的用法
- Python爬蟲實戰
- Python爬蟲實戰(1):爬取糗事百科段子
- Python爬蟲實戰(2):百度貼吧帖子
- Python爬蟲實戰(3):計算大學本學期績點
- Python爬蟲實戰(4):模擬登錄淘寶并獲取所有訂單
- Python爬蟲實戰(5):抓取淘寶MM照片
- Python爬蟲實戰(6):抓取愛問知識人問題并保存至數據庫
- Python爬蟲利器
- Python爬蟲文章
- Python爬蟲(一)--豆瓣電影抓站小結(成功抓取Top100電影)
- Python爬蟲(二)--Coursera抓站小結
- Python爬蟲(三)-Socket網絡編程
- Python爬蟲(四)--多線程
- Python爬蟲(五)--多線程續(Queue)
- Python爬蟲(六)--Scrapy框架學習
- Python爬蟲(七)--Scrapy模擬登錄
- Python筆記
- python 知乎爬蟲
- Python 爬蟲之——模擬登陸
- python的urllib2 模塊解析
- 蜘蛛項目要用的數據庫操作
- gzip 壓縮格式的網站處理方法
- 通過瀏覽器的調試得出 headers轉換成字典
- Python登錄到weibo.com
- weibo v1.4.5 支持 RSA協議(模擬微博登錄)
- 搭建Scrapy爬蟲的開發環境
- 知乎精華回答的非專業大數據統計
- 基于PySpider的weibo.cn爬蟲
- Python-實現批量抓取妹子圖片
- Python庫
- python數據庫-mysql
- 圖片處理庫PIL
- Mac OS X安裝 Scrapy、PIL、BeautifulSoup
- 正則表達式 re模塊
- 郵件正則
- 正則匹配,但過濾某些字符串
- dict使用方法和快捷查找
- httplib2 庫的使用