聚沙成塔--爬蟲系列(六)(請做個(gè)內(nèi)外兼修的高手)

風(fēng)清揚(yáng)

版權(quán)聲明:本文為作者原創(chuàng)文章钱豁,可以隨意轉(zhuǎn)載,但必須在明確位置標(biāo)明出處7柚I摺!

上一篇文章我們介紹了函數(shù)的使用幌蚊,以及函數(shù)設(shè)計(jì)應(yīng)該參考的原則谤碳,但這些還不夠的,函數(shù)只是讓我們的代碼可讀性、可復(fù)用性提高了。并沒有讓我們的程序看上去那么健壯颗味,專業(yè)術(shù)語叫「穩(wěn)定行」飘言;什么是穩(wěn)定性呢,就是我們的程序經(jīng)不起考驗(yàn)旦事,就像大海中的孤舟經(jīng)不起一點(diǎn)風(fēng)浪的拍打芭碍,我們上一篇文章的代碼只適合在風(fēng)平浪靜的時(shí)候出海董瞻,因?yàn)槌绦虿]有一點(diǎn)容錯(cuò)處理卷仑,下面該是修煉內(nèi)功的時(shí)候了峻村,不能只有招式?jīng)]有內(nèi)涵啊...,那樣的話你講永遠(yuǎn)成為不了高手锡凝。

內(nèi)功修煉總綱--異常處理

什么是異常處理粘昨,異常處理就是處理程序運(yùn)行期間出現(xiàn)的任何意外或異常情況,程序員的日常生活中窜锯,錯(cuò)誤幾乎是每天都會發(fā)現(xiàn)张肾,有些錯(cuò)誤是致命的,會直接導(dǎo)致程序終止锚扎,直到錯(cuò)誤被修復(fù)后再次執(zhí)行程序吞瞪,但是有些錯(cuò)誤是可以忽略的,所有我們需要有一個(gè)機(jī)制能在程序運(yùn)行中捕獲到異常信息工秩,這樣可以幫助我們更快的解決問題尸饺。

什么是錯(cuò)誤

錯(cuò)誤是指語法或是邏輯上的,語法錯(cuò)誤是值不能被解釋器解釋或不能被編譯器編譯助币,這些錯(cuò)誤必須在程序執(zhí)行前糾正浪听。

當(dāng)語法上沒有什么錯(cuò)后,就剩下邏輯上的錯(cuò)誤了眉菱,邏輯錯(cuò)誤可能是由用戶不完整或不合法的輸入所致迹栓。

什么是異常

程序出現(xiàn)了錯(cuò)誤而在正常流程以外采取的行為,這個(gè)行為分為兩個(gè)階段俭缓,首先是引起異常發(fā)生的錯(cuò)誤克伊,其次是采取可能措施的階段。

第一個(gè)階段是發(fā)生在一個(gè)異常條件后發(fā)生的华坦,只要檢測到并意識到異常條件愿吹,解釋器就會觸發(fā)一個(gè)異常。
第二個(gè)階段是異常發(fā)生后惜姐,程序員可以做出各種不同的處理邏輯犁跪,當(dāng)然也也可忽略異常。異常發(fā)生后需要特別注意的是當(dāng)前異常發(fā)生后的邏輯都不會被執(zhí)行了歹袁,如下我們定義了一個(gè)長度為4的list坷衍,而我們試圖去訪問第五個(gè)元素,那么就會產(chǎn)生一個(gè)越界的異常条舔,而hello world將不再會被執(zhí)行到

items = [1,2,3,4]
print(items[5])

print('hello world')

結(jié)果

Traceback (most recent call last):
  File "exception.py", line 2, in <module>
    print(items[5])
IndexError: list index out of range

檢測和處理異常語法

異撤愣可以通過try語句來檢測,任何在try語句塊里的代碼都將被監(jiān)測孟抗,except用來捕獲異常迁杨,任何在try語句塊里被檢測到的異常都會被except捕獲到钻心,所以對于程序員來說我們需要考慮當(dāng)異常發(fā)生后做一些收尾工作,像釋放資源仑最,重連數(shù)據(jù)庫等等...

