【Python】DAY02學(xué)習(xí)日記,記一次慘絕人寰的debug

解決在啟用Fiddler的環(huán)境里,爬蟲報requests.exceptions.SSLError的問題

錯誤原因

image.png

源自:https://www.zhihu.com/question/42104344/answer/158407685

感謝知乎老哥通俗易懂又深刻的解釋!

解決辦法:

1.在requests.get()里設(shè)置參數(shù)verify = FALSE,跳過驗證環(huán)節(jié)

response = requests.get(url,verify = False)

但是這樣會報一個很煩人的InsecureRequestWarning,所以需要加上下面的代碼:

import urllib3
urllib3.disable_warnings()

這樣就完美解決了。

2.理論上可以導(dǎo)出Fiddler的根證書,使用OpenSSL轉(zhuǎn)換成.pem格式,然后設(shè)置verify的值為證書的路徑,驗證的時候就會去驗證Fiddler的根證書。但我不知道為什么,我這樣做了,卻沒有成功,報的錯誤是:

OSError: Could not find a suitable TLS CA certificate bundle, 
invalid path: Bili_Index/new.pem

參考資料來源:
官方文檔ssl-warnings
http與https代理中的差異及細(xì)節(jié)
HTTP:07---連接管理之(Connection首部
TLS詳解
詳解 HTTPS、TLS、SSL、HTTP區(qū)別和關(guān)系
python使用requests掛fiddler代理時提示SSLError,HTTPSConnectionPool
知乎-少年曉琦OliverCh的回答)

感謝 !

——前來debug的游客請止步——

因為下面全是毫無意義的廢話。

可算是明白了一杯茶一包煙一個Bug調(diào)一天的感覺了。
但是作為一個第二天學(xué)習(xí)Python的小萌新,我不禁思考,第二天就遇到了如此嚴(yán)峻、如此慘絕人寰的問題,是不是應(yīng)該早點苦海無涯回頭是岸?

先來看看報錯信息

