利用python的爬蟲技術爬取百度貼吧的帖子

在爬取糗事百科的段子後,我又在知乎上找了一個爬取百度貼吧帖子的實例,爲了鞏固提高已掌握的爬蟲知識,因而我打算本身也作一個。html

實現目標:1,爬取樓主所發的帖子python

              2,顯示所爬去的樓層以及帖子題目正則表達式

              3,將爬取的內容寫入到文件裏,並實現動態顯示爬取進度編程

實現工具:python的requests庫和正則表達式以及bs4庫app

首先咱們爬取的帖子網址爲:https://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,該網址是隻看樓主的帖子的網址,所以該網站的源代碼內容均爲樓主所發貼的內容,爬取起來也比較方便。咱們發現須要爬取的帖子一共有5頁,咱們能夠經過for循環來進行對每一頁信息的爬取。函數

接下來咱們來總體構建爬取的思路:工具

  1,爬取該網頁的源代碼post

  2,用正則表達式提取所需內容網站

  3,用正則匹配對所取內容進行精準修改以達到咱們想要的內容ui

  4,把內容寫入到文件並顯示寫入進度

下面來介紹每一步的具體實現:

首先是獲取源代碼,這個已經比較簡單了,大多數獲取源代碼的方式均可以用這段代碼來實現:

def getHTMLText(url):
    try:
        user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        headers = {'User-Agent': user_agent}
        r = requests.get(url,headers = headers)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""

其中的user_agent配置能夠在網頁的源代碼中找到,其目的是將爬蟲進行假裝成用戶以此來獲取更好的爬取體驗

接下來咱們要經過正則表達式來獲取咱們須要的「標題」,「帖子主要內容」以及「樓層」信息

經過分析源代碼咱們發現「標題」在

<title>......</title>

中能夠找到,「帖子主要內容」在

<div id="post_content_\d*" class="d_post_content j_d_post_content ">......</div>

中能夠找到,「樓層」信息能夠在

<span class="tail-info">......</span><span class="tail-info">

中找到。其中「.......」表示所要提取內容,咱們分別用兩個函數來實現對此的提取

def printTitle(html):
    try:
        soup = BeautifulSoup(html, "html.parser")
        titleTag = soup.find_all('title')
        patten = re.compile(r'<title>(.*?)</title>', re.S)
        title = re.findall(patten, str(titleTag))
        return title
    except:
        return ""
def fillUnivlist(lis,li,html):
    try:
        patten = re.compile(r'<div id="post_content_\d*" class="d_post_content j_d_post_content ">(.*?)</div>', re.S)
        nbaInfo = re.findall(patten, str(html))
        pattenFloor = re.compile(r'<span class="tail-info">(\d*樓)</span><span class="tail-info">', re.S)
        floorText = re.findall(pattenFloor, str(html))
        number = len(nbaInfo)
        for i in range(number):
            Info = textTools.remove(nbaInfo[i])
            Info1 = textTools.remove(floorText[i])
            lis.append(Info1)
            li.append(Info)
    except:
        return ""

咱們對每一個方法都用try except 來保證其強健性。

可是咱們發現咱們對所提取的帖子內容有不少多餘的成分:

