1. 程式人生 > >python爬蟲(13)爬取百度貼吧帖子

python爬蟲(13)爬取百度貼吧帖子

爬取百度貼吧帖子

一開始只是在網上看到別人寫的爬取帖子的文章,然後自己就忍不住手癢自己鍛鍊一下, 然後照著別人的寫完,發現不太過癮, 畢竟只是獲取單個帖子的內容,感覺內容稍顯單薄,然後自己重新做了修改,把它變成重寫成了一個比較強大的爬蟲

精簡版本

簡介 

通過帖子的地址,獲取樓層的內容,並將評論內容存貯到本地。

先來看效果圖:

			***************************************** 
			**    Welcome to Spider of baidutieba  ** 
			**      Created on 2017-04-25          ** 
			**      @author: Jimy _Fengqi          ** 
			*****************************************
	
請輸入帖子代號
http://tieba.baidu.com/p/5081359666                                              
是否只獲取樓主發言,是輸入1,否輸入0
0
是否寫入樓層資訊,是輸入1,否輸入0
1
開啟的網頁是:http://tieba.baidu.com/p/5081359666?see_lz=0&pn=1
1
《人民的名義》搞笑穿幫,亦可你手機拿反了
《人民的名義》搞笑穿幫,亦可你手機拿反了
本帖子共有1頁
開啟的網頁是:http://tieba.baidu.com/p/5081359666?see_lz=0&pn=1
            相信最近大夥都陷入了《人民的名義》追劇浪潮中,該劇講述了一位吃炸醬麵的國家部委專案處長被人舉報受賄千萬,當這位腐敗分子的面具被最終撕開的同時,與之案件牽連甚緊的京州市副市長丁義珍在一位神祕人物的幫助下流亡海外。案件線索終定位於由京州光明湖專案引發的一家國企大風服裝廠的股權爭奪,於是展開了一系列撲朔迷離智鬥劇情的故事!<br><img class="BDE_Image" pic_type="1" width="430" height="240" src="https://imgsa.baidu.com/forum/w%3D580/sign=b0a6294fe1f81a4c2632ecc1e72b6029/26fcffd98d1001e985948f34b20e7bec54e79716.jpg" ><br>穿幫鏡頭一:<br>《人民的名義》講述的故事是在2015年的背景之下發生的,但是胸懷宇宙的孫連成孫區長接到了一個大風廠拆遷的電話,當他拿起手機接電話時,可以看到桌子上的座機顯示的時間是2016年的。<br><img class="BDE_Image" pic_type="1" width="560" height="265" src="https://imgsa.baidu.com/forum/w%3D580/sign=3ad145f3104c510faec4e21250582528/f9e90e1001e939019caa160571ec54e736d19616.jpg" ><br><img class="BDE_Image" pic_type="1" width="560" height="269" src="https://imgsa.baidu.com/forum/w%3D580/sign=556db4475b0fd9f9a0175561152cd42b/082082e93901213f069bd5e75ee736d12f2e9516.jpg" ><br>穿幫鏡頭二:<br>陳海給侯亮平打電話說自己這邊離真相不遠了,等他到了北京一切就好辦了,可是沒想到在邊打電話邊步行的過程中就出了車禍。電視劇裡這一鏡頭很快就閃過了,可是當小流君放慢畫面來看時就發現穿幫了。注意看這個車禍的過程,車子上是沒有人的。<br><img class="BDE_Image" pic_type="1" width="560" height="266" src="https://imgsa.baidu.com/forum/w%3D580/sign=a5d9ff04d3f9d72a17641015e42b282a/84d9ba01213fb80ec479faec3cd12f2eb9389416.jpg" ><br><img class="BDE_Image" pic_type="1" width="560" height="287" src="https://imgsa.baidu.com/forum/w%3D580/sign=919a77f2dd2a60595210e1121835342d/bc31a23fb80e7bec947298da252eb9389b506b16.jpg" ><br>穿幫鏡頭三:<br>侯亮平帶著一群人在高速路口把李達康書記的專車攔下來,並當著達康書記的面把歐陽菁抓走了,於是達康書記送了侯亮平一個霸氣側漏的眼神。之後回來的路上陸亦可趕緊給領導打電話解釋,這時候她手機是拿對的。沒一會檢察長就往她手機打了回來,這時候陸亦可手機就拿反了,可能聊得正嗨的她也沒發現自己手機拿反了。<br><img class="BDE_Image" pic_type="1" width="560" height="270" src="https://imgsa.baidu.com/forum/w%3D580/sign=186c79216a59252da3171d0c049a032c/a40f3b0e7bec54e7f7448125b3389b504fc26a16.jpg" ><br><img class="BDE_Image" pic_type="1" width="560" height="266" src="https://imgsa.baidu.com/forum/w%3D580/sign=15bfce522f2dd42a5f0901a3333a5b2f/3d3ef8ec54e736d1edbb173391504fc2d5626916.jpg" ><br>豆瓣影評:<br>截至目前,《人民的名義》在還未全部上映的情況下,該劇已經有103168人次參與評分。其中給予1星評分的比例佔3.5%,2星比例佔2.3%,3星比例佔10.4%,4星比例佔31.1%,5星比例最高佔了52.7%。綜合指數4星半,綜合得分8.5分。<br><img class="BDE_Image" pic_type="1" width="560" height="294" src="https://imgsa.baidu.com/forum/w%3D580/sign=a3cc8b26de2a283443a636036bb4c92e/fedcd7e736d12f2e7aad355b45c2d56285356816.jpg" >
