爬蟲的基本流程
發起請求
通過HTTP庫向目標站點發起請求,也就是發送一個Request,請求可以包含額外的header等信息,等待服務器響應
獲取響應內容
如果服務器能正常響應,會得到一個Response,Response的內容便是所要獲取的頁面內容,類型可能是HTML,Json字符串,二進制數據(圖片或者視頻)等類型
解析內容
得到的內容可能是HTML,可以用正則表達式,頁面解析庫進行解析,可能是Json,可以直接轉換為Json對象解析,可能是二進制數據,可以做保存或者進一步的處理
保存數據
保存形式多樣,可以存為文本,也可以保存到數據庫,或者保存特定格式的文件
Request,Response
瀏覽器發送消息給網址所在的服務器,這個過程就叫做HTPP Request
服務器收到瀏覽器發送的消息后,能夠根據瀏覽器發送消息的內容,做相應的處理,然后把消息回傳給瀏覽器,這個過程就是HTTP Response
瀏覽器收到服務器的Response信息后,會對信息進行相應的處理,然后展示
Request中包含什么?
請求方式
主要有:GET/POST兩種類型常用,另外還有HEAD/PUT/DELETE/OPTIONS
GET和POST的區別就是:請求的數據GET是在url中,POST則是存放在頭部
GET:向指定的資源發出“顯示”請求。使用GET方法應該只用在讀取數據,而不應當被用於產生“副作用”的操作中,例如在Web Application中。其中一個原因是GET可能會被網絡蜘蛛等隨意訪問
POST:向指定資源提交數據,請求服務器進行處理(例如提交表單或者上傳文件)。數據被包含在請求本文中。這個請求可能會創建新的資源或修改現有資源,或二者皆有。
HEAD:與GET方法一樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法可以在不必傳輸全部內容的情況下,就可以獲取其中“關於該資源的信息”(元信息或稱元數據)。
PUT:向指定資源位置上傳其最新內容。
OPTIONS:這個方法可使服務器傳回該資源所支持的所有HTTP請求方法。用'*'來代替資源名稱,向Web服務器發送OPTIONS請求,可以測試服務器功能是否正常運作。
DELETE:請求服務器刪除Request-URI所標識的資源。
請求URL
URL,即統一資源定位符,也就是我們說的網址,統一資源定位符是對可以從互聯網上得到的資源的位置和訪問方法的一種簡潔的表示,是互聯網上標准資源的地址。互聯網上的每個文件都有一個唯一的URL,它包含的信息指出文件的位置以及瀏覽器應該怎么處理它。
URL的格式由三個部分組成:
第一部分是協議(或稱為服務方式)。
第二部分是存有該資源的主機IP地址(有時也包括端口號)。
第三部分是主機資源的具體地址,如目錄和文件名等。
爬蟲爬取數據時必須要有一個目標的URL才可以獲取數據,因此,它是爬蟲獲取數據的基本依據。
請求頭
- 包含請求時的頭部信息,如User-Agent,Host,Cookies等信息,下圖是請求請求百度時,所有的請求頭部信息
請求體
請求是攜帶的數據,如提交表單數據時候的表單數據(POST)
Response中包含了什么
所有HTTP響應的第一行都是狀態行,依次是當前HTTP版本號,3位數字組成的狀態代碼,以及描述狀態的短語,彼此由空格分隔。
響應狀態
有多種響應狀態,如:200代表成功,301跳轉,404找不到頁面,502服務器錯誤
- 1xx消息——請求已被服務器接收,繼續處理
- 2xx成功——請求已成功被服務器接收、理解、並接受
- 3xx重定向——需要后續操作才能完成這一請求
- 4xx請求錯誤——請求含有詞法錯誤或者無法被執行
- 5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤 常見代碼: 200 OK 請求成功 400 Bad Request 客戶端請求有語法錯誤,不能被服務器所理解 401 Unauthorized 請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用 403 Forbidden 服務器收到請求,但是拒絕提供服務 404 Not Found 請求資源不存在,eg:輸入了錯誤的URL 500 Internal Server Error 服務器發生不可預期的錯誤 503 Server Unavailable 服務器當前不能處理客戶端的請求,一段時間后可能恢復正常 301 目標永久性轉移 302 目標暫時性轉移
響應頭
如內容類型,類型的長度,服務器信息,設置Cookie,如下圖
響應體
最主要的部分,包含請求資源的內容,如網頁HTMl,圖片,二進制數據等
能爬取什么樣的數據
網頁文本:如HTML文檔,Json格式化文本等
圖片:獲取到的是二進制文件,保存為圖片格式
視頻:同樣是二進制文件
其他:只要請求到的,都可以獲取
如何解析數據
- 直接處理
- Json解析
- 正則表達式處理
- BeautifulSoup解析處理
- PyQuery解析處理
- XPath解析處理
關於抓取的頁面數據和瀏覽器里看到的不一樣的問題
出現這種情況是因為,很多網站中的數據都是通過js,ajax動態加載的,所以直接通過get請求獲取的頁面和瀏覽器顯示的不同。
如何解決js渲染的問題?
分析ajax
Selenium/webdriver
Splash
PyV8,Ghost.py
怎樣保存數據
文本:純文本,Json,Xml等
關系型數據庫:如mysql,oracle,sql server等結構化數據庫
非關系型數據庫:MongoDB,Redis等key-value形式存儲
什么是Requests
Requests是用python語言基於urllib編寫的,采用的是Apache2 Licensed開源協議的HTTP庫
如果你看過上篇文章關於urllib庫的使用,你會發現,其實urllib還是非常不方便的,而Requests它會比urllib更加方便,可以節約我們大量的工作。(用了requests之后,你基本都不願意用urllib了)一句話,requests是python實現的最簡單易用的HTTP庫,建議爬蟲使用requests庫。
默認安裝好python之后,是沒有安裝requests模塊的,需要單獨通過pip安裝
requests功能詳解
總體功能的一個演示

我們可以看出response使用起來確實非常方便,這里有個問題需要注意一下:
很多情況下的網站如果直接response.text會出現亂碼的問題,所以這個使用response.content
這樣返回的數據格式其實是二進制格式,然后通過decode()轉換為utf-8,這樣就解決了通過response.text直接返回顯示亂碼的問題.
請求發出后,Requests 會基於 HTTP 頭部對響應的編碼作出有根據的推測。當你訪問 response.text 之時,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什么編碼,並且能夠使用 response.encoding 屬性來改變它.如:
response =requests.get("http://www.baidu.com") response.encoding="utf-8" print(response.text)
不管是通過response.content.decode("utf-8)的方式還是通過response.encoding="utf-8"的方式都可以避免亂碼的問題發生
各種請求方式
requests里提供個各種請求方式
import requests requests.post("http://httpbin.org/post") requests.put("http://httpbin.org/put") requests.delete("http://httpbin.org/delete") requests.head("http://httpbin.org/get") requests.options("http://httpbin.org/get")
請求
基本GET請求
import requests response = requests.get('http://httpbin.org/get') print(response.text)
帶參數的GET請求,例子1
import requests response = requests.get("http://httpbin.org/get?name=zhaofan&age=23") print(response.text)
如果我們想要在URL查詢字符串傳遞數據,通常我們會通過httpbin.org/get?key=val方式傳遞。Requests模塊允許使用params關鍵字傳遞參數,以一個字典來傳遞這些參數,例子如下:
import requests data = { "name":"zhaofan", "age":22 } response = requests.get("http://httpbin.org/get",params=data) print(response.url) print(response.text)
上述兩種的結果是相同的,通過params參數傳遞一個字典內容,從而直接構造url
注意:第二種方式通過字典的方式的時候,如果字典中的參數為None則不會添加到url上
解析json
import requests import json response = requests.get("http://httpbin.org/get") print(type(response.text)) print(response.json()) print(json.loads(response.text)) print(type(response.json()))
從結果可以看出requests里面集成的json其實就是執行了json.loads()方法,兩者的結果是一樣的
獲取二進制數據
在上面提到了response.content,這樣獲取的數據是二進制數據,同樣的這個方法也可以用於下載圖片以及
視頻資源
添加headers
和前面我們將urllib模塊的時候一樣,我們同樣可以定制headers的信息,如當我們直接通過requests請求知乎網站的時候,默認是無法訪問的
import requests response =requests.get("https://www.zhihu.com") print(response.text)
這樣會得到如下的錯誤
因為訪問知乎需要頭部信息,這個時候我們在谷歌瀏覽器里輸入chrome://version,就可以看到用戶代理,將用戶代理添加到頭部信息
import requests headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" } response =requests.get("https://www.zhihu.com",headers=headers) print(response.text)
這樣就可以正常的訪問知乎了
基本POST請求
通過在發送post請求時添加一個data參數,這個data參數可以通過字典構造成,這樣
對於發送post請求就非常方便
import requests data = { "name":"zhaofan", "age":23 } response = requests.post("http://httpbin.org/post",data=data) print(response.text)
同樣的在發送post請求的時候也可以和發送get請求一樣通過headers參數傳遞一個字典類型的數據
響應
我們可以通過response獲得很多屬性,例子如下
import requests response = requests.get("http://www.baidu.com") print(type(response.status_code),response.status_code) print(type(response.headers),response.headers) print(type(response.cookies),response.cookies) print(type(response.url),response.url) print(type(response.history),response.history)
結果如下:
狀態碼判斷
Requests還附帶了一個內置的狀態碼查詢對象
主要有如下內容:
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("訪問成功")
requests高級用法
文件上傳
實現方法和其他參數類似,也是構造一個字典然后通過files參數傳遞
import requests files= {"files":open("git.jpeg","rb")} response = requests.post("http://httpbin.org/post",files=files) print(response.text)
結果如下:
獲取cookie
import requests response = requests.get("http://www.baidu.com") print(response.cookies) for key,value in response.cookies.items(): print(key+"="+value)
會話維持
cookie的一個作用就是可以用於模擬登陸,做會話維持
import requests s = requests.Session() s.get("http://httpbin.org/cookies/set/number/123456") response = s.get("http://httpbin.org/cookies") print(response.text)
這是正確的寫法,而下面的寫法則是錯誤的
import requests requests.get("http://httpbin.org/cookies/set/number/123456") response = requests.get("http://httpbin.org/cookies") print(response.text)
心得體會
request是最基本的爬蟲庫了, 建議新手把測試案例都運行一下,體會一下爬蟲的過程。
體會一下模擬瀏覽器訪問各個階段、數據是如何交互的、如何處理的。