python爬蟲入門 實戰(zhàn)(二)---爬百度貼吧


本篇涉及知識點:
1瓢姻、xpath語法
2、正則表達式

踩坑:
1音诈、xpath解析出的結(jié)點文本內(nèi)容中文亂碼幻碱。
2、xpath解析時细溅,結(jié)點內(nèi)有多余標簽褥傍,文本被截斷。
3喇聊、用正則表達式匹配的分組輸出亂碼恍风。


發(fā)送請求獲取html文本內(nèi)容

import urllib2

#目標url,這里see_lz=1代表只看樓主誓篱,pn=1代表頁碼為1
url='https://tieba.baidu.com/p/3267113128?see_lz=1&pn=1’

request=urllib2.Request(url)#封裝請求
response=urllib2.urlopen(request)#打開url資源朋贬,得到響應
str_html=response.read()#讀取html文本內(nèi)容
response.close()#關閉資源

print str_html#打印html文本

我們看輸出結(jié)果,這里中文是正常窜骄,沒有亂碼的:

html文本輸出

xpath解析并獲取每一個樓層的內(nèi)容

1. 我們先來分析html文本結(jié)構(gòu)

點擊左邊小紅框的按鈕再點擊目標锦募,即可查看到相應的標簽。


樓層內(nèi)容的標簽

可以看到這個div有很明顯的特征邻遏,id=“post_content_56723422722”糠亩,即id包括字段“post_content_”,我們可以直接用contains函數(shù)通過id來找到准验。

tree.xpath('//div[contains(@id,"post_content_")]']

不過為了保險起見赎线,也為了多熟悉一下xpath解析的語法,我們多往上看兩層糊饱。目標是class=“d_post_content_main ”的div下的一個class=“p_content ”的div里的cc標簽下的唯一div垂寥。
所以我們可以用以下語句找到所有樓層的目標內(nèi)容:

tree.xpath('//div[@class="d_post_content_main "]/div[@class="p_content  "]/cc/div')

同理我們可以分析結(jié)構(gòu),解析得到相應的樓層號:

tree.xpath('//div[@class="d_post_content_main "]/div[@class="core_reply j_lzl_wrapper"]/div[@class="core_reply_tail clearfix"]/div[@class="post-tail-wrap"]/span[2]')

2. 獲取每個樓層的內(nèi)容

import urllib2
from lxml import html

url='https://tieba.baidu.com/p/3267113128?see_lz=1&pn=1'#目標url
request=urllib2.Request(url)#封裝請求
response=urllib2.urlopen(request)#打開url資源,得到響應
str_html=response.read()#讀取html文本內(nèi)容
response.close()#關閉資源

tree=html.fromstring(str_html)
nodes=tree.xpath('//div[@class="d_post_content_main "]')#先找的所有的樓層再進一步解析樓層號碼和內(nèi)容

for node in nodes:
    layer=node.xpath('div[@class="core_reply j_lzl_wrapper"]/div[@class="core_reply_tail clearfix"]/div[@class="post-tail-wrap"]/span[2]')[0].text
    print layer#輸出樓層號
     
    content=node.xpath('div[@class="p_content  "]/cc/div')[0].text
    print content#輸出樓層內(nèi)容

這里就出現(xiàn)第一個坑了矫废,前面輸出html文本是沒有中文亂碼的盏缤,這里xpath解析完以后輸出就中文亂碼了。運行一下看輸出結(jié)果:

xpath解析中文亂碼

不過在python里內(nèi)置了unicode字符類型蓖扑,這是解決亂碼問題的神器唉铜。將str類型的字符串轉(zhuǎn)換成unicode字符類型就可以了。轉(zhuǎn)換方法有兩種:

s1 = u"字符串"
s2 = unicode("字符串", "utf-8")

想具體了解unicode和python里的亂碼問題的律杠,可以參考這一篇博客:關于Python的編碼潭流、亂碼以及Unicode的一些研究

下面看修改后的代碼:
注:因為一樓的div的class有兩個,所以將獲取每個樓層結(jié)點的代碼改成有contains函數(shù)

import urllib2
from lxml import html

url='https://tieba.baidu.com/p/3267113128?see_lz=1&pn=1'#目標url
request=urllib2.Request(url)#封裝請求
response=urllib2.urlopen(request)#打開url資源柜去,得到響應
str_html=response.read()#讀取html文本內(nèi)容
response.close()#關閉資源

str_html=unicode(str_html,'utf-8')#將string字符類型轉(zhuǎn)換成unicode字符類型

tree=html.fromstring(str_html)
nodes=tree.xpath('//div[contains(@class,"d_post_content_main")]')#先找的所有的樓層再進一步解析樓層號碼和內(nèi)容

for node in nodes:
    layer=node.xpath('div[@class="core_reply j_lzl_wrapper"]/div[@class="core_reply_tail clearfix"]/div[@class="post-tail-wrap"]/span[2]')[0].text
    print layer#輸出樓層號
     
    content=node.xpath('div[@class="p_content  "]/cc/div')[0].text
    print content#輸出樓層內(nèi)容

運行可以看到結(jié)果里有一些樓層的輸出內(nèi)容不完整灰嫉,這就是第二個坑

樓層不完整

我們返回瀏覽器查看結(jié)構(gòu),可以發(fā)現(xiàn)嗓奢,原來是這個div里有其他標簽(a和br)讼撒,不是純文本的。這里就可以用string函數(shù)來過濾掉多余的標簽股耽。

