版權(quán)聲明:本文為作者原創(chuàng)文章,可以隨意轉(zhuǎn)載,但必須在明確位置表明出處?。。?/h3>
通過上一遍文章我們對(duì)python的基礎(chǔ)語法和正則表達(dá)式有了一定的了解,從這邊文章開始我們將進(jìn)入實(shí)戰(zhàn),大家不要害怕,學(xué)習(xí)爬蟲我們首先要做的是定一個(gè)目標(biāo),要相信沒有爬不下來的東西,畢竟網(wǎng)頁都是人寫得,它們也遵循h(huán)tml規(guī)則,關(guān)于html標(biāo)簽語言可以到菜鳥教材·去稍微了解一下。這里我們通過爬取糗事百科的段子來作為python3爬蟲系列的第一個(gè)教程,因?yàn)轸苁掳倏苹旧鲜庆o態(tài)網(wǎng)頁,所以這類網(wǎng)頁是是最好爬取的網(wǎng)頁,非常適合爬蟲的入門教程。
urllib和re(正則表達(dá)式模塊)
爬蟲,爬蟲,就是把網(wǎng)上的內(nèi)容爬到本地來,urllib模塊的作用就是把網(wǎng)頁上的內(nèi)容去回到本地來,urllib是封裝了http協(xié)議,至于http協(xié)議是怎么回事本篇內(nèi)容不做討論,http協(xié)議會(huì)在該系列之后的文章中對(duì)它做出詳細(xì)的講解。這里我們只需要知道urllib的作用是將網(wǎng)頁上的內(nèi)容取回到本地。取回到本地后它就是文本信息了,而要從文本信息中提取我們需要的信息就需要我們使用re模塊(正則表達(dá)式模塊)。
查看頁面元素
首先我們來看一下糗事百科的主頁長(zhǎng)什么樣子

從主頁上可以看到每一個(gè)用戶發(fā)表的一條段子都是一塊,一塊的分割開的。這就是html標(biāo)簽語言<div>...</div>的作用,如果你對(duì)htm標(biāo)簽語言不熟習(xí),可以花個(gè)半天時(shí)間去了解了解,了解html標(biāo)簽語言有助于更好的理解網(wǎng)頁的布局,當(dāng)然對(duì)你編寫正則表達(dá)式規(guī)則也有莫大的幫助。
F12開發(fā)則工具
瀏覽器上都自帶了開發(fā)則工具,只要你是web開發(fā)人員,這個(gè)工具你肯定會(huì)使用到,不管你是調(diào)試JS,還是css都會(huì)用到此工具。當(dāng)然我們爬蟲程序更需要用到此工具了,下面我們?cè)隰苁掳倏频捻撁姘聪骆I盤上的F12將會(huì)出現(xiàn)下面界面。

當(dāng)然你看到的界面可能和我的不一樣,這是瀏覽器不同造成的,我使用的是Chrome瀏覽器,如果你們使用的是IE或者Firefox瀏覽器按下F12肯定和我的是不一樣的。
Elements、Network
按下F12后在出現(xiàn)的界面中有一行菜單欄,這里我們主要用到Elements、Network兩個(gè)菜單
- Elements: 元素,指的是當(dāng)前網(wǎng)頁元素(如果是靜態(tài)網(wǎng)頁那么在當(dāng)前頁面右鍵鼠標(biāo)-->查看網(wǎng)頁源代碼所看到的頁面元素和這里看到的頁面元素是一樣的,只是這里的看到的元素是格式化了的,方便開發(fā)人員查看)
- Network: 網(wǎng)絡(luò),指的是瀏覽器和web服務(wù)器發(fā)生的所有網(wǎng)絡(luò)交互
快速定位元素
在Elements菜單下移動(dòng)鼠標(biāo)到<div class="article block untagged mb15 typs_long" id="qiushi_tag_119595801"></div>項(xiàng),可以看到頁面上有“遇奸”這個(gè)用戶的塊區(qū)域被選中了。

