
版權(quán)聲明:本文為作者原創(chuàng)文章,可以隨意轉(zhuǎn)載,但必須在明確位置標(biāo)明出處?。?!
前面章節(jié)的代碼可以看出,我們并沒有把「好笑數(shù)」,「圖像的url地址」取到,從糗百主頁我們可以看到有些用戶發(fā)表的段子里包含了圖片,那我們怎么取到這些圖片呢,本篇老謝將教會大家如何把網(wǎng)絡(luò)上的圖片取到本地,這個技能你們一定要get到。get到了以后再看到漂亮妹子的圖片再也不用右鍵將圖片保存在本地了,哈哈哈...;此種神功,我練定了。

練此神功口訣
通過糗事百科主頁我們可以看出有些用戶發(fā)表的段子帶了圖片,有些又沒有圖片只有文本內(nèi)容,前面文章的代碼我們只能取出每個用戶的文本內(nèi)容并不能取到圖片內(nèi)容,那么要怎么才能取到圖片內(nèi)容呢?
-
口訣一:觀察!觀察!觀察!
重要的事說三遍,做爬蟲最精髓的地方就是觀察,外加一點(diǎn)猜測,觀察什么呢,觀察帶圖片發(fā)表的用戶和只有文本用戶在html源碼中的區(qū)別,怎么在源碼中快速找到它們的區(qū)別呢,當(dāng)然是用F12開發(fā)者工具了,如果你是初學(xué)者請一定要看前面章節(jié)的內(nèi)容。 -
口訣二: 過濾,篩選
觀察過后,使用正則表達(dá)式過濾,篩選是個永恒的主題,當(dāng)然后面我們還會講到Beautify soup、xpath、lxml等第三方庫,第三方庫有它的優(yōu)勢,也有它的劣勢,就拿Beautify Soup來說,如果我們訪問的頁面不標(biāo)準(zhǔn)或者不完整,那么你或許拿不到你想要的結(jié)果。這種第三方庫解決不了的問題就只能用最原始的正則表達(dá)式了,所以學(xué)好正則表達(dá)式是你不得不具備的技能。
觀察
通過F12開發(fā)者工具我們看看帶圖片的和不帶圖片的有什么區(qū)別,當(dāng)我們通過快速定位是可以看到有圖片的包含在一個</div class='thumb'>...</div>中,這個時候我們不要馬上就取編寫正則表達(dá)式,我們還得去看看沒有圖片的有沒有包含這對元素,如果有那么我們編寫代碼的邏輯就會和沒有的不一樣了,通過觀察我們發(fā)現(xiàn)不帶圖片的并沒有包含</div class='thumb'>...</div>這對元素,如下圖:

過濾
差異找到了剩下的就是過濾了,如何過濾呢,這里就需要我們稍稍東東腦子了,想想有的用戶發(fā)表是帶了圖片的有的用戶發(fā)表是沒有帶圖片的,如果這個時候我們還像前面文章那樣把正則表達(dá)式寫在
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<div class="thumb">.*?src="(.*?)".*?<i class="number">(.*?)</i>', re.S)
這個表達(dá)式里,那么我們肯定得不到我們想要的結(jié)果,想想是為什么,因為如果某個用戶沒有發(fā)表圖片,那么我們使用這個表達(dá)式肯定連該用戶發(fā)表的文本信息也拿不到了。所以我們必須的把匹配圖片的正則表達(dá)式和之前的正則表達(dá)式分開來寫
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>', re.S)
picture = re.compile(r'<div class="thumb">.*?src="(.*?)"', re.S)
編碼實現(xiàn)
from urllib import request
from urllib import error
import re
import os
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}
def read_html(url, headers, codec):
'''[read_html]
[讀取html頁面內(nèi)容]
Arguments:
url {[string]} -- [url地址]
headers {[dict]} -- [用戶代理,這里是一個字典類型]
codec {[string]} -- [編碼方式]
Returns:
[string] -- [頁面內(nèi)容]
'''
# 構(gòu)建一個請求對象
try:
req = request.Request(url, headers=headers)
# 打開一個請求
response = request.urlopen(req)
# 讀取服務(wù)器返回的頁面數(shù)據(jù)內(nèi)容
content = response.read().decode(codec)
return content
except error.URLError as e:
print(e.reason)
return None
def match_element(content, pattern):
'''[match_element]
[匹配元素]
Arguments:
content {[string]} -- [文本內(nèi)容]
pattern {[object]} -- [匹配模式]
Returns:
[list] -- [匹配到的元素]
'''
# 匹配所有用戶信息
userinfos = re.findall(pattern, content)
return userinfos
content = read_html(url, headers, 'utf-8')
pattern = re.compile(r'<div class="article block untagged mb15[\s\S]*?class="stats-vote".*?</div>', re.S)
if content:
userinfos = match_element(content, pattern)
if userinfos:
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>', re.S)
picture = re.compile(r'<div class="thumb">.*?src="(.*?)"', re.S)
for userinfo in userinfos:
item = match_element(userinfo, pattern)
pictures = match_element(userinfo, picture)
try:
if item:
userid, name, content, num = 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)
if pictures:
path = './users/'
if not os.path.exists(path):
os.makedirs(path)
request.urlretrieve('http:' + pictures[0], path + os.path.basename(pictures[0]))
print((userid, name, content, num, pictures[0]))
else:
print((userid, name, content, num ))
except Exception as e:
print(e)
執(zhí)行結(jié)果
('/users/20735073/', '優(yōu)雅文藝范', '我小徒弟問我:“師傅,你多大了?”“22啊,問這個干嘛”“那你結(jié)婚了嗎”“沒有啊”“你怎么還不結(jié)婚???現(xiàn)在像你這么大的二胎都會走路了”“那你多大了?”“22啊”“那你孩子呢?”“在家我婆婆帶著呢,怎么了?”“。。。沒事,我就問問”', '683')
('/users/30716689/', '蒼老師首席女配音', '閨蜜懷孕中,抱怨老公回家就躲在客廳打游戲,經(jīng)常鬧別扭。一次偶然的機(jī)會,她遇到大師,大師說她家風(fēng)水有問題,要改變房間格局。閨蜜聽大師的安排,果然,老公回家就往臥室鉆,不玩手機(jī)也不吵鬧了。我好奇的問閨蜜,大師真那么靈驗,你怎么改房間格局的?閨蜜笑笑,也沒有什么,只是把客廳的路由器改在臥室窗臺。', '988')
('/users/33065062/', '耶穌安拉寧有種乎', '初中時候,班里有個很漂亮女孩,屬于班花那種,有次下課我往她臉上親了一口就跑,其實我一點(diǎn)都不喜歡她,她男友是學(xué)校外面那種小混混,我就喜歡那種被追著打一個星期的感覺,死亡如風(fēng),常伴吾身。', '411')
('/users/28491276/', '不知取什么名903…', '組織淫才給我答案', '227', '//pic.qiushibaike.com/system/pictures/11966/119663321/medium/app119663321.jpg')
('/users/26635010/', '本少丶霸道', '這個OK么', '15', '//pic.qiushibaike.com/system/pictures/10590/105905488/medium/app105905488.jpg')
('/users/10144082/', '°小姐姐', '閨蜜看上了一支TF(湯姆福特)的口紅,我們倆正在討論,老公湊過來問:聊什么呢?我:口紅,說了你也不懂。老公看到我手機(jī)上的圖片激動的說:這個我知道我知道。TFboys,他們還賣口紅啊。-_-||。。。。。', '1653')
('/users/5623502/', '"別扭丶!', '最近幾天把衣柜翻了個底朝天都沒找到我想穿的那件秋衣,問老公吧,逗逗他?!拔夷乔镆挛以趺匆舱也坏搅耍闶遣皇峭低到o誰了?”“你還找我要?還我給誰?你不定丟誰家了呢?”我擦,好想問候他祖宗十八代!', '4460')
('/users/30031169/', '空霖', '今天中午做了個終生難忘的夢夢見自己被關(guān)在監(jiān)獄里 但我一直想不起做錯了什么 夢里還記得進(jìn)過兩次監(jiān)獄 后來有人說我在KTV喝醉了 聽見有人唱歌說我 然后我就把他打了 嚇得我醒了 還在想自己有沒有進(jìn)過監(jiān)獄', '107')
('/users/31854592/', '流云徐徐', '陪4歲蘿莉小侄女去超市買東西,小姑娘見到吃的東西就走不動路了,睜著大眼睛可憐兮兮地看著我讓買零食。我就逗她:“點(diǎn)點(diǎn),叔叔沒錢,買不起?!彪S即大眼睛一陣暗淡,突然又亮起來!只見她跑到導(dǎo)購阿姨那邊跟人聊天,五分鐘以后歡快地帶著阿姨過來。阿姨說:“小姑娘夸了你半天,想把你賣了換零食?!蔽遥骸YI買買,小吃貨從小就惹不起!', '376')

從上圖可以看到我們已經(jīng)把圖片下載到本地磁盤了。
代碼解析
本次代碼新增了一個正則表達(dá)式,一個目錄創(chuàng)建,一個從通過url地址下載的函數(shù)
- picture = re.compile(r'<div class="thumb">.?src="(.?)"', re.S):
該表達(dá)式的作用是匹配到圖片的url地址 - makedirs:該函數(shù)是創(chuàng)建目錄,于我們在linux系統(tǒng)中執(zhí)行mkdir -p path1/path2/path3的功能是一樣的,它可以層級遞歸的去創(chuàng)建目錄,不需要我們手動每一級每一級的去創(chuàng)建目錄, 看到這里是不是比windows創(chuàng)建目錄方便多了。
-
urlretrieve:該函數(shù)是下載一個網(wǎng)絡(luò)對象到本地文件,該函數(shù)返回一個元組,(local_filename, headers),函數(shù)定義如下:
urllib.request.urlretrieve(url, filename=None, reporthook=None,data=None)
url就是我們要從網(wǎng)絡(luò)中下載的文件地址,filename是下載后存放在本地的文件名 - os.path.basename(pictures[0]):該函數(shù)的作用是返回文件名,什么意思呢,pictures[0]取到是//pic.qiushibaike.com/system/pictures/11966/119663395/medium/app119663395.jpg地址,通過這個函數(shù)處理后我們得到將是app119663395.jpg
note: 作為初學(xué)者,學(xué)習(xí)一種語言就是要不斷的敲代碼,只有通過敲代碼才能發(fā)現(xiàn)問題,然后修正問題,最后才能把別人的東西變成自己的,不然別人的永遠(yuǎn)都是別人的,一定要實踐,切記!切記!