<img class="BDE_Image" src="https://imgsa.baidu.com/forum/w%3D580/sign=cb6ab1f8708b4710ce2ffdc4f3ccc3b2/06381f30e924b899d8ca30e16c061d950b7bf671.jpg" pic_ext="jpeg"  pic_type="0" width="339" height="510"><br><br><br><br>50 驚喜新人王 <a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivS12AkS11WcjnMQsTddE2yXZInIi4k8KEu5449mWp1SxBADVCHPuUFSTGH+WZuV+ecUBG6CY6mAz/Zq1mzxbFxzAG+4Cm4FSU0="  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'邁卡威\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">邁卡威</a><br>上賽季數據<br>籃板 6.2  助攻 6.3  搶斷 1.9 蓋帽  0.6 失誤 3.5 犯規  3  得分 16.7<br><br><br>       新賽季第50位,我給上賽季的新人王<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivS12AkS11WcjnMQsTddE2yXZInIi4k8KEu5449mWp1SxBADVCHPuUFSTGH+WZuV+ecUBG6CY6mAz/Zq1mzxbFxzAG+4Cm4FSU0="  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'邁卡威\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">邁卡威</a>。 上賽季邁卡威在完全重建的<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivTbCBRGuF91e6cwvXwi+nOsUCFQWyjKvntqT9uy6c+e1s3eo9XM+kBUaJGaqtq7WOznXcLnooXruQBvuApuBUlN"  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'76人\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">76人</a>中迅速掌握了球隊,一開始就三雙搞定了熱火贏得了萬千眼球。後來也屢屢有經驗的表現,新秀賽季就拿過三雙的球員很少,邁卡威如今能夠說在76人站穩了腳跟。<br>       做爲上賽季弱隊的老大,<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivS12AkS11WcjnMQsTddE2yXZInIi4k8KEu5449mWp1SxBADVCHPuUFSTGH+WZuV+ecUBG6CY6mAz/Zq1mzxbFxzAG+4Cm4FSU0="  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'邁卡威\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">邁卡威</a>刷出了不錯的數據,但咱們靜下心來看一看他,仍是發現他有不少問題。首先,投籃偏弱剛剛40%的命中率和慘淡的26%的三分命中率確定是不合格的!加之身體瘦弱,個字高大橫移速度通常,防守端並無數據表現得這麼好!做爲控衛失誤偏多,離巨星仍是有必定的差距,小子你是一飛沖天,仍是迅速隕落,就看你的努力了!<br>       說完缺點,來講說優勢,做爲後衛籃板球很是突出,高大的身形能較好的影響對方的出手,也能發現己方的空位球員。突破雖然速度通常,但節奏感不錯,大局觀也在平均水準之上。提醒瘦而高大,不會投籃,突破節奏好,大局觀不錯!這在幾年前說出來是誰?沒錯斷腿前的<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivT5ggWFC92MLwFHpDNBmn4rETPyFf5XUHwripOOA15C4U+GRIwDgEI46b99l0XyUM/jR49NyMTc/6qmUGNB+hoByExmB9N/65I="  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'利文斯頓\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">利文斯頓</a>! <br>       就球隊地位而言,<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivS12AkS11WcjnMQsTddE2yXZInIi4k8KEu5449mWp1SxBADVCHPuUFSTGH+WZuV+ecUBG6CY6mAz/Zq1mzxbFxzAG+4Cm4FSU0="  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'邁卡威\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">邁卡威</a>如今是絕對的老大,球你想怎麼玩就怎麼玩,數據你想怎麼刷就怎麼刷!去年的潛力新人<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivTKm3O5uii9sKBrDcAE8/xDK4qTjgNeQuFPhkSMA4BCOOm/fZdF8lDP40ePTcjE3P+qplBjQfoaAchMZgfTf+uS"  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'諾爾\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">諾爾</a>是<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivQdeSiO+EjvouPd1sAEaAOyK4qTjgNeQuFPhkSMA4BCOOm/fZdF8lDP40ePTcjE3P+qplBjQfoaAchMZgfTf+uS"  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'藍領\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">藍領</a>,其餘人均可以清退,恩比德還受傷不能打,<a href="http://jump2.bdimg.com/safecheck/index?url=x+Z5mMbGPAsY/M/Q/im9DR3tEqEFWbC4Yzg89xsWivTbCBRGuF91e6cwvXwi+nOsUCFQWyjKvntqT9uy6c+e1s3eo9XM+kBUaJGaqtq7WOznXcLnooXruQBvuApuBUlN"  class="ps_cb" target="_blank" onclick="$.stats.track(0, \'nlp_ps_word\',{obj_name:\'76人\'});$.stats.track(\'Pb_content_wordner\',\'ps_callback_statics\')">76人</a>隊的戰績怎麼樣,就看你了!可是等到諾爾成熟(假如不是水貨),恩比德傷愈(他技術上不可能水,只是看傷病了)你就有一隊很好的內線組合了!你能把他們帶成什麼成績,這時候就是考驗你邁卡威除了刷數據還有什麼能力的時候了。'

這段提取的信息裏有着大量多餘的信息,所以須要咱們進行細分,基本思路爲將多餘的信息用正則匹配出來,而後利用正則的替換方法把這些多餘的內容替換爲空格或者換行

在這裏,咱們來構建一個處理信息的類

class Tools:
    removeImg = re.compile('<img.*?>')
    removBr = re.compile('<br>')
    removeHef = re.compile('<a href.*?>')
    removeA = re.compile('</a>')
    removeClass = re.compile('<a class.*?>|<aclass.*?>')
    removeNull = re.compile(' ')


    def remove(self,te):
        te = re.sub(self.removeImg,'',te)
        te = re. sub(self.removBr,'\n',te)
        te = re.sub(self.removeHef,'',te)
        te = re.sub(self.removeA,'',te)
        te = re.sub(self.removeClass,'',te)
        te = re.sub(self.removeNull, '', te)
        return  te

將剛纔亂碼的信息通過這個類的處理後,咱們能夠獲得下列信息:

50驚喜新人王邁卡威
上賽季數據
籃板6.2助攻6.3搶斷1.9蓋帽0.6失誤3.5犯規3得分16.7


新賽季第50位,我給上賽季的新人王邁卡威。上賽季邁卡威在完全重建的76人中迅速掌握了球隊,一開始就三雙搞定了熱火贏得了萬千眼球。後來也屢屢有經驗的表現,新秀賽季就拿過三雙的球員很少,邁卡威如今能夠說在76人站穩了腳跟。
做爲上賽季弱隊的老大,邁卡威刷出了不錯的數據,但咱們靜下心來看一看他,仍是發現他有不少問題。首先,投籃偏弱剛剛40%的命中率和慘淡的26%的三分命中率確定是不合格的!加之身體瘦弱,個字高大橫移速度通常,防守端並無數據表現得這麼好!做爲控衛失誤偏多,離巨星仍是有必定的差距,小子你是一飛沖天,仍是迅速隕落,就看你的努力了!
說完缺點,來講說優勢,做爲後衛籃板球很是突出,高大的身形能較好的影響對方的出手,也能發現己方的空位球員。突破雖然速度通常,但節奏感不錯,大局觀也在平均水準之上。提醒瘦而高大,不會投籃,突破節奏好,大局觀不錯!這在幾年前說出來是誰?沒錯斷腿前的利文斯頓!
就球隊地位而言,邁卡威如今是絕對的老大,球你想怎麼玩就怎麼玩,數據你想怎麼刷就怎麼刷!去年的潛力新人諾爾是藍領,其餘人均可以清退,恩比德還受傷不能打,76人隊的戰績怎麼樣,就看你了!可是等到諾爾成熟(假如不是水貨),恩比德傷愈(他技術上不可能水,只是看傷病了)你就有一隊很好的內線組合了!你能把他們帶成什麼成績,這時候就是考驗你邁卡威除了刷數據還有什麼能力的時候了。

這樣的表達效果就可讓我清晰看到提取到的信息,因此這個類是成功的。接下來咱們只須要將提取的信息輸出就行。

咱們先寫一個寫入標題信息和主體內容的方法,由於標題只在第一個網頁上因此能夠單獨寫一個方法

def writeText(titleText,fpath):
    try:
        with open(fpath, 'a', encoding='utf-8') as f:
            f.write(str(titleText) + '\n')
            f.write('\n')
            f.close()
    except:
        return ""
def writeUnivlist(lis,li,fpath,num):
    with open(fpath, 'a', encoding='utf-8') as f:
        for i in range(num):
            f.write(str(lis[i])+'\n')
            f.write('*'*50 + '\n')
            f.write(str(li[i]) + '\n')
            f.write('*' * 50 + '\n')
        f.close()

 接下來咱們只須要寫一個執行的主函數便可,咱們定義一下所要寫入文件的路徑,而後先寫入文件的標題

    count = 0
    url = 'https://tieba.baidu.com/p/3138733512?see_lz=1&pn=1'
    output_file = 'D:/StockInfo.txt'
    html = getHTMLText(url)
    titleText = printTitle(html)
    writeText(titleText, output_file)