可以看到Elements菜單下有很多類是<div class="...">...</div>這樣的標(biāo)簽,每一對(duì)這樣的標(biāo)簽表示包含一個(gè)用戶的所有信息,所以我們要查看“遇奸”這個(gè)用戶發(fā)布的段子,那么肯定是在<div class="article block untagged mb15 typs_long" id="qiushi_tag_119595801"></div>之間了。
快速的定位到發(fā)表的段子
- 點(diǎn)擊菜單欄最左邊的按鈕。
-
滑動(dòng)鼠標(biāo)到發(fā)表的段子上。
是的只需要兩步,你可以看到開發(fā)則工具界面就定位到發(fā)布的段子上了。

是時(shí)候表演真正的技術(shù)了
通過上面的介紹我們對(duì)頁面的基本元素已經(jīng)有所了解了,那么如何從頁面元素中獲取用戶發(fā)表的段子呢,這里我們定一個(gè)目標(biāo),我們需要獲取用戶名、用戶ID、段子、好笑數(shù)、如果用戶發(fā)表的是圖片我們還需要獲取圖片的url地址,下面正則表達(dá)式這位英雄就要登場(chǎng)了。
獲取段子
當(dāng)然最重要的就是段子了,我們的目的不就是為了爬取段子嗎。首先我們?cè)隰馨夙撁嬗益I鼠標(biāo)-->產(chǎn)看頁面源代碼,然后Ctrl + a(全選), Ctrl + c(復(fù)制), 然后粘貼到正則表達(dá)式在線測(cè)試工具中。

我們先通過在線工具來測(cè)試我們寫的正則表達(dá)式是否正確。
爬取思想
在解決一個(gè)事情之前我們首先要做的是先在腦子里想一想該怎么去解決這個(gè)問題,第一步做什么、第二步做什么、最后做什么、爬蟲也一樣我們首先要去想一想怎么去爬到我們需要的信息。從文章的前半部分開發(fā)者工具截圖的Elements可以看出每個(gè)用戶的信息都是包含在一對(duì)<div class="...">...</div>之中的,所以我們的第一步是把每個(gè)用戶的所有信息取出來。
- 第一步:取出包含在<div class="..">...</div>中的所有信息
- 第二步:從第一步的結(jié)果中取出該用戶發(fā)布的段子、用戶名、用戶ID、url圖片地址、好笑數(shù)
首先取出包含在<div class="..">...</div>中的所有信息,正則表達(dá)式如下:
<div class="article block untagged mb15.*?</div>
鼠標(biāo)點(diǎn)擊[測(cè)試匹配]發(fā)現(xiàn)匹配結(jié)果為[沒有匹配],如下圖

這是怎么回事呢,點(diǎn)符號(hào)表示匹配任何字符除換行符外,星號(hào)表示匹配前面出現(xiàn)的正則表達(dá)式零次或多次,問號(hào)表示非貪婪匹配,非貪婪匹配的意思是盡可能少的匹配,我會(huì)在后面的章節(jié)中詳細(xì)說明。按理說我們的正則表達(dá)式是沒有問題的啊?這里需要特別主要了從上一篇聚沙成塔--爬蟲系列(三)(正則表達(dá)式)文章介紹我們知道點(diǎn)符號(hào)匹配任何字符串(除換行符外),所以這里我們不能用點(diǎn)符號(hào)來匹配了,我們需要用到特殊符號(hào)\s表示匹配所有的空白符,\S表示匹配左右的非空白符,所以這兩個(gè)符號(hào)的組合就表示匹配所以字符包括換行符,下面我們改寫正則如下:
<div class="article block untagged mb15[\s\S]*</div>
鼠標(biāo)點(diǎn)擊[測(cè)試匹配]發(fā)現(xiàn)匹配結(jié)果為[沒有匹配],如下圖