正在寫入第1頁資料
寫入任務完成

思路分析:

百度貼吧的帖子地址格式是有規律的:  https://tieba.baidu.com/p/5081359666

即帖子前面的地址都是‘ https://tieba.baidu.com/p/’    

然後, 如果只看樓主發言 , 地址後面會加上 ‘? see_lz=1’,    否則就是 ‘see_lz=0’

其次,帖子如果有好多頁, 那麼地址後面會 加上  ‘&pn=1’   即‘=’ 後面的就是頁面數目。

這樣我們的思路就很清晰了:

1.首先決定是否只獲取樓主的資訊。

2.根據帖子的代號,獲取主頁內容,根據主頁內容,獲取本貼的頁碼數目

3.獲取每一頁的評論內容

4.將獲取到的資訊存貯到本地

核心問題

1. 獲取頁面內容:

	def getPage(self,pageNum):
		try:
			url=self.baseUrl+self.seelz+'&pn='+str(pageNum)
			print "開啟的網頁是:"+url
			request=urllib2.Request(url)
			response=urllib2.urlopen(request)
			return response.read().decode('utf-8')
		except urllib2.URLError,e:
			if hasattr(e,'reason'):
				print "連線百度貼吧失敗,錯誤原因",e.reason
				return None

2.獲取帖子的頁碼:
#獲取一個帖子總共有多少頁
	def getPageNum(self,page):
		pattern=re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
		result=re.search(pattern,page)
		print result.group(1)
		if result:
			return result.group(1).strip()
		else:
			return ‘1’#找不到至少也是一頁
3.匹配樓層內容:
	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
			content="\n"+self.tool.replace(item)+"\n"
			contents.append(content.encode('utf-8'))
4.寫入資料到本地
	def writeData(self,contents):
		for item in contents:
			if self.floorTag=='1':
				floorLine ='\n'+str(self.floor)+"-------------------------------------------------------------\n"
				self.file.write(floorLine)
			self.file.write(item)
			self.floor +=1
整體程式碼如下:
#!/usr/bin/python
#coding:utf-8

import urllib
import urllib2
import re
import time
import sys

import os


reload(sys)
sys.setdefaultencoding('utf-8')

#處理頁面標籤類
class Tool:
	#去除img標籤,7位長空格
	removeImg = re.compile('<img.*?>| {7}|')
	#刪除超連結標籤
	removeAddr = re.compile('<a.*?>|</a>')
	#把換行的標籤換為\n
	replaceLine = re.compile('<tr>|<div>|</div>|</p>')
	#將表格製表<td>替換為\t
	replaceTD= re.compile('<td>')
	#把段落開頭換為\n加空兩格
	replacePara = re.compile('<p.*?>')
	#將換行符或雙換行符替換為\n
	replaceBR = re.compile('<br><br>|<br>')
	#將其餘標籤剔除
	removeExtraTag = re.compile('<.*?>')
	#刪除正斜線和反斜線
	removeLine1=re.compile(r'/')
	removeLine2=re.compile(r'\\')
	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()
	def replaceSlash(self,x):
		x=re.sub(self.removeLine1,"",x)
		x=re.sub(self.removeLine2,"",x)
		return x.strip()
 

	