常見的異常錯(cuò)誤

錯(cuò)誤類型 描述
AttributeError 屬性錯(cuò)誤扔役,特性引用和賦值失敗時(shí)會引發(fā)屬性錯(cuò)誤
NameError 試圖訪問的變量名不存在
SyntaxError 語法錯(cuò)誤,代碼形式錯(cuò)誤
Exception 所有異常的基類警医,因?yàn)樗衟ython異常類都是基類Exception的其中一員亿胸,異常都是從基類Exception繼承的,并且都在exceptions模塊中定義预皇。
IOError 一般常見于打開不存在文件時(shí)會引發(fā)IOError錯(cuò)誤侈玄,也可以解理為輸出輸入錯(cuò)誤
KeyError 使用了映射中不存在的關(guān)鍵字(鍵)時(shí)引發(fā)的關(guān)鍵字錯(cuò)誤
IndexError 索引錯(cuò)誤,使用的索引不存在吟温,常索引超出序列范圍序仙,什么是索引
TypeError 類型錯(cuò)誤,內(nèi)建操作或是函數(shù)應(yīng)于在了錯(cuò)誤類型的對象時(shí)會引發(fā)類型錯(cuò)誤
ZeroDivisonError 除數(shù)為0鲁豪,在用除法操作時(shí)潘悼,第二個(gè)參數(shù)為0時(shí)引發(fā)了該錯(cuò)誤
ValueError 值錯(cuò)誤,傳給對象的參數(shù)類型不正確爬橡,像是給int()函數(shù)傳入了字符串?dāng)?shù)據(jù)類型的參數(shù)治唤。

try-except語句

try-except是最常見的異常檢測捕獲寫法,它由try塊和except塊組成糙申,也可以有一個(gè)可選的錯(cuò)誤原因

try:
    try_suite # watch for exceptions here 監(jiān)控這里的異常
except Exception[, reason]:
    except_suite # exception-handling code 異常處理代碼

try-except-finally語句

在try-except語句中我們可以有多個(gè)except語句塊來捕獲不同的錯(cuò)誤類型宾添,finally語句塊是不管有沒有異常發(fā)生都會被執(zhí)行到,它通常用來最后釋放資源柜裸。

try:
    try_suite
except Exception1:
    suite_for_Exception1
except (Exception2, Exception3, Exception4):
    suite_for_Exceptions_2_3_and_4
except Exception5, Argument5:
    suite_for_Exception5_plus_argument
except (Exception6, Exception7), Argument67:
    suite_for_Exceptions6_and_7_plus_argument
except:
    suite_for_all_other_exceptions
else:
    no_exceptions_detected_suite
finally:
    always_execute_suite

總綱目錄

同過上面的介紹可能有人要問我寫代碼的時(shí)候不知道可能會發(fā)生什么異常缕陕,有可能一種異常,也有可能幾種異常疙挺,那怎么辦呢扛邑,嗯,不錯(cuò)能想到這個(gè)問題的人證明都是認(rèn)真去思考過的人铐然,下面我們看看異常的繼承關(guān)系蔬崩,這里還沒有講到類的繼承關(guān)系,不過不要緊锦爵,你就想象成你與你爹,你爺爺?shù)年P(guān)系就好奥裸,你爺爺把財(cái)產(chǎn)都留給你了你爹险掀,你爹以后也會把財(cái)產(chǎn)留給你。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

可以看到BaseException是異常的祖先湾宙,BaseException下有4兄弟樟氢,分別是SystemExit冈绊、KeyboardInterrupt、GeneratorExit埠啃、Exception異常死宣,所以如何你要捕捉Ctrl + c終端異常那么你的except語句應(yīng)該寫成except KeyboardInterrupt as e;那么從上圖我們可以看到絕大部分的異常都是繼承自Exception的碴开,所以如果我們要捕獲除SystemExit毅该、KeyboardInterrupt、GeneratorExit三種異常外都可以寫成except Exception as e潦牛;