ssl.SSLCertVerificationError:[SSL:CERTIFICATE_VERIFY_FAILED]
certificate verify failed:unable to get local issuer certificate (_ssl.c:1056)
During handling of the above exception, another exception occurred:urllib3.exceptions.MaxRetryError:HTTPSConnectionPool(host='www.bilibili.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL:CERTIFICATE_VERIFY_FAIL
requests.exceptions.SSLError:HTTPSConnectionPool(host='www.bilibili.com', port=443): Max retries exceeded with url:/ (Caused by SSLError(SSLCertVerificationError
(1, '[SSL: CERTIFICATE_VERIFY_FAILED]

簡單翻譯一下:

SSL證書錯誤:SSL證書驗證失?。簾o法獲取本地頒布的證書
在處理上述異常期間,又來了異常:
urllib3中的最大重試錯誤:重試訪問url超過最大連接數(shù):由SSLError引起(SSL證書錯誤[SSL證書驗證失敗])
requests中的SSLError:HTTPS連接池:重試訪問url超過最大連接數(shù):由SSLError引起(SSL證書錯誤[SSL證書驗證失敗])
(狗屁不通......)

嘗試分析以上的錯誤信息:每個報錯都寫著,SSL證書驗證失敗,但是為什么開著Fiddler代理就會出現(xiàn)這個問題呢?
因為requests的根證書和Fiddler的根證書沖突了,并且requests的證書驗證是默認(rèn)開啟的。

嘗試解決辦法1:關(guān)閉SSL證書驗證。

response = requests.get(url,verify=False)

驗證失敗那就不驗證了嘛。
嘗試結(jié)果:我去,不僅沒有解決,還甩了一個不安全警告。

InsecureRequestWarning: 
Unverified HTTPS request is being made to host 'www.bilibili.com'. 
Adding certificate verification is strongly advised.
See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning,

不安全請求警告:正在向主機(jī)發(fā)出未經(jīng)驗證的HTTPS請求。強(qiáng)烈建議增加證書驗證。
另外這里還有個配套的不安全警告處理措施,就是禁用警告

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

感覺不太合適的樣子。

既然甩了一個官方文檔的連接,那就去看看嘛。
追溯到官方的文檔:https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
在里面,我注意到這樣的信息:

image.png

HTTP和HTTPS代理
HTTP和HTTPS代理都支持HTTP和HTTPS目標(biāo)。其中唯一的差別是,是否需要先向代理創(chuàng)建一個TLS(傳輸層協(xié)議)連接。你可以通過指定正確的代理方案來指定你所需要連接的代理。

問題又來了,我雖然把Fiddler當(dāng)工具在用,但我真的不懂HTTP和HTTPS代理是什么,那就了解一下。
找到了一篇好文章:https://www.cnblogs.com/selol/p/5446965.html

image.png

看到上面的圖我深受啟發(fā),所以Connection首部又是什么啊。
找到了一篇好文章:https://blog.csdn.net/qq_41453285/article/details/95162180

image.png

謎底揭開了。

然后我理解了這兩句話:


image.png

所以服務(wù)器和客戶端達(dá)成keep-alive共識的時候,代理層懵逼了,我是誰我在哪兒你倆想干啥,得,關(guān)連接吧。

接著博主的文章往下看:

image.png

哦哦哦原來如此,盲中繼沒有理解Proxy-Connection這個Conection首部的其他首部字段名,并且轉(zhuǎn)發(fā)了這個字段。

接著看:

image.png
image.png
image.png

我好像理解了!感謝博主!

劃重點:HTTPS代理兩側(cè)連接是同步的,要斷一起斷。

把目光轉(zhuǎn)向之前沒看完的官方文檔:

image.png

HTTPS 代理+ HTTPS 目標(biāo)
一個TLS-in-TSL 隧道(?)將被創(chuàng)建。一個初始的TLS連接將被創(chuàng)建給代理,然后發(fā)送一個HTTP連接來創(chuàng)建一個通往目標(biāo)的TCP連接,最后創(chuàng)建第二個通往目標(biāo)的TLS連接。你可以自定義ssl.SSLContext用于通過ProxyManager類的proxy_ssl_context參數(shù)進(jìn)行代理TLS連接。
(狗屁不通X2)

我勉強(qiáng)理解一下,就是先創(chuàng)建一個初始TLS連接給代理,然后......算了我理解不了,先去搜搜看什么是TSL連接吧。

找到了一篇寫得很好的文章:http://www.itdecent.cn/p/1fc7130eb2c2

TLS握手過程:

image.png

看完了,還沒太理解,又找到了另一篇好文章:https://blog.csdn.net/chan70707/article/details/82932153

我好像懂得了什么:


image.png

回頭看看報錯信息:

ssl.SSLCertVerificationError: 
[SSL: CERTIFICATE_VERIFY_FAILED]
 certificate verify failed: unable to get local issuer certificate 

證書驗證失敗的原因是:沒有獲取到本地頒布的證書。

對啊,那我為什么不想辦法告訴它怎么獲取證書呢?

嘗試解決辦法2:指定SSL證書。
https://blog.csdn.net/qq_33958297/article/details/82291009
按照這篇文章里的步驟操作了一下,失敗了orz。

gProxies = {"http":"http://192.168.1.103:8888","https":"http://192.168.1.103:8888"}
cert = "D:\py文件\Bili_Index\\fiddlerroot.crt"
response = requests.get(url,proxies=gProxies,verify=cert)

(我現(xiàn)在好餓......)

接下來,我把FiddlerRoot證書導(dǎo)出,用OpenSSL把它從.cer轉(zhuǎn)換成.pem,然后把我的代碼改成了這個樣子:

response = requests.get(url,
                        proxies={"http": "http://127.0.0.1:8888", 
                                     "https":"http:127.0.0.1:8888"},
                        verify="D:\py文件\Bili_Index\\fdlroot.pem")

好了,又一次失敗的嘗試。

不過也有驚喜哦,那就是關(guān)了Fiddler,還附送了一個臉生的新錯誤哦:

requests.exceptions.ProxyError: 
HTTPSConnectionPool(host='www.bilibili.com', port=443):
 Max retries exceeded with url:
 / (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError
('<urllib3.connection.HTTPSConnection object at 0x00000251156835C0>: 
Failed to establish a new connection:
 [WinError 10061] 由于目標(biāo)計算機(jī)積極拒絕,無法連接。')))

好家伙,竟然敢拒絕我。
這下連HTTPS連接都建立不了。
(算了,休息會兒,回來面向百度debug)

回來了。
積極百度了一下還是運行不了。

草,我是智障嗎!
我都把Fiddler關(guān)了,還怪人家為什么連不上代理!??!
嗚嗚嗚嗚嗚對不起我的錯。
我這就把Fiddler打開。

哎,還是熟悉的SSLError。

 SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] 

這還能咋辦呢,回頭去看看官方文檔吧,嘆氣。

image.png

對于HTTPS代理,我們還支持使用絕對URI將請求轉(zhuǎn)發(fā)到HTTPS目的地,前提是use_forwarding_For_HTTPS參數(shù)設(shè)置為True。我們強(qiáng)烈建議您僅將此選項用于受信任的代理或公司代理,因為代理將完全可見您的請求。

這我設(shè)置的沒問題哈,略過這條。

這一條是關(guān)于上面的InsecureRequestWarning:


image.png

所以官方文檔還得多讀,再翻翻看。

發(fā)現(xiàn)了關(guān)鍵字Certificate Verification:


image.png

上面提示HTTPS連接現(xiàn)在是默認(rèn)驗證了。
雖然可以通過設(shè)置cert_reqs = 'CERT_NONE'來拒絕證書驗證,但是還是強(qiáng)烈建議順其自然。
除非另外指定的urllib3將嘗試加載默認(rèn)系統(tǒng)證書商店,最值得信賴的的跨平臺方法是使用certifi包,它提供Mozilla的根證書包。

既然官網(wǎng)都這么說了,那就整一個?


image.png

太慢了,真的太慢了,這下載速度。

繼續(xù)官方文檔:
一旦你擁有證書,你可以創(chuàng)建一個PoolManager,在發(fā)送請求的時候驗證證書。

image.png

啊這......跟我好像關(guān)系不是很大嘛。
目前要解決的問題是:
certificate verify failed: unable to get local issuer certificate
可是指定證書給它,它還是報這個錯誤。

不活了。

草草草草草草可以了!
就是加個參數(shù)verify=FALSE!
能跑出來不過有個不安全警告!
之前沒有跑出來是因為我一共寫了兩個get():

response = requests.get(url,verify=False)
img = requests.get(iurl,verify=False,timeout = 5).content

但是我剛剛只補(bǔ)了一個verify=False。

總之程序跑出來了,警告的話忽略就行了,去看看Fiddler抓到的包——

image.png

User-Agent:python-requests/2.24.0
Connection:keep-alive
長連接是沒問題的。

好像還發(fā)現(xiàn)了什么奇怪的東西?


image.png

Host: ocsp.globalsign.com是什么啊......

image.png

驗證證書的啊,明白了。

注意到下載完最后一張圖片以后,Conection依然是Keep-Alive:

image.png

好像這樣也行吧,程序能跑,F(xiàn)iddler能抓包,不安全警告可以disable掉,就是沒有證書驗證的環(huán)節(jié)。

但是,全局設(shè)置不驗證ssl證書——

ssl._create_default_https_context =ssl._create_unverified_context

也運行不出來,還是報相同的錯誤。

繼續(xù)探索正常驗證證書的辦法。

無意間看到有老哥解釋的原因:

image.png

https://www.zhihu.com/question/42104344/answer/158407685

這個解釋真是清晰明了!?。?/p>

最后提到的,將fiddler中下載的證書在requests中的參數(shù)設(shè)置,這方法我用過,不行的啊。
不如再試試?

試了,報錯報錯報錯報錯......

破案了!!我寫錯路徑了??!

原來寫的絕對路徑:

verify=r"D:\py文件\Bili_Index\fdlroot.pem"

改成相對路徑以后:

verify=r"Bili_Index/fdlroot.pem"

報的錯誤信息變化了耶!

image.png
OSError: Could not find a suitable TLS CA certificate bundle, 
invalid path: Bili_Index/fdlroot.pem

讓我來看看錯誤是什么...invalid path....好的哦。
可是路徑是我從pycharm里面右鍵copy path的,所以這其實是解析路徑的時候出了什么問題吧。

等等......如果其實證書是無效的呢?

我重新導(dǎo)出一下證書,再換成.pem格式。

image.png

reset再重來。

image.png
image.png
image.png

警告多得我很恐慌,總覺得自己在按川川辦公室的核*彈按鈕。

image.png

重新試了一下還是報相同的錯誤。

應(yīng)該還是證書的問題吧,搜了一下,驗證12306的證書的時候,也會報這個錯誤。而我剛剛注意到這里:


image.png
OSError: Could not find a suitable TLS CA certificate bundle, 
invalid path: Bili_Index/new.pem

我死心了,全網(wǎng)沒找到辦法,stackoverflow上面有個問題很像但不是。

餓死了,吃飯去。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容