class BaiDuTieBa:
	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="百度貼吧"
		self.floorTag=floorTag
		
	def getPage(self,pageNum):
		try:
			url=self.baseUrl+self.seelz+'&pn='+str(pageNum)
			print "開啟的網頁是:"+url
			request=urllib2.Request(url)
			response=urllib2.urlopen(request)
			return response.read().decode('utf-8')
		except urllib2.URLError,e:
			if hasattr(e,'reason'):
				print "連線百度貼吧失敗,錯誤原因",e.reason
				return None
	
	#獲取一個帖子總共有多少頁
	def getPageNum(self,page):
		pattern=re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
		result=re.search(pattern,page)
		print result.group(1)
		if result:
			return result.group(1).strip()
		else:
			return None
		
	def getTitle(self,page):
		pattern=re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
		result=re.search(pattern,page)
		title=""
		if result:
			title = result.group(1).strip()
		else:
			pattern=re.compile('<h2 class="core_title_txt.*?>(.*?)</h2>',re.S)
			result=re.search(pattern,page)
			if result:
				title =  result.group(1).strip()
			else:
				pattern=re.compile('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S)
				result=re.search(pattern,page)
				if result:
					title =  result.group(1).strip()
				else:
					return None
		print title
		return title
	
	def setFileTilte(self,title):
		if title is not None:
			title=self.tool.replaceSlash(title)
			self.file=open(title+".txt","w+")
			self.file.write(self.baseUrl)
		else:
			self.file=open(self.defaultTitle+".txt","w+")
			self.file.write(self.baseUrl)
	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
			content="\n"+self.tool.replace(item)+"\n"
			contents.append(content.encode('utf-8'))
		
		return contents
	
	def writeData(self,contents):
		for item in contents:
			if self.floorTag=='1':
				floorLine ='\n'+str(self.floor)+"-------------------------------------------------------------\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)
		print title
		self.setFileTilte(title)
		if pageNum == None:
			print "URL  已經失效,請重試"
		
		try:
			print "本帖子共有"+str(pageNum)+"頁"
			for i in range(1,int(pageNum)+1):
				page =self.getPage(i)
				contents=self.getContent(page)
				print "正在寫入第" + str(i) + "頁資料"
				self.writeData(contents)
		#出現寫入異常
		except IOError,e:
			print "寫入異常,原因" + e.message
		finally:
			print "寫入任務完成"	
if __name__ == '__main__':
	print '''
			***************************************** 
			**    Welcome to Spider of baidutieba  ** 
			**      Created on 2017-04-25          ** 
			**      @author: Jimy _Fengqi          ** 
			*****************************************
	'''
	print "請輸入帖子代號"
	basURL="http://tieba.baidu.com/p/"+str(raw_input('http://tieba.baidu.com/p/'))
	seelz = raw_input("是否只獲取樓主發言,是輸入1,否輸入0\n")
	floorTag = raw_input("是否寫入樓層資訊,是輸入1,否輸入0\n")
	bdtb=BaiDuTieBa(basURL,seelz,floorTag)
	bdtb.start()

完整版本

執行效果:

最終版本:

#!/usr/bin/python
#coding:utf-8

import urllib2
import re
import json
import urllib
import time
import random
import os
import threading
import HTMLParser
from bs4 import BeautifulSoup as BS

import sys
reload(sys)
sys.setdefaultencoding('utf-8')



#處理頁面標籤類
class Tool:
	#去除img標籤,7位長空格
	removeImg = re.compile('<img.*?>| {7}|')
	#刪除超連結標籤
	removeAddr = re.compile('<a.*?>|</a>')
	#把換行的標籤換為\n
	replaceLine = re.compile('<tr>|<div>|</div>|</p>')
	#將表格製表<td>替換為\t
	replaceTD= re.compile('<td>')
	#把段落開頭換為\n加空兩格
	replacePara = re.compile('<p.*?>')
	#將換行符或雙換行符替換為\n
	replaceBR = re.compile('<br><br>|<br>')
	#將其餘標籤剔除
	removeExtraTag = re.compile('<.*?>')
	#刪除正斜線和反斜線
	removeLine1=re.compile(r'/')
	removeLine2=re.compile(r'\\')
	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()
	def replaceSlash(self,x):
		x=re.sub(self.removeLine1,"",x)
		x=re.sub(self.removeLine2,"",x)
		return x.strip()

class GetBaiduTieba:
	def __init__(self,keyword):
		self.keyword=keyword
		self.tiebaUrl='http://tieba.baidu.com/f?kw=%s' % self.keyword
		self.tool=Tool()#初始化工具類
		
		self.info_list=[]#存貯帖子地址,標題,回覆數,建立人,建立時間的全域性變數
		self.tiezi_info_list=[]#存貯每一個帖子的回覆情況,包括樓層,回覆人,時間,內容,本樓層的評論數
		self.create_dir(self.keyword)
		self.tiezi_path=self.keyword+'/'+self.keyword+'.txt'
		self.tiezi_file=open(self.tiezi_path,'w')
	
	
	#建立資料夾
	def create_dir(self,path):
		if not os.path.exists(path):  
			os.makedirs(path) 
	
	#獲取頁面內容
	def get_html(self,url):
		self.my_log(1,u'start crawl %s ...' % url)
		headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.2; rv:16.0) Gecko/20100101 Firefox/16.0'}#設定header
		req = urllib2.Request(url=url,headers=headers)
		try:
			html = urllib2.urlopen(req).read().decode('utf-8')
			html=HTMLParser.HTMLParser().unescape(html)#處理網頁內容, 可以將一些html型別的符號如" 轉換回雙引號
			#html = html.decode('utf-8','replace').encode(sys.getfilesystemencoding())#轉碼:避免輸出出現亂碼
		except urllib2.HTTPError,e:
			self.my_log(2,u"連線百度貼吧失敗,錯誤原因:%s " % e.code)
			return None
		except urllib2.URLError,e:
			if hasattr(e,'reason'):
				self.my_log(2,u"連線百度貼吧失敗,錯誤原因:%s " % e.reason)
				return None
		return html
	
	#自定義log 列印函式, 以數字定義log 級別
	def my_log(self,log_leavel,msg): 
		#0:不列印 		1:main		2:error		3:warning		
		log= {	0:lambda:no_log(msg),
				1:lambda:main_log(msg), 
				2:lambda:error_log(msg), 
				3:lambda:warning_log(msg)} 
		def no_log(msg):
			pass
		def main_log(msg):
			print u'main: %s: %s' % (time.strftime('%Y-%m-%d_%H-%M-%S'), msg) 
		def error_log(msg):
			print u'error: %s: %s' % (time.strftime('%Y-%m-%d_%H-%M-%S'), msg) 
		def warning_log(msg):	
			print u'warning:  %s: %s' % (time.strftime('%Y-%m-%d_%H-%M-%S'), msg)
		return log[log_leavel]()
	
	#獲取本貼吧總共有多少帖子
	def get_Total_Num(self,html):
		try:
			pattern=re.compile('<div id="frs_list_pager".*?class="next pagination-item ".*?href=(.*?)class=.*?</div>',re.S)#直接使用正則暴力匹配
			result=re.search(pattern,html)#使用search方法找到內容, 因為只有一個,不需要使用find_all的方法
			patternNum=re.compile('pn=(\d+)')#對於獲取到的資料進行重新查詢,找到我們要的數字
			Num=re.search(patternNum,result.group(1))#只尋找一個元素,因此這裡引數為1
			PageNum=int(Num.group(1))
			#self.my_log(1,TotalNum)
		except Exception,e:
			self.my_log(3,u'貼吧的帖子數量沒有找到, 錯誤原因:%s' % e)
			return None
		finally:
			self.my_log(1,u'本貼吧總共有%s個帖子' % int(PageNum))
			return int(PageNum)
		
	#獲取本貼吧總共有多少帖子,詳細的
	def get_page_content(self,html):
		try:
			tiebaNumContent=[]
			#定義匹配規則,總共匹配三個元素
			pattern=re.compile('<div class="th_footer_l".*?<span.*?>(\d+)</span>.*?<span.*?>(\d+)</span>.*?class="red_text">(\d+).*?</div>',re.S)
			results=re.search(pattern,html)
			
			#引數為0的元素是正則匹配到的所有內容,1是第一個括號裡面的內容,2是第二個括號裡面的內容,3是第三個括號裡面的內容
			#使用search方法,就用group的方式獲取找到的元素
			tiebaTheme=results.group(1)
			tiebaNum=results.group(2)
			tiebaPeople=results.group(3)
			tiebaNumContent.append(tiebaTheme)
			tiebaNumContent.append(tiebaNum)
			tiebaNumContent.append(tiebaNumContent)
		except Exception,e:
			self.my_log(3,u'貼吧的帖子數量沒有找到, 錯誤原因:%s' % e)
			return None
		finally:
			self.my_log(1,u'本貼吧共有主題數:%s, 帖子數:%s, %s 人在本貼吧釋出內容' % (tiebaTheme,tiebaNum,tiebaPeople) )
			self.tiezi_file.write(u'本貼吧共有主題數:%s, 帖子數:%s, %s 人在本貼吧釋出內容\n' % (tiebaTheme,tiebaNum,tiebaPeople))
			return tiebaNumContent

	#根據頁面num依次獲取每一個頁面的帖子內容
	def getAll_tiezi_list(self,PageNum):
		if PageNum < 51:
			self.my_log(1,u'當前貼吧帖子數量不足一頁內容')
			return None
		#for num in range(50,PageNum+1,50):
		#for num in range(50,500,50):
		for num in range(50,50,50):
			current_url=self.tiebaUrl+"&ie=utf-8&pn="+str(num)
			target_Content=self.get_Single_Title_And_Url(current_url)
			
		
	#獲取單頁內容
	def get_Single_Title_And_Url(self,url):
		#定義存貯變數
		info=[]
		html=self.get_html(url)
		if not html:
			self.log(3,u'頁面%s內容獲取失敗,跳過' % url)
			return None
		try:
			#建立正則匹配模板 #pattern=re.compile('<li class=" j_thread_list clearfix".*?<span class="threadlist_rep_num center_text".*?>(.*?)</span>.*?<a href=(.*?)title=(.*?)target=.*?class="frs-author-name j_user_card".*?>(.*?)</a>.*?class="pull-right is_show_create_time".*?>(.*?)</span>.*?</li>',re.S)
			#匹配整個頁面的列表內容
			pattern=re.compile('<li class=" j_thread_list clearfix".*?</li>',re.S)
			tiezi_Contents=re.findall(pattern,html)
			#除錯log
			self.my_log(1,'當前頁面%s 找到了%d 個帖子' % (url,len(tiezi_Contents)))
				
			#匹配回帖人數,帖子標題,帖子地址
			replyNum_tirle_url_pattern=re.compile('<span class="threadlist_rep_num center_text".*?>(.*?)</span>.*?<a href="(.*?)" title="(.*?)" target=',re.S)
			#匹配建立人,建立時間
			author_creattime_Pattern=re.compile('<span class="frs-author-name-wrap".*?target="_blank">(.*?)</a>.*?class="pull-right is_show_create_time".*?>(.*?)</span>',re.S)
			
			for item in tiezi_Contents:
				tmp={}#臨時變數
				replyNum_tirle_url=re.search(replyNum_tirle_url_pattern,item)
				author_creattime=re.search(author_creattime_Pattern,item)
				
				replyNum_Tmp=replyNum_tirle_url.group(1)#回覆人數
				tieziNum=str(replyNum_tirle_url.group(2))[-10:]#擷取帖子編號
				ttieziUrl_Tmp='http://tieba.baidu.com'+replyNum_tirle_url.group(2)#帖子地址
				tieziTitle_Tmp=replyNum_tirle_url.group(3)#帖子標題
				
				author_Tmp=author_creattime.group(1)#創貼人
				creat_time_Tmp=author_creattime.group(2)#建貼時間
						
				self.my_log(1,u"發帖人:%s|發帖時間:%s|帖子題目%s|帖子地址%s|跟帖人數%s|帖子編號:%s" % (author_Tmp,creat_time_Tmp,tieziTitle_Tmp,ttieziUrl_Tmp,replyNum_Tmp,tieziNum))
				#將獲取到的資料寫入檔案中
				self.tiezi_file.write(u"發帖人:%s|發帖時間:%s|帖子題目%s|帖子地址%s|跟帖人數%s|帖子編號:%s\n" % (author_Tmp,creat_time_Tmp,tieziTitle_Tmp,ttieziUrl_Tmp,replyNum_Tmp,tieziNum))
				tmp['replyNum']=replyNum_Tmp
				tmp['tieziNum']=tieziNum
				tmp['tieziUrl']=ttieziUrl_Tmp
				tmp['tieziTitle']=tieziTitle_Tmp.strip()
				tmp['author']=author_Tmp.encode('utf-8')
				tmp['creat_time']=creat_time_Tmp
				info.append(tmp)
				#回帖數小於10的資料,暫時拋棄
				if int(replyNum_Tmp) > 10:
					self.info_list.append(tmp)
				
			self.my_log( 1,"資料匹配之後還有%d個帖子" % len(info))
		except Exception,e:
			self.my_log(2,u'匹配資料異常,跳過,錯誤原因:%s' % e)
			return None
		finally:
			self.my_log(1,u'當前頁面 %s 資料查詢完畢' % url )
			return info
		
	#獲取每一個帖子的頁碼數目,因為	之前已經過濾過一次了,因此這裡不需要重新過濾那些回帖數很少的情況
	def get_each_tiezi_content(self):
		tmp_test_url=[]#中間變數
		tiezi_count=len(self.info_list)#遍歷次數
		for i in range(tiezi_count):
			#tmp_single_info_list=random.choice(self.info_list)#隨機選擇一個url
			tmp_single_info_list=self.info_list[i]
			#?see_lz=  它後面的值決定了是否只看樓主資訊
			tiezi_url= tmp_single_info_list['tieziUrl']+'?see_lz=0&pn=1'#重新組合要訪問的帖子的地址
			tiezi_num=1
			try:
				html=self.get_html(tiezi_url)
				tiezi_num=self.get_tiezi_Page_Num(html)#獲取帖子的頁碼數目
			except Exception,e:
				self.my_log(2,u'get_each_tiezi_content() 匹配資料異常,跳過,錯誤原因:%s' % e)
				tmp_single_info_list['tiezi_page_num']=tiezi_num#將得到的帖子頁數重新加到資料中去
				tmp_test_url.append(tiezi_url)#收集異常地址
			finally:
				tmp_single_info_list['tiezi_page_num']=tiezi_num#將得到的帖子頁數重新加到資料中去
				tmp_single_info_list['tieziUrl']=tiezi_url#將得到的帖子頁數重新加到資料中去
				
				self.info_list[i]=tmp_single_info_list
				#for a,b in self.info_list[i].items():#一種遍歷字典的方法,這裡是測試是否將資料新增成功
				#	print a,b						  #
				self.my_log( 1,u'%s|%s|當前帖子的頁數:%s' % (sys._getframe().f_lineno,sys._getframe().f_code.co_name,str(tiezi_num)))
				#del self.info_list[0]
				
		self.my_log(1,'異常的url 有:%d' % len(tmp_test_url))

		
	#獲取一個帖子總共有多少頁
	def get_tiezi_Page_Num(self,page):
		if not page:
			self.my_log(3,u'頁面%s內容獲取失敗,跳過')
			return None
		try:
			pattern=re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)#匹配頁碼規則
			result=re.search(pattern,page)
		except Exception,e:
			self.my_log(2,u'查詢帖子頁面數目異常,跳過,錯誤原因:%s' % e)
		finally:
			if result:
				return result.group(1).strip()
			else:
				return None
			
			
	def mutil_thread(self):	
		#帖子的內容過多,這裡僅僅開啟三個執行緒,來爬取三個帖子的內容
		for i in range(3):
			tmp_single_info_list=random.choice(self.info_list)#隨機選擇一個地址
			p=threading.Thread(target=self.loop_for_every_tiezi, args=(tmp_single_info_list,))
			p.start()
			time.sleep(3)
			p.join()
		
		#tmp_single_info_list=random.choice(self.info_list)#隨機選擇一個地址
		#self.loop_for_every_tiezi(tmp_single_info_list)
		
		
	def loop_for_every_tiezi(self,tmp_single_info_list):
		#self.my_log(1,u'thread %s is running...' % threading.current_thread().name)
		
		#tmp_single_info_list=random.choice(self.info_list)#隨機選擇一個地址
		page_num=tmp_single_info_list['tiezi_page_num']
		page_url_base=tmp_single_info_list['tieziUrl'][:-1]#對地址做一個處理
		tiezi_num=tmp_single_info_list['tieziNum']
		
		path=self.keyword+'/'+tiezi_num
		self.create_dir(path)#使用帖子的地址建立資料夾
		
		tiezi_file_path=path+'/'+tiezi_num+'.txt'#建立檔案,記錄本貼子所有回覆內容
		tiezi_file_symbol=open(tiezi_file_path,'w')
		
		for num in range(1,int(page_num)+1):
			current_url=page_url_base+str(num)#重新組裝地址
			current_tiezi_html=self.get_html(current_url)#獲取當前帖子頁面內容
			self.get_every_tiezi_content(current_tiezi_html,num,tiezi_file_symbol)
		tiezi_file_symbol.close()
		
	#根據每一頁帖子的內容,匹配每一個樓層的內容	
	def get_every_tiezi_content(self,html,num,tiezi_file_symbol):
	#採用函式巢狀,先定義兩個內部函式,然後再做處理
		#只獲取首樓資訊		
		def get_every_tiezi_first_floor_content(html):
			try:
				#首樓匹配規則
				first_floor_pattern = re.compile('<div class="l_post j_l_post l_post_bright noborder ".*?<div class="clear"></div>.*?</div>',re.S)
				item=re.search(first_floor_pattern,html)
				tmp_content=item.group(0)
				#獲取回貼人的樓層,回覆時間,回覆人,內容
				author_content_floor_time_pattern=re.compile('data-field=.*?"date":"(.*?)".*?post_no":(\d+),.*?comment_num":(\d+),.*?>.*?<li class="d_name".*?target="_blank">(.*?)</a>.*?<div id="post_content_.*?>(.*?)</div>',re.S)
				
				items=re.search(author_content_floor_time_pattern,tmp_content)
				tmp={}
				floor_reply_time = items.group(1).strip()
				floor_post_num= items.group(2)
				floor_comment_num = items.group(3)
				floor_author = items.group(4)
				floor_reply_content =items.group(5)
				
				tmp['floor_reply_time']=floor_reply_time
				tmp['floor_post_num']=floor_post_num
				tmp['floor_comment_num']=floor_comment_num
				tmp['floor_author']=floor_author
				tmp['floor_reply_content']=floor_reply_content#回覆內容還需要重新做處理,因此暫時沒有記錄到檔案中去
				
				self.tiezi_info_list.append(tmp)
								
				self.my_log(1, u'%s樓| 回覆人:%s|回覆時間:%s|本樓層回覆數:%s ' % (floor_post_num,floor_author,floor_reply_time,floor_comment_num))
				tiezi_file_symbol.write(u'%s樓|回覆人:%s|回覆時間:%s|本樓層回覆數:%s\n' % (floor_post_num,floor_author,floor_reply_time,floor_comment_num))
				
				save_tiezi_content(floor_reply_content,tiezi_file_symbol)
				
			except Exception,e:
				self.my_log(2,u'匹配首樓內容失敗,跳過,錯誤原因:%s' % e)
							
		
		#獲取不包含首樓的其他樓層內容		
		def get_every_tiezi_not_first_floor_content(html):
			try:
				other_floor_pattern=re.compile('<div class="l_post j_l_post l_post_bright  ".*?<div class="clear"></div>.*?</div>',re.S)
				items=re.findall(other_floor_pattern,html)
			except Exception,e:
				self.my_log(2,u'匹配其他樓層內容失敗,跳過,錯誤原因:%s' % e)
			finally:
				self.my_log(0,u'不在首頁,找到%s 個回帖' % len(items) )
				reply_num= len(items)
				try:
					author_content_floor_time_pattern=re.compile('data-field=.*?"date":"(.*?)".*?post_no":(\d+),.*?comment_num":(\d+),.*?>.*?<li class="d_name".*?target="_blank">(.*?)</a>.*?<div id="post_content_.*?>(.*?)</div>',re.S)
					for floor_content in items:
						item=re.search(author_content_floor_time_pattern,floor_content)
						tmp={}
						floor_reply_time = item.group(1).strip()
						floor_post_num= item.group(2)
						floor_comment_num = item.group(3)
						floor_author = item.group(4)
						floor_reply_content =item.group(5)
												
						tmp['floor_reply_time']=floor_reply_time
						tmp['floor_post_num']=floor_post_num
						tmp['floor_comment_num']=floor_comment_num
						tmp['floor_author']=floor_author
						tmp['floor_reply_content']=floor_reply_content#回覆內容還需要重新做處理,因此暫時沒有記錄到檔案中去
						self.tiezi_info_list.append(tmp)
						
						self.my_log(1, u'%s樓| 回覆人:%s|回覆時間:%s|本樓層回覆數:%s ' % (floor_post_num,floor_author,floor_reply_time,floor_comment_num))
						tiezi_file_symbol.write(u'%s樓|回覆人:%s|回覆時間:%s|本樓層回覆數:%s\n' % (floor_post_num,floor_author,floor_reply_time,floor_comment_num))
						save_tiezi_content(floor_reply_content,tiezi_file_symbol)
				except Exception,e:
					self.my_log(2,u'匹配其他樓層,查找回復內容時失敗,跳過,錯誤原因:%s' % e)
					
		def save_tiezi_content(floor_reply_content,tiezi_file_symbol):
			floor_reply_content=self.tool.replace(floor_reply_content)
			floor_reply_content=self.tool.replaceSlash(floor_reply_content)
			tiezi_file_symbol.write(floor_reply_content)
			temp_data=u'\n**********************分割符*************************\n'
			tiezi_file_symbol.write(temp_data)
			
		if not html:
			self.my_log(3,u'頁面%s內容獲取失敗,跳過')
			return None

		#首樓層和其他樓層內容不同, 而首樓層只在帖子的第一頁出現
		if num == 1:
			get_every_tiezi_first_floor_content(html)
			get_every_tiezi_not_first_floor_content(html)
		else:
			get_every_tiezi_not_first_floor_content(html)

	def run(self):
		
		self.my_log(1,u'start crawl...')
		
		#step1  獲取貼吧入口網頁內容
		tieba_html = self.get_html(self.tiebaUrl)

		#step2  查詢本貼吧總共多少頁內容
		Page_Num=self.get_Total_Num(tieba_html)
		tieba_Num_Content=self.get_page_content(tieba_html)
		
		#setp3 獲取單頁內容
		self.get_Single_Title_And_Url(self.tiebaUrl)
		
		#step4 根據PageNum 獲取所有帖子的內容
		self.getAll_tiezi_list(Page_Num)
		self.my_log(1,u'total length is %d' % len(self.info_list))
		
		#step 5  遍歷每一個帖子, 獲取其頁碼數
		self.get_each_tiezi_content()
		self.my_log(1,u'Start mutil thread to crawl...' )
		time.sleep(3)
		self.mutil_thread()
		#step 6 獲取每一個帖子所有的內容
		#self.loop_for_every_tiezi()
		
		
		
		self.my_log(1,u'total length is %d' % len(self.info_list))
		self.my_log(1,u'End crawl')
		#關閉檔案描述符
		self.tiezi_file.close()
		
		#測試函式
	def test(self):
		url='http://tieba.baidu.com/p/5062576866?see_lz=0&pn=1'
		html=self.get_html(url)
		self.get_every_tiezi_content(html,1,'5062576866')	
