2010-06-06 49 views
100

我在Ruby(1.9)中編寫了一個爬取程序,它從很多隨機站點中消耗大量HTML。
當試圖提取鏈接時,我決定只使用.scan(/href="(.*?)"/i)而不是nokogiri/hpricot(主要加速)。問題是我現在收到很多「invalid byte sequence in UTF-8」錯誤。
從我的理解,net/http庫沒有任何編碼的具體選項,並進來的東西基本上沒有正確標記。
實際使用傳入數據的最佳方式是什麼?我試圖.encode與取代的和無效的選項集,但是沒有成功,到目前爲止...ruby​​ 1.9:UTF-8中的字節順序無效

+0

的東西,有可能打破字符,但保持串有效期爲其他庫: valid_string = untrusted_string.unpack( 'C *')包(「U *。 ') – 2011-08-06 07:17:32

+0

有確切的問題,嘗試了相同的其他解決方案。沒愛。試過馬克的,但它似乎玷污了一切。你確定'U *''撤消了'C *'嗎? – 2011-10-24 03:05:15

+0

不,它沒有:)我只是在一個webcrawler中使用它,在那裏我關心的第三方庫不會比我在這裏和那裏碰到的更多。 – 2012-11-29 09:48:16

回答

0

您使用scan之前,請確保所請求的頁面的Content-Type頭是text/html,因爲可以有鏈接之類的東西哪些是圖像沒有用UTF-8編碼。如果您在<link>元素中挑選了href,該頁面也可能不是html。如何檢查這一點取決於您使用的HTTP庫。然後,確保結果僅爲String#ascii_only?(不是UTF-8,因爲HTML只能使用ascii,否則可以使用實體)。如果這兩項測試都通過,則使用scan是安全的。

+0

謝謝,但這不是我的問題:) 我只提取URL的主機部分,只打首頁。我的問題是,我的輸入顯然不是UTF-8,而1.9編碼foo變得不合適 – 2010-06-06 00:57:10

+0

@Marc Seeger:你的意思是「我的輸入」是什麼意思? Stdin,網址或頁面正文? – Adrian 2010-06-06 01:14:14

+0

HTML可以用UTF-8編碼: http://en.wikipedia.org/wiki/Character_encodings_in_HTML – Eduardo 2010-06-06 01:39:39

4

我建議你使用HTML解析器。找到最快的一個。

解析HTML並不像看起來那麼容易。

瀏覽器在UTF-8 HTML文檔中解析無效的UTF-8序列,只是放上「 」符號。所以一旦HTML中的無效UTF-8序列被解析,結果文本就是一個有效的字符串。

即使裏面的屬性值,你必須解碼HTML實體,如放

下面是總結了,爲什麼你不能可靠地解析HTML使用正則表達式一個很大的問題: RegEx match open tags except XHTML self-contained tags

+2

