代碼是抓取百度貼吧帖子的回覆內容的。包括帖子標題、帖子回覆數量,帖子頁碼,回覆樓層、回覆時間,也可以只查看樓主的回覆信息。最後將獲取到的帖子信息記錄到記事本中。
具體結果看圖:
上面的圖片打印的是帖子的回覆總數140,總共5頁。這5頁信息是隻有樓主的回覆,沒有包含全部的回覆。帖子標題。第一頁數量30個回覆,下面依次打印每一樓的回覆信息,每樓層之間用*號隔開。
這個記事本保存的是樓主的回覆內容,包括每次回覆的樓層。
這是保存的記事本文檔信息截圖
這是全部回覆,包括不是樓主的回覆內容
控制檯打印的最後一樓信息。
最後一樓回覆的網頁截圖信息。
第一步:獲取帖子html代碼內容
#獲取頁面html文本內容
def getPageContent(self,url):
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode('UTF-8')
在這裏需要指定解碼格式,具體的html文本解碼格式,需要具體查看html頁面的格式。如下圖:
第二步:獲取帖子標題和帖子回覆總數、頁碼
箭頭指向分別指向了三個信息。正則表達式如下:
#獲取帖子標題
def decodeTieziTitle(self,pagecontent):
pattern = re.compile('<h\d.*?class="core_title_txt.*?>(.*?)</h\d>',re.S)
title = re.findall(pattern, pagecontent)
return title[0]
#獲取頁面回覆數量和總頁碼
def decodePageContentNum(self,pagecontent):
patten = re.compile('<li class="l_reply_num".*?<span class="red".*?>(.*?)'+
'</span>.*?<span class="red".*?>(.*?)</span>',re.S)
num =re.search(patten, pagecontent);
return num
num = self.decodePageContentNum(pagecontent)
print num.group(1),u'回覆貼,共',num.group(2),u'頁'
通過返回num值,group()方法來獲取頁碼和回覆帖子數量
第四步:解析文檔內容,獲取有用信息
#獲取四個信息,用戶名,內容,樓層,時間
def decodePageContent(self,pagecontent):
#獲取四個信息,用戶名d,內容,(),樓層,時間
# pattern = re.compile('<div.class="d_author">.*?<ul class="p_author">.*?'+
# '<li class="d_name.*?<a.*?target="_blank">(.*?)</a>.*?'+
# '<div class="d_post_content_main.*?<div class="p_content ".*?<cc>.*?'+
# '<div.*?class="d_post_content j_d_post_content.*?>(.*?)'+
# '</div>.*?</cc>.*?'+
# '<div class="core_reply j_lzl_wrapper">.*?'+
# '<div class="core_reply_tail clearfix">.*?'+
# '<div class="post-tail-wrap">.*?<span class="j_jb_ele">.*?</span>'+
# '(.*?)'+
# '<span class="tail-info">(.*?)'+'[\u697c]'+'</span>.*?'+
# '<span class="tail-info">(.*?)</span>.*?</div>.*?'+
# '<ul class="p_props_tail props_appraise_wrap"></ul>',re.S)
pattern = re.compile('<div.class="d_author">.*?<ul class="p_author">.*?'+
'<li class="d_name.*?<a.*?target="_blank">(.*?)</a>.*?'+
'<div class="d_post_content_main.*?<div class="p_content'+
' ".*?<cc>.*?'+
'<div.*?class="d_post_content j_d_post_content.*?>(.*?)'+
'</div>.*?</cc>.*?'+
'<div class="core_reply j_lzl_wrapper">.*?'+
'<div class="core_reply_tail clearfix">.*?'+
'<div class="post-tail-wrap">'+
'(.*?)</div>',re.S)
# pattern = re.compile('<div class="d_author">.*?<ul class="p_author">.*?'+
# '<li class="d_name".*?<a.*?'+
# 'target="_blank">(.*?)</a>',re.S)
# pattern = re.compile('<li class="d_name" data-field=".*?<a data-field=".*?'+
# 'target="_blank">(.*?)</a>.*?</li>',re.S)
# pattern = re.compile('<div class="d_author">(.*?)</div>',re.S)
content = re.findall(pattern, pagecontent)
print "len=",len(content)
ls = []
tail2 = re.compile('<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>', re.S)
tail3 = re.compile('<span class="tail-info">.*?</span>.*?'+
'<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>',re.S)
import sys;reload(sys);sys.setdefaultencoding('utf8');
for item in content:
n = re.subn('tail-info', 'tail', item[2])
if n[1]==4:
group3 = re.search(tail3, item[2])
ls.append((item[0],item[1],group3.group(1),group3.group(2)))
elif n[1]==3:
group2 = re.search(tail2, item[2])
ls.append((item[0],item[1],group2.group(1),group2.group(2)))
else:
print 'n error n=',n[1],'=='+item[2].decode('utf-8')
return ls
這裏的正則表達式比較長,需要根據具體的html代碼進行調整。我想說的是,大家在寫正則表達式的時候,一點點的測試,不要一下子就寫很長的正則表達式,一次是寫不對的,保證一點點的擴展,慢慢的增加內容,不斷縮小獲取信息的範圍。
值的一提的是,上面的例子中,在獲取樓層,時間信息的時候,有點麻煩,原因在有些樓層的回覆是手機端回覆的,html代碼中是這樣的:
就會有三個tail-info的span節點,如果是電腦端回覆的帖子,就沒有第一個節點span,只有後面兩個span節點,並且,tail-info節點不止這三個,如圖:
對於這種情況,無法再正則表達式中進行匹配,所以,我把中間的信息使用(.*?)獲取到,之後替換tail-info字符串,如果替換的次數有四次,說明是客戶端app發的帖子,直接取後面兩個span節點信息。如果替換次數是三次,說明是電腦端發的帖子。分別使用正則表達式進行匹配獲取信息。
tail2 = re.compile('<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>', re.S)
tail3 = re.compile('<span class="tail-info">.*?</span>.*?'+
'<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>',re.S)
import sys;reload(sys);sys.setdefaultencoding('utf8');
for item in content:
n = re.subn('tail-info', 'tail', item[2])
if n[1]==4:
group3 = re.search(tail3, item[2])
ls.append((item[0],item[1],group3.group(1),group3.group(2)))
elif n[1]==3:
group2 = re.search(tail2, item[2])
ls.append((item[0],item[1],group2.group(1),group2.group(2)))
else:
print 'n error n=',n[1],'=='+item[2].decode('utf-8')
這就是代碼中的邏輯部分。
完成了這部分內容之後,最後一步,就是循環頁碼,獲取每頁帖子回覆的信息記錄到記事本中:
for i in range(int(self.pagenum)+1):#range函數不包括最大的值
pagecontent = self.getPageContent(\
self.baseUrl+urlid+self.seellz+str(see_lz)+self.urlpn+str(i))
content = self.decodePageContent(pagecontent)
picpattern = re.compile('<img class="BDE_Image" src="(.*?)".*?',re.S)
#獲取四個信息,用戶名,內容,樓層,時間
for con in content:
print '*'*20
strcon = '*'*20+os.linesep
print u'樓層:',con[2]
strcon += '樓層:'+con[2].encode('utf-8')+os.linesep
print u'時間:',con[3]
strcon += '時間:'+con[3].encode('utf-8')+os.linesep
print u'用戶名字:',con[0]
strcon += '用戶名字:'+con[0].encode('utf-8')+os.linesep
li = re.findall(picpattern, con[1])
if li is not None:
for l in li:
print u'內容圖片:',l
strcon += '內容圖片:'+l.encode('utf-8')+os.linesep
print u'內容:',self.tool.replace(con[1])
strcon += '內容:'+self.tool.replace(con[1]).encode('utf-8')+os.linesep
self.writeTxt(strcon+os.linesep,f)
#帖子的每一個回覆內容寫入文件中
def writeTxt(self,strs,f):
f.writelines(strs)
之後給出整個代碼如下:
#_*_encoding=utf8_*_
'''
Created on 2015年11月22日
@author: 15361
'''
import urllib2
import re
import os
from scr.tool import Tool
class BDTB:
def __init__(self):
# 'http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1' 百度貼吧URL地址
self.baseUrl = 'http://tieba.baidu.com/p/'
self.seellz = '?see_lz=' #=1只看樓主 =0查看全部
self.urlpn = '&pn=' #代表頁碼
self.tool =Tool()
#帖子的每一個回覆內容寫入文件中
def writeTxt(self,strs,f):
f.writelines(strs)
#獲取四個信息,用戶名,內容,樓層,時間
def decodePageContent(self,pagecontent):
#獲取四個信息,用戶名d,內容,(),樓層,時間
# pattern = re.compile('<div.class="d_author">.*?<ul class="p_author">.*?'+
# '<li class="d_name.*?<a.*?target="_blank">(.*?)</a>.*?'+
# '<div class="d_post_content_main.*?<div class="p_content ".*?<cc>.*?'+
# '<div.*?class="d_post_content j_d_post_content.*?>(.*?)'+
# '</div>.*?</cc>.*?'+
# '<div class="core_reply j_lzl_wrapper">.*?'+
# '<div class="core_reply_tail clearfix">.*?'+
# '<div class="post-tail-wrap">.*?<span class="j_jb_ele">.*?</span>'+
# '(.*?)'+
# '<span class="tail-info">(.*?)'+'[\u697c]'+'</span>.*?'+
# '<span class="tail-info">(.*?)</span>.*?</div>.*?'+
# '<ul class="p_props_tail props_appraise_wrap"></ul>',re.S)
pattern = re.compile('<div.class="d_author">.*?<ul class="p_author">.*?'+
'<li class="d_name.*?<a.*?target="_blank">(.*?)</a>.*?'+
'<div class="d_post_content_main.*?<div class="p_content'+
' ".*?<cc>.*?'+
'<div.*?class="d_post_content j_d_post_content.*?>(.*?)'+
'</div>.*?</cc>.*?'+
'<div class="core_reply j_lzl_wrapper">.*?'+
'<div class="core_reply_tail clearfix">.*?'+
'<div class="post-tail-wrap">'+
'(.*?)</div>',re.S)
# pattern = re.compile('<div class="d_author">.*?<ul class="p_author">.*?'+
# '<li class="d_name".*?<a.*?'+
# 'target="_blank">(.*?)</a>',re.S)
# pattern = re.compile('<li class="d_name" data-field=".*?<a data-field=".*?'+
# 'target="_blank">(.*?)</a>.*?</li>',re.S)
# pattern = re.compile('<div class="d_author">(.*?)</div>',re.S)
content = re.findall(pattern, pagecontent)
print "len=",len(content)
ls = []
tail2 = re.compile('<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>', re.S)
tail3 = re.compile('<span class="tail-info">.*?</span>.*?'+
'<span class="tail-info">(.*?)</span>.*?'+
'<span class="tail-info">(.*?)</span>',re.S)
import sys;reload(sys);sys.setdefaultencoding('utf8');
for item in content:
n = re.subn('tail-info', 'tail', item[2])
if n[1]==4:
group3 = re.search(tail3, item[2])
ls.append((item[0],item[1],group3.group(1),group3.group(2)))
elif n[1]==3:
group2 = re.search(tail2, item[2])
ls.append((item[0],item[1],group2.group(1),group2.group(2)))
else:
print 'n error n=',n[1],'=='+item[2].decode('utf-8')
return ls
#獲取帖子標題
def decodeTieziTitle(self,pagecontent):
pattern = re.compile('<h\d.*?class="core_title_txt.*?>(.*?)</h\d>',re.S)
title = re.findall(pattern, pagecontent)
return title[0]
#獲取頁面回覆數量和總頁碼
def decodePageContentNum(self,pagecontent):
patten = re.compile('<li class="l_reply_num".*?<span class="red".*?>(.*?)'+
'</span>.*?<span class="red".*?>(.*?)</span>',re.S)
num =re.search(patten, pagecontent);
return num
#獲取頁面html文本內容
def getPageContent(self,url):
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode('UTF-8')
def start(self):
urlid = raw_input("輸入貼吧帖子ID:").strip()
see_lz = raw_input("是否只看樓主的帖子(是輸入1,否輸入0):")
sss = self.baseUrl+urlid+self.seellz+str(see_lz)+self.urlpn+str(1)
# sss = 'http://tieba.baidu.com/p/3138733512?see_lz=0&pn=1'
print 'url=',sss
pagecontent = self.getPageContent(\
sss)
num = self.decodePageContentNum(pagecontent)
print num.group(1),u'回覆貼,共',num.group(2),u'頁'
self.pagenum = num.group(2) #保存頁碼數量
self.title = self.decodeTieziTitle(pagecontent)
import sys;reload(sys);sys.setdefaultencoding('utf8');
if see_lz==1:
f = open(self.title+'-樓主.txt'.encode('utf-8'),'w+')
else:
f = open(self.title+'-全部.txt'.encode('utf-8'),'w+')
self.writeTxt(num.group(1)+' post number,'+num.group(2)+'page number'+os.linesep,f)
print u'帖子標題:%s' %self.title
for i in range(int(self.pagenum)+1):#range函數不包括最大的值
pagecontent = self.getPageContent(\
self.baseUrl+urlid+self.seellz+str(see_lz)+self.urlpn+str(i))
content = self.decodePageContent(pagecontent)
picpattern = re.compile('<img class="BDE_Image" src="(.*?)".*?',re.S)
#獲取四個信息,用戶名,內容,樓層,時間
for con in content:
print '*'*20
strcon = '*'*20+os.linesep
print u'樓層:',con[2]
strcon += '樓層:'+con[2].encode('utf-8')+os.linesep
print u'時間:',con[3]
strcon += '時間:'+con[3].encode('utf-8')+os.linesep
print u'用戶名字:',con[0]
strcon += '用戶名字:'+con[0].encode('utf-8')+os.linesep
li = re.findall(picpattern, con[1])
if li is not None:
for l in li:
print u'內容圖片:',l
strcon += '內容圖片:'+l.encode('utf-8')+os.linesep
print u'內容:',self.tool.replace(con[1])
strcon += '內容:'+self.tool.replace(con[1]).encode('utf-8')+os.linesep
self.writeTxt(strcon+os.linesep,f)
bdtb = BDTB()
bdtb.start()
最後需要提一點的是,以上代碼是對於
http://tieba.baidu.com/p/3138733512?see_lz=0&pn=1
該地址的帖子的信息的獲取,其中3138733512代表帖子ID值。
see_lz代表是否只看樓主的回覆帖子
該值=0 查看全部回覆帖子
該值=1 只查看樓主回覆帖子
pn 代表頁碼
對於該網址信息下的網頁 信息,以上代碼是正確的。對於別的帖子的內容,本人試了一下好像需要做調整才行。
下載完整代碼的,請猛戳這裏!
Github地址,在這裏!
該github地址下,不僅僅有該博客的內容,還有淘寶淘女郎、糗事百科網頁信息的抓取內容,歡迎關注!^_^