if __name__ == '__main__':
	print '''
			***************************************** 
			**    Welcome to Spider of baidutieba  ** 
			**      Created on 2017-04-25          ** 
			**      @author: Jimy _Fengqi          ** 
			*****************************************
	'''
	keyword=raw_input(u'請輸入要獲取的貼吧名字:')
	if not keyword:
		keyword='python'
	print '將要獲取%s 貼吧的內容' % keyword
	#GetBaiduTieba(keyword).test()
	GetBaiduTieba(keyword).run()
	#mytieba=GetBaiduTieba(keyword)
	#mytieba.run()


相關推薦

python爬蟲(13)帖子

爬取百度貼吧帖子 一開始只是在網上看到別人寫的爬取帖子的文章,然後自己就忍不住手癢自己鍛鍊一下, 然後照著別人的寫完,發現不太過癮, 畢竟只是獲取單個帖子的內容,感覺內容稍顯單薄,然後自己重新做了修改,把它變成重寫成了一個比較強大的爬蟲 精簡版本 簡介  通過帖子的地址,獲

Python爬蟲例項--小說

Python爬蟲例項–爬取百度貼吧小說 寫在前面 本篇文章是我在簡書上寫的第一篇技術文章,作為一個理科生,能把僅剩的一點文筆拿出來獻醜已是不易,希望大家能在指教我的同時給予我一點點鼓勵,謝謝。 一.介紹 小說吧:顧名思義,是一個小說