我很想保留正則表達式,因爲它快了10倍,我真的不想正確解析html,只是想提取鏈接。 我應該可以通過以下操作替換ruby中的無效部分: ok_string = bad_string.encode(「UTF-8」,{:invalid =>:replace,:undef =>:替換}) 但是,似乎沒有工作:( – 2010-06-06 11:02:43

2

我遇到字符串,其中有英文,俄文和其他一些字母的混合,這引起了異常。我只需要俄語和英語,而目前這對我的作品:

ec1 = Encoding::Converter.new "UTF-8","Windows-1251",:invalid=>:replace,:undef=>:replace,:replace=>"" 
ec2 = Encoding::Converter.new "Windows-1251","UTF-8",:invalid=>:replace,:undef=>:replace,:replace=>"" 
t = ec2.convert ec1.convert t 
23

我目前的解決方案是運行:

my_string.unpack("C*").pack("U*") 

這將至少幹掉這是我的主要問題

例外的
+3

我正在使用這種方法結合'valid_encoding?',它似乎檢測什麼時候發生了什麼錯誤。'val.unpack('C *')。pack('U *')if! val.valid_encoding?'。 – 2012-01-19 16:41:43

+0

這一個爲我工作,成功地將我的'\ xB0'轉換回度符號,甚至'valid_encoding?'回來,但我仍然檢查它是否不正確,並刪除使用的字符上面的答案是:'string.encode!('UTF-8','binary',invalid::replace,undef::replace,replace:'')'我也嘗試過'force_encoding'路由,但是失敗了。 – hamstar 2014-08-04 23:48:19

+0

這很好,謝謝。 – 2015-12-17 03:58:53

166

在Ruby 1.9.3中,可以使用String.encode來「忽略」無效的UTF-8序列。下面是一個片段,將在1.8(iconv)和1.9工作都(String#encode):

require 'iconv' unless String.method_defined?(:encode) 
if String.method_defined?(:encode) 
    file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace) 
else 
    ic = Iconv.new('UTF-8', 'UTF-8//IGNORE') 
    file_contents = ic.iconv(file_contents) 
end 

,或者如果你真的有麻煩的輸入,你可以做從UTF-8雙轉換爲UTF-16和回UTF-8:

require 'iconv' unless String.method_defined?(:encode) 
if String.method_defined?(:encode) 
    file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') 
    file_contents.encode!('UTF-8', 'UTF-16') 
else 
    ic = Iconv.new('UTF-8', 'UTF-8//IGNORE') 
    file_contents = ic.iconv(file_contents) 
end 
+0

我比較過根據我的解決方案,我發現,我的遺失了一些信件,至少是'ё':'「Alena V。\」'。儘管你的解決方案保留了它:'「Ale \ u0308na V。\」'。尼斯。 – Nakilon 2012-01-16 01:20:07

+3

對於一些有問題的輸入,我還使用了從UTF-8到UTF-16的雙重轉換,然後返回到UTF-8'file_contents.encode!('UTF-16','UTF-8',:invalid =>:replace ,:replace =>'')'file_contents.encode!('UTF-8','UTF-16')' – ecerulm 2012-01-16 09:28:49

+7

還有'force_encoding'的選項。如果讀取ISO8859-1作爲UTF-8(因此該字符串包含無效的UTF-8),那麼可以使用the_string.force_encoding(「ISO8859-1」)將其重新解釋爲ISO8859-1,並且工作正常與它的真正的編碼字符串。 – ecerulm 2012-02-20 14:36:06

1

雖然Nakilon的解決方案有效,至少儘可能讓過去的錯誤,在我的情況,我有這個奇怪的F-編了字符從Microsoft Excel始發轉換爲CSV這是登記在紅寶石作爲(得到這個)紅寶石K是一個粗體K.爲了解決這個問題,我用'iso-8859-1'即。CSV.parse(f, :encoding => "iso-8859-1"),它把我的任性deaky西里爾K公司的進入更易於管理/\xCA/,我會再與string.gsub!(/\xCA/, '')

+0

同樣,我只想指出,雖然Nakilon(和其他人)修復程序是爲源自(哈哈)Cyrillia的西里爾字符,但此輸出是從xls轉換而來的csv的標準輸出! – 2012-10-16 03:57:18

1

這似乎是工作刪除:

def sanitize_utf8(string) 
    return nil if string.nil? 
    return string if string.valid_encoding? 
    string.chars.select { |c| c.valid_encoding? }.join 
end 
69

接受的答案,也不是我的其他答案的工作。我發現this post其中建議

string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') 

這解決了我的問題。

+1

這解決了我的問題,我喜歡使用非棄用的方法(我現在有Ruby 2.0)。 – 2014-04-26 19:51:42

+1

這是唯一的作品!我已經嘗試了所有上述方案的,他們沒有工作 字符串,在測試 使用 「fdsfdsf dfsf的SFD FS自衛隊

hello

fooo??? {[email protected]#$%^&*()_+}

\ XEF \ XBF \ XBD \ XEF \ XBF \ x9c
\xc2\x90
\ XC2 \ X90」 – 2016-01-07 21:47:42

-1

如果你不這樣做你可以做類似的數據「照顧」:

search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"

我只是用valid_encoding?以獲得通過的。我是一個搜索領域,所以我一遍又一遍地發現同樣的怪異,所以我使用了類似的東西:只是爲了讓系統不中斷。由於我不會控制用戶體驗以在發送此信息之前進行自動驗證(例如自動反饋說「虛擬起來!」),所以我可以將它取出,將其除去並返回空白結果。

6

試試這個:

def to_utf8(str) 
    str = str.force_encoding('UTF-8') 
    return str if str.valid_encoding? 
    str.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '') 
end 
+0

最佳答案我案件!謝謝 – Aldo 2016-01-19 10:17:40

3
attachment = file.read 

begin 
    # Try it as UTF-8 directly 
    cleaned = attachment.dup.force_encoding('UTF-8') 
    unless cleaned.valid_encoding? 
    # Some of it might be old Windows code page 
    cleaned = attachment.encode('UTF-8', 'Windows-1252') 
    end 
    attachment = cleaned 
rescue EncodingError 
    # Force it to UTF-8, throwing out invalid bits 
    attachment = attachment.force_encoding("ISO-8859-1").encode("utf-8", replace: nil) 
end 
相關問題