私はRuby(1.9)でクローラーを書いています。これは、多くのランダムなサイトから多くのHTMLを消費します。
リンクを抽出しようとしたときに、nokogiri/hpricotの代わりに.scan(/href="(.*?)"/i)
を使用することにしました(大幅な高速化)。問題は、多くの「invalid byte sequence in UTF-8
」エラーを受け取るようになったことです。
私が理解したことから、net/http
ライブラリにはエンコーディング固有のオプションがなく、入ってくるものは基本的に適切にタグ付けされていません。
着信データを実際に処理する最良の方法は何でしょうか?置換オプションと無効なオプションを設定して.encode
を試しましたが、今のところ成功していません...
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
受け入れられた答えも他の答えも私のために働く。 この投稿 が見つかりました
string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
これで問題は解決しました。
私の現在のソリューションは、実行することです:
my_string.unpack("C*").pack("U*")
これは少なくとも私の主な問題であった例外を取り除くでしょう
これを試して:
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
HTMLパーサーを使用することをお勧めします。最速のものを見つけてください。
HTMLの解析は、見かけほど簡単ではありません。
ブラウザは、UTF-8 HTMLドキュメント内の無効なUTF-8シーケンスを解析し、単に「�」記号を付けます。したがって、HTMLの無効なUTF-8シーケンスが解析されると、結果のテキストは有効な文字列になります。
属性値の中であっても、ampのようなHTMLエンティティをデコードする必要があります
ここに、正規表現でHTMLを確実に解析できない理由を要約した素晴らしい質問があります。 RegExは、XHTML自己完結型タグ
これはうまくいくようです:
def sanitize_utf8(string)
return nil if string.nil?
return string if string.valid_encoding?
string.chars.select { |c| c.valid_encoding? }.join
end
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
英語、ロシア語、およびその他のアルファベットが混在している文字列に遭遇したため、例外が発生しました。私はロシア語と英語だけが必要で、これは現在私のために働いています:
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
Nakilonのソリューションが機能している間、少なくともエラーを回避する限り、私の場合、Microsoft Excelから発信されたこの奇妙なf-edキャラクターをRubyに(getこれ)Rubyで太字のKであったキリル文字K。これを修正するために、「iso-8859-1」を使用しました。 CSV.parse(f, :encoding => "iso-8859-1")
。これにより、私の気紛れなキリル文字のKがはるかに管理しやすい/\xCA/
に変わり、string.gsub!(/\xCA/, '')
で削除できます
scan
を使用する前に、要求されたページのContent-Type
ヘッダーがtext/html
であることを確認してください。UTF-8でエンコードされていない画像などへのリンクがある可能性があります。 <link>
要素のようなものでhref
を選択した場合、ページはHTML以外になる可能性もあります。これを確認する方法は、使用しているHTTPライブラリによって異なります。次に、結果がString#ascii_only?
を持つasciiのみであることを確認します(HTMLはasciiのみを使用するため、UTF-8ではありません。エンティティは他の方法で使用できます)。これらのテストの両方に合格した場合、scan
を使用しても安全です。