接下來利用for循環來實現對每一個網頁的信息的輸入,並打印寫入文件的進度

    for i in range(5):
        i = i + 1
        lis = []
        li = []
        url = 'https://tieba.baidu.com/p/3138733512?see_lz=1&pn=' + str(i)
        html = getHTMLText(url)
        fillUnivlist(lis, li, html)
        writeUnivlist(lis, li, output_file, len(lis))
        count = count + 1
        print("\r當前進度: {:.2f}%".format(count * 100 / 5), end="")

以上就是爬取百度貼吧的帖子的因此內容,最後我認爲若是咱們將這些函數方法封裝成一個類,效果會更好。

如下是所有代碼

import  requests
from bs4 import BeautifulSoup
import re


class Tools:
    removeImg = re.compile('<img.*?>')
    removBr = re.compile('<br>')
    removeHef = re.compile('<a href.*?>')
    removeA = re.compile('</a>')
    removeClass = re.compile('<a class.*?>|<aclass.*?>')
    removeNull = re.compile(' ')


    def remove(self,te):
        te = re.sub(self.removeImg,'',te)
        te = re. sub(self.removBr,'\n',te)
        te = re.sub(self.removeHef,'',te)
        te = re.sub(self.removeA,'',te)
        te = re.sub(self.removeClass,'',te)
        te = re.sub(self.removeNull, '', te)
        return  te

textTools = Tools()

def getHTMLText(url):
    try:
        user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        headers = {'User-Agent': user_agent}
        r = requests.get(url,headers = headers)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""

def printTitle(html):
    try:
        soup = BeautifulSoup(html, "html.parser")
        titleTag = soup.find_all('title')
        patten = re.compile(r'<title>(.*?)</title>', re.S)
        title = re.findall(patten, str(titleTag))
        return title
    except:
        return ""

def fillUnivlist(lis,li,html):
    try:
        patten = re.compile(r'<div id="post_content_\d*" class="d_post_content j_d_post_content ">(.*?)</div>', re.S)
        nbaInfo = re.findall(patten, str(html))
        pattenFloor = re.compile(r'<span class="tail-info">(\d*樓)</span><span class="tail-info">', re.S)
        floorText = re.findall(pattenFloor, str(html))
        number = len(nbaInfo)
        for i in range(number):
            Info = textTools.remove(nbaInfo[i])
            Info1 = textTools.remove(floorText[i])
            lis.append(Info1)
            li.append(Info)
    except:
        return ""

def writeText(titleText,fpath):
    try:
        with open(fpath, 'a', encoding='utf-8') as f:
            f.write(str(titleText) + '\n')
            f.write('\n')
            f.close()
    except:
        return ""

def writeUnivlist(lis,li,fpath,num):
    with open(fpath, 'a', encoding='utf-8') as f:
        for i in range(num):
            f.write(str(lis[i])+'\n')
            f.write('*'*50 + '\n')
            f.write(str(li[i]) + '\n')
            f.write('*' * 50 + '\n')
        f.close()

def main():
    count = 0
    url = 'https://tieba.baidu.com/p/3138733512?see_lz=1&pn=1'
    output_file = 'D:/StockInfo.txt'
    html = getHTMLText(url)
    titleText = printTitle(html)
    writeText(titleText, output_file)
    for i in range(5):
        i = i + 1
        lis = []
        li = []
        url = 'https://tieba.baidu.com/p/3138733512?see_lz=1&pn=' + str(i)
        html = getHTMLText(url)
        fillUnivlist(lis, li, html)
        writeUnivlist(lis, li, output_file, len(lis))
        count = count + 1
        print("\r當前進度: {:.2f}%".format(count * 100 / 5), end="")

main()

這個還有不少完善的地方,但願你們多多指教

                                                  -------來自一個熱愛自學編程的小白

相關文章
相關標籤/搜索