在前面的代碼里根盒,輸出樓層的語句換成:

content=node.xpath('div[@class="p_content  "]/cc/div')[0]
content_txt=content.xpath('string(.)')#用string函數(shù)過濾掉多余子標簽,.代表本結(jié)點
print content_txt#輸出樓層內(nèi)容

但是br也被過濾掉了物蝙,有些樓層有分序號的輸出結(jié)果也是一行炎滞,看著不方便。我們可以用正則表達式匹配上數(shù)字序號诬乞,并在之前插入換行符“\n”册赛。re模塊的sub是匹配并替換字符串的方法。python中正則表達式的更多使用可以參考Python正則表達式指南震嫉。

re_num=re.compile('([0-9]\.)') #匹配所有的數(shù)字序號(數(shù)字后通常跟".")森瘪,加上括號作為分組1,這樣可以進行替換的時候用上
for node in nodes:
    layer=node.xpath('div[@class="core_reply j_lzl_wrapper"]/div[@class="core_reply_tail clearfix"]/div[@class="post-tail-wrap"]/span[2]')[0].text
    print layer#輸出樓層號
     
    content=node.xpath('div[@class="p_content  "]/cc/div')[0]
    content_txt=content.xpath('string(.)')
    content_txt=re.sub(re_num,'\n\1',content_txt) #將目標匹配替換成換行符+本身分組(也就是分組1)

    print content_txt #輸出樓層內(nèi)容

但輸出結(jié)果顯示责掏,換行成功了柜砾,數(shù)字序號成了亂碼。這里是第三個坑换衬,漏掉了一個小地方。


修改倒數(shù)第二句為:

 content_txt=re.sub(re_num,r'\n\1',content_txt)#加上一個字母'r'

這個“r”是代表防止字符轉(zhuǎn)義证芭,也就是“original”原生字符瞳浦。關于正則中字符的轉(zhuǎn)義,有興趣的同學可以google废士、百度叫潦。

接下來我們簡單將代碼寫成面向?qū)ο蟮娘L格。完整代碼如下:

import urllib2
from lxml import html
import re

class BaiduTieba:
    url_base='https://tieba.baidu.com/p/3267113128?see_lz='
    file = open("../text/bdtb.txt","w+")
    
    def __init__(self,see_lz):#see_lz=1表示只看樓主
        self.url_base=self.url_base+see_lz
    
    def setPage(self,page):
        self.url=self.url_base+'&pn='+page#目標url

    def printPage(self):
        request=urllib2.Request(self.url)#封裝請求
        response=urllib2.urlopen(request)#打開url資源官硝,得到響應
        str_html=response.read()#讀取html文本內(nèi)容
        response.close()#關閉資源
        
        str_html=unicode(str_html,'utf-8')#將string字符類型轉(zhuǎn)換成unicode字符類型
        
        tree=html.fromstring(str_html)
        nodes=tree.xpath('//div[contains(@class,"d_post_content_main")]')#先找的所有的樓層再進一步解析樓層號碼和內(nèi)容
        
        re_num=re.compile('([0-9]\.)')
        for node in nodes:
            layer=node.xpath('div[@class="core_reply j_lzl_wrapper"]/div[@class="core_reply_tail clearfix"]/div[@class="post-tail-wrap"]/span[2]')[0].text
            
            self.file.write("\n")
            self.file.write("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n")
            self.file.write("------------------------------------------------------------------------------------\n")
            self.file.write("                                  "+layer.encode("utf-8")+"                                         \n")
            self.file.write("------------------------------------------------------------------------------------\n")
            content=node.xpath('div[@class="p_content  "]/cc/div')[0]
            content_txt=content.xpath('string(.)')
            content_txt=re.sub(re_num,r'\n\1',content_txt)
        
            self.file.write(content_txt.encode("utf-8"))#輸出樓層內(nèi)容
            self.file.write("------------------------------------------------------------------------------------\n")


crawl_bdtb=BaiduTieba("1")
for i in range(5):#爬5頁
    crawl_bdtb.setPage(str(i+1))
    crawl_bdtb.printPage()

爬取結(jié)果截圖:


爬取結(jié)果

參考

Python正則表達式指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矗蕊,一起剝皮案震驚了整個濱河市短蜕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌傻咖,老刑警劉巖朋魔,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卿操,居然都是意外死亡警检,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門害淤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扇雕,“玉大人,你說我怎么就攤上這事窥摄∠夥睿” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵崭放,是天一觀的道長腮鞍。 經(jīng)常有香客問我,道長莹菱,這世上最難降的妖魔是什么移国? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮道伟,結(jié)果婚禮上迹缀,老公的妹妹穿的比我還像新娘。我一直安慰自己蜜徽,他們只是感情好祝懂,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拘鞋,像睡著了一般砚蓬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盆色,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天灰蛙,我揣著相機與錄音,去河邊找鬼隔躲。 笑死摩梧,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宣旱。 我是一名探鬼主播仅父,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了笙纤?” 一聲冷哼從身側(cè)響起耗溜,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎省容,沒想到半個月后抖拴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蓉冈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年城舞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寞酿。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡家夺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伐弹,到底是詐尸還是另有隱情拉馋,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布惨好,位于F島的核電站煌茴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏日川。R本人自食惡果不足惜蔓腐,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望龄句。 院中可真熱鬧回论,春花似錦、人聲如沸分歇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽职抡。三九已至葬燎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缚甩,已是汗流浹背谱净。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹄胰,地道東北人岳遥。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像裕寨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容