努力做個(gè)像風(fēng)清揚(yáng)一樣的高手

通過上面的介紹眶掌,接下來我們來分析分析我為什么說之前的代碼只適合在風(fēng)平浪靜的時(shí)候出海呢,從我們的代碼可以看出我們的代碼寫出來的邏輯是理想中沒有任何錯(cuò)誤才能得出正確結(jié)果的巴碗,那么如果我們的url地址是個(gè)不可訪問的地址會發(fā)生什么呢朴爬,我們將url地址改為「https://www.qiushibaike1.com」看看會有什么結(jié)果:

from urllib import request
import re

url = 'https://www.qiushibaike1.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]} -- [用戶代理,這里是一個(gè)字典類型]
        codec {[string]} -- [編碼方式]
    
    Returns:
        [string] -- [頁面內(nèi)容]
    '''
    # 構(gòu)建一個(gè)請求對象
    req = request.Request(url, headers=headers)
    # 打開一個(gè)請求
    response = request.urlopen(req)
    # 讀取服務(wù)器返回的頁面數(shù)據(jù)內(nèi)容
    content = response.read().decode(codec)

    return content

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)
userinfos = match_element(content, pattern)

if userinfos:
    pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>', re.S)
    for userinfo in userinfos:
        item = match_element(userinfo, pattern)
        #print(item)
        if 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))

結(jié)果

Traceback (most recent call last):
 File "func_scrap.py", line 49, in <module>
   content = read_html(url, headers, 'utf-8')
 File "func_scrap.py", line 25, in read_html
   response = request.urlopen(req)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 223, in urlopen
   return opener.open(url, data, timeout)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 526, in open
   response = self._open(req, data)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 544, in _open
   '_open', req)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 504, in _call_chain
   result = func(*args)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 1361, in https_open
   context=self._context, check_hostname=self._check_hostname)
 File "E:\Program Files\Python36\lib\urllib\request.py", line 1320, in do_open
   raise URLError(err)
urllib.error.URLError: <urlopen error [Errno 11001] getaddrinfo failed>

可以從結(jié)果中看出我們解釋器檢測到了一個(gè)urllib.error.URLError異常,異常信息是urlopen error [Errno 11001] getaddrinfo failed(獲取地址信息失斠菥簟)

為程序增加容錯(cuò)處理機(jī)制具滴,讓程序變得更健壯

修改后的程序代碼如下:

from urllib import request
import re

url = 'https://www.qiushibaike1.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]} -- [用戶代理,這里是一個(gè)字典類型]
        codec {[string]} -- [編碼方式]
    
    Returns:
        [string] -- [頁面內(nèi)容]
    '''
    # 構(gòu)建一個(gè)請求對象
    try:
        req = request.Request(url, headers=headers)
        # 打開一個(gè)請求
        response = request.urlopen(req)
        # 讀取服務(wù)器返回的頁面數(shù)據(jù)內(nèi)容
        content = response.read().decode(codec)

        return content
    except Exception as e:
        print(e)
        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)
print('執(zhí)行到這里了痊银,表示我還沒有終止抵蚊!')
if content:
    userinfos = match_element(content, pattern)

    if userinfos:
        pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>', re.S)
        for userinfo in userinfos:
            item = match_element(userinfo, pattern)
            #print(item)
            if 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))

執(zhí)行結(jié)果

<urlopen error [Errno 11001] getaddrinfo failed>
執(zhí)行到這里了贞绳,表示我還沒有終止!

可以看到我們的程序并沒有在拋出異常導(dǎo)致程序終止致稀,如果我們不寫try-except再去執(zhí)行上面的代碼冈闭,程序肯定執(zhí)行不到“執(zhí)行到這里了,表示我還沒有終止抖单!”這里了萎攒。大家可以試試

urllib.error.URLError異常

