版權(quán)聲明:本文為作者原創(chuàng)文章泥兰,可以隨意轉(zhuǎn)載,但必須在明確位置表明出處V缙帧!筒主!
通過上一遍文章我們對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é)議會在該系列之后的文章中對它做出詳細(xì)的講解。這里我們只需要知道urllib的作用是將網(wǎng)頁上的內(nèi)容取回到本地绍哎。取回到本地后它就是文本信息了来农,而要從文本信息中提取我們需要的信息就需要我們使用re模塊(正則表達(dá)式模塊)。
查看頁面元素
首先我們來看一下糗事百科的主頁長什么樣子
從主頁上可以看到每一個(gè)用戶發(fā)表的一條段子都是一塊崇堰,一塊的分割開的沃于。這就是html標(biāo)簽語言<div>...</div>的作用,如果你對htm標(biāo)簽語言不熟習(xí)海诲,可以花個(gè)半天時(shí)間去了解了解繁莹,了解html標(biāo)簽語言有助于更好的理解網(wǎng)頁的布局,當(dāng)然對你編寫正則表達(dá)式規(guī)則也有莫大的幫助特幔。
F12開發(fā)則工具
瀏覽器上都自帶了開發(fā)則工具咨演,只要你是web開發(fā)人員,這個(gè)工具你肯定會使用到蚯斯,不管你是調(diào)試JS薄风,還是css都會用到此工具饵较。當(dāng)然我們爬蟲程序更需要用到此工具了,下面我們在糗事百科的頁面按下鍵盤上的F12將會出現(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菜單下移動鼠標(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)簽,每一對這樣的標(biāo)簽表示包含一個(gè)用戶的所有信息卸耘,所以我們要查看“遇奸”這個(gè)用戶發(fā)布的段子退敦,那么肯定是在<div class="article block untagged mb15 typs_long" id="qiushi_tag_119595801"></div>之間了。
快速的定位到發(fā)表的段子
- 點(diǎn)擊菜單欄最左邊的按鈕蚣抗。
-
滑動鼠標(biāo)到發(fā)表的段子上侈百。
是的只需要兩步,你可以看到開發(fā)則工具界面就定位到發(fā)布的段子上了翰铡。
是時(shí)候表演真正的技術(shù)了
通過上面的介紹我們對頁面的基本元素已經(jīng)有所了解了钝域,那么如何從頁面元素中獲取用戶發(fā)表的段子呢,這里我們定一個(gè)目標(biāo)锭魔,我們需要獲取用戶名例证、用戶ID、段子迷捧、好笑數(shù)织咧、如果用戶發(fā)表的是圖片我們還需要獲取圖片的url地址,下面正則表達(dá)式這位英雄就要登場了漠秋。
獲取段子
當(dāng)然最重要的就是段子了笙蒙,我們的目的不就是為了爬取段子嗎。首先我們在糗百頁面右鍵鼠標(biāo)-->產(chǎn)看頁面源代碼庆锦,然后Ctrl + a(全選)捅位, Ctrl + c(復(fù)制), 然后粘貼到正則表達(dá)式在線測試工具中。
我們先通過在線工具來測試我們寫的正則表達(dá)式是否正確。
爬取思想
在解決一個(gè)事情之前我們首先要做的是先在腦子里想一想該怎么去解決這個(gè)問題绿渣,第一步做什么朝群、第二步做什么、最后做什么中符、爬蟲也一樣我們首先要去想一想怎么去爬到我們需要的信息姜胖。從文章的前半部分開發(fā)者工具截圖的Elements可以看出每個(gè)用戶的信息都是包含在一對<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)擊[測試匹配]發(fā)現(xiàn)匹配結(jié)果為[沒有匹配]郭膛,如下圖
這是怎么回事呢晨抡,點(diǎn)符號表示匹配任何字符除換行符外,星號表示匹配前面出現(xiàn)的正則表達(dá)式零次或多次则剃,問號表示非貪婪匹配耘柱,非貪婪匹配的意思是盡可能少的匹配,我會在后面的章節(jié)中詳細(xì)說明棍现。按理說我們的正則表達(dá)式是沒有問題的暗骷濉?這里需要特別主要了從上一篇聚沙成塔--爬蟲系列(三)(正則表達(dá)式)文章介紹我們知道點(diǎn)符號匹配任何字符串(除換行符外)己肮,所以這里我們不能用點(diǎn)符號來匹配了士袄,我們需要用到特殊符號\s表示匹配所有的空白符,\S表示匹配左右的非空白符谎僻,所以這兩個(gè)符號的組合就表示匹配所以字符包括換行符娄柳,下面我們改寫正則如下:
<div class="article block untagged mb15[\s\S]*</div>
鼠標(biāo)點(diǎn)擊[測試匹配]發(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對般甲,所以就不會匹配到最外層的</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è)請求對象
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/>符號
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/', '楊槐樹的花香', '逛商場宙址,一美女把一條大狗拉到商場里了轴脐。樣子很嚇人。估計(jì)碰到一個(gè)男的抡砂,嚇得他大叫一聲:我的個(gè)媽呀大咱,拉好你老公行不?美女不愿意說男的罵她舀患。兩人糾纏看熱鬧的越來越多徽级,大家看美女把狗拉到商城都反感一邊倒支持男的。人群中有人說話聊浅,著男的怎么這樣餐抢,說狗是人家老公,真沒素質(zhì)低匙。美女聽到有人替她說話又硬氣三分旷痕。另一個(gè)人回答就是,哪狗明明是人家兒子……\x01[生氣了]\x01[生氣了]\x01[生氣了]')
('/users/24703920/', '甜甜橙※', '去浴池洗澡顽冶,發(fā)現(xiàn)一個(gè)大姐老是偷偷看我欺抗,也沒在意!等我準(zhǔn)備穿衣服的時(shí)候强重,偷瞄一眼大姐已經(jīng)穿好了绞呈!突然大姐“嗖”的飄到我跟前,用手揉了一把我 咪 咪间景,嘴里碎碎念:“這么大這么好看居然是真的佃声?!真的倘要?圾亏!”我擦。。志鹃。')
('/users/226122/', '想愛愛就別給老婆', '陰天夭问,LZ白天在家一直睡覺,3歲兒子在客廳玩耍一會跑過來問我:媽媽曹铃,你睡覺缰趋,睡醒了嗎?一會又跑回來問:媽媽铛只,你可以起床了嗎埠胖?好吧,知道你無聊沒事做了')
('/users/21674803/', '小曉超人', '我這個(gè)人最怕別人跟我比淳玩!若果你滿世界的亂拉屎直撤,我就可以把屎糊滿你的全世界!')
('/users/31240860/', '殘落之輝', '不知道有幾個(gè)人有類似的經(jīng)歷蜕着。當(dāng)你走在大街上谋竖,突然聽見一蒼老又陌生的聲音在叫你的小名,你以為是許久未見的長輩而滿懷激動的心情轉(zhuǎn)過身時(shí)……卻看見一只小土狗撒著歡的沖向一位老人……別攔我承匣,讓我咬死那只狗1统恕!韧骗!')
('/users/34710929/', '星舞飛碟', '上公交車遇到一奶奶帶一小孩嘉抒,小孩又哭又鬧,吵的全車人不得安寧袍暴,后來一高中生問他奶奶:你家小孩多少錢些侍?瞬間不哭了')
('/users/32215536/', '吃了兩碗又盛', '那天一朋友被他兒子的班主任請去談話,回來時(shí)一臉俄羅斯方塊政模。問他怎么了岗宣,他說:馬 勒戈壁的,這小兔 崽 子不知道跟哪個(gè)王 八犢 子學(xué)會了說臟話淋样!')
('/users/30381978/', 'LOVE藍(lán)可', '單身狗一枚 去參加朋友婚禮 結(jié)果把車炸成這樣了耗式,注定孤獨(dú)一生吧')
('/users/30476775/', '我的個(gè)腦子呀', '分享#今天早上去幫弟弟買作業(yè)本的時(shí)候遇到弟弟的好朋友在騎自行車,速度很快結(jié)果沒剎住車一頭撞在了樹上趁猴,我本來想關(guān)心他一下刊咳,可是想到了前幾天發(fā)生的事,我竟大笑了起來儡司。芦缰。。枫慷。。。弟弟的這個(gè)好朋友或听,剛剛買了一輛自行車勤學(xué)苦練終于學(xué)會了探孝,可不久之后聽弟弟說他掉進(jìn)了糞坑,然后不久又鉆進(jìn)了水溝誉裆,有一次來我家找我弟弟玩就把車停在了大門外顿颅,我爸說讓他推進(jìn)家里來,可是他就是不聽說沒事足丢,和弟弟玩了一會他們正準(zhǔn)備出去玩粱腻,可是發(fā)現(xiàn)他的自行車不見啦,最后他們找啊找啊急得不得了就是找不到斩跌,嚇得不敢回家绍些,就去了他奶奶家,(他奶…<span class="contentForAll">查看全文')
('/users/21447817/', '嘟嘟豆豆5', '洗洗睡覺了R弧柬批!')
('/users/30998801/', '紅塵一笑醉紅顏~', '深 夜 纏 綿,情到深處忍不住呻 吟 淺 唱袖订,老公說你小聲點(diǎn)氮帐,我說不要,那樣憋著多難受啊……然后他直接吻住我的嘴洛姑,把我憋的“嗚嗚”叫上沐,奶奶的,一腳給他踹下去了楞艾,他委屈的爬起來告訴我参咙,他聽到隔壁有說話的聲音,怕人家聽到.....我說“你不在家我每天聽他們家叫产徊,現(xiàn)在也該讓他們嘗嘗我的厲害了昂勒!”.....')
('/users/21821649/', '卟溫柔', '閨蜜老公感冒了,讓我開車捎他們兩口子去醫(yī)院打針舟铜「暧看完醫(yī)生去打針,只見那個(gè)小護(hù)士很利索的把針頭扎了進(jìn)去谆刨,閨蜜問她老公有感覺沒塘娶?她老公看了看那么細(xì)的針頭,說這么細(xì)痊夭,扎進(jìn)去能有啥感覺刁岸?閨蜜看了她老公一眼說:“老公,你終于體會到我的感受了......”\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)動,回到上海鍛煉一小時(shí)以后喝了口冰水開始胃疼疏哗∏航玻”我:“醫(yī)生我鍛煉了以后胃疼什么情況?以前沒發(fā)生過這種事返奉”锤椋”醫(yī)生:“你不用緊張,這種情況不用吃藥芽偏,過一會自己就好了雷逆。”我:“謝謝哮针,那耽誤我晚上喝酒不关面?”醫(yī)生:“那我還是給你開點(diǎn)藥吧∈幔”我:......')
('/users/16696217/', '黃半仙Demigo…', '男:等太,,蛮放,缩抡,,包颁,女:瞻想。。娩嚼。蘑险。。岳悟。男:好女:來接我')
('/users/13521795/', '哥褲襠有殺氣', '萬惡的馬賽克(轉(zhuǎn))')
('/users/30683297/', '傻妞也', '老師:“凡事要爭第一佃迄,第二和倒數(shù)第一沒什么區(qū)別」笊伲”小寶:“為什么呵俏?”老師:“我舉個(gè)例子,你說世界上最高的山峰是什么峰滔灶?”小寶:“不知道啊普碎,啥峰啊录平?”')
('/users/31087078/', '大冰冰兒兒', '別再自欺欺人了麻车,鹿晗關(guān)曉彤倆人就是在一起了缀皱,我和彭于晏也要宣布了請大家不要傷心我們會幸福的[微笑] @所有人')
('/users/9005124/', '男人不將就', '同事兒子六歲,上幼兒園动猬。經(jīng)常被一同學(xué)欺負(fù)唆鸡,老師訓(xùn)斥也不管用。同事氣不過枣察,又不能和小孩一般見識,晚上怒氣沖沖對他兒子說“你明天上學(xué)不把他(欺負(fù)他的同學(xué))揍一頓燃逻,回來你就要挨打序目,若是打贏了晚上回來還有獎勵〔螅”第二天他兒子一入園就找那小子報(bào)仇去了猿涨,還把他打的流鼻血。老師問為什么打架姆怪,他兒子理直氣壯的說“不打他晚上回去我爸要打我的叛赚。”搞的老師有點(diǎn)哭笑不得稽揭。同事被請去學(xué)校俺附,在辦公室老師當(dāng)著好幾個(gè)老師的面講給他聽,同事說當(dāng)時(shí)真覺得丟人溪掀,過后又覺得很贊事镣。當(dāng)晚同事就帶他兒子肯德基去了,還獎勵了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小諾', '小仙女請發(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)鍵伙单,爬蟲所有的動作都需要模擬人怎么去操作获高,這樣服務(wù)器才不會攔截你的訪問,user_agent表示我們使用的是什么瀏覽器去訪問的吻育,這里需要說明一下反爬策略念秧,常見的反爬策略是服務(wù)器端會監(jiān)測你的user_agent,ip, 訪問頻率布疼,如果你同一個(gè)ip地址短時(shí)間內(nèi)訪問過多摊趾,服務(wù)器端會認(rèn)為你不是人為操作,它會認(rèn)為你是個(gè)機(jī)器人游两,因?yàn)槿藶椴僮鳑]有這么頻繁砾层。所以爬和反爬是一對天敵,后面我會講爬蟲的高級應(yīng)用器罐,所謂“道高一尺梢为,魔高一丈”是也。user_agent參數(shù)可以通過開發(fā)者工具Network菜單下的Headers查看
-
Request(...):該函數(shù)是創(chuàng)建一個(gè)請求實(shí)例轰坊,我們可以看看開發(fā)文檔的描述
urlopen(...): 打開一個(gè)url铸董,該函數(shù)的定義如下
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
url參數(shù)可以是一個(gè)字符串也可以是一個(gè)Request對象,更多的介紹可以查看開發(fā)文檔read(): 讀取頁面返回的內(nèi)容肴沫。
-
decode():解碼粟害,我們讀取回來的內(nèi)容是經(jīng)過編碼后的字符串,如果不解碼就看不明白都會來的字符串颤芬,如何查看頁面是什么編碼呢悲幅?我們可以通過開發(fā)者工具查看,也可以通過查看頁面源代碼查看網(wǎng)頁編碼站蝠。
compile(): 編譯正則表達(dá)式模塊汰具,這個(gè)函數(shù)將返回一個(gè)匹配模式對象,這里推薦大家寫正則表達(dá)式的時(shí)候先把正則表達(dá)式預(yù)編譯成一個(gè)對象菱魔,這樣在查找的時(shí)候程序就不需要每次都再去編譯一次正則表達(dá)式了留荔,特別是對于在海量文本中查找的時(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á)式中用()括號括起來的表示你要取的元素,它將會以元組的形式方法胜嗓。到這里我們就完成了一個(gè)最基本的爬蟲程序了凿宾,你是不是可以收集海量段子了,時(shí)不時(shí)的給妹子發(fā)個(gè)段子兼蕊,是不是可以在妹子面前展示一下你的幽默詼諧了,哈哈件蚕。孙技。。排作。牵啦,騷年這還不夠,繼續(xù)努力吧妄痪。哈雏。。衫生。
更多的文章可以關(guān)注我的blog:http://www.gavinxyj.com