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


在爬取糗事百科的段子后,我又在知乎上找了一個爬取百度貼吧帖子的實例,為了鞏固提升已掌握的爬蟲知識,於是我打算自己也做一個。

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

              2,顯示所爬去的樓層以及帖子題目

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

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

首先我們爬取的帖子網址為:https://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,該網址是只看樓主的帖子的網址,因此該網站的源代碼內容均為樓主所發貼的內容,爬取起來也比較方便。我們發現需要爬取的帖子一共有5頁,我們可以通過for循環來進行對每一頁信息的爬取。

接下來我們來整體構建爬取的思路:

  1,爬取該網頁的源代碼

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

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

  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()

這個還有很多完善的地方,希望大家多多指教

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM