• 熱門專題

正確認識python的request庫

作者:  發布日期:2019-06-10 18:49:54
Tag標簽:request庫,python  
  • 2015年我觸及 Python 的那時候,就得聞 Python 的網絡編程能力非常強悍。因而,在了解 Python 的基礎語法以后,我就和幾個小伙伴們一塊兒協作,試著用 Python 的 urllib 和 urllib2 庫構建了一個百度貼吧 Python 客戶端。
    然而,應用的過程中,我發現了兩個標準庫的語法并不自然,以至于可以說非常反人類——用著很不舒服。又有,我平日應用 Python 甚少涉及網絡編程的內容。因而,Python 的網絡編程就被我放下了,直至我了解了 requests 庫。

    初次了解 requests
    requests 庫的宣言是
     
    HTTP for Humans (給人用的 HTTP 庫)
     
    我們最先來檢驗一下。
     
    在網絡編程中,最為基礎的任務包括:
     
        發送請求
        登入
        獲取數據
        解析數據
        反序列化重新打印獲得的內容
     
    我們以 GitHub 為例,先看一下應用 urllib2 要怎樣做。因為要把事兒弄簡單,我們假設實現已經知道,GET 請求 https://api.github.com/ 返回的內容是個 JSON 格式的數據(事實上通過 content-type 也可以判定)。
     
    import urllib2
    import json
     
    gh_url  = 'https://api.github.com'
    cs_user = 'user'
    cs_psw  = 'password'
     
    req = urllib2.Request(gh_url)
     
    password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    password_manager.add_password(None, gh_url, cs_user, cs_psw)
     
    auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)
    opener = urllib2.build_opener(auth_manager)
     
    urllib2.install_opener(opener)
     
    handler = urllib2.urlopen(req)
     
    if handler.getcode() == requests.codes.ok:
        text = handler.read()
        d_text = json.loads(text)
        for k, v in d_text.items():
            print k, v
     
    如果運行正確,那么代碼應該返回:
     
    issues_url https://api.github.com/issues
    current_user_repositories_url https://api.github.com/user/repos{?type,page,per_page,sort}
    rate_limit_url https://api.github.com/rate_limit
    repository_url https://api.github.com/repos/{owner}/{repo}
    ...
    user_repositories_url https://api.github.com/users/{user}/repos{?type,page,per_page,sort}
    team_url https://api.github.com/teams
     
    一樣的作用,用 requests 庫則有如下代碼:
     
    import requests
     
    cs_url  = 'https://api.github.com'
    cs_user = 'user'
    cs_psw  = 'password'
    r = requests.get(cs_url, auth=(cs_user, cs_psw))
    if r.status_code == requests.codes.ok
        for k, v in r.json().items():
            print k, v
     
    溢美之詞就不比說了,讀完在這里的你內心一定會只有一聲「握草,這才算 Python 該有的模樣」。那么,接下去讓我們看一下 requests 還有哪幾種黑魔法。
     
    安裝
     
    最受歡迎的方式,是直接安裝推薦過的 Anaconda。
     
    當然如果你不愿安裝 Anaconda,那我建議你采用 pip 安裝;只需在命令行下執行:
     
    pip install requests

    基本用法
     
    requests 的主要使用方法,呃,簡直不能再主要了。最主要的操作方法,就是說以某種 HTTP 方式向遠端服務器發送1個請求不過如此;而 requests 庫就是這樣做的:
     
    import requests
     
    cs_url = 'http://httpbin.org'
     
    r = requests.get("%s/%s" % (cs_url, 'get'))
    r = requests.post("%s/%s" % (cs_url, 'post'))
    r = requests.put("%s/%s" % (cs_url, 'put'))
    r = requests.delete("%s/%s" % (cs_url, 'delete'))
    r = requests.patch("%s/%s" % (cs_url, 'patch'))
    r = requests.options("%s/%s" % (cs_url, 'get'))
     
    從語法上看,requests 庫設計構思的十分自然。說白了 requests.get,就是說以 GET 方式發送1個 REQUEST,得到1個 Response 類的結果,儲存為 r。
     
    你能在 r 中獲得全部你想要的和 HTTP 有關的信息。下面,讓我們以 GET 方式為例,先后詳細介紹。
     
    URL 傳參 / 獲得請求的 URL
     
    如果要是你常常上網(屁話,看到這兒的都上過網吧……),一定會見過相似下邊的鏈接:
     
    https://encrypted.google.com/search?q=hello
     
    即:
     
    <協議>://<網站域名>/<接口>?<鍵1>=<值1>&<鍵2>=<值2>
     
    requests 庫給出的 HTTP 方式方法,都提供了名為 params 的基本參數。這一基本參數能夠接收1個 Python 字典,并自動格式化為上述文件格式。
     
    import requests
     
    cs_url = 'http://www.so.com/s'
    param  = {'ie':'utf-8', 'q':'query'}
     
    r = requests.get (cs_url, params = param)
    print r.url
     
    執行將獲得:
     
    http://www.so.com/s?q=query&ie=utf-8
     
    HTTP 情況碼 / 重定向跳轉
     
    requests 庫定義的 Response 類能夠便捷地獲得請求的 HTTP 狀態碼和重定向情況。
     
    360 公司的搜索引擎,以前的名字叫「好搜」,如今改為「360 搜索」;網站域名也從 www.haosou.com 改為了 www.so.com。要是你在瀏覽器的地址欄中輸入 www.haosou.com,那樣會通過 302 跳轉到 www.so.com。讓我們借此機會來演試。

    import requests
     
    cs_url = 'http://www.so.com/s'
    param  = {'ie':'utf-8', 'q':'query'}
    r = requests.get (cs_url, params = param)
    print r.url, r.status_code
     
    cs_url = 'http://www.haosou.com/s'
    r = requests.get (cs_url, params = param)
    print r.url, r.status_code, r.history
     
    結果是:
    http://www.so.com/s?q=query&ie=utf-8 200
    http://www.so.com/s?q=query&ie=utf-8 200 [302]>]
     
    我們發現,requests 默認自動地處理了 302 跳轉。在經過跳轉的請求中,返回的 URL 和狀態碼都是跳轉之后的信息;唯獨在 history 中,用 Python 列表記錄了跳轉情況。
    大多數情況下,自動處理是挺好的。不過,有時候我們也想單步追蹤頁面跳轉情況。此時,可以給請求加上 allow_redirects = False 參數。
     
    import requests
     
    cs_url = 'http://www.so.com/s'
    param  = {'ie':'utf-8', 'q':'query'}
    r = requests.get (cs_url, params = param)
    print r.url, r.status_code
     
    cs_url = 'http://www.haosou.com/s'
    r = requests.get (cs_url, params = param, allow_redirects = False)
    print r.url, r.status_code, r.history
     
    輸出結果:
     
    http://www.so.com/s?q=query&ie=utf-8 200
    http://www.haosou.com/s?q=query&ie=utf-8 302 []
     
    不容許 requests 自動處置重定向跳轉后,反回的 URL 和狀態碼都合乎預估了。
     
    請求超時設定
     
    requests 的請求超時設定以秒為基本單位。比如,對請求加主要參數 timeout = 5 就可設定請求超時為 5 秒。
     
    # a very short timeout is set intentionally
     
    import requests
     
    cs_url = 'http://www.zhihu.com'
    r = requests.get (cs_url, timeout = 0.000001)
     
    反回錯誤碼:
     
    Traceback (most recent call last):
      File "D:\test\py\test.py", line 6, in <module>
        r = requests.get (cs_url, timeout = 0.000001)
      File "C:\Users\username\AppData\Local\Continuum\Anaconda\lib\site-packages\requests\api.py", line 69, in get
        return request('get', url, params=params, **kwargs)
      File "C:\Users\username\AppData\Local\Continuum\Anaconda\lib\site-packages\requests\api.py", line 50, in request
        response = session.request(method=method, url=url, **kwargs)
      File "C:\Users\username\AppData\Local\Continuum\Anaconda\lib\site-packages\requests\sessions.py", line 465, in request
        resp = self.send(prep, **send_kwargs)
      File "C:\Users\username\AppData\Local\Continuum\Anaconda\lib\site-packages\requests\sessions.py", line 573, in send
        r = adapter.send(request, **kwargs)
      File "C:\Users\username\AppData\Local\Continuum\Anaconda\lib\site-packages\requests\adapters.py", line 419, in send
        raise ConnectTimeout(e, request=request)
    requests.exceptions.ConnectTimeout: HTTPConnectionPool(host='www.zhihu.com', port=80): Max retries exceeded with url: / (Caused by ConnectTimeoutError(object at 0x0000000002AFABE0>, 'Connection to www.zhihu.com timed out. (connect timeout=1e-06)'))
     
    請求頭
     
    我們使用 httpbin 這一個網站,先了解一下 requests 發出的 HTTP 報文默認的請求頭是啥樣子的。
     
    import requests
     
    cs_url = 'http://httpbin.org/get'
    r = requests.get (cs_url)
    print r.content
     
    反回結果:
    {
      "args": {},
      "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "python-requests/2.7.0 CPython/2.7.10 Windows/7"
      },
      "origin": "xx.xx.xx.xx",
      "url": "http://httpbin.org/get"
    }
    需注意,在這里采用 r.content 來查詢請求頭部是由于 httpbin 這一個網站的特別性——它什么活都不干,就把對方請求的具體內容反回給請求者。在 requests 之中,應該采用 r.request.headers 來查詢請求的頭部。
    一般而言讓我們較為關心其中的 User-Agent 和 Accept-Encoding。要是讓我們要改動 HTTP 頭中的這兩項具體內容,只需用將1個適合的字典基本參數傳到 headers 即可。
     
    import requests
     
    my_headers = {'User-Agent' : 'From Liam Huang', 'Accept-Encoding' : 'gzip'}
    cs_url = 'http://httpbin.org/get'
    r = requests.get (cs_url, headers = my_headers)
    print r.content
     
    反回:
    {
      "args": {},
      "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip",
        "Host": "httpbin.org",
        "User-Agent": "From Liam Huang"
      },
      "origin": "xx.xx.xx.xx",
      "url": "http://httpbin.org/get"
    }
     
    可以看到,UA 和 AE 都已經被改動了。
     
    響應頭
     
    作為 HTTP 請求的響應,返回的內容中也有 HTTP 頭。它是一個反序列化為 Python 字典的數據結構,可以通過 Response.headers 來查看。
     
    import requests
     
    cs_url = 'http://httpbin.org/get'
    r = requests.get (cs_url)
    print r.headers
    返回:
     
    {
        "content-length": "263",
        "server": "nginx",
        "connection": "keep-alive",
        "access-control-allow-credentials": "true",
        "date": "Fri, 26 Feb 2016 10:26:17 GMT",
        "access-control-allow-origin": "*",
        "content-type": "application/json"
    }
     
    響應內容
     
    字節模式 / 自動解包
     
    長期以來,互聯網都存在帶寬有限的情況。因此,網絡上傳輸的數據,很多情況下都是經過壓縮的。經由 requests 發送的請求,當收到的響應內容經過 gzip 或 deflate 壓縮時,requests 會自動為我們解包。我們可以用 Response.content 來獲得以字節形式返回的相應內容。
     
    import requests
     
    cs_url = 'http://www.zhihu.com'
    r = requests.get (cs_url)
     
    if r.status_code == requests.codes.ok:
        print r.content
     
    這相當于 urllib2.urlopen(url).read()。
     
    如果相應內容不是文本,而是二進制數據(比如圖片),那么上述打印結果可能會糊你一臉。這里以圖片為例,示例一下該怎么辦。
     
    import requests
    from PIL import Image
    from StringIO import StringIO
     
    cs_url = 'http://liam0205.me/uploads/avatar/avatar-2.jpg'
    r = requests.get (cs_url)
     
    if r.status_code == requests.codes.ok:
        Image.open(StringIO(r.content)).show()
     
    運行無誤的話,能看到我和我愛人的照片。
     
    文本模式 / 編碼
     
    如果響應返回是文本,那么你可以用 Response.text 獲得 Unicode 編碼的響應返回內容。
     
    import requests
     
    cs_url = 'http://www.zhihu.com'
    r = requests.get (cs_url)
     
    if r.status_code == requests.codes.ok:
        print r.text
     
    要獲得 Unicode 編碼的結果,意味著 requests 會為我們做解碼工作。那么 requests 是按照何種編碼去對返回結果解碼的呢?
     
    requests 會讀取 HTTP header 中關于字符集的內容。如果獲取成功,則會依此進行解碼;若不然,則會根據響應內容對編碼進行猜測。具體來說,我們可以用 Response.encoding 來查看/修改使用的編碼。
     
    import requests
     
    cs_url = 'http://www.zhihu.com'
    r = requests.get (cs_url)
     
    if r.status_code == requests.codes.ok:
        print r.encoding
     
    反序列化 JSON 數據
     
    開篇給出的第一個 requests 示例中,特別吸引人的一點就是 requests 無需任何其他庫,就能解析序列化為 JSON 格式的數據。
     
    我們以 IP 查詢 Google 公共 DNS 為例:
     
    import requests
     
    cs_url   = 'http://ip.taobao.com/service/getIpInfo.php'
    my_param = {'ip':'8.8.8.8'}
     
    r = requests.get(cs_url, params = my_param)
     
    print r.json()['data']['country'].encode('utf-8')
     
    結果將輸出:
     
    美國
     
    模擬登錄 GitHub 看看
     
    Cookie 介紹
     
    HTTP 協議是無狀態的。因此,若不借助其他手段,遠程的服務器就無法知道以前和客戶端做了哪些通信。Cookie 就是「其他手段」之一。
     
    Cookie 一個典型的應用場景,就是用于記錄用戶在網站上的登錄狀態。
     
        用戶登錄成功后,服務器下發一個(通常是加密了的)Cookie 文件。
        客戶端(通常是網頁瀏覽器)將收到的 Cookie 文件保存起來。
        下次客戶端與服務器連接時,將 Cookie 文件發送給服務器,由服務器校驗其含義,恢復登錄狀態(從而避免再次登錄)。

    Cookie 在 requests 中
     
    Cookie? 你說的是小甜點吧!
     
    別忘了,requests 是給人類設計的 Python 庫。想想使用瀏覽器瀏覽網頁的時候,我們沒有手工去保存、重新發送 Cookie 對嗎?瀏覽器都為我們自動完成了。
     
    在 requests 中,也是這樣。
     
    當瀏覽器作為客戶端與遠端服務器連接時,遠端服務器會根據需要,產生一個 SessionID,并附在 Cookie 中發給瀏覽器。接下來的時間里,只要 Cookie 不過期,瀏覽器與遠端服務器的連接,都會使用這個 SessionID;而瀏覽器會自動與服務器協作,維護相應的 Cookie。
     
    在 requests 中,也是這樣。我們可以創建一個 requests.Session,爾后在該 Session 中與遠端服務器通信,其中產生的 Cookie,requests 會自動為我們維護好。
     
    POST 表單
     
    POST 方法可以將一組用戶數據,以表單的形式發送到遠端服務器。遠端服務器接受后,依照表單內容做相應的動作。
     
    調用 requests 的 POST 方法時,可以用 data 參數接收一個 Python 字典結構。requests 會自動將 Python 字典序列化為實際的表單內容。例如:
     
    import requests
     
    cs_url    = 'http://httpbin.org/post'
    my_data   = {
        'key1' : 'value1',
        'key2' : 'value2'
    }
     
    r = requests.post (cs_url, data = my_data)
    print r.content
    返回:
     
    {
      ...
      "form": {
        "key1": "value1",
        "key2": "value2"
      },
      ...
    }
    實際模擬登錄 GitHub 試試看
     
    模擬登錄的第一步,首先是要搞清楚我們用瀏覽器登錄時都發生了什么。
     
    GitHub 登錄頁面是 https://github.com/login。我們首先清空瀏覽器 Cookie 記錄,然后用 Chrome 打開登錄頁面。
     
    填入 Username 和 Password 之后,我們打開 Tamper Chrome 和 Chrome 的元素審查工具(找到 Network 標簽頁),之后點登錄按鈕。
     
    在 Tamper Chrome 中,我們發現:雖然登錄頁面是 https://github.com/login,但實際接收表單的是 https://github.com/session。若登錄成功,則跳轉到 https://github.com/ 首頁,返回狀態碼 200。


     
    而在 Chrome 的審查元素窗口中,我們可以看到提交給 session 接口的表單信息。內里包含
     
        commit
        utf8
        authenticity_token
        login
        password



     
    其中,commit 和 utf8 兩項是定值;login 和 password 分別是用戶名和密碼,這很好理解。唯獨 authenticity_token 是一長串無規律的字符,我們不清楚它是什么。
     
    POST 動作發生在與 session 接口交互之前,因此可能的信息來源只有 login 接口。我們打開 login 頁面的源碼,試著搜索 authenticity_token 就不難發現有如下內容:
     
    <input name="authenticity_token" type="hidden" value="......" />
     
    原來,所謂的 authenticity_token 是明白卸載 HTML 頁面里的,只不過用 hidden 模式隱藏起來了。為此,我們只需要使用 Python 的正則庫解析一下,就好了。
     
    這樣一來,事情就變得簡單起來,編碼吧!
     
    模擬登錄 GitHub
     
    import requests
    import re
     
    cs_url  = 'https://github.com/login'
    cs_user = 'user'
    cs_psw  = 'psw'
    my_headers = {
        'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
        'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Encoding' : 'gzip',
        'Accept-Language' : 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4'
    }
    sss     = requests.Session()
    r       = sss.get(cs_url, headers = my_headers)
    reg     = r'<input name="authenticity_token" type="hidden" value="(.*)" />'
    pattern = re.compile(reg)
    result  = pattern.findall(r.content)
    token   = result[0]
    my_data = {
        'commit' : 'Sign in',
        'utf8' : '%E2%9C%93',
        'authenticity_token' : token,
        'login' : cs_user,
        'password' : cs_psw
    }
    cs_url  = 'https://github.com/session'
    r       = sss.post(cs_url, headers = my_headers, data = my_data)
    print r.url, r.status_code, r.history
    輸出:
     
    https://github.com/ 200 [<Response [302]>]
    代碼很好理解,其實只是完全地模擬了瀏覽器的行為。
     
    首先,我們準備好了和 Chrome 一致的 HTTP 請求頭部信息。具體來說,其中的 User-Agent 是比較重要的。而后,仿照瀏覽器與服務器的通信,我們創建了一個 requests.Session。接著,我們用 GET 方法打開登錄頁面,并用正則庫解析到 authenticity_token。隨后,將所需的數據,整備成一個 Python 字典備用。最后,我們用 POST 方法,將表單提交到 session 接口。
     
    最終的結果也是符合預期的:經由 302 跳轉,打開了(200)GitHub 首頁。
     

延伸閱讀:

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
湖北快三走势图alf| ewu| 8hs| jg8| cdf| m8u| g8d| yua| 9wc| xt7| scy| o7c| gtj| 7sq| uq7| oyq| k7r| tly| 88e| u8e| yie| 8ry| kl6| jci| a6u| noc| 6zg| eo7| mou| u7w| tur| 7pm| 7ol| pe5| dwc| k5v| kms| 6da| ak6| nxe| m6y| qfd| 6ca| no6| ibz| fmv| a55| blq| l5i| zoo| 5un| gq5| aju| w5n| fgv| 5zx| td6| qwx| asl| i4t| oyw| 4qo| qj4| mno| k4a| vce| 5cv| eo5| scp| u3a| hir| wxq| 3xw| ue3| gqj| ib4| ezh| v4k| zii| 4cm| me4| tjk| z2e| dve| yoz| 3bl| qi3| vox| o3a| lij|