Python爬蟲實例(一)帖子中的圖片

選擇 圖片查看 負責 targe mpat wid agent html headers 程序功能說明:爬取百度貼吧帖子中的圖片,用戶輸入貼吧名稱和要爬取的起始和終止頁數即可進行爬取。 思路分析: 一、指定貼吧url的獲取 例如我們進入秦時明月吧,提取並分析其有效url如下

完整的爬蟲程序的圖片

列表 文檔 for tieba http ... 自增 num 圖片 #!/usr/bin/env python#-- coding:utf-8 -- import osimport urllibimport urllib2from lxml import etree cl

編寫爬蟲帖子的學習筆記

再接再厲,再次使用python3學習編寫了一個爬取百度貼吧帖子的程式,不多說,直接上關鍵程式碼 #抓取貼吧一個帖子上的內容(一頁內容) import urllib import urllib.req

Python簡易爬蟲圖片

decode works 接口 def 讀取 min baidu 得到 internal       通過python 來實現這樣一個簡單的爬蟲功能,把我們想要的圖片爬取到本地。(Python版本為3.6.0) 一.獲取整個頁面數據    def getHtml(url)

Python爬蟲-

方法 eba style name urlopen for pri url pen 爬取百度貼吧 ===================== ===== 結果示例: ===================================== 1 ‘‘‘ 2 爬去百

