在爬取糗事百科的段子後,我又在知乎上找了一個爬取百度貼吧帖子的實例,爲了鞏固提高已掌握的爬蟲知識,因而我打算本身也作一個。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()
這個還有不少完善的地方,但願你們多多指教
-------來自一個熱愛自學編程的小白