Requests庫基本使用

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ù)的推測咆瘟。當(dāng)訪問 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'

#二進(jìn)制響應(yīng)內(nèi)容

#以字節(jié)的方式訪問請求響應(yīng)體,對于非文本請求:

print(type(response.content))?

print(type(response.text))

response.text返回的是Unicode型的數(shù)據(jù)茎毁。---文本

response.content返回的是bytes型也就是二進(jìn)制的數(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 的錯誤細(xì)節(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碧库,就可以得到用戶代理

need-to-insert-img

image

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)

重定向與請求歷史

默認(rèn)情況下拣挪,除了 HEAD, Requests 會自動處理所有重定向擦酌。

可以使用響應(yīng)對象的 history 方法來追蹤重定向。

Response.history 是一個 Response 對象的列表菠劝,為了完成請求而創(chuàng)建了這些對象赊舶。這個對象列表按照從最老到最近的請求進(jìn)行排序。例如赶诊,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ù)滋捶。如果不使用痛悯,的程序可能會永遠(yuǎn)失去響應(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

遇到網(wǎng)絡(luò)問題(如:DNS 查詢失敗炒考、拒絕連接等)時,Requests 會拋出一個 ConnectionError 異常霎迫。

如果 HTTP 請求返回了不成功的狀態(tài)碼, Response.raise_for_status() 會拋出一個 HTTPError 異常帘靡。

若請求超時知给,則拋出一個 Timeout 異常。

若請求超過了設(shè)定的最大重定向次數(shù),則會拋出一個 TooManyRedirects 異常涩赢。

所有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)對象

任何時候進(jìn)行了類似 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)

準(zhǔn)備的請求 (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 對象做什么特殊事情艇挨,立即準(zhǔn)備和修改了 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 驗證默認(rèn)是開啟的贼陶,如果證書驗證失敗刃泡,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-warningsInsecureRequestWarning)

解決方法為:

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]>

默認(rèn)情況下芹啥, 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)容工作流

默認(rèn)情況下碴犬,當(dāng)進(jìn)行網(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

? ...

可以進(jìn)一步使用 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ā)出的任何請求都會自動復(fù)用恰當(dāng)?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)

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

塊編碼請求

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

def gen():

? ? yield 'hi'

? ? yield 'there'

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

對于分塊的編碼請求物赶,我們最好使用Response.iter_content()對其數(shù)據(jù)進(jìn)行迭代。在理想情況下尚骄,你的 request 會設(shè)置stream=True块差,這樣你就可以通過調(diào)用iter_content并將分塊大小參數(shù)設(shè)為None,從而進(jìn)行分塊的迭代倔丈。如果你要設(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',

...

}

警告

強烈建議你用二進(jìn)制模式(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ù)返回一個值抛计,默認(rèn)以該值替換傳進(jìn)來的數(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中提供了兩種常見的的身份驗證方案:HTTPBasicAuth和HTTPDigestAuth弟断。

假設(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來進(jìn)行網(wǎng)絡(luò)請求:

>>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))

<Response [200]>

流式請求

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

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))

當(dāng)使用 decode_unicode=True 在Response.iter_lines()Response.iter_content()中時刘急,你需要提供一個回退編碼方式棚菊,以防服務(wù)器沒有提供默認(rèn)回退編碼,從而導(dǎo)致錯誤:

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不保證重進(jìn)入時的安全性叔汁。多次調(diào)用該方法 會導(dǎ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_PROXY和HTTPS_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另假,可以使用 http://user:password@host/ 語法:

proxies = {

? ? "http": "http://user:pass@10.10.1.10:3128/",

}

要為某個特定的連接方式或者主機設(shè)置代理像屋,使用 scheme://hostname 作為 key, 它會針對指定的主機和連接方式進(jìn)行匹配边篮。

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ī)范的考慮導(dǎo)致一些行為對于不熟悉相關(guān)規(guī)范的人來說看似有點奇怪。

編碼方式

當(dāng)你收到一個響應(yīng)時杈湾,Requests 會猜測響應(yīng)的編碼方式解虱,用于在你調(diào)用Response.text方法時對響應(yīng)進(jìn)行解碼。Requests 首先在 HTTP 頭部檢測是否存在指定的編碼方式漆撞,如果不存在殴泰,則會使用charade來嘗試猜測編碼方式。

只有當(dāng) HTTP 頭部不存在明確指定的字符集浮驳,并且Content-Type頭部字段包含text值之時悍汛, Requests 才不去猜測編碼方式。在這種情況下至会,RFC 2616指定默認(rèn)字符集必須是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 提供了詳細(xì)示例塘匣。

我將從最常使用的動詞 GET 開始。HTTP GET 是一個冪等方法兆解,從給定的 URL 返回一個資源馆铁。因而,當(dāng)你試圖從一個 web 位置獲取數(shù)據(jù)之時锅睛,你應(yīng)該使用這個動詞埠巨。一個使用示例是嘗試從 Github 上獲取關(guān)于一個特定 commit 的信息历谍。假設(shè)我們想獲取 Requests 的 commita050faf的信息。我們可以這樣去做:

>>>> import requests

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

我們應(yīng)該確認(rèn) 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 方法券坞。別擔(dān)心鬓催,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 提供了默認(rèn)的 HTTP 和 HTTPS 交互薛匪。每當(dāng)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)細(xì)節(jié)不在本文檔的覆蓋范圍內(nèi)肃晚,不過你可以看看接下來這個簡單的 SSL 用例。更多的用法仔戈,你也許該考慮為BaseAdapter創(chuàng)建子類关串。

示例: 指定的 SSL 版本

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

阻塞和非阻塞

使用默認(rèn)的傳輸適配器,Requests 不提供任何形式的非阻塞 IO溅漾。Response.content屬性會阻塞山叮,直到整個響應(yīng)下載完成。如果你需要更多精細(xì)控制添履,該庫的數(shù)據(jù)流功能(見流式請求) 允許你每次接受少量的一部分響應(yīng)屁倔,不過這些調(diào)用依然是阻塞式的。

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

Header 排序

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

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

超時(timeout)

為防止服務(wù)器不能及時響應(yīng)笆包,大部分發(fā)至外部服務(wù)器的請求都應(yīng)該帶著 timeout 參數(shù)环揽。在默認(rèn)情況下,除非顯式指定了 timeout 值色查,requests 是不會自動進(jìn)行超時處理的薯演。如果沒有 timeout,你的代碼可能會掛起若干分鐘甚至更長時間秧了。

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

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

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

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

這一 timeout 值將會用作connect和read二者的 timeout。如果要分別制定喉脖,就傳入一個元組:

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

如果遠(yuǎn)端服務(wù)器很慢椰苟,你可以讓 Request 永遠(yuǎn)等待,傳入一個 None 作為 timeout 值树叽,然后就沖咖啡去吧舆蝴。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市题诵,隨后出現(xiàn)的幾起案子洁仗,更是在濱河造成了極大的恐慌,老刑警劉巖性锭,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赠潦,死亡現(xiàn)場離奇詭異,居然都是意外死亡草冈,警方通過查閱死者的電腦和手機祭椰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門臭家,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人方淤,你說我怎么就攤上這事钉赁。” “怎么了携茂?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵你踩,是天一觀的道長。 經(jīng)常有香客問我讳苦,道長带膜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任鸳谜,我火速辦了婚禮膝藕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咐扭。我一直安慰自己芭挽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布蝗肪。 她就那樣靜靜地躺著袜爪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薛闪。 梳的紋絲不亂的頭發(fā)上辛馆,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音豁延,去河邊找鬼昙篙。 笑死,一個胖子當(dāng)著我的面吹牛诱咏,可吹牛的內(nèi)容都是我干的瓢对。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼胰苏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了醇疼?” 一聲冷哼從身側(cè)響起硕并,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秧荆,沒想到半個月后倔毙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡乙濒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年陕赃,在試婚紗的時候發(fā)現(xiàn)自己被綠了卵蛉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡么库,死狀恐怖傻丝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诉儒,我是刑警寧澤葡缰,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站忱反,受9級特大地震影響泛释,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜温算,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一怜校、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧注竿,春花似錦茄茁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喂分,卻和暖如春锦庸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒲祈。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工甘萧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梆掸。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓扬卷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親酸钦。 傳聞我的和親對象是個殘疾皇子怪得,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361