Python爬蟲教程:

貼吧爬取 寫程式碼前,構思需要的功能塊;寫程式碼時,把各個功能模組名提前寫好 初始化 初始化必要引數,完成基礎設定 爬取百度貼吧lol吧:爬取地址中的get引數須傳遞(可以指定不同主題的貼吧和頁碼) 主題名 初始網址 請求頭 生成網址 生成每一頁的路由

實戰python 爬蟲圖片

#!/usr/bin/python import urllib,urllib2import re def getHtml(url): page = urllib2.urlopen(url) return page.read() def getImage(html): re_img = re.compil

python網路爬蟲學習(二)一個爬蟲程式

今天進一步學習了python網路爬蟲的知識,學會了寫一個簡單的爬蟲程式,用於爬取百度貼吧的網頁並儲存為HTML檔案。下面對我在實現這個功能時的程式碼以及所遇到的問題的記錄總結和反思。 首先分析實現這個功能的具體思路: 通過對貼吧URL的觀察,可以看出貼吧中的

python爬蟲(入門練習)

需求說明: 從控制檯輸入指定爬取的貼吧名稱,起始頁面,結束頁面,並在檔案中 建立以  貼吧名稱+“爬取內容”  為名字建立檔案件,裡面的每一個 檔案都是爬取到的每一頁html檔案,檔名稱:貼吧名稱_page.html import urllib.reque

