首先說結論,發送requests請求必須帶上headers不然沒法保持bs之間的會話。從而報上述的錯誤。html
昨天一個朋友在爬網頁時出現的一個問題,以及後續我對這個問題進行了簡單的測試。python
先說出現的問題的簡單描述。瀏覽器
首先是使用urllib請求網頁:服務器
#urllib.request發起的請求 import urllib.request response = urllib.request.urlopen("https://baike.baidu.com") html = response.read().decode('utf8') print(type(html)) print(html)
結果正常顯示了百科的頁面信息:cookie
咱們使用requests來請求這個https頁面dom
#requests發起的請求 import requests html = requests.get('https://baike.baidu.com') print(type(html)) print(html)
而後報錯了:ide
報錯是重定向超過三十個,百度的結果是取消默認容許的重定向。測試
到這裏咱們得出第一條結論:編碼
urllib和requests發送的請求默認會根據響應的location進行重定向。url
百度了一下,根據衆網友的一致推薦,咱們關閉allow_redirects這個字段。
看一看源碼裏默認是容許重定向的。
關閉了重定向之後,頁面再也不跳轉。
#requests發起的請求,關閉重定向 import requests html = requests.get('https://baike.baidu.com', allow_redirects=False).text print(type(html)) print(html)
禁止了重定向頁面必然不能顯示正常的百科主頁了,這裏咱們獲得的是302的跳轉頁面。
再次代表一下,百度裏總有一些人只解決當前一個問題而不說明解決思路,或者試出來的結果就放上來看成回答的行爲是很不負責的。
這裏重定向的問題根本不在於頁面跳轉了,而是頁面爲何會屢次跳轉。
我查到一篇關於請求亞馬遜超出重定向限制的文章:http://www.it1352.com/330504.html。
簡單來講就是沒有與服務器創建會話,頁面重定向成了環形的死循環。即你的原始URL重定向一個沒有新的URL B,其重定向到C,它重定向到B,等等。
文章的結尾提到加請求頭來保持會話的持久性。
#requests發起的請求,添加請求頭 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} html = requests.get('https://baike.baidu.com', headers=headers).text print(type(html)) print(html)
請求的頁面應當是正確的,可是卻出現了以下亂碼:
本文的第二個結論也出來了:http頭部沒有編碼方式,requests默認使用本身的編碼方式。也是很任性,具體關於requests的亂碼行爲的出現緣由及解決方案,在這篇博客有詳細介紹,能夠看一下。https://www.cnblogs.com/billyzh/p/6148066.html。
#requests發起的請求,解決亂碼問題 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} html = requests.get('https://baike.baidu.com', headers=headers).content.decode('utf8') print(type(html)) print(html)
此時頁面顯示無異常,正確顯示百科的地址。
#requests發起的請求,加上重定向禁止 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} html = requests.get('https://baike.baidu.com', headers=headers, allow_redirects=False).content.decode('utf8') print(type(html)) print(html)
結果沒有影響,因此前面提到的解決重定向問題解決方案,多數人提到的禁止重定向根本無效,根本在於保持會話,防止重定向進入死循環。
本文結論三:多Google少百度(只針對技術性問題)。
到這裏咱們到底在模擬發送請求時請求頭帶了哪些東西致使的出現上面的問題呢?只能一步步分析請求頭的信息。
#urllib請求時發送的請求頭 import urllib.request request = urllib.request.Request("https://baike.baidu.com") print(request.headers)#{} print(request.get_header("User-agent"))#None
但實際上確定是不能發送一個空的請求頭的,因此咱們抓包獲取發送的請求信息。
urllib的響應頭
#urllib請求時迴應的響應頭 import urllib.request request = urllib.request.urlopen("https://baike.baidu.com") print(request.headers)
urllib在請求的時候什麼也沒作,請求頭也沒東西,然而服務器對他溫柔以待,響應了正確的跳轉頁面。
#requests請求超出30次重定向,暫時沒法獲得他的請求頭 import requests h=requests.get('https://baike.baidu.com') print(h.request.headers)
同理響應頭我也看不到。
#requests阻止重定向他的請求頭 import requests h=requests.get('https://baike.baidu.com', allow_redirects=False) print(h.request.headers)
{'User-Agent': 'python-requests/2.18.4', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
User-Agent表示了本身是python解釋器的請求。
#requests阻止重定向他的響應頭 import requests h=requests.get('https://baike.baidu.com', allow_redirects=False).headers print(h)
{'Connection': 'keep-alive', 'Content-Length': '154', 'Content-Type': 'text/html', 'Date': 'Wed, 31 Jan 2018 04:07:32 GMT', 'Location': 'https://baike.baidu.com/error.html?status=403&uri=/', 'P3p': 'CP=" OTI DSP COR IVA OUR IND COM "', 'Server': 'Apache', 'Set-Cookie': 'BAIDUID=C827DBDDF50E38C0C10F649F1DAAA462:FG=1; expires=Thu, 31-Jan-19 04:07:32 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1'}
鏈接是keep alive,有location顯示重定向地址。
#requests帶上本身瀏覽器信息的請求頭 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} h=requests.get('https://baike.baidu.com', allow_redirects=False,headers=headers) print(h.request.headers)
{'User-Agent': 'User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
#requests帶上本身瀏覽器信息的請求頭,默認容許重定向 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} h=requests.get('https://baike.baidu.com',headers=headers) print(h.request.headers)
與上面同樣,再次驗證阻不阻止頁面重定向不是解決問題的關鍵點。
根據上面的測試,我有一個大膽的猜想,urllib請求會被服務器接受並響應了setcookie字段,有了cookie建立一個會話最後保證了重定向的正常請求到一個最終的頁面,可是requests不加請求頭並不會被服務器返回setcookie,產生環形的重定向,最終沒法定位到跳轉的頁面,而加上請求頭User-Agent字段,那麼服務器默認會創建會話保證跳轉到正常的頁面。
補充一點,結論是不加請求頭,requests沒法保證與服務器之間的會話,每次鏈接服務器都被看成一條新請求直接讓他跳轉,不存在重定向環路的問題。
# #requests禁止跳轉的請求頭 import requests h=requests.get('https://baike.baidu.com', allow_redirects=False,verify=False) print(h.request.headers)
抓到的get的請求包:
# #requests帶上本身瀏覽器信息的請求頭 import requests headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} h=requests.get('https://baike.baidu.com', allow_redirects=False,headers=headers,verify=False) print(h.request.headers)
因此使用requests記得必定加上請求頭信息。
但願各位大神若是一不當心看完這篇文章請指出我說的不對的地方,或者哪些方面理解的還不夠深入。謝謝。
測試的時候沒考慮太多,其實能夠經過http://httpbin.org來查看請求響應信息更加直觀方便,這個網址是專門用來測試http請求的。