背景
?最近開始學習python爬蟲,剛好看到網(wǎng)上的爬蟲實例玫恳,轉自靜覓爬蟲 焦影。轉載是為了記錄學習過程中遇到的問題,并促使自己在學習過程中養(yǎng)成一個文檔記錄的習慣女坑。
實驗目標
1填具、對百度貼吧的任意帖子進行抓取
2、指定只抓取樓主的發(fā)帖內(nèi)容
3匆骗、將抓取的內(nèi)容保存到文檔中
1.URL格式的確定
?首先我們關注任意一個百度貼吧的帖子比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1 這是一個關于14-15賽季現(xiàn)役50大球員分享,分析下這個地址誉简。
http:// 代表資源使用的http協(xié)議 tieba.baidu.com 是百度的二級域名碉就,指向百度貼吧的服務器 /p/3138733512 是服務器某個資源,即這個帖子的地址定位符 see_lz=1&pn=1是該URL的兩個參數(shù)闷串,分別代表了只看樓主和帖子頁碼瓮钥,等于1表示該條件為真
所以我們可以把URL分為兩部分,一部分為基礎部分烹吵,一部分為參數(shù)部分碉熄。
例如,上面的URL我們劃分基礎部分是 http://tieba.baidu.com/p/3138733512 肋拔, 參數(shù)部分是 ?see_lz=1&pn=1
2.頁面的抓取
?熟悉了URL的格式锈津,那就讓我們使用urllib2庫來抓取頁面的內(nèi)容,這次會直接使用面向對象的編程方法凉蜂,定義一個叫做BDTB的類琼梆,一個初始化方法,一個獲取頁面的方法窿吩。
其中有些我們想指定給程序是否要只看樓主茎杂,所以我們把只看樓主的參數(shù)初始化放在類的初始化上,即init方法纫雁。另外獲取頁面的方法我們需要知道一個參數(shù)就是帖子頁碼煌往,所以這個參數(shù)的指定我們放在該方法中。
綜上所述轧邪,我們初步構建出基礎代碼如下:
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
#百度貼吧爬蟲類class BDTB:
class BDTB:
#初始化刽脖,傳入基地址羞海,是否只看樓主的參數(shù)
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 NonebaseURL = 'http://tieba.baidu.com/p/3138733512'bdtb = BDTB(baseURL,1)bdtb.getPage(1)
?運行代碼曾棕,我們可以在屏幕中看到樓主第一頁發(fā)言的所有內(nèi)容扣猫,其內(nèi)容為html形式。
3.提取相關信息
1)提取帖子標題
?首先讓我們抓取帖子的標題翘地,采用chrome瀏覽器的開發(fā)者工具申尤,發(fā)現(xiàn)關于標題的那段html代碼如下:
<h3 class="core_title_txt pull-left text-overflow " title="純原創(chuàng)我心中的NBA2014-2015賽季現(xiàn)役50大" style="width: 416px">純原創(chuàng)我心中的NBA2014-2015賽季現(xiàn)役50大</h3>
?所以我們想要抓取<h3>
標簽中的內(nèi)容,需要指定class確定唯一衙耕,因為 <h3>
標簽實在是太多了昧穿。因此我們這邊是匹配到標題的正則表達式可以寫成:
<h3 class="core_title_txt.*?>(.*?)</h3>
?所以我們這邊添加一個增加獲取帖子標題的方法
def getTitle(self,page):
pattern = re.compile('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None
2)獲取帖子總數(shù)
?同樣我們這邊獲得帖子的總頁數(shù)可以通過分析通共?頁來獲得因此獲得頁數(shù)的方法橙喘。
def getPageNum(self,page):
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None
3)獲取正文的內(nèi)容
?審查元素时鸵,我們發(fā)現(xiàn)每個帖子的主要內(nèi)容在<div id="post_content_xxxxx><div\>
所以我們給出的正則表達式是
<div id="post_content_.*?>(.*?)</div>
?獲取帖子內(nèi)容的方法
def getContent(self,page):
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
contents = []
for item in items:
print item
?先打印一下內(nèi)容的顯示情況。
?帖子內(nèi)容中有很多東西是我們并不需要的厅瞎,所以需要一個工具直接替換或者刪除掉我們不需要的內(nèi)容饰潜。這里我們直接用原帖子的替換類。
import reclass Tool:
removeImg = re.compile('<img.*?>| {7}|')
removeAddr = re.compile('<a.*?>|</a>')
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
replaceTD = re.compile('<td>')
replacePara = re.compile('<p.*?>')
replaceBR = re.compile('<br><br>|<br>')
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)
return x
?這個工具類可以在同目錄下新建一個工具腳本和簸,然后從這個腳本中導入定義的方法彭雾。所以我們的代碼變成了如下形式:
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
from Tool import *
#百度貼吧爬蟲類
class BDTB:
#初始化,傳入基地址锁保,是否只看樓主的參數(shù)
def __init__(self,baseUrl,seeLZ):
self.baseURL = baseUrl
self.seeLZ = '?see_lz='+str(seeLZ)
self.tool = Tool()
#傳入頁碼薯酝,獲取該頁帖子的代碼
def getPage(self,pageNum):
try:
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode('utf-8')
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"連接百度貼吧失敗,錯誤原因",e.reason
return None
#獲取帖子標題
def getTitle(self):
page = self.getPage(1)
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:
return None
#獲取帖子一共有多少頁
def getPageNum(self):
page = self.getPage(1)
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
#print result.group(1) #測試輸出
return result.group(1).strip()
else:
return None
#獲取每一層樓的內(nèi)容,傳入頁面內(nèi)容
def getContent(self,page):
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
#for item in items:
# print item
print self.tool.replace(items[1])
baseURL = 'http://tieba.baidu.com/p/3138733512'
bdtb = BDTB(baseURL,1)
bdtb.getContent(bdtb.getPage(1))
重新運行查看 以及是我們想要的形式了:
4)寫入文件
?主要是下面的兩句
file = open(“tb.txt”,”w”)
file.writelines(obj)
5)完善代碼
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
from Tool import *
class BDTB:
def __init__(self,baseUrl,seeLZ,floorTag):
self.baseURL = baseUrl
self.see_LZ = '?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.see_LZ+'&pn='+str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
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('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None
def getPageNum(self,page):
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
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):
print contents
print type(contents)
for item in contents:
if self.floorTag == '1':
floorLine = "\n" + str(self.floor) + u"********************************************************" \
u"********************************\n"
self.file.write(floorLine)
self.file.write(item)
print item
print "*"*10
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) + "頁數(shù)據(jù)"
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/'))
baseURL = 'http://tieba.baidu.com/p/3138733512'
# seeLZ = raw_input("是否只獲取樓主發(fā)言,是輸入1浩村,否輸入0\n")
seeLZ = 1
floorTag = raw_input("是否寫入樓層信息做葵,是輸入1,否輸入0\n")
bdtb = BDTB(baseURL,seeLZ,floorTag)
bdtb.start()
?另外再學習借鑒的過程中遇到了一些問題穴亏,比如遇到TypeError:excepted string or buffer (因為圖片上傳暫時未解決),蛋疼我也忘了遇見啥問題了蜂挪。。嗓化。文檔編寫要及時棠涮,實驗過程中要有循序漸進的過程疟赊。