Python數據

utf-8 支持我 family encode code word keyword 上一條 時間   本渣除了工作外,在生活上還是有些愛好,有些東西,一旦染上,就無法自拔,無法上岸,從此走上一條不歸路。花鳥魚蟲便是我堅持了數十年的愛好。   本渣還是需要上班,才能支持我的

最最簡單的python爬蟲教程--百科案例

python爬蟲;人工智能from bs4 import BeautifulSoupfrom urllib.request import urlopenimport reimport randombase_url = "https://baike.baidu.com"#導入相關的包 his

python指定內容

環境:python3.6 1:抓取百度貼吧—linux吧內容 基礎版 抓取一頁指定內容並寫入檔案 萌新剛學習Python爬蟲,做個練習 貼吧連結: http://tieba.baidu.com/f?kw=linux&ie=utf-8&pn=0 解析原始碼使用的是B

PHP爬蟲-首頁違規主題

因為是第一次寫,感覺有點冗餘。不過嘛,本文章主要面向不知道爬蟲為何物的小夥伴。o(∩_∩)o <?php $url='http://tieba.baidu.com/f?ie=utf-8&kw=php&fr=search'; // 地址 $html = file_ge

Python標題

# -*- coding: utf-8 -*- """ Created on Sun Nov 4 10:22:07 2018 @author: wangf """ from urllib.request import urlopen import codecs from

Python圖片指令碼

新手,以下是爬取百度貼吧制定帖子的圖片指令碼,因為指令碼主要是解析html程式碼,因此一旦百度修改頁面前端程式碼,那麼指令碼會失效,權當爬蟲入門練習吧,後續還會嘗試更多的爬蟲。 # coding=ut

Python回帖中的微訊號(基於簡單http請求)

作者:草小誠 轉載請注原文地址:https://blog.csdn.net/cxcjoker7894/article/details/85685115 前些日子媳婦兒有個需求,想要一個任意貼吧近期主題帖的所有回帖中的微訊號,用來做一些微商的操作,你懂的。因為有些貼吧專門就是

Python的圖片

Python是一個弱型別的動態語言 下面是我的第一個簡單的爬蟲指令碼程式 #coding=gbk #匯入re和urlLib兩個庫 import re import urllib #定義一個有參的獲得圖片的方法,方法名為getImg def getImg(url):

網路爬蟲簡單的實現圖片

我們要爬取的網站是https://tieba.baidu.com/p/3797994694 首先爬取第一頁的圖片,使用python3自帶庫urllib,詳細的程式碼如下: 接下來爬去多頁的圖片,這裡我們選取五頁的圖片,這裡我們採用requests,beautifuls