從上面的途中我們可以看到程序拋出了urllib.error.URLError異常,所以我們可以捕捉這個(gè)異常矛绘,這個(gè)異常的參數(shù)定義可以查看開發(fā)文檔如下

urllib.error.URLError定義

所以我們可以把except Exception as e:耍休,改寫城except urllib.error.URLError as e:異常的精準(zhǔn)捕獲。


note: 做為開發(fā)人員要時(shí)刻提醒自己對異常货矮,對容錯(cuò)能力的處理羊精,讓我們開發(fā)的程序能夠更穩(wěn)定,更堅(jiān)固囚玫,讓我們的程序能在服務(wù)器上長時(shí)間運(yùn)行不宕機(jī)喧锦,當(dāng)然這也是成為一個(gè)高手的必要素質(zhì)读规。

歡迎關(guān)注我:「愛做飯的老謝」,老謝一直在努力...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末燃少,一起剝皮案震驚了整個(gè)濱河市束亏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阵具,老刑警劉巖碍遍,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異怔昨,居然都是意外死亡雀久,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門趁舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赖捌,“玉大人,你說我怎么就攤上這事矮烹≡奖樱” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵奉狈,是天一觀的道長卤唉。 經(jīng)常有香客問我,道長仁期,這世上最難降的妖魔是什么桑驱? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮跛蛋,結(jié)果婚禮上熬的,老公的妹妹穿的比我還像新娘。我一直安慰自己赊级,他們只是感情好押框,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著理逊,像睡著了一般橡伞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晋被,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天兑徘,我揣著相機(jī)與錄音,去河邊找鬼羡洛。 笑死挂脑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播最域,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锈麸!你這毒婦竟也來了镀脂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤忘伞,失蹤者是張志新(化名)和其女友劉穎薄翅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氓奈,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翘魄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舀奶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暑竟。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖育勺,靈堂內(nèi)的尸體忽然破棺而出但荤,到底是詐尸還是另有隱情,我是刑警寧澤涧至,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布腹躁,位于F島的核電站,受9級特大地震影響南蓬,放射性物質(zhì)發(fā)生泄漏纺非。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一赘方、第九天 我趴在偏房一處隱蔽的房頂上張望烧颖。 院中可真熱鬧,春花似錦蒜焊、人聲如沸倒信。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖悠。三九已至,卻和暖如春优妙,著一層夾襖步出監(jiān)牢的瞬間乘综,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工套硼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卡辰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像九妈,于是被迫代替她去往敵國和親反砌。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1 前言 作為一名合格的數(shù)據(jù)分析師萌朱,其完整的技術(shù)知識體系必須貫穿數(shù)據(jù)獲取宴树、數(shù)據(jù)存儲、數(shù)據(jù)提取晶疼、數(shù)據(jù)分析酒贬、數(shù)據(jù)挖掘、...
    whenif閱讀 18,072評論 45 523
  • Python異常處理 異常概念: 異常:就是不正常的情況翠霍,程序開發(fā)過程中錯(cuò)誤和BUG都是補(bǔ)充正常的情況 異常發(fā)生的...
    youngkun閱讀 924評論 0 4
  • 第十一章 使用加載項(xiàng)自定義ArcGIS界面 ||| 附錄A 自動執(zhí)行Python腳本 我們將在本章介紹以下案例: ...
    muyan閱讀 8,996評論 0 2
  • 一锭吨、簡介 Python最強(qiáng)大的結(jié)構(gòu)之一就是它的異常處理能力,所有的標(biāo)準(zhǔn)異常都使用類來實(shí)現(xiàn)寒匙,都是基類Exceptio...
    隨風(fēng)化作雨閱讀 3,073評論 0 1
  • “波羅的海上散列的成千島嶼零如,將斯德哥爾摩附近的水面全勻擺的波平如鏡,如同無限延伸的大湖锄弱,大多時(shí)候埠况,津浦無人,桅檣參...
    最親愛的李揚(yáng)閱讀 167評論 0 0