powershell Invoke-WebRequest Invoke-RestMethod 亂碼 encoding sharset CharacterSet
Invoke-WebRequest和Invoke-RestMethod 爬部分網址會亂碼。這個問題很久了,很多人知道。
似乎從有這兩個命令的時候起,就有這個問題,至今已經4年有余了,但沒人知道原因。或許是沒人關注它。
其實這個問題並不難,經我研究,找出了原因,指出了解決方案。
但我當時片面地認為,這有可能和linux的http服務器有關,但后來發現,
http://www.msn.com 是iis網站,微軟官方網址,這個網址也有此亂碼現象,
最后才確定了這是Invoke-WebRequest Invoke-RestMethod,這兩個命令的bug。
才敢給微軟提交bug,這個亂碼最終的消除,還是要靠微軟。
powershell 傳教士 原創文章 2016-05-01 允許轉載,但必須保留名字和出處,否則追究法律責任
2017-02-10更新: 參見下面的bug報告,由於我報告bug時,只給出了utf8的例子,現在ps5.1版中,發現utf8的bug已經修復了,但gb2312的還沒修復。
------------【第一章 編碼知識點】-----------------
編碼類型,和編碼值,是不可分割的一對。所有亂碼的產生,是由於只知道編碼值,而不知道編碼類型! 如:
編碼值【70 00 73 00 20 4F 59 65 EB 58】 和 編碼類型【utf16】 結合起來,才知道,上述內容是【ps傳教士】。
這也是微軟發明,在文本中使用【bom頭】的原因。
【bom頭】【bom頭】,有頭無亂碼!
【bom頭】【bom頭】,用的人多的牛x文本編輯器,都支持【bom頭】,如vi,gedit等。
我以前遇到的某些爛人,怪人。他們很討厭,微軟使用的文本【bom頭】,非要不用。非要用某些野路子的奇技淫巧猜測編碼。 那么將導致:
1)必然有一定的猜錯幾率。此乃故意給自己亂碼吃。
2)某些文檔,如html,可能是多種編碼組合的。或許在【<>】中就使用了單獨的charset編碼。在這種單文件多編碼情況下,猜錯幾率更多。
3)不用【bom頭】的.py文檔,必然要用 coding:之類的。它們是同一種東西,都是編碼類型的標識。
有能耐你別用【bom頭】,也別用【coding】,純猜!腳本編碼未知,解析中文注釋報錯,導致的運行不了 活該! 寧可py腳本不能運行,也別用【bom頭】和【coding】
【bom頭】只解決了,純文本文件的亂碼。傳輸字符串的時候,也必須跟着編碼類型。一旦編碼類型丟失或未知,將產生亂碼。
--------------------【第二章 序】--------------------
(PowerShell中的)兩只爬蟲,兩只爬蟲,跑地快,爬網頁不賴~~~
一只基於com版的ie,一只基於.net中的WebRequest類,都是老奶奶,不奇怪 。。。
雖然很老了,但爬的也很快 。。。
如果你的系統是win8,或者win8以上,或者win7安裝了powershell 4.0,5.0,那么 powershell中自帶了這樣的兩個命令,【Invoke-WebRequest】和【Invoke-RestMethod】。 第一個命令返回的是對象,第二個返回的是(整個網頁)字符串。
這兩個命令有時候會返回亂碼,很長一段時間,我認為,是這個命令有解碼bug,但后來發現,把結果用其自帶的-outfile參數輸出到文件之后,編碼是正確的。 也就是說,其實是我們不知道怎么解碼。只能用寫入磁盤的慢方法。
后來我看了博客園友【昵稱:老馬說編程】的這兩篇帖子,琢磨出來的,感謝他! 也請大家先看看這兩篇亂碼修復類文章。
http://www.cnblogs.com/swiftma/p/5420145.html
http://www.cnblogs.com/swiftma/p/5430007.html
------------【第三章 正文】-----------------
亂碼命令版本:
所有版本的powershell。
亂碼原因:大概90%以上都是這種問題。
網頁編碼為utf8,但是接收到編碼后,把網頁源碼,編碼類型認錯誤了,或者說丟失了。 把utf8編碼網頁源碼,錯誤地認為是iso8859-1編碼類型的編碼,把此utf8再次轉換成了utf8,然后給我們呈現了。
bug重現powershell命令:
Invoke-WebRequest -Uri 'http://www.msn.com' # return chinese messy code (Invoke-WebRequest -Uri 'http://www.msn.com').BaseResponse.CharacterSet # utf8 web page,but return ISO-8859-1 Invoke-RestMethod -Uri 'http://www.msn.com'
修復辦法: 對上述編碼進行逆轉換。
bug修復powershell命令:
$utf8 = [System.Text.Encoding]::GetEncoding(65001) $iso88591 = [System.Text.Encoding]::GetEncoding(28591) #ISO 8859-1 ,Latin-1 $wrong_string = Invoke-RestMethod -Uri 'http://www.msn.com' $wrong_bytes = $utf8.GetBytes($wrong_string) $right_bytes = [System.Text.Encoding]::Convert($utf8,$iso88591,$wrong_bytes) #仔細看這里 $right_string = $utf8.GetString($right_bytes) #仔細看這里 write-host $right_string
gbk亂碼的解決:網頁源碼聲明了gb2312,瀏覽器打開正常,但powershell識別不正常的解決。
$gbk = [System.Text.Encoding]::GetEncoding(936) $utf8 = [System.Text.Encoding]::GetEncoding(65001) $iso88591 = [System.Text.Encoding]::GetEncoding(28591) #ISO 8859-1 ,Latin-1 $wrong_string = Invoke-RestMethod -Uri 'http://1212.ip138.com/ic.asp' $wrong_bytes = $utf8.GetBytes($wrong_string) $right_bytes = [System.Text.Encoding]::Convert($utf8,$iso88591,$wrong_bytes) #仔細看這里 $right_string = $gbk.GetString($right_bytes) #仔細看這里 write-host $right_string
歡迎去頂這個bug:
https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/13685217-invoke-restmethod-and-invoke-webrequest-encoding-b
問:在這個bug沒修復之前,如何用powershell爬數據?
答:請看我這篇文章: 轉帖不會亂碼的,powershell網絡蜘蛛 http://www.cnblogs.com/piapia/p/5093201.html
------------【第四章 后記:分析 列舉 網頁常用編碼類型】-----------------
wincodepage 名稱
936 gbk
54936 gb18030
GB18030使用變長編碼,有的字符是兩個字節,有的是四個字節。 在兩字節編碼中,字節表示范圍與GBK一樣。在四字節編碼中,第一個字節的值從0x81到0xFE,第二個字節的值從0x30到0x39,第三個字節的值從0x81到0xFE,第四個字節的值從0x30到0x39。 解析二進制時,如何知道是兩個字節還是四個字節表示一個字符呢?看第二個字節的范圍,如果是0x30到0x39就是四個字節表示,因為兩個字節編碼中第二字節都比這個大。
932 japanese
949 korean
950 big5
20127 us-ascii us 7bit
1252 ISO-8859-1
28591 ISO 8859-1 又稱Latin-1
1200 utf-16
1201 utf-16 Big-Endian
12000 utf-32
12001 utf-32 Big-Endian
65001 utf-8
gb2312,gbk,gb18030,之間是兼容的。由於網頁中都是簡單中文,所以可以把它們看作是同一種編碼。 所以常用(網頁!)編碼只有,gbk,big5,utf8,ISO 8859-1,1252, 所以常用(文本!)編碼只有,gbk,big5,utf8,ISO 8859-1,1252,utf16le,
摘自: https://msdn.microsoft.com/zh-cn/library/system.text.encodinginfo.codepage.aspx
------------【第五章 相關問題】-----------------
問:如何獲取網頁編碼?
答: 下載網頁,並查找網頁中的charset關鍵字。
powershell代碼: $網址 = 'http://www.baidu.com' $網頁編碼字串 = (Invoke-RestMethod -Uri $網址 ) -split '>' | select-string "Content-Type.*charset" #如這個百度網頁,有些網頁沒有 "`n" 換行符
問:【Invoke-WebRequest】和【Invoke-RestMethod】如何獲取網頁編碼?
答:
這個獲取方法是不可靠的,有些是錯誤的。powershell傳教士注
(Invoke-WebRequest -Uri www.baidu.com ).BaseResponse.CharacterSet #返回 utf-8 (Invoke-WebRequest -Uri news.qq.com ).BaseResponse.CharacterSet #返回 GB2312 (Invoke-WebRequest -Uri http://www.nmc.cn ).Headers.'content-type' #text/html (Invoke-WebRequest -Uri http://www.nmc.cn ).BaseResponse.CharacterSet #ISO-8859-1 (Invoke-WebRequest -Uri http://www.scielo.br).BaseResponse.CharacterSet
問:如何給網頁傳值?
答:
$text = '要發送的內容' $postData = [System.Text.Encoding]::UTF8.GetBytes($text) Invoke-WebRequest -Uri 'http://www.mydomain.com/' -Method Post -Body $postData -ContentType "text/plain; charset=utf-8"