Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 欧美一区二区三区免费高,日韩免费视频一区二区,国产男女爽爽爽免费视频

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          環(huán)西|S12 霸氣!吉爾伯特單飛奪冠,羅格利奇保住紅衫

          西第十二賽段,菲利普-吉爾伯特(快步)憑借自己的天賦和經(jīng)驗(yàn),單飛奪冠。此次是他今年自巴黎-魯貝后的第一次勝利,也是他職業(yè)生涯中的第十個(gè)大環(huán)賽冠軍。亞歷克斯-阿蘭布魯(西班牙農(nóng)業(yè)銀行)獲第二,費(fèi)爾南多-巴塞洛(巴斯克-穆里亞斯)第三。

          賽段

          總成績(jī)方面仍舊沒有發(fā)生改變。

          GC

          盡管最初不斷有車手嘗試進(jìn)攻,但在前110公里的比賽中,高速的節(jié)奏讓他們始終保持在一起。終點(diǎn)62公里處,維利-斯米特(喀秋莎)發(fā)起決定性的進(jìn)攻,隨后帶出了一個(gè)18人的突圍集團(tuán)。包括吉爾伯特、蒂姆-德克勒爾(快步)、瓦萊里奧-孔蒂和馬可-馬爾卡托(阿聯(lián)酋航空)、朗西斯科-何塞-本托索(CCC)、何塞-華金-羅哈斯(移動(dòng)之星)、尼基亞斯-阿恩特(太陽(yáng)網(wǎng))、約翰-德根科爾布(崔克)、海因里希-豪斯勒(巴林美利達(dá))、曼努埃萊-博阿羅(阿斯塔納)等。

          最后40公里,進(jìn)入Alto de Urruztamendi爬坡(全長(zhǎng)2.5公里,坡度9.2%),突圍集團(tuán)中德根科爾布、本托索和豪斯勒掉隊(duì)。格羅斯沙特納進(jìn)攻,格爾邁加入。兩人結(jié)伴,并領(lǐng)先40秒,但這一舉動(dòng)并沒堅(jiān)持多久。

          來(lái)到Alto de Arraiz,隨著吉爾伯特和孔蒂一起來(lái)到前方,突圍集團(tuán)的人數(shù)不斷減少。不久,吉爾伯特進(jìn)攻,阿蘭布魯和巴塞洛迅速做出反應(yīng)。吉爾伯特率先下坡后,他們繼續(xù)追趕。

          雖然兩人追擊集團(tuán)在最后兩公里與吉爾伯特時(shí)間差只有10秒,但吉爾伯特并沒有給他們機(jī)會(huì),仍加速前進(jìn)。當(dāng)他在終點(diǎn)500米處繞過最后一個(gè)彎道時(shí),環(huán)顧四周,知道自己勝券在握。阿蘭布魯和巴塞洛在吉爾伯特越過終點(diǎn)線的三秒后,也相繼撞線。

          而主集團(tuán)進(jìn)入最后一段陡峭的爬坡,不斷有車手掉隊(duì)。紅衫羅格利奇一直頂在前方,旁邊是洛佩斯、金塔納和巴爾維德。三分鐘后,主集團(tuán)輕松地越過了終點(diǎn)線。

          圖片來(lái)源:ASO

          編輯:夏春花

          原文鏈接:http://www.wildto.com/news/48775.html


          先我們介紹一個(gè) Python 庫(kù),叫做 urllib,利用它我們可以實(shí)現(xiàn) HTTP 請(qǐng)求的發(fā)送,而不用去關(guān)心 HTTP 協(xié)議本身甚至更低層的實(shí)現(xiàn)。我們只需要指定請(qǐng)求的 URL、請(qǐng)求頭、請(qǐng)求體等信息即可實(shí)現(xiàn) HTTP 請(qǐng)求的發(fā)送,同時(shí) urllib 還可以把服務(wù)器返回的響應(yīng)轉(zhuǎn)化為 Python 對(duì)象,通過該對(duì)象我們便可以方便地獲取響應(yīng)的相關(guān)信息了,如響應(yīng)狀態(tài)碼、響應(yīng)頭、響應(yīng)體等等。

          注意:在 Python 2 中,有 urllib 和 urllib2 兩個(gè)庫(kù)來(lái)實(shí)現(xiàn)請(qǐng)求的發(fā)送。而在 Python 3 中,已經(jīng)不存在 urllib2 這個(gè)庫(kù)了,統(tǒng)一為 urllib,其官方文檔鏈接為:https://docs.python.org/3/library/urllib.html。

          首先,我們來(lái)了解一下 urllib 庫(kù)的使用方法,它是 Python 內(nèi)置的 HTTP 請(qǐng)求庫(kù),也就是說(shuō)不需要額外安裝即可使用。它包含如下 4 個(gè)模塊。

          • request:它是最基本的 HTTP 請(qǐng)求模塊,可以用來(lái)模擬發(fā)送請(qǐng)求。就像在瀏覽器里輸入網(wǎng)址然后回車一樣,只需要給庫(kù)方法傳入 URL 以及額外的參數(shù),就可以模擬實(shí)現(xiàn)這個(gè)過程了。
          • error:異常處理模塊,如果出現(xiàn)請(qǐng)求錯(cuò)誤,我們可以捕獲這些異常,然后進(jìn)行重試或其他操作以保證程序不會(huì)意外終止。
          • parse:一個(gè)工具模塊,提供了許多 URL 處理方法,比如拆分、解析和合并等。
          • robotparser:主要用來(lái)識(shí)別網(wǎng)站的 robots.txt 文件,然后判斷哪些網(wǎng)站可以爬,哪些網(wǎng)站不可以爬,它其實(shí)用得比較少。

          1. 發(fā)送請(qǐng)求

          使用 urllib 的 request 模塊,我們可以方便地實(shí)現(xiàn)請(qǐng)求的發(fā)送并得到響應(yīng)。我們先來(lái)看下它的具體用法。

          urlopen

          urllib.request 模塊提供了最基本的構(gòu)造 HTTP 請(qǐng)求的方法,利用它可以模擬瀏覽器的一個(gè)請(qǐng)求發(fā)起過程,同時(shí)它還帶有處理授權(quán)驗(yàn)證(Authentication)、重定向(Redirection)、瀏覽器 Cookie 以及其他內(nèi)容。

          下面我們來(lái)看一下它的強(qiáng)大之處。這里以 Python 官網(wǎng)為例,我們來(lái)把這個(gè)網(wǎng)頁(yè)抓下來(lái):

          import urllib.request
          
          response = urllib.request.urlopen('https://www.python.org')
          print(response.read().decode('utf-8'))

          運(yùn)行結(jié)果如圖所示。

          圖 運(yùn)行結(jié)果

          這里我們只用了兩行代碼,便完成了 Python 官網(wǎng)的抓取,輸出了網(wǎng)頁(yè)的源代碼。得到源代碼之后呢?我們想要的鏈接、圖片地址、文本信息不就都可以提取出來(lái)了嗎?

          接下來(lái),看看它返回的到底是什么。利用 type 方法輸出響應(yīng)的類型:

          import urllib.request
          
          response = urllib.request.urlopen('https://www.python.org')
          print(type(response))

          輸出結(jié)果如下:

          <class 'http.client.HTTPResponse'>

          可以發(fā)現(xiàn),它是一個(gè) HTTPResposne 類型的對(duì)象,主要包含 read、readinto、getheader、getheaders、fileno 等方法,以及 msg、version、status、reason、debuglevel、closed 等屬性。

          得到這個(gè)對(duì)象之后,我們把它賦值為 response 變量,然后就可以調(diào)用這些方法和屬性,得到返回結(jié)果的一系列信息了。

          例如,調(diào)用 read 方法可以得到返回的網(wǎng)頁(yè)內(nèi)容,調(diào)用 status 屬性可以得到返回結(jié)果的狀態(tài)碼,如 200 代表請(qǐng)求成功,404 代表網(wǎng)頁(yè)未找到等。

          下面再通過一個(gè)實(shí)例來(lái)看看:

          import urllib.request
          
          response = urllib.request.urlopen('https://www.python.org')
          print(response.status)
          print(response.getheaders())
          print(response.getheader('Server'))

          運(yùn)行結(jié)果如下:

          200
          [('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'DENY'), ('Via', '1.1 vegur'), ('Via', '1.1 varnish'), ('Content-Length', '48775'), ('Accept-Ranges', 'bytes'), ('Date', 'Sun, 15 Mar 2020 13:29:01 GMT'), ('Via', '1.1 varnish'), ('Age', '708'), ('Connection', 'close'), ('X-Served-By', 'cache-bwi5120-BWI, cache-tyo19943-TYO'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '2, 518'), ('X-Timer', 'S1584278942.717942,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
          nginx

          可見,前兩個(gè)輸出分別輸出了響應(yīng)的狀態(tài)碼和響應(yīng)的頭信息,最后一個(gè)輸出通過調(diào)用 getheader 方法并傳遞一個(gè)參數(shù) Server 獲取了響應(yīng)頭中的 Server 值,結(jié)果是 nginx,意思是服務(wù)器是用 Nginx 搭建的。

          利用最基本的 urlopen 方法,可以完成最基本的簡(jiǎn)單網(wǎng)頁(yè)的 GET 請(qǐng)求抓取。

          如果想給鏈接傳遞一些參數(shù),該怎么實(shí)現(xiàn)呢?首先看一下 urlopen 方法的 API:

          urllib.request.urlopen(url, data=None, [timeout,]*, cafile=None, capath=None, cadefault=False, context=None)

          可以發(fā)現(xiàn),除了第一個(gè)參數(shù)可以傳遞 URL 之外,我們還可以傳遞其他內(nèi)容,比如 data(附加數(shù)據(jù))、timeout(超時(shí)時(shí)間)等。

          下面我們?cè)敿?xì)說(shuō)明這幾個(gè)參數(shù)的用法。

          data 參數(shù)

          data 參數(shù)是可選的。如果要添加該參數(shù),需要使用 bytes 方法將參數(shù)轉(zhuǎn)化為字節(jié)流編碼格式的內(nèi)容,即 bytes 類型。另外,如果傳遞了這個(gè)參數(shù),則它的請(qǐng)求方式就不再是 GET 方式,而是 POST 方式。

          下面用實(shí)例來(lái)看一下:

          import urllib.parse
          import urllib.request
          
          data = bytes(urllib.parse.urlencode({'name': 'germey'}), encoding='utf-8')
          response = urllib.request.urlopen('https://httpbin.org/post', data=data)
          print(response.read().decode('utf-8'))

          這里我們傳遞了一個(gè)參數(shù) word,值是 hello。它需要被轉(zhuǎn)碼成 bytes(字節(jié)流)類型。其中轉(zhuǎn)字節(jié)流采用了 bytes 方法,該方法的第一個(gè)參數(shù)需要是 str(字符串)類型,需要用 urllib.parse 模塊里的 urlencode 方法來(lái)將參數(shù)字典轉(zhuǎn)化為字符串;第二個(gè)參數(shù)指定編碼格式,這里指定為 utf-8。

          這里請(qǐng)求的站點(diǎn)是 httpbin.org,它可以提供 HTTP 請(qǐng)求測(cè)試。本次我們請(qǐng)求的 URL 為 https://httpbin.org/post,這個(gè)鏈接可以用來(lái)測(cè)試 POST 請(qǐng)求,它可以輸出 Request 的一些信息,其中就包含我們傳遞的 data 參數(shù)。

          運(yùn)行結(jié)果如下:

          {
            "args": {},
            "data": "",
            "files": {},
            "form": {
              "name": "germey"
            },
            "headers": {
              "Accept-Encoding": "identity",
              "Content-Length": "11",
              "Content-Type": "application/x-www-form-urlencoded",
              "Host": "httpbin.org",
              "User-Agent": "Python-urllib/3.7",
              "X-Amzn-Trace-Id": "Root=1-5ed27e43-9eee361fec88b7d3ce9be9db"
            },
            "json": null,
            "origin": "17.220.233.154",
            "url": "https://httpbin.org/post"
          }

          我們傳遞的參數(shù)出現(xiàn)在了 form 字段中,這表明是模擬了表單提交的方式,以 POST 方式傳輸數(shù)據(jù)。

          timeout 參數(shù)

          timeout 參數(shù)用于設(shè)置超時(shí)時(shí)間,單位為秒,意思就是如果請(qǐng)求超出了設(shè)置的這個(gè)時(shí)間,還沒有得到響應(yīng),就會(huì)拋出異常。如果不指定該參數(shù),就會(huì)使用全局默認(rèn)時(shí)間。它支持 HTTP、HTTPS、FTP 請(qǐng)求。

          下面用實(shí)例來(lái)看一下:

          import urllib.request
          
          response = urllib.request.urlopen('https://httpbin.org/get', timeout=0.1)
          print(response.read())

          運(yùn)行結(jié)果可能如下:

          During handling of the above exception, another exception occurred:
          Traceback (most recent call last): File "/var/py/python/urllibtest.py", line 4, in <module> response =
          urllib.request.urlopen('https://httpbin.org/get', timeout=0.1)
          ...
          urllib.error.URLError: <urlopen error _ssl.c:1059: The handshake operation timed out>

          這里我們?cè)O(shè)置的超時(shí)時(shí)間是 1 秒。程序運(yùn)行 1 秒過后,服務(wù)器依然沒有響應(yīng),于是拋出了 URLError 異常。該異常屬于 urllib.error 模塊,錯(cuò)誤原因是超時(shí)。

          因此,可以通過設(shè)置這個(gè)超時(shí)時(shí)間來(lái)控制一個(gè)網(wǎng)頁(yè)如果長(zhǎng)時(shí)間未響應(yīng),就跳過它的抓取。這可以利用 try…except 語(yǔ)句來(lái)實(shí)現(xiàn),相關(guān)代碼如下:

          import socket
          import urllib.request
          import urllib.error
          
          try:
              response = urllib.request.urlopen('https://httpbin.org/get', timeout=0.1)
          except urllib.error.URLError as e:
              if isinstance(e.reason, socket.timeout):
                  print('TIME OUT')

          這里我們請(qǐng)求了 https://httpbin.org/get 這個(gè)測(cè)試鏈接,設(shè)置的超時(shí)時(shí)間是 0.1 秒,然后捕獲了 URLError 這個(gè)異常,然后判斷異常類型是 socket.timeout,意思就是超時(shí)異常。因此,得出它確實(shí)是因?yàn)槌瑫r(shí)而報(bào)錯(cuò),打印輸出了 TIME OUT。

          運(yùn)行結(jié)果如下:

          TIME OUT

          按照常理來(lái)說(shuō),0.1 秒內(nèi)基本不可能得到服務(wù)器響應(yīng),因此輸出了 TIME OUT 的提示。

          通過設(shè)置 timeout 這個(gè)參數(shù)來(lái)實(shí)現(xiàn)超時(shí)處理,有時(shí)還是很有用的。

          其他參數(shù)

          除了 data 參數(shù)和 timeout 參數(shù)外,還有 context 參數(shù),它必須是 ssl.SSLContext 類型,用來(lái)指定 SSL 設(shè)置。

          此外,cafile 和 capath 這兩個(gè)參數(shù)分別指定 CA 證書和它的路徑,這個(gè)在請(qǐng)求 HTTPS 鏈接時(shí)會(huì)有用。

          cadefault 參數(shù)現(xiàn)在已經(jīng)棄用了,其默認(rèn)值為 False。

          前面講解了 urlopen 方法的用法,通過這個(gè)最基本的方法,我們可以完成簡(jiǎn)單的請(qǐng)求和網(wǎng)頁(yè)抓取。若需更加詳細(xì)的信息,可以參見官方文檔:https://docs.python.org/3/library/urllib.request.html。

          Request

          我們知道利用 urlopen 方法可以實(shí)現(xiàn)最基本請(qǐng)求的發(fā)起,但這幾個(gè)簡(jiǎn)單的參數(shù)并不足以構(gòu)建一個(gè)完整的請(qǐng)求。如果請(qǐng)求中需要加入 Headers 等信息,就可以利用更強(qiáng)大的 Request 類來(lái)構(gòu)建。

          首先,我們用實(shí)例來(lái)感受一下 Request 類的用法:

          import urllib.request
          
          request = urllib.request.Request('https://python.org')
          response = urllib.request.urlopen(request)
          print(response.read().decode('utf-8'))

          可以發(fā)現(xiàn),我們依然用 urlopen 方法來(lái)發(fā)送這個(gè)請(qǐng)求,只不過這次該方法的參數(shù)不再是 URL,而是一個(gè) Request 類型的對(duì)象。通過構(gòu)造這個(gè)數(shù)據(jù)結(jié)構(gòu),一方面我們可以將請(qǐng)求獨(dú)立成一個(gè)對(duì)象,另一方面可更加豐富和靈活地配置參數(shù)。

          下面我們看一下 Request 可以通過怎樣的參數(shù)來(lái)構(gòu)造,它的構(gòu)造方法如下:

          class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)

          其中,第一個(gè)參數(shù) url 用于請(qǐng)求 URL,這是必傳參數(shù),其他都是可選參數(shù)。

          第二個(gè)參數(shù) data 如果要傳,必須傳 bytes(字節(jié)流)類型的。如果它是字典,可以先用 urllib.parse 模塊里的 urlencode() 編碼。

          第三個(gè)參數(shù) headers 是一個(gè)字典,它就是請(qǐng)求頭。我們?cè)跇?gòu)造請(qǐng)求時(shí),既可以通過 headers 參數(shù)直接構(gòu)造,也可以通過調(diào)用請(qǐng)求實(shí)例的 add_header() 方法添加。

          添加請(qǐng)求頭最常用的方法就是通過修改 User-Agent 來(lái)偽裝瀏覽器。默認(rèn)的 User-Agent 是 Python-urllib,我們可以通過修改它來(lái)偽裝瀏覽器。比如要偽裝火狐瀏覽器,你可以把它設(shè)置為:

          Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11

          第四個(gè)參數(shù) origin_req_host 指的是請(qǐng)求方的 host 名稱或者 IP 地址。

          第五個(gè)參數(shù) unverifiable 表示這個(gè)請(qǐng)求是否是無(wú)法驗(yàn)證的,默認(rèn)是 False,意思就是說(shuō)用戶沒有足夠權(quán)限來(lái)選擇接收這個(gè)請(qǐng)求的結(jié)果。例如,我們請(qǐng)求一個(gè) HTML 文檔中的圖片,但是我們沒有自動(dòng)抓取圖像的權(quán)限,這時(shí) unverifiable 的值就是 True。

          第六個(gè)參數(shù) method 是一個(gè)字符串,用來(lái)指示請(qǐng)求使用的方法,比如 GET、POST 和 PUT 等。

          下面我們傳入多個(gè)參數(shù)來(lái)構(gòu)建請(qǐng)求:

          from urllib import request, parse
          
          url = 'https://httpbin.org/post'
          headers = {
              'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
              'Host': 'httpbin.org'
          }
          dict = {'name': 'germey'}
          data = bytes(parse.urlencode(dict), encoding='utf-8')
          req = request.Request(url=url, data=data, headers=headers, method='POST')
          response = request.urlopen(req)
          print(response.read().decode('utf-8'))

          這里我們通過 4 個(gè)參數(shù)構(gòu)造了一個(gè)請(qǐng)求,其中 url 即請(qǐng)求 URL,headers 中指定了 User-Agent 和 Host,參數(shù) data 用 urlencode 和 bytes 方法轉(zhuǎn)成字節(jié)流。另外,指定了請(qǐng)求方式為 POST。

          運(yùn)行結(jié)果如下:

          {
            "args": {},
            "data": "",
            "files": {},
            "form": {
              "name": "germey"
            },
            "headers": {
              "Accept-Encoding": "identity",
              "Content-Length": "11",
              "Content-Type": "application/x-www-form-urlencoded",
              "Host": "httpbin.org",
              "User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
              "X-Amzn-Trace-Id": "Root=1-5ed27f77-884f503a2aa6760df7679f05"
            },
            "json": null,
            "origin": "17.220.233.154",
            "url": "https://httpbin.org/post"
          }

          觀察結(jié)果可以發(fā)現(xiàn),我們成功設(shè)置了 data、headers 和 method。

          另外,headers 也可以用 add_header 方法來(lái)添加:

          req = request.Request(url=url, data=data, method='POST')
          req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')

          如此一來(lái),我們就可以更加方便地構(gòu)造請(qǐng)求,實(shí)現(xiàn)請(qǐng)求的發(fā)送啦。

          高級(jí)用法

          在上面的過程中,我們雖然可以構(gòu)造請(qǐng)求,但是對(duì)于一些更高級(jí)的操作(比如 Cookies 處理、代理設(shè)置等),該怎么辦呢?

          接下來(lái),就需要更強(qiáng)大的工具 Handler 登場(chǎng)了。簡(jiǎn)而言之,我們可以把它理解為各種處理器,有專門處理登錄驗(yàn)證的,有處理 Cookie 的,有處理代理設(shè)置的。利用它們,我們幾乎可以做到 HTTP 請(qǐng)求中所有的事情。

          首先,介紹一下 urllib.request 模塊里的 BaseHandler 類,它是所有其他 Handler 的父類,它提供了最基本的方法,例如 default_open、protocol_request 等。

          接下來(lái),就有各種 Handler 子類繼承這個(gè) BaseHandler 類,舉例如下。

          • HTTPDefaultErrorHandler 用于處理 HTTP 響應(yīng)錯(cuò)誤,錯(cuò)誤都會(huì)拋出 HTTPError 類型的異常。
          • HTTPRedirectHandler 用于處理重定向。
          • HTTPCookieProcessor 用于處理 Cookies。
          • ProxyHandler 用于設(shè)置代理,默認(rèn)代理為空。
          • HTTPPasswordMgr 用于管理密碼,它維護(hù)了用戶名和密碼的表。
          • HTTPBasicAuthHandler 用于管理認(rèn)證,如果一個(gè)鏈接打開時(shí)需要認(rèn)證,那么可以用它來(lái)解決認(rèn)證問題。

          另外,還有其他的 Handler 類,這里就不一一列舉了,詳情可以參考官方文檔: https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler。

          關(guān)于怎么使用它們,現(xiàn)在先不用著急,后面會(huì)有實(shí)例演示。

          另一個(gè)比較重要的類就是 OpenerDirector,我們可以稱為 Opener。我們之前用過 urlopen 這個(gè)方法,實(shí)際上它就是 urllib 為我們提供的一個(gè) Opener。

          那么,為什么要引入 Opener 呢?因?yàn)樾枰獙?shí)現(xiàn)更高級(jí)的功能。之前使用的 Request 和 urlopen 相當(dāng)于類庫(kù)為你封裝好了極其常用的請(qǐng)求方法,利用它們可以完成基本的請(qǐng)求,但是現(xiàn)在不一樣了,我們需要實(shí)現(xiàn)更高級(jí)的功能,所以需要深入一層進(jìn)行配置,使用更底層的實(shí)例來(lái)完成操作,所以這里就用到了 Opener。

          Opener 可以使用 open 方法,返回的類型和 urlopen 如出一轍。那么,它和 Handler 有什么關(guān)系呢?簡(jiǎn)而言之,就是利用 Handler 來(lái)構(gòu)建 Opener。

          下面用幾個(gè)實(shí)例來(lái)看看它們的用法。

          驗(yàn)證

          在訪問某些設(shè)置了身份認(rèn)證的網(wǎng)站時(shí),例如 https://ssr3.scrape.center/,我們可能會(huì)遇到這樣的認(rèn)證窗口,如圖 2- 所示:

          圖 2- 認(rèn)證窗口

          如果遇到了這種情況,那么這個(gè)網(wǎng)站就是啟用了基本身份認(rèn)證,英文叫作 HTTP Basic Access Authentication,它是一種用來(lái)允許網(wǎng)頁(yè)瀏覽器或其他客戶端程序在請(qǐng)求時(shí)提供用戶名和口令形式的身份憑證的一種登錄驗(yàn)證方式。

          那么,如果要請(qǐng)求這樣的頁(yè)面,該怎么辦呢?借助 HTTPBasicAuthHandler 就可以完成,相關(guān)代碼如下:

          from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
          from urllib.error import URLError
          
          username = 'admin'
          password = 'admin'
          url = 'https://ssr3.scrape.center/'
          
          p = HTTPPasswordMgrWithDefaultRealm()
          p.add_password(None, url, username, password)
          auth_handler = HTTPBasicAuthHandler(p)
          opener = build_opener(auth_handler)
          
          try:
              result = opener.open(url)
              html = result.read().decode('utf-8')
              print(html)
          except URLError as e:
              print(e.reason)

          這里首先實(shí)例化 HTTPBasicAuthHandler 對(duì)象,其參數(shù)是 HTTPPasswordMgrWithDefaultRealm 對(duì)象,它利用 add_password 方法添加進(jìn)去用戶名和密碼,這樣就建立了一個(gè)處理驗(yàn)證的 Handler。

          接下來(lái),利用這個(gè) Handler 并使用 build_opener 方法構(gòu)建一個(gè) Opener,這個(gè) Opener 在發(fā)送請(qǐng)求時(shí)就相當(dāng)于已經(jīng)驗(yàn)證成功了。

          接下來(lái),利用 Opener 的 open 方法打開鏈接,就可以完成驗(yàn)證了。這里獲取到的結(jié)果就是驗(yàn)證后的頁(yè)面源碼內(nèi)容。

          代理

          在做爬蟲的時(shí)候,免不了要使用代理,如果要添加代理,可以這樣做:

          from urllib.error import URLError
          from urllib.request import ProxyHandler, build_opener
          
          proxy_handler = ProxyHandler({
              'http': 'http://127.0.0.1:8080',
              'https': 'https://127.0.0.1:8080'
          })
          opener = build_opener(proxy_handler)
          try:
              response = opener.open('https://www.baidu.com')
              print(response.read().decode('utf-8'))
          except URLError as e:
              print(e.reason)

          這里我們?cè)诒镜匦枰仁孪却罱ㄒ粋€(gè) HTTP 代理,運(yùn)行在 8080 端口上。

          這里使用了 ProxyHandler,其參數(shù)是一個(gè)字典,鍵名是協(xié)議類型(比如 HTTP 或者 HTTPS 等),鍵值是代理鏈接,可以添加多個(gè)代理。

          然后,利用這個(gè) Handler 及 build_opener 方法構(gòu)造一個(gè) Opener,之后發(fā)送請(qǐng)求即可。

          Cookie

          Cookie 的處理就需要相關(guān)的 Handler 了。

          我們先用實(shí)例來(lái)看看怎樣將網(wǎng)站的 Cookie 獲取下來(lái),相關(guān)代碼如下:

          import http.cookiejar, urllib.request
          
          cookie = http.cookiejar.CookieJar()
          handler = urllib.request.HTTPCookieProcessor(cookie)
          opener = urllib.request.build_opener(handler)
          response = opener.open('https://www.baidu.com')
          for item in cookie:
              print(item.name + "=" + item.value)

          首先,我們必須聲明一個(gè) CookieJar 對(duì)象。接下來(lái),就需要利用 HTTPCookieProcessor 來(lái)構(gòu)建一個(gè) Handler,最后利用 build_opener 方法構(gòu)建出 Opener,執(zhí)行 open 函數(shù)即可。

          運(yùn)行結(jié)果如下:

          BAIDUID=A09E6C4E38753531B9FB4C60CE9FDFCB:FG=1
          BIDUPSID=A09E6C4E387535312F8AA46280C6C502
          H_PS_PSSID=31358_1452_31325_21088_31110_31253_31605_31271_31463_30823
          PSTM=1590854698
          BDSVRTM=10
          BD_HOME=1

          可以看到,這里輸出了每個(gè) Cookie 條目的名稱和值。

          不過既然能輸出,那可不可以輸出成文件格式呢?我們知道 Cookie 實(shí)際上也是以文本形式保存的。

          答案當(dāng)然是肯定的,這里通過下面的實(shí)例來(lái)看看:

          import urllib.request, http.cookiejar
          
          filename = 'cookie.txt'
          cookie = http.cookiejar.MozillaCookieJar(filename)
          handler = urllib.request.HTTPCookieProcessor(cookie)
          opener = urllib.request.build_opener(handler)
          response = opener.open('https://www.baidu.com')
          cookie.save(ignore_discard=True, ignore_expires=True)

          這時(shí) CookieJar 就需要換成 MozillaCookieJar,它在生成文件時(shí)會(huì)用到,是 CookieJar 的子類,可以用來(lái)處理 Cookie 和文件相關(guān)的事件,比如讀取和保存 Cookie,可以將 Cookie 保存成 Mozilla 型瀏覽器的 Cookie 格式。

          運(yùn)行之后,可以發(fā)現(xiàn)生成了一個(gè) cookie.txt 文件,其內(nèi)容如下:

          # Netscape HTTP Cookie File
          # http://curl.haxx.se/rfc/cookie_spec.html
          # This is a generated file!  Do not edit.
          
          .baidu.com	TRUE	/	FALSE	1622390755	BAIDUID	0B4A68D74B0C0E53E5B82AFD9BF9178F:FG=1
          .baidu.com	TRUE	/	FALSE	3738338402	BIDUPSID	0B4A68D74B0C0E53471FA6329280FA58
          .baidu.com	TRUE	/	FALSE		H_PS_PSSID	31262_1438_31325_21127_31110_31596_31673_31464_30823_26350
          .baidu.com	TRUE	/	FALSE	3738338402	PSTM	1590854754
          www.baidu.com	FALSE	/	FALSE		BDSVRTM	0
          www.baidu.com	FALSE	/	FALSE		BD_HOME	1

          另外,LWPCookieJar 同樣可以讀取和保存 Cookie,但是保存的格式和 MozillaCookieJar 不一樣,它會(huì)保存成 libwww-perl(LWP)格式的 Cookie 文件。

          要保存成 LWP 格式的 Cookie 文件,可以在聲明時(shí)就改為:

          cookie = http.cookiejar.LWPCookieJar(filename)

          此時(shí)生成的內(nèi)容如下:

          #LWP-Cookies-2.0
          Set-Cookie3: BAIDUID="1F30EEDA35C7A94320275F991CA5B3A5:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2021-05-30 16:06:39Z"; comment=bd; version=0
          Set-Cookie3: BIDUPSID=1F30EEDA35C7A9433C97CF6245CBC383; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2088-06-17 19:20:46Z"; version=0
          Set-Cookie3: H_PS_PSSID=31626_1440_21124_31069_31254_31594_30841_31673_31464_31715_30823; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
          Set-Cookie3: PSTM=1590854799; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2088-06-17 19:20:46Z"; version=0
          Set-Cookie3: BDSVRTM=11; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
          Set-Cookie3: BD_HOME=1; path="/"; domain="www.baidu.com"; path_spec; discard; version=0

          由此看來(lái),生成的格式還是有比較大差異的。

          那么,生成了 Cookie 文件后,怎樣從文件中讀取并利用呢?

          下面我們以 LWPCookieJar 格式為例來(lái)看一下:

          import urllib.request, http.cookiejar
          
          cookie = http.cookiejar.LWPCookieJar()
          cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
          handler = urllib.request.HTTPCookieProcessor(cookie)
          opener = urllib.request.build_opener(handler)
          response = opener.open('https://www.baidu.com')
          print(response.read().decode('utf-8'))

          可以看到,這里調(diào)用 load 方法來(lái)讀取本地的 Cookie 文件,獲取到了 Cookie 的內(nèi)容。不過前提是我們首先生成了 LWPCookieJar 格式的 Cookie,并保存成文件,然后讀取 Cookie 之后使用同樣的方法構(gòu)建 Handler 和 Opener 即可完成操作。

          運(yùn)行結(jié)果正常的話,會(huì)輸出百度網(wǎng)頁(yè)的源代碼。

          通過上面的方法,我們可以實(shí)現(xiàn)絕大多數(shù)請(qǐng)求功能的設(shè)置了。

          這便是 urllib 庫(kù)中 request 模塊的基本用法,如果想實(shí)現(xiàn)更多的功能,可以參考官方文檔的說(shuō)明:https://docs.python.org/3/library/urllib.request.html#basehandler-objects。

          2. 處理異常

          在前一節(jié)中,我們了解了請(qǐng)求的發(fā)送過程,但是在網(wǎng)絡(luò)不好的情況下,如果出現(xiàn)了異常,該怎么辦呢?這時(shí)如果不處理這些異常,程序很可能因報(bào)錯(cuò)而終止運(yùn)行,所以異常處理還是十分有必要的。

          urllib 的 error 模塊定義了由 request 模塊產(chǎn)生的異常。如果出現(xiàn)了問題,request 模塊便會(huì)拋出 error 模塊中定義的異常。

          URLError

          URLError 類來(lái)自 urllib 庫(kù)的 error 模塊,它繼承自 OSError 類,是 error 異常模塊的基類,由 request 模塊產(chǎn)生的異常都可以通過捕獲這個(gè)類來(lái)處理。

          它具有一個(gè)屬性 reason,即返回錯(cuò)誤的原因。

          下面用一個(gè)實(shí)例來(lái)看一下:

          from urllib import request, error
          
          try:
              response = request.urlopen('https://cuiqingcai.com/404')
          except error.URLError as e:
              print(e.reason)

          我們打開一個(gè)不存在的頁(yè)面,照理來(lái)說(shuō)應(yīng)該會(huì)報(bào)錯(cuò),但是這時(shí)我們捕獲了 URLError 這個(gè)異常,運(yùn)行結(jié)果如下:

          Not Found

          程序沒有直接報(bào)錯(cuò),而是輸出了如上內(nèi)容,這樣就可以避免程序異常終止,同時(shí)異常得到了有效處理。

          HTTPError

          它是 URLError 的子類,專門用來(lái)處理 HTTP 請(qǐng)求錯(cuò)誤,比如認(rèn)證請(qǐng)求失敗等。它有如下 3 個(gè)屬性。

          • code:返回 HTTP 狀態(tài)碼,比如 404 表示網(wǎng)頁(yè)不存在,500 表示服務(wù)器內(nèi)部錯(cuò)誤等。
          • reason:同父類一樣,用于返回錯(cuò)誤的原因。
          • headers:返回請(qǐng)求頭。

          下面我們用幾個(gè)實(shí)例來(lái)看看:

          from urllib import request, error
          
          try:
              response = request.urlopen('https://cuiqingcai.com/404')
          except error.HTTPError as e:
              print(e.reason, e.code, e.headers, sep='\n')

          運(yùn)行結(jié)果如下:

          Not Found
          404
          Server: nginx/1.10.3 (Ubuntu)
          Date: Sat, 30 May 2020 16:08:42 GMT
          Content-Type: text/html; charset=UTF-8
          Transfer-Encoding: chunked
          Connection: close
          Set-Cookie: PHPSESSID=kp1a1b0o3a0pcf688kt73gc780; path=/
          Pragma: no-cache
          Vary: Cookie
          Expires: Wed, 11 Jan 1984 05:00:00 GMT
          Cache-Control: no-cache, must-revalidate, max-age=0
          Link: <https://cuiqingcai.com/wp-json/>; rel="https://api.w.org/"

          依然是同樣的網(wǎng)址,這里捕獲了 HTTPError 異常,輸出了 reason、code 和 headers 屬性。

          因?yàn)?URLError 是 HTTPError 的父類,所以可以先選擇捕獲子類的錯(cuò)誤,再去捕獲父類的錯(cuò)誤,所以上述代碼的更好寫法如下:

          from urllib import request, error
          
          try:
              response = request.urlopen('https://cuiqingcai.com/404')
          except error.HTTPError as e:
              print(e.reason, e.code, e.headers, sep='\n')
          except error.URLError as e:
              print(e.reason)
          else:
              print('Request Successfully')

          這樣就可以做到先捕獲 HTTPError,獲取它的錯(cuò)誤原因、狀態(tài)碼、headers 等信息。如果不是 HTTPError 異常,就會(huì)捕獲 URLError 異常,輸出錯(cuò)誤原因。最后,用 else 來(lái)處理正常的邏輯。這是一個(gè)較好的異常處理寫法。

          有時(shí)候,reason 屬性返回的不一定是字符串,也可能是一個(gè)對(duì)象。再看下面的實(shí)例:

          import socket
          import urllib.request
          import urllib.error
          
          try:
              response = urllib.request.urlopen('https://www.baidu.com', timeout=0.01)
          except urllib.error.URLError as e:
              print(type(e.reason))
              if isinstance(e.reason, socket.timeout):
                  print('TIME OUT')

          這里我們直接設(shè)置超時(shí)時(shí)間來(lái)強(qiáng)制拋出 timeout 異常。

          運(yùn)行結(jié)果如下:

          <class'socket.timeout'>
          TIME OUT

          可以發(fā)現(xiàn),reason 屬性的結(jié)果是 socket.timeout 類。所以,這里我們可以用 isinstance 方法來(lái)判斷它的類型,作出更詳細(xì)的異常判斷。

          本節(jié)中,我們講述了 error 模塊的相關(guān)用法,通過合理地捕獲異常可以做出更準(zhǔn)確的異常判斷,使程序更加穩(wěn)健。

          3. 解析鏈接

          前面說(shuō)過,urllib 庫(kù)里還提供了 parse 模塊,它定義了處理 URL 的標(biāo)準(zhǔn)接口,例如實(shí)現(xiàn) URL 各部分的抽取、合并以及鏈接轉(zhuǎn)換。它支持如下協(xié)議的 URL 處理:file、ftp、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、sip、sips、snews、svn、svn+ssh、telnet 和 wais。本節(jié)中,我們介紹一下該模塊中常用的方法來(lái)看一下它的便捷之處。

          urlparse

          該方法可以實(shí)現(xiàn) URL 的識(shí)別和分段,這里先用一個(gè)實(shí)例來(lái)看一下:

          from urllib.parse import urlparse
          
          result = urlparse('https://www.baidu.com/index.html;user?id=5#comment')
          print(type(result))
          print(result)

          這里我們利用 urlparse 方法進(jìn)行了一個(gè) URL 的解析。首先,輸出了解析結(jié)果的類型,然后將結(jié)果也輸出出來(lái)。

          運(yùn)行結(jié)果如下:

          <class 'urllib.parse.ParseResult'>
          ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

          可以看到,返回結(jié)果是一個(gè) ParseResult 類型的對(duì)象,它包含 6 個(gè)部分,分別是 scheme、netloc、path、params、query 和 fragment。

          觀察一下該實(shí)例的 URL:

          https://www.baidu.com/index.html;user?id=5#comment

          可以發(fā)現(xiàn),urlparse 方法將其拆分成了 6 個(gè)部分。大體觀察可以發(fā)現(xiàn),解析時(shí)有特定的分隔符。比如,:// 前面的就是 scheme,代表協(xié)議;第一個(gè) / 符號(hào)前面便是 netloc,即域名,后面是 path,即訪問路徑;分號(hào);后面是 params,代表參數(shù);問號(hào) ? 后面是查詢條件 query,一般用作 GET 類型的 URL;井號(hào) # 后面是錨點(diǎn),用于直接定位頁(yè)面內(nèi)部的下拉位置。

          所以,可以得出一個(gè)標(biāo)準(zhǔn)的鏈接格式,具體如下:

          scheme://netloc/path;params?query#fragment

          一個(gè)標(biāo)準(zhǔn)的 URL 都會(huì)符合這個(gè)規(guī)則,利用 urlparse 方法可以將它拆分開來(lái)。

          除了這種最基本的解析方式外,urlparse 方法還有其他配置嗎?接下來(lái),看一下它的 API 用法:

          urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

          可以看到,它有 3 個(gè)參數(shù)。

          • urlstring:這是必填項(xiàng),即待解析的 URL。
          • scheme:它是默認(rèn)的協(xié)議(比如 http 或 https 等)。假如這個(gè)鏈接沒有帶協(xié)議信息,會(huì)將這個(gè)作為默認(rèn)的協(xié)議。我們用實(shí)例來(lái)看一下:
          from urllib.parse import urlparse
          
          result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https')
          print(result)

          運(yùn)行結(jié)果如下:

          ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')

          可以發(fā)現(xiàn),我們提供的 URL 沒有包含最前面的 scheme 信息,但是通過默認(rèn)的 scheme 參數(shù),返回的結(jié)果是 https。

          假設(shè)我們帶上了 scheme:

          result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', scheme='https')

          則結(jié)果如下:

          ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

          可見,scheme 參數(shù)只有在 URL 中不包含 scheme 信息時(shí)才生效。如果 URL 中有 scheme 信息,就會(huì)返回解析出的 scheme。

          • allow_fragments:即是否忽略 fragment。如果它被設(shè)置為 False,fragment 部分就會(huì)被忽略,它會(huì)被解析為 path、parameters 或者 query 的一部分,而 fragment 部分為空。

          下面我們用實(shí)例來(lái)看一下:

          from urllib.parse import urlparse
          
          result = urlparse('https://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)
          print(result)

          運(yùn)行結(jié)果如下:

          ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')

          假設(shè) URL 中不包含 params 和 query,我們?cè)偻ㄟ^實(shí)例看一下:

          from urllib.parse import urlparse
          
          result = urlparse('https://www.baidu.com/index.html#comment', allow_fragments=False)
          print(result)

          運(yùn)行結(jié)果如下:

          ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')

          可以發(fā)現(xiàn),當(dāng) URL 中不包含 params 和 query 時(shí),fragment 便會(huì)被解析為 path 的一部分。

          返回結(jié)果 ParseResult 實(shí)際上是一個(gè)元組,我們既可以用索引順序來(lái)獲取,也可以用屬性名獲取。示例如下:

          from urllib.parse import urlparse
          result = urlparse('https://www.baidu.com/index.html#comment', allow_fragments=False)
          print(result.scheme, result[0], result.netloc, result[1], sep='\n')

          這里我們分別用索引和屬性名獲取了 scheme 和 netloc,其運(yùn)行結(jié)果如下:

          https
          https
          www.baidu.com
          www.baidu.com

          可以發(fā)現(xiàn),二者的結(jié)果是一致的,兩種方法都可以成功獲取。

          urlunparse

          有了 urlparse 方法,相應(yīng)地就有了它的對(duì)立方法 urlunparse。它接收的參數(shù)是一個(gè)可迭代對(duì)象,但是它的長(zhǎng)度必須是 6,否則會(huì)拋出參數(shù)數(shù)量不足或者過多的問題。先用一個(gè)實(shí)例看一下:

          from urllib.parse import urlunparse
          data = ['https', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
          print(urlunparse(data))

          這里參數(shù) data 用了列表類型。當(dāng)然,你也可以用其他類型,比如元組或者特定的數(shù)據(jù)結(jié)構(gòu)。

          運(yùn)行結(jié)果如下:

          https://www.baidu.com/index.html;user?a=6#comment

          這樣我們就成功實(shí)現(xiàn)了 URL 的構(gòu)造。

          urlsplit

          這個(gè)方法和 urlparse 方法非常相似,只不過它不再單獨(dú)解析 params 這一部分,只返回 5 個(gè)結(jié)果。上面例子中的 params 會(huì)合并到 path 中。示例如下:

          from urllib.parse import urlsplit
          result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')
          print(result)

          運(yùn)行結(jié)果如下:

          SplitResult(scheme='https', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')

          可以發(fā)現(xiàn),返回結(jié)果是 SplitResult,它其實(shí)也是一個(gè)元組類型,既可以用屬性獲取值,也可以用索引來(lái)獲取。示例如下:

          from urllib.parse import urlsplit
          result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')
          print(result.scheme, result[0])

          運(yùn)行結(jié)果如下:

          https https

          urlunsplit

          與 urlunparse 方法類似,它也是將鏈接各個(gè)部分組合成完整鏈接的方法,傳入的參數(shù)也是一個(gè)可迭代對(duì)象,例如列表、元組等,唯一的區(qū)別是長(zhǎng)度必須為 5。示例如下:

          from urllib.parse import urlunsplit
          data = ['https', 'www.baidu.com', 'index.html', 'a=6', 'comment']
          print(urlunsplit(data))

          運(yùn)行結(jié)果如下:

          https://www.baidu.com/index.html?a=6#comment

          urljoin

          有了 urlunparse 和 urlunsplit 方法,我們可以完成鏈接的合并,不過前提是必須要有特定長(zhǎng)度的對(duì)象,鏈接的每一部分都要清晰分開。

          此外,生成鏈接還有另一個(gè)方法,那就是 urljoin 方法。我們可以提供一個(gè) base_url(基礎(chǔ)鏈接)作為第一個(gè)參數(shù),將新的鏈接作為第二個(gè)參數(shù),該方法會(huì)分析 base_url 的 scheme、netloc 和 path 這 3 個(gè)內(nèi)容并對(duì)新鏈接缺失的部分進(jìn)行補(bǔ)充,最后返回結(jié)果。

          下面通過幾個(gè)實(shí)例看一下:

          from urllib.parse import urljoin
          print(urljoin('https://www.baidu.com', 'FAQ.html'))
          print(urljoin('https://www.baidu.com', 'https://cuiqingcai.com/FAQ.html'))
          print(urljoin('https://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))
          print(urljoin('https://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))
          print(urljoin('https://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))
          print(urljoin('https://www.baidu.com', '?category=2#comment'))
          print(urljoin('www.baidu.com', '?category=2#comment'))
          print(urljoin('www.baidu.com#comment', '?category=2'))

          運(yùn)行結(jié)果如下:

          https://www.baidu.com/FAQ.html
          https://cuiqingcai.com/FAQ.html
          https://cuiqingcai.com/FAQ.html
          https://cuiqingcai.com/FAQ.html?question=2
          https://cuiqingcai.com/index.php
          https://www.baidu.com?category=2#comment
          www.baidu.com?category=2#comment
          www.baidu.com?category=2

          可以發(fā)現(xiàn),base_url 提供了三項(xiàng)內(nèi)容 scheme、netloc 和 path。如果這 3 項(xiàng)在新的鏈接里不存在,就予以補(bǔ)充;如果新的鏈接存在,就使用新的鏈接的部分。而 base_url 中的 params、query 和 fragment 是不起作用的。

          通過 urljoin 方法,我們可以輕松實(shí)現(xiàn)鏈接的解析、拼合與生成。

          urlencode

          這里我們?cè)俳榻B一個(gè)常用的方法 —— urlencode,它在構(gòu)造 GET 請(qǐng)求參數(shù)的時(shí)候非常有用,示例如下:

          from urllib.parse import urlencode
          
          params = {
              'name': 'germey',
              'age': 25
          }
          base_url = 'https://www.baidu.com?'
          url = base_url + urlencode(params)
          print(url)

          這里首先聲明一個(gè)字典來(lái)將參數(shù)表示出來(lái),然后調(diào)用 urlencode 方法將其序列化為 GET 請(qǐng)求參數(shù)。

          運(yùn)行結(jié)果如下:

          https://www.baidu.com?name=germey&age=25

          可以看到,參數(shù)成功地由字典類型轉(zhuǎn)化為 GET 請(qǐng)求參數(shù)了。

          這個(gè)方法非常常用。有時(shí)為了更加方便地構(gòu)造參數(shù),我們會(huì)事先用字典來(lái)表示。要轉(zhuǎn)化為 URL 的參數(shù)時(shí),只需要調(diào)用該方法即可。

          parse_qs

          有了序列化,必然就有反序列化。如果我們有一串 GET 請(qǐng)求參數(shù),利用 parse_qs 方法,就可以將它轉(zhuǎn)回字典,示例如下:

          from urllib.parse import parse_qs
          query = 'name=germey&age=25'
          print(parse_qs(query))

          運(yùn)行結(jié)果如下:

          {'name': ['germey'], 'age': ['25']}

          可以看到,這樣就成功轉(zhuǎn)回為字典類型了。

          parse_qsl

          另外,還有一個(gè) parse_qsl 方法,它用于將參數(shù)轉(zhuǎn)化為元組組成的列表,示例如下:

          from urllib.parse import parse_qsl
          query = 'name=germey&age=25'
          print(parse_qsl(query))

          運(yùn)行結(jié)果如下:

          [('name', 'germey'), ('age', '25')]

          可以看到,運(yùn)行結(jié)果是一個(gè)列表,而列表中的每一個(gè)元素都是一個(gè)元組,元組的第一個(gè)內(nèi)容是參數(shù)名,第二個(gè)內(nèi)容是參數(shù)值。

          quote

          該方法可以將內(nèi)容轉(zhuǎn)化為 URL 編碼的格式。URL 中帶有中文參數(shù)時(shí),有時(shí)可能會(huì)導(dǎo)致亂碼的問題,此時(shí)可以用這個(gè)方法可以將中文字符轉(zhuǎn)化為 URL 編碼,示例如下:

          from urllib.parse import quote
          keyword = '壁紙'
          url = 'https://www.baidu.com/s?wd=' + quote(keyword)
          print(url)

          這里我們聲明了一個(gè)中文的搜索文字,然后用 quote 方法對(duì)其進(jìn)行 URL 編碼,最后得到的結(jié)果如下:

          https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8

          unquote

          有了 quote 方法,當(dāng)然還有 unquote 方法,它可以進(jìn)行 URL 解碼,示例如下:

          from urllib.parse import unquote
          url = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
          print(unquote(url))

          這是上面得到的 URL 編碼后的結(jié)果,這里利用 unquote 方法還原,結(jié)果如下:

          https://www.baidu.com/s?wd=壁紙

          可以看到,利用 unquote 方法可以方便地實(shí)現(xiàn)解碼。

          本節(jié)中,我們介紹了 parse 模塊的一些常用 URL 處理方法。有了這些方法,我們可以方便地實(shí)現(xiàn) URL 的解析和構(gòu)造,建議熟練掌握。

          4. 分析 Robots 協(xié)議

          利用 urllib 的 robotparser 模塊,我們可以實(shí)現(xiàn)網(wǎng)站 Robots 協(xié)議的分析。本節(jié)中,我們來(lái)簡(jiǎn)單了解一下該模塊的用法。

          1. Robots 協(xié)議

          Robots 協(xié)議也稱作爬蟲協(xié)議、機(jī)器人協(xié)議,它的全名叫作網(wǎng)絡(luò)爬蟲排除標(biāo)準(zhǔn)(Robots Exclusion Protocol),用來(lái)告訴爬蟲和搜索引擎哪些頁(yè)面可以抓取,哪些不可以抓取。它通常是一個(gè)叫作 robots.txt 的文本文件,一般放在網(wǎng)站的根目錄下。

          當(dāng)搜索爬蟲訪問一個(gè)站點(diǎn)時(shí),它首先會(huì)檢查這個(gè)站點(diǎn)根目錄下是否存在 robots.txt 文件,如果存在,搜索爬蟲會(huì)根據(jù)其中定義的爬取范圍來(lái)爬取。如果沒有找到這個(gè)文件,搜索爬蟲便會(huì)訪問所有可直接訪問的頁(yè)面。

          下面我們看一個(gè) robots.txt 的樣例:

          User-agent: *
          Disallow: /
          Allow: /public/

          這實(shí)現(xiàn)了對(duì)所有搜索爬蟲只允許爬取 public 目錄的功能,將上述內(nèi)容保存成 robots.txt 文件,放在網(wǎng)站的根目錄下,和網(wǎng)站的入口文件(比如 index.php、index.html 和 index.jsp 等)放在一起。

          上面的 User-agent 描述了搜索爬蟲的名稱,這里將其設(shè)置為 * 則代表該協(xié)議對(duì)任何爬取爬蟲有效。比如,我們可以設(shè)置:

          User-agent: Baiduspider

          這就代表我們?cè)O(shè)置的規(guī)則對(duì)百度爬蟲是有效的。如果有多條 User-agent 記錄,就有多個(gè)爬蟲會(huì)受到爬取限制,但至少需要指定一條。

          Disallow 指定了不允許抓取的目錄,比如上例子中設(shè)置為 / 則代表不允許抓取所有頁(yè)面。

          Allow 一般和 Disallow 一起使用,一般不會(huì)單獨(dú)使用,用來(lái)排除某些限制。上例中我們?cè)O(shè)置為 /public/,則表示所有頁(yè)面不允許抓取,但可以抓取 public 目錄。

          下面我們?cè)賮?lái)看幾個(gè)例子。禁止所有爬蟲訪問任何目錄的代碼如下:

          User-agent: *
          Disallow: /

          允許所有爬蟲訪問任何目錄的代碼如下:

          User-agent: *
          Disallow:

          另外,直接把 robots.txt 文件留空也是可以的。

          禁止所有爬蟲訪問網(wǎng)站某些目錄的代碼如下:

          User-agent: *
          Disallow: /private/
          Disallow: /tmp/

          只允許某一個(gè)爬蟲訪問的代碼如下:

          User-agent: WebCrawler
          Disallow:
          User-agent: *
          Disallow: /

          這些是 robots.txt 的一些常見寫法。

          爬蟲名稱

          大家可能會(huì)疑惑,爬蟲名是從哪兒來(lái)的?為什么就叫這個(gè)名?其實(shí)它是有固定名字的了,比如百度的就叫作 BaiduSpider。表 2- 列出了一些常見搜索爬蟲的名稱及對(duì)應(yīng)的網(wǎng)站。

          表 一些常見搜索爬蟲的名稱及其對(duì)應(yīng)的網(wǎng)站

          爬蟲名稱

          名稱

          網(wǎng)站

          BaiduSpider

          百度

          www.baidu.com

          Googlebot

          谷歌

          www.google.com

          360Spider

          360 搜索

          www.so.com

          YodaoBot

          有道

          www.youdao.com

          ia_archiver

          Alexa

          www.alexa.cn

          Scooter

          altavista

          www.altavista.com

          Bingbot

          必應(yīng)

          www.bing.com

          robotparser

          了解 Robots 協(xié)議之后,我們就可以使用 robotparser 模塊來(lái)解析 robots.txt 了。該模塊提供了一個(gè)類 RobotFileParser,它可以根據(jù)某網(wǎng)站的 robots.txt 文件來(lái)判斷一個(gè)爬蟲是否有權(quán)限來(lái)爬取這個(gè)網(wǎng)頁(yè)。

          該類用起來(lái)非常簡(jiǎn)單,只需要在構(gòu)造方法里傳入 robots.txt 的鏈接即可。首先看一下它的聲明:

          urllib.robotparser.RobotFileParser(url='')

          當(dāng)然,也可以在聲明時(shí)不傳入,默認(rèn)為空,最后再使用 set_url 方法設(shè)置一下即可。

          下面列出了這個(gè)類常用的幾個(gè)方法。

          • set_url:用來(lái)設(shè)置 robots.txt 文件的鏈接。如果在創(chuàng)建 RobotFileParser 對(duì)象時(shí)傳入了鏈接,那么就不需要再使用這個(gè)方法設(shè)置了。
          • read:讀取 robots.txt 文件并進(jìn)行分析。注意,這個(gè)方法執(zhí)行一個(gè)讀取和分析操作,如果不調(diào)用這個(gè)方法,接下來(lái)的判斷都會(huì)為 False,所以一定記得調(diào)用這個(gè)方法。這個(gè)方法不會(huì)返回任何內(nèi)容,但是執(zhí)行了讀取操作。
          • parse:用來(lái)解析 robots.txt 文件,傳入的參數(shù)是 robots.txt 某些行的內(nèi)容,它會(huì)按照 robots.txt 的語(yǔ)法規(guī)則來(lái)分析這些內(nèi)容。
          • can_fetch:該方法用兩個(gè)參數(shù),第一個(gè)是 User-Agent,第二個(gè)是要抓取的 URL。返回的內(nèi)容是該搜索引擎是否可以抓取這個(gè) URL,返回結(jié)果是 True 或 False。
          • mtime:返回的是上次抓取和分析 robots.txt 的時(shí)間,這對(duì)于長(zhǎng)時(shí)間分析和抓取的搜索爬蟲是很有必要的,你可能需要定期檢查來(lái)抓取最新的 robots.txt。
          • modified:它同樣對(duì)長(zhǎng)時(shí)間分析和抓取的搜索爬蟲很有幫助,將當(dāng)前時(shí)間設(shè)置為上次抓取和分析 robots.txt 的時(shí)間。

          下面我們用實(shí)例來(lái)看一下:

          from urllib.robotparser import RobotFileParser
          rp = RobotFileParser()
          rp.set_url('https://www.baidu.com/robots.txt')
          rp.read()
          print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
          print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
          print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

          這里以百度為例,首先創(chuàng)建 RobotFileParser 對(duì)象,然后通過 set_url 方法設(shè)置了 robots.txt 的鏈接。當(dāng)然,不用這個(gè)方法的話,可以在聲明時(shí)直接用如下方法設(shè)置:

          rp = RobotFileParser('https://www.baidu.com/robots.txt')

          接著利用 can_fetch 方法判斷網(wǎng)頁(yè)是否可以被抓取。

          運(yùn)行結(jié)果如下:

          True
          True
          False

          這里同樣可以使用 parse 方法執(zhí)行讀取和分析,示例如下:
          可以看到這里我們利用 Baiduspider 可以抓取百度等首頁(yè)以及 homepage 頁(yè)面,但是 Googlebot 就不能抓取 homepage 頁(yè)面。

          打開百度的 robots.txt 文件看下,可以看到如下的信息:

          User-agent: Baiduspider
          Disallow: /baidu
          Disallow: /s?
          Disallow: /ulink?
          Disallow: /link?
          Disallow: /home/news/data/
          Disallow: /bh
          User-agent: Googlebot
          Disallow: /baidu
          Disallow: /s?
          Disallow: /shifen/
          Disallow: /homepage/
          Disallow: /cpro
          Disallow: /ulink?
          Disallow: /link?
          Disallow: /home/news/data/
          Disallow: /bh

          由此我們可以看到,Baiduspider 沒有限制 homepage 頁(yè)面的抓取,而 Googlebot 則限制了 homepage 頁(yè)面的抓取。

          這里同樣可以使用 parse 方法執(zhí)行讀取和分析,示例如下:

          from urllib.request import urlopen
          from urllib.robotparser import RobotFileParser
          
          rp = RobotFileParser()
          rp.parse(urlopen('https://www.baidu.com/robots.txt').read().decode('utf-8').split('\n'))
          print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
          print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
          print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

          運(yùn)行結(jié)果一樣:

          True
          True
          False

          本節(jié)介紹了 robotparser 模塊的基本用法和實(shí)例,利用它,我們可以方便地判斷哪些頁(yè)面可以抓取,哪些頁(yè)面不可以抓取。

          5. 總結(jié)

          本節(jié)內(nèi)容比較多,我們介紹了 urllib 的 request、error、parse、robotparser 模塊的基本用法。這些是一些基礎(chǔ)模塊,其中有一些模塊的實(shí)用性還是很強(qiáng)的,比如我們可以利用 parse 模塊來(lái)進(jìn)行 URL 的各種處理。

          本節(jié)代碼:https://github.com/Python3WebSpider/UrllibTest。

          西第十二賽段,菲利普-吉爾伯特(快步)憑借自己的天賦和經(jīng)驗(yàn),單飛奪冠。此次是他今年自巴黎-魯貝后的第一次勝利,也是他職業(yè)生涯中的第十個(gè)大環(huán)賽冠軍。亞歷克斯-阿蘭布魯(西班牙農(nóng)業(yè)銀行)獲第二,費(fèi)爾南多-巴塞洛(巴斯克-穆里亞斯)第三。

          賽段

          總成績(jī)方面仍舊沒有發(fā)生改變。

          GC

          盡管最初不斷有車手嘗試進(jìn)攻,但在前110公里的比賽中,高速的節(jié)奏讓他們始終保持在一起。終點(diǎn)62公里處,維利-斯米特(喀秋莎)發(fā)起決定性的進(jìn)攻,隨后帶出了一個(gè)18人的突圍集團(tuán)。包括吉爾伯特、蒂姆-德克勒爾(快步)、瓦萊里奧-孔蒂和馬可-馬爾卡托(阿聯(lián)酋航空)、朗西斯科-何塞-本托索(CCC)、何塞-華金-羅哈斯(移動(dòng)之星)、尼基亞斯-阿恩特(太陽(yáng)網(wǎng))、約翰-德根科爾布(崔克)、海因里希-豪斯勒(巴林美利達(dá))、曼努埃萊-博阿羅(阿斯塔納)等。

          最后40公里,進(jìn)入Alto de Urruztamendi爬坡(全長(zhǎng)2.5公里,坡度9.2%),突圍集團(tuán)中德根科爾布、本托索和豪斯勒掉隊(duì)。格羅斯沙特納進(jìn)攻,格爾邁加入。兩人結(jié)伴,并領(lǐng)先40秒,但這一舉動(dòng)并沒堅(jiān)持多久。

          來(lái)到Alto de Arraiz,隨著吉爾伯特和孔蒂一起來(lái)到前方,突圍集團(tuán)的人數(shù)不斷減少。不久,吉爾伯特進(jìn)攻,阿蘭布魯和巴塞洛迅速做出反應(yīng)。吉爾伯特率先下坡后,他們繼續(xù)追趕。

          雖然兩人追擊集團(tuán)在最后兩公里與吉爾伯特時(shí)間差只有10秒,但吉爾伯特并沒有給他們機(jī)會(huì),仍加速前進(jìn)。當(dāng)他在終點(diǎn)500米處繞過最后一個(gè)彎道時(shí),環(huán)顧四周,知道自己勝券在握。阿蘭布魯和巴塞洛在吉爾伯特越過終點(diǎn)線的三秒后,也相繼撞線。

          而主集團(tuán)進(jìn)入最后一段陡峭的爬坡,不斷有車手掉隊(duì)。紅衫羅格利奇一直頂在前方,旁邊是洛佩斯、金塔納和巴爾維德。三分鐘后,主集團(tuán)輕松地越過了終點(diǎn)線。

          圖片來(lái)源:ASO

          編輯:夏春花

          原文鏈接:http://www.wildto.com/news/48775.html


          主站蜘蛛池模板: 亚洲电影唐人社一区二区| 日韩毛片基地一区二区三区| 亚洲日韩国产精品第一页一区| 美女一区二区三区| 日韩AV片无码一区二区不卡| 国偷自产一区二区免费视频| 一区二区视频免费观看| 日韩在线一区高清在线| 一区一区三区产品乱码| 无码乱人伦一区二区亚洲| 无码午夜人妻一区二区三区不卡视频| 一区二区三区杨幂在线观看| 冲田杏梨高清无一区二区| 福利一区二区视频| 天码av无码一区二区三区四区| 精品日韩一区二区| 日韩精品一区二区三区中文3d| 国产丝袜无码一区二区视频| 色一乱一伦一区一直爽| 无码日韩精品一区二区人妻| 日本精品夜色视频一区二区| 激情啪啪精品一区二区| 久久99久久无码毛片一区二区| 国产婷婷一区二区三区| 中文字幕精品亚洲无线码一区| 亚洲综合av永久无码精品一区二区 | 熟女性饥渴一区二区三区| 国产激情一区二区三区 | 国产成人无码一区二区三区| 无码人妻精品一区二区三区在线 | 无码国产精品一区二区免费I6| 日本无卡码一区二区三区| 欧美av色香蕉一区二区蜜桃小说| 最新中文字幕一区二区乱码 | 日韩视频在线一区| 国产一区二区三区播放| 伦理一区二区三区| 无码乱码av天堂一区二区| 精品无码国产一区二区三区麻豆| 亚洲福利视频一区二区| 亚州日本乱码一区二区三区|