結(jié)果顯示了我們匹配到了25個(gè)結(jié)果,這25個(gè)結(jié)果正好是糗百一頁發(fā)布的數(shù)量,也表示一頁上有25個(gè)用戶, 但是這個(gè)時(shí)候我們查看結(jié)果的時(shí)候發(fā)現(xiàn)并沒有包含段子內(nèi)容,為什么呢?是因?yàn)槲覀兊恼齽t表達(dá)式匹配到</div>結(jié)束,但是在最外層的</div class="...">...</div>之中還有類似的div對(duì),所以就不會(huì)匹配到最外層的</div>結(jié)束標(biāo)記,修改正則表達(dá)式如下:
<div class="article block untagged mb15[\s\S]*?class="stats-vote".*?</div>
取用戶名、用戶ID、段子、圖片url地址
通過上一步我們已經(jīng)取到了25個(gè)用戶的所有信息,那么用戶名和用戶ID等等需要從這25個(gè)用戶信息中獲取到。這里我們又要回到開發(fā)者工具,通過上面所講的操作,可以輕松定位到用戶名,用戶ID,段子

可以發(fā)現(xiàn)用戶ID是在herf后面,用戶名是在<h2>...</h2>之間,段子是在<span>...</span>之間的,所以我們需要的正則表達(dá)式如下:
<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>
代碼實(shí)現(xiàn)
from urllib import request
import re
url = 'https://www.qiushibaike.com'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
headers = {'User-Agent': user_agent}
# 構(gòu)建一個(gè)請(qǐng)求對(duì)象
req = request.Request(url, headers=headers)
# 打開一個(gè)url
response = request.urlopen(req)
# 讀取服務(wù)器返回的頁面數(shù)據(jù)內(nèi)容
content = response.read().decode('utf-8')
# 匹配所有用戶信息
#pattern = re.compile(r'<div class="article block untagged mb15[\s\S]*?<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>',re.S)
pattern = re.compile(r'<div class="article block untagged mb15[\s\S]*?class="stats-vote".*?</div>', re.S)
userinfos = re.findall(pattern, content)
print(len(userinfos))
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>', re.S)
for userinfo in userinfos:
item = re.findall(pattern, userinfo)
#print(item)
userid, name, content = item[0]
# 去掉換行符,<span></span>,<br/>符號(hào)
userid = re.sub(r'\n|<span>|</span>|<br/>', '', userid)
name = re.sub(r'\n|<span>|</span>|<br/>', '', name)
content = re.sub(r'\n|<span>|</span>|<br/>', '', content)
print((userid, name, content))
運(yùn)行結(jié)果
('/users/34093917/', '楊槐樹的花香', '逛商場(chǎng),一美女把一條大狗拉到商場(chǎng)里了。樣子很嚇人。估計(jì)碰到一個(gè)男的,嚇得他大叫一聲:我的個(gè)媽呀,拉好你老公行不?美女不愿意說男的罵她。兩人糾纏看熱鬧的越來越多,大家看美女把狗拉到商城都反感一邊倒支持男的。人群中有人說話,著男的怎么這樣,說狗是人家老公,真沒素質(zhì)。美女聽到有人替她說話又硬氣三分。另一個(gè)人回答就是,哪狗明明是人家兒子……\x01[生氣了]\x01[生氣了]\x01[生氣了]')
('/users/24703920/', '甜甜橙※', '去浴池洗澡,發(fā)現(xiàn)一個(gè)大姐老是偷偷看我,也沒在意!等我準(zhǔn)備穿衣服的時(shí)候,偷瞄一眼大姐已經(jīng)穿好了!突然大姐“嗖”的飄到我跟前,用手揉了一把我 咪 咪,嘴里碎碎念:“這么大這么好看居然是真的?!真的?!”我擦。。。')
('/users/226122/', '想愛愛就別給老婆', '陰天,LZ白天在家一直睡覺,3歲兒子在客廳玩耍一會(huì)跑過來問我:媽媽,你睡覺,睡醒了嗎?一會(huì)又跑回來問:媽媽,你可以起床了嗎?好吧,知道你無聊沒事做了')
('/users/21674803/', '小曉超人', '我這個(gè)人最怕別人跟我比!若果你滿世界的亂拉屎,我就可以把屎糊滿你的全世界!')
('/users/31240860/', '殘落之輝', '不知道有幾個(gè)人有類似的經(jīng)歷。當(dāng)你走在大街上,突然聽見一蒼老又陌生的聲音在叫你的小名,你以為是許久未見的長(zhǎng)輩而滿懷激動(dòng)的心情轉(zhuǎn)過身時(shí)……卻看見一只小土狗撒著歡的沖向一位老人……別攔我,讓我咬死那只狗!?。?)
('/users/34710929/', '星舞飛碟', '上公交車遇到一奶奶帶一小孩,小孩又哭又鬧,吵的全車人不得安寧,后來一高中生問他奶奶:你家小孩多少錢?瞬間不哭了')
('/users/32215536/', '吃了兩碗又盛', '那天一朋友被他兒子的班主任請(qǐng)去談話,回來時(shí)一臉俄羅斯方塊。問他怎么了,他說:馬 勒戈壁的,這小兔 崽 子不知道跟哪個(gè)王 八犢 子學(xué)會(huì)了說臟話!')
('/users/30381978/', 'LOVE藍(lán)可', '單身狗一枚 去參加朋友婚禮 結(jié)果把車炸成這樣了,注定孤獨(dú)一生吧')
('/users/30476775/', '我的個(gè)腦子呀', '分享#今天早上去幫弟弟買作業(yè)本的時(shí)候遇到弟弟的好朋友在騎自行車,速度很快結(jié)果沒剎住車一頭撞在了樹上,我本來想關(guān)心他一下,可是想到了前幾天發(fā)生的事,我竟大笑了起來。。。。。。弟弟的這個(gè)好朋友,剛剛買了一輛自行車勤學(xué)苦練終于學(xué)會(huì)了,可不久之后聽弟弟說他掉進(jìn)了糞坑,然后不久又鉆進(jìn)了水溝,有一次來我家找我弟弟玩就把車停在了大門外,我爸說讓他推進(jìn)家里來,可是他就是不聽說沒事,和弟弟玩了一會(huì)他們正準(zhǔn)備出去玩,可是發(fā)現(xiàn)他的自行車不見啦,最后他們找啊找啊急得不得了就是找不到,嚇得不敢回家,就去了他奶奶家,(他奶…<span class="contentForAll">查看全文')
('/users/21447817/', '嘟嘟豆豆5', '洗洗睡覺了?。?)
('/users/30998801/', '紅塵一笑醉紅顏~', '深 夜 纏 綿,情到深處忍不住呻 吟 淺 唱,老公說你小聲點(diǎn),我說不要,那樣憋著多難受啊……然后他直接吻住我的嘴,把我憋的“嗚嗚”叫,奶奶的,一腳給他踹下去了,他委屈的爬起來告訴我,他聽到隔壁有說話的聲音,怕人家聽到.....我說“你不在家我每天聽他們家叫,現(xiàn)在也該讓他們嘗嘗我的厲害了!”.....')
('/users/21821649/', '卟溫柔', '閨蜜老公感冒了,讓我開車捎他們兩口子去醫(yī)院打針??赐赆t(yī)生去打針,只見那個(gè)小護(hù)士很利索的把針頭扎了進(jìn)去,閨蜜問她老公有感覺沒?她老公看了看那么細(xì)的針頭,說這么細(xì),扎進(jìn)去能有啥感覺?閨蜜看了她老公一眼說:“老公,你終于體會(huì)到我的感受了......”\x01[驚訝]\x01[驚訝]\x01[驚訝]我是不是發(fā)現(xiàn)了什么\x01[doge]\x01[doge]\x01[doge]')
('/users/12679679/', 'o初戀o', '一個(gè)客戶在我早上熟睡之際因?yàn)橛唵螁栴}給我電話,我愛發(fā)嗲,加上沒睡醒更是軟綿綿的聲音,然后連著5天他每天早上都給我打電話喊我起床,關(guān)鍵我凌晨三四點(diǎn)才睡覺的,可煩死人啦')
('/users/31973521/', '錘子是主謀', '背景:“國慶回家過了半個(gè)月荒誕的生活。一直沒有運(yùn)動(dòng),回到上海鍛煉一小時(shí)以后喝了口冰水開始胃疼。”我:“醫(yī)生我鍛煉了以后胃疼什么情況?以前沒發(fā)生過這種事?!贬t(yī)生:“你不用緊張,這種情況不用吃藥,過一會(huì)自己就好了?!蔽遥骸爸x謝,那耽誤我晚上喝酒不?”醫(yī)生:“那我還是給你開點(diǎn)藥吧?!蔽遥?.....')
('/users/16696217/', '黃半仙Demigo…', '男:,,,,,,女:。。。。。。男:好女:來接我')
('/users/13521795/', '哥褲襠有殺氣', '萬惡的馬賽克(轉(zhuǎn))')
('/users/30683297/', '傻妞也', '老師:“凡事要爭(zhēng)第一,第二和倒數(shù)第一沒什么區(qū)別?!毙殻骸盀槭裁??”老師:“我舉個(gè)例子,你說世界上最高的山峰是什么峰?”小寶:“不知道啊,啥峰啊?”')
('/users/31087078/', '大冰冰兒兒', '別再自欺欺人了,鹿晗關(guān)曉彤倆人就是在一起了,我和彭于晏也要宣布了請(qǐng)大家不要傷心我們會(huì)幸福的[微笑] @所有人')
('/users/9005124/', '男人不將就', '同事兒子六歲,上幼兒園。經(jīng)常被一同學(xué)欺負(fù),老師訓(xùn)斥也不管用。同事氣不過,又不能和小孩一般見識(shí),晚上怒氣沖沖對(duì)他兒子說“你明天上學(xué)不把他(欺負(fù)他的同學(xué))揍一頓,回來你就要挨打,若是打贏了晚上回來還有獎(jiǎng)勵(lì)。”第二天他兒子一入園就找那小子報(bào)仇去了,還把他打的流鼻血。老師問為什么打架,他兒子理直氣壯的說“不打他晚上回去我爸要打我的?!备愕睦蠋熡悬c(diǎn)哭笑不得。同事被請(qǐng)去學(xué)校,在辦公室老師當(dāng)著好幾個(gè)老師的面講給他聽,同事說當(dāng)時(shí)真覺得丟人,過后又覺得很贊。當(dāng)晚同事就帶他兒子肯德基去了,還獎(jiǎng)勵(lì)了20。之后再也沒…<span class="contentForAll">查看全文')
('/users/28264578/', '屌絲小叔', '我想說在群里搶紅包說少的發(fā),搶了個(gè)最少的0.53元,到我發(fā)時(shí)想發(fā)個(gè)5.3元手一抖53元沒了糗吧!這還不是經(jīng)典,經(jīng)典的剛洗澡時(shí)把洗面膏當(dāng)洗發(fā)膏用了,半天不起沫才發(fā)現(xiàn)用錯(cuò)了!唉')
('/users/19216493/', 'CTR小諾', '小仙女請(qǐng)發(fā)言 過麥')
('/users/9942197/', '植哥哥', '太單純,怪我不懂……')
('/users/27749688/', '無言,無緣', '兩手抓咪咪')
('/users/33390668/', '吃土吃夠了', '我爸要給我買火箭了,再過兩天就是我27歲的生日了,我跟我爸說想出去玩,讓他給我買張機(jī)票當(dāng)禮物,我爸說∶“我給你買個(gè)火箭你直接上天,免得老嫁不出去呆在地球上丟人!”我∶\x01[捂臉]\x01[捂臉]……')
('/users/26260464/', '紫燕雙飛飛飛的', '好像很拉風(fēng)。')
代碼解讀
import: 關(guān)鍵字,是導(dǎo)入python庫文件的,當(dāng)然也可以導(dǎo)入你自己寫得文件,它的意思是要引用哪個(gè)模塊,這里我們需要引用兩個(gè)模塊,一個(gè)是urllib模塊,一個(gè)是re(正則表達(dá)式模塊),引用了這兩個(gè)模塊后我們就能使用這兩個(gè)模塊提供的方法
url: 變量,這里的變量賦值為糗事百科的網(wǎng)址
-
user_agent: 用戶代理,這個(gè)參數(shù)很關(guān)鍵,爬蟲所有的動(dòng)作都需要模擬人怎么去操作,這樣服務(wù)器才不會(huì)攔截你的訪問,user_agent表示我們使用的是什么瀏覽器去訪問的,這里需要說明一下反爬策略,常見的反爬策略是服務(wù)器端會(huì)監(jiān)測(cè)你的user_agent,ip, 訪問頻率,如果你同一個(gè)ip地址短時(shí)間內(nèi)訪問過多,服務(wù)器端會(huì)認(rèn)為你不是人為操作,它會(huì)認(rèn)為你是個(gè)機(jī)器人,因?yàn)槿藶椴僮鳑]有這么頻繁。所以爬和反爬是一對(duì)天敵,后面我會(huì)講爬蟲的高級(jí)應(yīng)用,所謂“道高一尺,魔高一丈”是也。user_agent參數(shù)可以通過開發(fā)者工具Network菜單下的Headers查看
4-9 user_agent參數(shù)查看 -
Request(...):該函數(shù)是創(chuàng)建一個(gè)請(qǐng)求實(shí)例,我們可以看看開發(fā)文檔的描述
4-10 python3開發(fā)文檔urllib.request模塊定義 urlopen(...): 打開一個(gè)url,該函數(shù)的定義如下
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
url參數(shù)可以是一個(gè)字符串也可以是一個(gè)Request對(duì)象,更多的介紹可以查看開發(fā)文檔read(): 讀取頁面返回的內(nèi)容。
-
decode():解碼,我們讀取回來的內(nèi)容是經(jīng)過編碼后的字符串,如果不解碼就看不明白都會(huì)來的字符串,如何查看頁面是什么編碼呢?我們可以通過開發(fā)者工具查看,也可以通過查看頁面源代碼查看網(wǎng)頁編碼。
4-11 頁面編碼查看 compile(): 編譯正則表達(dá)式模塊,這個(gè)函數(shù)將返回一個(gè)匹配模式對(duì)象,這里推薦大家寫正則表達(dá)式的時(shí)候先把正則表達(dá)式預(yù)編譯成一個(gè)對(duì)象,這樣在查找的時(shí)候程序就不需要每次都再去編譯一次正則表達(dá)式了,特別是對(duì)于在海量文本中查找的時(shí)候預(yù)編譯正則規(guī)則尤為重要,預(yù)編譯正則規(guī)則能提高你的代碼執(zhí)行效率
findall,sub等函數(shù)上一篇聚沙成塔--爬蟲系列(三)(正則表達(dá)式)文章已經(jīng)介紹過了,這里不在累訴。
note: 這里我們用到了前面章節(jié)介紹到的python的兩種數(shù)據(jù)結(jié)果,列表和元組,還有for循環(huán)語法,函數(shù)findall返回的是一個(gè)list(列表),所以我們需要循環(huán)列表里的每個(gè)元素,每個(gè)元素代表一個(gè)被匹配上的用戶信息,正則表達(dá)式中用()括號(hào)括起來的表示你要取的元素,它將會(huì)以元組的形式方法。到這里我們就完成了一個(gè)最基本的爬蟲程序了,你是不是可以收集海量段子了,時(shí)不時(shí)的給妹子發(fā)個(gè)段子,是不是可以在妹子面前展示一下你的幽默詼諧了,哈哈。。。。,騷年這還不夠,繼續(xù)努力吧。。。。
更多的文章可以關(guān)注我的blog:http://www.gavinxyj.com


