9.Requests庫基本使用

上一篇:8.Urllib庫基本使用
下一篇:10.正則表達式基礎(chǔ)

requests是python實現(xiàn)的最簡單易用的HTTP庫,建議爬蟲使用requests

import requests
url = "https://api.github.com/events"

獲取某個網(wǎng)頁

import requests
r = requests.get("https://api.github.com/events")
print(r)                # <Response [200]>
print(type(r))          # <class 'requests.models.Response'>  
print(r.status_code)    # 200
各種請求
# 發(fā)送一個 HTTP POST 請求:
r = requests.post("http://httpbin.org/post",data = {'key':'value'})
r = requests.delete('http://httpbin.org/delete')    # 發(fā)送一個 HTTP delete 請求:
r = requests.head('http://httpbin.org/get')         # 發(fā)送一個 HTTP head 請求:
r = requests.options('http://httpbin.org/get')      # 發(fā)送一個 HTTP options 請求:

`

get 傳遞URL參數(shù)

?+鍵值對
response1 = requests.get("http://httpbin.org/get?key1=value1")
print(response1.url)
#http://httpbin.org/get?key1=value1
requests提供了params關(guān)鍵字參數(shù)來傳遞參數(shù)
parameter = {
            "key1":"value1",
            "key2":"value2"
            }
response2 = requests.get("http://httpbin.org/get",params = parameter)
print(response2.url)
# http://httpbin.org/get?key1=value1&key2=value2
還可以將一個列表作為值傳入
parameter = {
            "key1":"value1",
            "key2":["value21","value22"]
}
response3 = requests.get("http://httpbin.org/get",params = parameter)
print(response3.url)
# http://httpbin.org/get?key1=value1&key2=value21&key2=value22
注意字典里值為 None 的鍵都不會被添加到 URL 的查詢字符串里帅矗。
parameter = {
            "key1":"value",
            "key2":None
}
response4 = requests.get("http://httpbin.org/get",params = parameter)
print(response4.url)    #http://httpbin.org/get?key1=value

響應(yīng)內(nèi)容

我們能讀取服務(wù)器響應(yīng)的內(nèi)容矮慕。再次以 GitHub 時間線為例:
Requests 會自動解碼來自服務(wù)器的內(nèi)容恳啥。大多數(shù) unicode 字符集都能被無縫地解碼亏拉。
response = requests.get("https://api.github.com/events")
print(response)         # <Response [200]>
# print(response.text)  # Json格式
請求發(fā)出后仔沿,Requests 會基于 HTTP 頭部對響應(yīng)的編碼作出有根據(jù)的推測坐桩。當訪問 r.text 之時,Requests 會使用其推測的文本編碼封锉∶圊危可以找出 Requests 使用了什么編碼,并且能夠使用 r.encoding 屬性來改變它:
print(response.encoding)    # utf-8

#使用 r.content 來找到編碼成福,然后設(shè)置 r.encoding 為相應(yīng)的編碼
print(response.encoding)    # ISO-8859-1
#改變編碼
response.encoding = 'ISO-8859-1'
#二進制響應(yīng)內(nèi)容
#以字節(jié)的方式訪問請求響應(yīng)體碾局,對于非文本請求:
print(type(response.content))   
print(type(response.text))
response.text返回的是Unicode型的數(shù)據(jù)。---文本
response.content返回的是bytes型也就是二進制的數(shù)據(jù)奴艾。-----圖片等
但是兩者打印輸出是一樣的

Json響應(yīng)內(nèi)容

Requests 中有一個內(nèi)置的 JSON 解碼器净当,處理 JSON 數(shù)據(jù)
response = requests.get(url)
# print(response.json())        #json數(shù)據(jù)
# json成功調(diào)用并不意外者響應(yīng)成功,有的服務(wù)器會在失敗的響應(yīng)中包含一個 JSON 對象(比如 HTTP 500 的錯誤細節(jié)需要status_code判斷
print(response.status_code)     #200
print(response.raise_for_status())  #none

原始響應(yīng)

暫未看懂蕴潦,先略過

定制請求頭

如果想為請求添加 HTTP 頭部像啼,只要簡單地傳遞一個 dict 給 headers 參數(shù)就可以了。
以知乎為例子
response =requests.get("https://www.zhihu.com")
print(response.text)    #報錯
此時會報錯潭苞,因為訪問知乎需要頭部信息忽冻,在谷歌瀏覽器輸入chrome://version,就可以得到用戶代理
image.png
import requests

new_headers = {
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36"
}

response = requests.get("https://www.zhihu.com",headers = new_headers)
print(response.text)        #正常輸出
這樣就可以正常的訪問知乎了

Post請求

發(fā)送一些編碼為表單形式的數(shù)據(jù)——非常像一個 HTML 表單此疹。要實現(xiàn)這個僧诚,只需簡單地傳遞一個字典給 data 參數(shù)遮婶。數(shù)據(jù)字典在發(fā)出請求時會自動編碼為表單形式:通過在發(fā)送post請求時添加一個data參數(shù),這個data參數(shù)可以通過字典構(gòu)造成湖笨,這樣對于發(fā)送post請求就非常方便
payload = {
    "key1":"value1",
    "key2":"value2"
}
response = requests.post("http://httpbin.org/post",data = payload)
print(response.text)
還可以為 data 參數(shù)傳入一個元組列表旗扑。在表單中多個元素使用同一 key 的時候,這種方式尤其有效赶么,字典會第二個值覆蓋第一個值
payload = (("key1","value1"),("key1","value2"))
response = requests.post("http://httpbin.org/post",data = payload)
print(response.text)
響應(yīng)
可以通過response獲得很多屬性肩豁,例子如下
import requests

response = requests.get("http://www.baidu.com")
print(type(response.status_code),response.status_code)          #< class 'int'> 200
print(type(response.headers),response.headers)                  # 頭部信息
print(type(response.cookies),response.cookies)                  #<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
print(type(response.url),response.url)                          # <class 'str'> http://www.baidu.com/
print(type(response.history),response.history)                  # <class 'list'> []
狀態(tài)碼判斷,requests還附帶了一個內(nèi)置的狀態(tài)碼查詢對象
主要有如下內(nèi)容:
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\o/', '?'),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),

Redirection.
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0

Client Error.
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),

Server Error.
500: ('internal_server_error', 'server_error', '/o\', '?'),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication'),
例子·
import requests

response= requests.get("http://www.baidu.com")
if response.status_code == requests.codes.ok:
    print("訪問成功")
# 可以直接使用狀態(tài)碼脊串,更方便
if response.status_code == 200:
    print("訪問成功")

POST一個多部分編碼(Multipart-Encoded)的文件

Requests 使得上傳多部分編碼文件變得很簡單:
文件上傳,實現(xiàn)方法和其他參數(shù)類似辫呻,也是構(gòu)造一個字典然后通過files參數(shù)傳遞

import requests

url = 'http://httpbin.org/post'
files = {"files":open('test.py', 'rb')}
response = requests.post(url,files = files)
print(response.text)

可以顯式地設(shè)置文件名,文件類型和請求頭:

url = 'http://httpbin.org/post'
files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
r = requests.post(url, files=files)
r.text

也可以發(fā)送作為文件來接收的字符串:

url = 'http://httpbin.org/post'
files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
r = requests.post(url, files=files)
r.text

Cookie

如果某個響應(yīng)中包含一些 cookie琼锋,可以快速訪問它們:

import requests

response = requests.get("http://www.baidu.com")
print(response.cookies)

for key,value in response.cookies.items():
    print(key+"="+value)

要想發(fā)送的cookies到服務(wù)器放闺,可以使用 cookies 參數(shù):

import requests

url = 'http://httpbin.org/cookies'
cookies = dict(cookies_are='working')
response = requests.get(url, cookies=cookies)
print(response.text)

Cookie 的返回對象為 RequestsCookieJar,它的行為和字典類似缕坎,但接口更為完整怖侦,適合跨域名跨路徑使用。還可以把 Cookie Jar 傳到 Requests 中:

jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
url = 'http://httpbin.org/cookies'
response = requests.get(url, cookies=jar)
print(response.text)
重定向與請求歷史
  1. 默認情況下谜叹,除了 HEAD, Requests 會自動處理所有重定向匾寝。
  2. 可以使用響應(yīng)對象的 history 方法來追蹤重定向。
  3. Response.history 是一個 Response 對象的列表荷腊,為了完成請求而創(chuàng)建了這些對象艳悔。這個對象列表按照從最老到最近的請求進行排序。例如女仰,Github 將所有的 HTTP 請求重定向到 HTTPS:
response = requests.get('http://github.com')
print(response.url)             # 'https://github.com/'
print(response.status_code)     # 200
print(response.history)         # [<Response [301]>]

如果使用的是GET猜年、OPTIONS、POST疾忍、PUT乔外、PATCH 或者 DELETE,那么可以通過 allow_redirects 參數(shù)禁用重定向處理:

response = requests.get('http://github.com',allow_redirects=False)
print(response.url)             # 'https://github.com/'
print(response.status_code)     # 300
print(response.history)         # []
如果使用了 HEAD一罩,也可以啟用重定向:
response = requests.head('http://github.com',allow_redirects=True)
print(response.url)             # 'https://github.com/'
print(response.status_code)     # 200
print(response.history)         # [<Response [301]>]
超時

可以告訴 requests 在經(jīng)過以 timeout 參數(shù)設(shè)定的秒數(shù)時間之后停止等待響應(yīng)杨幼。基本上所有的生產(chǎn)代碼都應(yīng)該使用這一參數(shù)聂渊。如果不使用推汽,的程序可能會永遠失去響應(yīng):

response1 = requests.get('http://github.com', timeout=100)
print(response1)            #<Response [200]>

response2 = requests.get('http://github.com', timeout=0.1)
print(response2)            # 報錯ReadTimeout

timeout 僅對連接過程有效,與響應(yīng)體的下載無關(guān)歧沪。 timeout 并不是整個下載響應(yīng)的時間限制歹撒,而是如果服務(wù)器在 timeout 秒內(nèi)沒有應(yīng)答,將會引發(fā)一個異常(更精確地說诊胞,是在 timeout 秒內(nèi)沒有從基礎(chǔ)套接字上接收到任何字節(jié)的數(shù)據(jù)時)If no timeout is specified explicitly, requests do not time out.

錯誤與異常

http://www.python-requests.org/en/master/api/#exceptions

  1. 遇到網(wǎng)絡(luò)問題(如:DNS 查詢失敗暖夭、拒絕連接等)時锹杈,Requests 會拋出一個 ConnectionError 異常。
  2. 如果 HTTP 請求返回了不成功的狀態(tài)碼迈着, Response.raise_for_status() 會拋出一個 HTTPError 異常竭望。
  3. 若請求超時,則拋出一個 Timeout 異常裕菠。
  4. 若請求超過了設(shè)定的最大重定向次數(shù)咬清,則會拋出一個 TooManyRedirects 異常。
  5. 所有Requests顯式拋出的異常都繼承自 requests.exceptions.RequestException 奴潘。

會話對象

會話對象讓能夠跨請求保持某些參數(shù)旧烧。它也會在同一個 Session 實例發(fā)出的所有請求之間保持 cookie, 期間使用 urllib3 的 connection pooling 功能画髓。所以如果向同一主機發(fā)送多個請求掘剪,底層的 TCP 連接將會被重用,從而帶來顯著的性能提升奈虾。 (參見 https://en.wikipedia.org/wiki/HTTP_persistent_connection).會話對象具有主要的 Requests API 的所有方法夺谁。

我們來跨請求保持一些 cookie:
session = requests.Session()
session.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
response = session.get("http://httpbin.org/cookies")
print(type(response))               # <class 'requests.models.Response'>
print(response.text)                # {"cookies":{"sessioncookie":"123456789"}}

會話也可用來為請求方法提供缺省數(shù)據(jù)。這是通過為會話對象的屬性提供數(shù)據(jù)來實現(xiàn)的:

s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

# both 'x-test' and 'x-test2' are sent
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

任何傳遞給請求方法的字典都會與已設(shè)置會話層數(shù)據(jù)合并肉微。方法層的參數(shù)覆蓋會話的參數(shù)匾鸥。

不過需要注意,就算使用了會話碉纳,方法級別的參數(shù)也不會被跨請求保持勿负。下面的例子只會和第一個請求發(fā)送 cookie ,而非第二個:


s = requests.Session()

r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'

r = s.get('http://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'

手動為會話添加 cookie村象,就使用 Cookie utility 函數(shù) 來操縱 Session.cookies笆环。

會話還可以用作前后文管理器:

with requests.Session() as s:
    s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

這樣就能確保 with 區(qū)塊退出后會話能被關(guān)閉,即使發(fā)生了異常也一樣

從字典參數(shù)中移除一個值有時會想省略字典參數(shù)中一些會話層的鍵厚者。要做到這一點躁劣,只需簡單地在方法層參數(shù)中將那個鍵的值設(shè)置為 None ,那個鍵就會被自動省略掉库菲。

請求與響應(yīng)對象

任何時候進行了類似 requests.get() 的調(diào)用账忘,都在做兩件主要的事情。其一熙宇,在構(gòu)建一個 Request 對象鳖擒, 該對象將被發(fā)送到某個服務(wù)器請求或查詢一些資源。其二烫止,一旦 requests 得到一個從服務(wù)器返回的響應(yīng)就會產(chǎn)生一個 Response 對象蒋荚。該響應(yīng)對象包含服務(wù)器返回的所有信息,也包含原來創(chuàng)建的 Request 對象馆蠕。如下是一個簡單的請求期升,從 Wikipedia 的服務(wù)器得到一些非常重要的信息:

# response = requests.get('http://en.wikipedia.org/wiki/Monty_Python')

如果想訪問服務(wù)器返回給我們的響應(yīng)頭部信息惊奇,可以這樣做:

print(response.headers)

然而,如果想得到發(fā)送到服務(wù)器的請求的頭部播赁,我們可以簡單地訪問該請求颂郎,然后是該請求的頭部:

print(response.request.headers)

準備的請求 (Prepared Request)

從 API 或者會話調(diào)用中收到一個 Response 對象時,request 屬性其實是使用了 PreparedRequest容为。有時在發(fā)送請求之前乓序,需要對 body 或者 header (或者別的什么東西)做一些額外處理,下面演示了一個簡單的做法:

s = requests.Session()

req = requests.Request('GET', url,
    data=data,
    headers=header
)
prepped = req.prepare()

# do something with prepped.body
# do something with prepped.headers

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)

由于沒有對 Request 對象做什么特殊事情坎背,立即準備和修改了 PreparedRequest 對象替劈,然后把它和別的參數(shù)一起發(fā)送到 requests.* 或者 Session.*。

然而沼瘫,上述代碼會失去 Requests Session 對象的一些優(yōu)勢抬纸, 尤其 Session 級別的狀態(tài)咙俩,例如 cookie 就不會被應(yīng)用到的請求上去耿戚。要獲取一個帶有狀態(tài)的 PreparedRequest, 請用 Session.prepare_request() 取代 Request.prepare() 的調(diào)用阿趁,如下所示:

from requests import Request, Session

s = Session()
req = Request('GET',  url,
    data=data
    headers=headers
)

prepped = s.prepare_request(req)

# do something with prepped.body
# do something with prepped.headers

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)

SSL 證書驗證

Requests 可以為 HTTPS 請求驗證 SSL 證書膜蛔,就像 web 瀏覽器一樣。SSL 驗證默認是開啟的脖阵,如果證書驗證失敗皂股,Requests 會拋出 SSLError:

response = requests.get('https://requestb.in')
print(response)         # 拋出異常  SSLError:
response = requests.get('https://github.com', verify=True)
print(response)

為了避免這種情況的發(fā)生可以通過verify=False但是這樣是可以訪問到頁面,但是會提示:InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning)

解決方法為:

import requests
from requests.packages import urllib3
urllib3.disable_warnings()    # 就這一句就可以解決
response = requests.get("https://www.12306.cn",verify=False)
print(response.status_code)

可以為 verify 傳入 CA_BUNDLE 文件的路徑命黔,或者包含可信任 CA 證書文件的文件夾路徑:

requests.get('https://github.com', verify='路徑')

或者將其保存在會話中:

s = requests.Session()
s.verify = '路徑'

注意:如果 verify 設(shè)為文件夾路徑呜呐,文件夾必須通過 OpenSSL 提供的 c_rehash 工具處理。

還可以通過 REQUESTS_CA_BUNDLE 環(huán)境變量定義可信任 CA 列表悍募。

如果將 verify 設(shè)置為 False蘑辑,Requests 也能忽略對 SSL 證書的驗證。

>>>requests.get('https://kennethreitz.org', verify=False)
#<Response [200]>

默認情況下坠宴, verify 是設(shè)置為 True 的洋魂。選項 verify 僅應(yīng)用于主機證書。對于私有證書喜鼓,也可以傳遞一個 CA_BUNDLE 文件的路徑給 verify副砍。也可以設(shè)置 # REQUEST_CA_BUNDLE 環(huán)境變量。

客戶端證書

也可以指定一個本地證書用作客戶端證書庄岖,可以是單個文件(包含密鑰和證書)或一個包含兩個文件路徑的元組:

>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>
或者保持在會話中:
s = requests.Session()
s.cert = '/path/client.cert'

如果指定了一個錯誤路徑或一個無效的證書:

>>> requests.get('https://kennethreitz.org', cert='/wrong_path/client.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use

警告本地證書的私有 key 必須是解密狀態(tài)豁翎。目前,Requests 不支持使用加密的 key隅忿。

響應(yīng)體內(nèi)容工作流

默認情況下心剥,當進行網(wǎng)絡(luò)請求后启搂,響應(yīng)體會立即被下載×跆眨可以通過 stream 參數(shù)覆蓋這個行為胳赌,推遲下載響應(yīng)體直到訪問 Response.content 屬性:

tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)

此時僅有響應(yīng)頭被下載下來了,連接保持打開狀態(tài)匙隔,因此允許我們根據(jù)條件獲取內(nèi)容:

if int(r.headers['content-length']) < TOO_LONG:
  content = r.content
  ...

可以進一步使用 Response.iter_content 和 Response.iter_lines 方法來控制工作流疑苫,或者以 Response.raw 從底層 urllib3 的 urllib3.HTTPResponse <urllib3.response.HTTPResponse 讀取未解碼的相應(yīng)體。

如果在請求中把 stream 設(shè)為 True纷责,Requests 無法將連接釋放回連接池捍掺,除非 消耗了所有的數(shù)據(jù),或者調(diào)用了 Response.close再膳。 這樣會帶來連接效率低下的問題挺勿。如果發(fā)現(xiàn)在使用 stream=True 的同時還在部分讀取請求的 body(或者完全沒有讀取 body),那么就應(yīng)該考慮使用 with 語句發(fā)送請求喂柒,這樣可以保證請求一定會被關(guān)閉:

with requests.get('http://httpbin.org/get', stream=True) as r:
    # 在此處理響應(yīng)不瓶。

保持活動狀態(tài)(持久連接)

好消息——歸功于 urllib3,同一會話內(nèi)的持久連接是完全自動處理的灾杰!同一會話內(nèi)你發(fā)出的任何請求都會自動復用恰當?shù)倪B接蚊丐!

注意:只有所有的響應(yīng)體數(shù)據(jù)被讀取完畢連接才會被釋放為連接池;所以確保將 stream 設(shè)置為 False 或讀取 Response 對象的 content 屬性艳吠。

流式上傳

Requests支持流式上傳麦备,這允許你發(fā)送大的數(shù)據(jù)流或文件而無需先把它們讀入內(nèi)存。要使用流式上傳昭娩,僅需為你的請求體提供一個類文件對象即可:

with open('massive-body') as f:
    requests.post('http://some.url/streamed', data=f)

強烈建議你用二進制模式(binary mode)打開文件凛篙。這是因為 requests 可能會為你提供 header 中的 Content-Length,在這種情況下該值會被設(shè)為文件的字節(jié)數(shù)栏渺。如果你用文本模式打開文件呛梆,就可能碰到錯誤。

塊編碼請求

對于出去和進來的請求迈嘹,Requests 也支持分塊傳輸編碼削彬。要發(fā)送一個塊編碼的請求,僅需為你的請求體提供一個生成器(或任意沒有具體長度的迭代器):

def gen():
    yield 'hi'
    yield 'there'

requests.post('http://some.url/chunked', data=gen())

對于分塊的編碼請求秀仲,我們最好使用 Response.iter_content() 對其數(shù)據(jù)進行迭代融痛。在理想情況下,你的 request 會設(shè)置 stream=True神僵,這樣你就可以通過調(diào)用 iter_content 并將分塊大小參數(shù)設(shè)為 None雁刷,從而進行分塊的迭代。如果你要設(shè)置分塊的最大體積保礼,你可以把分塊大小參數(shù)設(shè)為任意整數(shù)沛励。

POST 多個分塊編碼的文件

你可以在一個請求中發(fā)送多個文件责语。例如,假設(shè)你要上傳多個圖像文件到一個 HTML 表單目派,使用一個多文件 field 叫做 "images":

<input type="file" name="images" multiple="true" required="true"/>

要實現(xiàn)坤候,只要把文件設(shè)到一個元組的列表中,其中元組結(jié)構(gòu)為 (form_field_name, file_info):

>>>> url = 'http://httpbin.org/post'
>>> multiple_files = [
 ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
 ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
 ...
 'files': {'images': 'data:image/png;base64,iVBORw ....'}
 'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
 ...
}

警告

強烈建議你用二進制模式(binary mode)打開文件企蹭。這是因為 requests 可能會為你提供 header 中的 Content-Length白筹,在這種情況下該值會被設(shè)為文件的字節(jié)數(shù)。如果你用文本模式打開文件谅摄,就可能碰到錯誤徒河。

事件掛鉤

Requests有一個鉤子系統(tǒng),你可以用來操控部分請求過程送漠,或信號事件處理顽照。

可用的鉤子:

response:

從一個請求產(chǎn)生的響應(yīng)

你可以通過傳遞一個 {hook_name: callback_function} 字典給 hooks 請求參數(shù)為每個請求分配一個鉤子函數(shù):

hooks=dict(response=print_url)

callback_function 會接受一個數(shù)據(jù)塊作為它的第一個參數(shù)。

def print_url(r, *args, **kwargs):
  print(r.url)

若執(zhí)行你的回調(diào)函數(shù)期間發(fā)生錯誤闽寡,系統(tǒng)會給出一個警告代兵。

若回調(diào)函數(shù)返回一個值,默認以該值替換傳進來的數(shù)據(jù)下隧。若函數(shù)未返回任何東西奢人,也沒有什么其他的影響谓媒。

我們來在運行期間打印一些請求方法的參數(shù):

>>>> requests.get('http://httpbin.org', hooks=dict(response=print_url))
http://httpbin.org
<Response [200]>

自定義身份驗證

Requests 允許你使用自己指定的身份驗證機制淆院。

任何傳遞給請求方法的 auth 參數(shù)的可調(diào)用對象,在請求發(fā)出之前都有機會修改請求句惯。

自定義的身份驗證機制是作為 requests.auth.AuthBase 的子類來實現(xiàn)的土辩,也非常容易定義。Requests 在 requests.auth 中提供了兩種常見的的身份驗證方案: HTTPBasicAuthHTTPDigestAuth抢野。

假設(shè)我們有一個web服務(wù)拷淘,僅在 X-Pizza 頭被設(shè)置為一個密碼值的情況下才會有響應(yīng)。雖然這不太可能指孤,但就以它為例好了启涯。

from requests.auth import AuthBase

class PizzaAuth(AuthBase):
    """Attaches HTTP Pizza Authentication to the given Request object."""
    def __init__(self, username):
        # setup any auth-related data here
        self.username = username

    def __call__(self, r):
        # modify and return the request
        r.headers['X-Pizza'] = self.username
        return r

然后就可以使用我們的PizzaAuth來進行網(wǎng)絡(luò)請求:

>>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>

流式請求

使用 Response.iter_lines() 你可以很方便地對流式 API (例如 Twitter 的流式 API ) 進行迭代。簡單地設(shè)置 streamTrue 便可以使用 iter_lines 對相應(yīng)進行迭代:

 import json
import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

    # filter out keep-alive new lines
    if line:
        decoded_line = line.decode('utf-8')
        print(json.loads(decoded_line))

當使用 <cite>decode_unicode=True</cite> 在 Response.iter_lines()Response.iter_content() 中時恃轩,你需要提供一個回退編碼方式结洼,以防服務(wù)器沒有提供默認回退編碼,從而導致錯誤:

r = requests.get('http://httpbin.org/stream/20', stream=True)

if r.encoding is None:
    r.encoding = 'utf-8'

for line in r.iter_lines(decode_unicode=True):
    if line:
        print(json.loads(line))

警告

iter_lines 不保證重進入時的安全性叉跛。多次調(diào)用該方法 會導致部分收到的數(shù)據(jù)丟失松忍。如果你要在多處調(diào)用它,就應(yīng)該使用生成的迭代器對象:

lines = r.iter_lines()
# 保存第一行以供后面使用筷厘,或者直接跳過

first_line = next(lines)

for line in lines:
    print(line)

代理

如果需要使用代理鸣峭,你可以通過為任意請求方法提供 proxies 參數(shù)來配置單個請求:

mport requests

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}

requests.get("http://example.org", proxies=proxies)

你也可以通過環(huán)境變量 HTTP_PROXYHTTPS_PROXY 來配置代理宏所。

$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"

$ python
>>> import requests
>>> requests.get("http://example.org")

若你的代理需要使用HTTP Basic Auth,可以使用 <cite>http://user:password@host/</cite> 語法:

proxies = {
    "http": "http://user:pass@10.10.1.10:3128/",
}

要為某個特定的連接方式或者主機設(shè)置代理摊溶,使用 <cite>scheme://hostname</cite> 作為 key爬骤, 它會針對指定的主機和連接方式進行匹配。

proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

注意莫换,代理 URL 必須包含連接方式盖腕。

SOCKS

2.10.0 新版功能.

除了基本的 HTTP 代理,Request 還支持 SOCKS 協(xié)議的代理浓镜。這是一個可選功能溃列,若要使用, 你需要安裝第三方庫膛薛。

pip install requests[socks]

安裝好依賴以后听隐,使用 SOCKS 代理和使用 HTTP 代理一樣簡單:

proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

合規(guī)性

Requests 符合所有相關(guān)的規(guī)范和 RFC,這樣不會為用戶造成不必要的困難哄啄。但這種對規(guī)范的考慮導致一些行為對于不熟悉相關(guān)規(guī)范的人來說看似有點奇怪雅任。

編碼方式

當你收到一個響應(yīng)時,Requests 會猜測響應(yīng)的編碼方式咨跌,用于在你調(diào)用 Response.text 方法時對響應(yīng)進行解碼沪么。Requests 首先在 HTTP 頭部檢測是否存在指定的編碼方式,如果不存在,則會使用charade 來嘗試猜測編碼方式也殖。

只有當 HTTP 頭部不存在明確指定的字符集臀蛛,并且 Content-Type 頭部字段包含 text 值之時, Requests 才不去猜測編碼方式殉摔。在這種情況下, RFC 2616 指定默認字符集必須是 ISO-8859-1 记焊。Requests 遵從這一規(guī)范逸月。如果你需要一種不同的編碼方式,你可以手動設(shè)置 Response.encoding 屬性遍膜,或使用原始的 Response.content碗硬。

HTTP動詞

Requests 提供了幾乎所有HTTP動詞的功能:GET、OPTIONS瓢颅、HEAD恩尾、POST、PUT惜索、PATCH特笋、DELETE。以下內(nèi)容為使用 Requests 中的這些動詞以及 Github API 提供了詳細示例。

我將從最常使用的動詞 GET 開始猎物。HTTP GET 是一個冪等方法虎囚,從給定的 URL 返回一個資源。因而蔫磨,當你試圖從一個 web 位置獲取數(shù)據(jù)之時淘讥,你應(yīng)該使用這個動詞。一個使用示例是嘗試從 Github 上獲取關(guān)于一個特定 commit 的信息堤如。假設(shè)我們想獲取 Requests 的 commit a050faf 的信息蒲列。我們可以這樣去做:

>>>> import requests
>>> r = requests.get('https://api.github.com/repos/requests/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')

我們應(yīng)該確認 GitHub 是否正確響應(yīng)。如果正確響應(yīng)搀罢,我們想弄清響應(yīng)內(nèi)容是什么類型的蝗岖。像這樣去做:

>>>> if (r.status_code == requests.codes.ok):
...     print r.headers['content-type']
...
application/json; charset=utf-8

可見,GitHub 返回了 JSON 數(shù)據(jù)榔至,非常好抵赢,這樣就可以使用 r.json 方法把這個返回的數(shù)據(jù)解析成 Python 對象。

>>>> commit_data = r.json()

>>> print commit_data.keys()
[u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message']

>>> print commit_data[u'committer']
{u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@kennethreitz.com', u'name': u'Kenneth Reitz'}

>>> print commit_data[u'message']
makin' history

到目前為止唧取,一切都非常簡單铅鲤。嗯,我們來研究一下 GitHub 的 API枫弟。我們可以去看看文檔邢享,但如果使用 Requests 來研究也許會更有意思一點。我們可以借助 Requests 的 OPTIONS 動詞來看看我們剛使用過的 url 支持哪些 HTTP 方法淡诗。

>>>> verbs = requests.options(r.url)
>>> verbs.status_code
500

額骇塘,這是怎么回事?毫無幫助嘛袜漩!原來 GitHub绪爸,與許多 API 提供方一樣,實際上并未實現(xiàn) OPTIONS 方法宙攻。這是一個惱人的疏忽,但沒關(guān)系介褥,那我們可以使用枯燥的文檔座掘。然而,如果 GitHub 正確實現(xiàn)了 OPTIONS柔滔,那么服務(wù)器應(yīng)該在響應(yīng)頭中返回允許用戶使用的 HTTP 方法溢陪,例如:

>>>> verbs = requests.options('http://a-good-website.com/api/cats')
>>> print verbs.headers['allow']
GET,HEAD,POST,OPTIONS

轉(zhuǎn)而去查看文檔,我們看到對于提交信息睛廊,另一個允許的方法是 POST形真,它會創(chuàng)建一個新的提交。由于我們正在使用 Requests 代碼庫超全,我們應(yīng)盡可能避免對它發(fā)送笨拙的 POST咆霜。作為替代邓馒,我們來玩玩 GitHub 的 Issue 特性。

本篇文檔是回應(yīng) Issue #482 而添加的蛾坯。鑒于該問題已經(jīng)存在光酣,我們就以它為例。先獲取它脉课。

>>> r.status_code
200

>>> issue = json.loads(r.text)

>>> print(issue[u'title'])
Feature any http verb in docs

>>> print(issue[u'comments'])
3

Cool救军,有 3 個評論。我們來看一下最后一個評論倘零。

>>>> r = requests.get(r.url + u'/comments')
>>> r.status_code
200
>>> comments = r.json()
>>> print comments[0].keys()
[u'body', u'url', u'created_at', u'updated_at', u'user', u'id']
>>> print comments[2][u'body']
Probably in the "advanced" section

嗯唱遭,那看起來似乎是個愚蠢之處。我們發(fā)表個評論來告訴這個評論者他自己的愚蠢呈驶。那么胆萧,這個評論者是誰呢?

>>>> print comments[2][u'user'][u'login']
kennethreitz

好俐东,我們來告訴這個叫 Kenneth 的家伙跌穗,這個例子應(yīng)該放在快速上手指南中。根據(jù) GitHub API 文檔虏辫,其方法是 POST 到該話題蚌吸。我們來試試看。

>>> url = u"https://api.github.com/repos/requests/requests/issues/482/comments"

>>> r = requests.post(url=url, data=body)
>>> r.status_code
404

額砌庄,這有點古怪哈羹唠。可能我們需要驗證身份娄昆。那就有點糾結(jié)了佩微,對吧?不對萌焰。Requests 簡化了多種身份驗證形式的使用哺眯,包括非常常見的 Basic Auth。

>>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('fake@example.com', 'not_a_real_password')

>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201

>>> content = r.json()
>>> print(content[u'body'])
Sounds great! I'll get right on it.

太棒了扒俯!噢奶卓,不!我原本是想說等我一會撼玄,因為我得去喂我的貓夺姑。如果我能夠編輯這條評論那就好了!幸運的是掌猛,GitHub 允許我們使用另一個 HTTP 動詞 PATCH 來編輯評論盏浙。我們來試試。

>>>> print(content[u"id"])
5804413

>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."})
>>> url = u"https://api.github.com/repos/requests/requests/issues/comments/5804413"

>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200

非常好。現(xiàn)在废膘,我們來折磨一下這個叫 Kenneth 的家伙竹海,我決定要讓他急得團團轉(zhuǎn),也不告訴他是我在搗蛋殖卑。這意味著我想刪除這條評論站削。GitHub 允許我們使用完全名副其實的 DELETE 方法來刪除評論。我們來清除該評論孵稽。

>>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'

很好许起。不見了。最后一件我想知道的事情是我已經(jīng)使用了多少限額(ratelimit)菩鲜。查查看园细,GitHub 在響應(yīng)頭部發(fā)送這個信息,因此不必下載整個網(wǎng)頁接校,我將使用一個 HEAD 請求來獲取響應(yīng)頭猛频。

>>>> r = requests.head(url=url, auth=auth)
>>> print r.headers
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...

很好。是時候?qū)憘€ Python 程序以各種刺激的方式濫用 GitHub 的 API蛛勉,還可以使用 4995 次呢鹿寻。

定制動詞

有時候你會碰到一些服務(wù)器,處于某些原因诽凌,它們允許或者要求用戶使用上述 HTTP 動詞之外的定制動詞毡熏。比如說 WEBDAV 服務(wù)器會要求你使用 MKCOL 方法。別擔心侣诵,Requests 一樣可以搞定它們痢法。你可以使用內(nèi)建的 .request 方法,例如:

>>>> r = requests.request('MKCOL', url, data=data)
>>> r.status_code
200 # Assuming your call was correct

這樣你就可以使用服務(wù)器要求的任意方法動詞了杜顺。

響應(yīng)頭鏈接字段

許多 HTTP API 都有響應(yīng)頭鏈接字段的特性财搁,它們使得 API 能夠更好地自我描述和自我顯露。

GitHub 在 API 中為 分頁 使用這些特性躬络,例如:

>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
'<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"'

Requests 會自動解析這些響應(yīng)頭鏈接字段尖奔,并使得它們非常易于使用:

>>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}

>>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}

傳輸適配器

從 v1.0.0 以后,Requests 的內(nèi)部采用了模塊化設(shè)計洗鸵。部分原因是為了實現(xiàn)傳輸適配器(Transport Adapter)越锈,你可以看看關(guān)于它的最早描述。傳輸適配器提供了一個機制膘滨,讓你可以為 HTTP 服務(wù)定義交互方法。尤其是它允許你應(yīng)用服務(wù)前的配置稀拐。

Requests 自帶了一個傳輸適配器火邓,也就是 HTTPAdapter。 這個適配器使用了強大的 urllib3,為 Requests 提供了默認的 HTTP 和 HTTPS 交互铲咨。每當 Session 被初始化躲胳,就會有適配器附著在 Session 上,其中一個供 HTTP 使用纤勒,另一個供 HTTPS 使用坯苹。

Request 允許用戶創(chuàng)建和使用他們自己的傳輸適配器,實現(xiàn)他們需要的特殊功能摇天。創(chuàng)建好以后粹湃,傳輸適配器可以被加載到一個會話對象上,附帶著一個說明泉坐,告訴會話適配器應(yīng)該應(yīng)用在哪個 web 服務(wù)上为鳄。

>>>> s = requests.Session()
>>> s.mount('http://www.github.com', MyAdapter())

這個 mount 調(diào)用會注冊一個傳輸適配器的特定實例到一個前綴上面。加載以后腕让,任何使用該會話的 HTTP 請求孤钦,只要其 URL 是以給定的前綴開頭,該傳輸適配器就會被使用到纯丸。

傳輸適配器的眾多實現(xiàn)細節(jié)不在本文檔的覆蓋范圍內(nèi)偏形,不過你可以看看接下來這個簡單的 SSL 用例。更多的用法觉鼻,你也許該考慮為 BaseAdapter 創(chuàng)建子類俊扭。

示例: 指定的 SSL 版本

Requests 開發(fā)團隊刻意指定了內(nèi)部庫(urllib3)的默認 SSL 版本。一般情況下這樣做沒有問題滑凉,不過是不是你可能會需要連接到一個服務(wù)節(jié)點统扳,而該節(jié)點使用了和默認不同的 SSL 版本。

你可以使用傳輸適配器解決這個問題畅姊,通過利用 HTTPAdapter 現(xiàn)有的大部分實現(xiàn)咒钟,再加上一個ssl_version 參數(shù)并將它傳遞到 urllib3 中。我們會創(chuàng)建一個傳輸適配器若未,用來告訴 urllib3 讓它使用 SSLv3:

import ssl

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager

class Ssl3HttpAdapter(HTTPAdapter):
    """"Transport adapter" that allows us to use SSLv3."""

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(num_pools=connections,
                                       maxsize=maxsize,
                                       block=block,
                                       ssl_version=ssl.PROTOCOL_SSLv3)

阻塞和非阻塞

使用默認的傳輸適配器朱嘴,Requests 不提供任何形式的非阻塞 IO。 Response.content 屬性會阻塞粗合,直到整個響應(yīng)下載完成萍嬉。如果你需要更多精細控制,該庫的數(shù)據(jù)流功能(見 流式請求) 允許你每次接受少量的一部分響應(yīng)隙疚,不過這些調(diào)用依然是阻塞式的壤追。

如果你對于阻塞式 IO 有所顧慮,還有很多項目可以供你使用供屉,它們結(jié)合了 Requests 和 Python 的某個異步框架行冰。典型的優(yōu)秀例子是 grequestsrequests-futures溺蕉。

Header 排序

在某些特殊情況下你也許需要按照次序來提供 header,如果你向 headers 關(guān)鍵字參數(shù)傳入一個OrderedDict悼做,就可以向提供一個帶排序的 header疯特。然而,Requests 使用的默認 header 的次序會被優(yōu)先選擇肛走,這意味著如果你在 headers 關(guān)鍵字參數(shù)中覆蓋了默認 header漓雅,和關(guān)鍵字參數(shù)中別的 header 相比,它們也許看上去會是次序錯誤的朽色。

如果這個對你來說是個問題邻吞,那么用戶應(yīng)該考慮在 Session 對象上面設(shè)置默認 header,只要將 Session 設(shè)為一個定制的 OrderedDict 即可纵搁。這樣就會讓它成為優(yōu)選的次序吃衅。

超時(timeout)

為防止服務(wù)器不能及時響應(yīng),大部分發(fā)至外部服務(wù)器的請求都應(yīng)該帶著 timeout 參數(shù)腾誉。在默認情況下徘层,除非顯式指定了 timeout 值,requests 是不會自動進行超時處理的利职。如果沒有 timeout趣效,你的代碼可能會掛起若干分鐘甚至更長時間。

連接超時指的是在你的客戶端實現(xiàn)到遠端機器端口的連接時(對應(yīng)的是connect()_)猪贪,Request 會等待的秒數(shù)跷敬。一個很好的實踐方法是把連接超時設(shè)為比 3 的倍數(shù)略大的一個數(shù)值,因為 TCP 數(shù)據(jù)包重傳窗口 (TCP packet retransmission window) 的默認大小是 3热押。

一旦你的客戶端連接到了服務(wù)器并且發(fā)送了 HTTP 請求西傀,讀取超時指的就是客戶端等待服務(wù)器發(fā)送請求的時間。(特定地桶癣,它指的是客戶端要等待服務(wù)器發(fā)送字節(jié)之間的時間拥褂。在 99.9% 的情況下這指的是服務(wù)器發(fā)送第一個字節(jié)之前的時間)。

如果你制訂了一個單一的值作為 timeout牙寞,如下所示:

r = requests.get('https://github.com', timeout=5)

這一 timeout 值將會用作 connectread 二者的 timeout饺鹃。如果要分別制定,就傳入一個元組:

r = requests.get('https://github.com', timeout=(3.05, 27))

如果遠端服務(wù)器很慢间雀,你可以讓 Request 永遠等待悔详,傳入一個 None 作為 timeout 值,然后就沖咖啡去吧惹挟。

r = requests.get('https://github.com', timeout=None)

有些地方不是太懂茄螃,先記錄。

上一篇:8.Urllib庫基本使用
下一篇:10.正則表達式基礎(chǔ)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末连锯,一起剝皮案震驚了整個濱河市责蝠,隨后出現(xiàn)的幾起案子党巾,更是在濱河造成了極大的恐慌萎庭,老刑警劉巖霜医,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驳规,居然都是意外死亡肴敛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門吗购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來医男,“玉大人,你說我怎么就攤上這事捻勉《扑螅” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵踱启,是天一觀的道長报账。 經(jīng)常有香客問我,道長埠偿,這世上最難降的妖魔是什么透罢? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮冠蒋,結(jié)果婚禮上羽圃,老公的妹妹穿的比我還像新娘。我一直安慰自己抖剿,他們只是感情好朽寞,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斩郎,像睡著了一般脑融。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孽拷,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天吨掌,我揣著相機與錄音,去河邊找鬼脓恕。 笑死膜宋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的炼幔。 我是一名探鬼主播秋茫,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乃秀!你這毒婦竟也來了肛著?” 一聲冷哼從身側(cè)響起圆兵,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枢贿,沒想到半個月后殉农,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡局荚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年超凳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耀态。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡轮傍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出首装,到底是詐尸還是另有隱情创夜,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布仙逻,位于F島的核電站驰吓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桨醋。R本人自食惡果不足惜棚瘟,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喜最。 院中可真熱鬧偎蘸,春花似錦、人聲如沸瞬内。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虫蝶。三九已至章咧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間能真,已是汗流浹背赁严。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粉铐,地道東北人疼约。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像蝙泼,于是被迫代替她去往敵國和親程剥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理汤踏,服務(wù)發(fā)現(xiàn)织鲸,斷路器舔腾,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 原文地址:Vuex basics: Tutorial and explanation 作者注:[2016.11 更...
    9d19c1b95dd4閱讀 432評論 1 7
  • 東風拂細柳 微雨綠南國 橋邊翩鴻影 搖曳一池春
    葉一半閱讀 77評論 2 0
  • -----------讀《清明上河圖密碼3》文/紀汐 和《清明上河圖密碼2》相比,3的可看性強多了搂擦。 當然一開始之...
    紀汐閱讀 811評論 0 0
  • #事業(yè)發(fā)展# 受榮格在《心理類型》末尾的總結(jié)心理術(shù)語表的啟發(fā)稳诚,萌發(fā)以此形式來構(gòu)建我的人格理論體系。1月19日 人對...
    陳原閱讀 895評論 0 0