J'écris un robot dans Ruby (1.9) qui consomme beaucoup de HTML provenant de nombreux sites aléatoires.
En essayant d'extraire des liens, j'ai décidé d'utiliser simplement .scan(/href="(.*?)"/i)
au lieu de nokogiri/hpricot (accélération majeure). Le problème est que je reçois maintenant beaucoup d'erreurs "invalid byte sequence in UTF-8
".
D'après ce que j'ai compris, la bibliothèque net/http
ne contient aucune option d'encodage spécifique et les éléments fournis ne sont en principe pas correctement étiquetés.
Quel serait le meilleur moyen de travailler avec ces données entrantes? J'ai essayé .encode
avec l'ensemble des options replace et invalid, mais aucun succès jusqu'à présent ...
Dans Ruby 1.9.3, il est possible d’utiliser String.encode pour "ignorer" les séquences UTF-8 non valides. Voici un extrait qui fonctionnera à la fois en 1.8 ( iconv ) et en 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
ou si vous avez des problèmes de saisie, vous pouvez effectuer une double conversion de UTF-8 à UTF-16 et revenir à 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
La réponse acceptée, ni l'autre réponse ne fonctionnent pour moi. J'ai trouvé ce post qui a suggéré
string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
Cela a résolu le problème pour moi.
Ma solution actuelle consiste à exécuter:
my_string.unpack("C*").pack("U*")
Cela éliminera au moins les exceptions qui étaient mon principal problème
Essaye ça:
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
Je vous recommande d'utiliser un analyseur HTML. Il suffit de trouver le plus rapide.
L'analyse HTML n'est pas aussi facile que cela puisse paraître.
Les navigateurs analysent les séquences UTF-8 non valides, dans les documents HTML UTF-8, en mettant simplement le symbole "". Donc, une fois que la séquence UTF-8 invalide dans le HTML est analysée, le texte résultant est une chaîne valide.
Même à l'intérieur des valeurs d'attribut, vous devez décoder des entités HTML comme amp
Voici une excellente question qui résume la raison pour laquelle vous ne pouvez pas analyser HTML de manière fiable avec une expression régulière: RegEx correspond aux balises ouvertes, à l'exception des balises autonomes XHTML
Cela semble fonctionner:
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
J'ai rencontré string, qui mélangeait l'anglais, le russe et d'autres alphabets, ce qui a provoqué une exception. Je n'ai besoin que du russe et de l'anglais, et cela fonctionne actuellement pour moi:
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
Tandis que la solution de Nakilon fonctionne, au moins en ce qui concerne l’erreur, dans mon cas, j’ai converti cet étrange personnage créé à partir de Microsoft Excel en CSV enregistré dans Ruby comme un Ruby était un K. audacieux. Pour résoudre ce problème, j'ai utilisé 'iso-8859-1', à savoir. CSV.parse(f, :encoding => "iso-8859-1")
, qui a transformé mon effrayant Keaks cyrillique en un /\xCA/
beaucoup plus maniable, que je pourrais ensuite supprimer avec string.gsub!(/\xCA/, '')
Avant d'utiliser scan
, assurez-vous que l'en-tête Content-Type
de la page demandée est text/html
, car il peut exister des liens vers des éléments tels que des images non codées en UTF-8. La page peut également être non-HTML si vous avez récupéré une href
dans quelque chose comme un élément <link>
. Comment vérifier cela varie selon la bibliothèque HTTP que vous utilisez. Ensuite, assurez-vous que le résultat est uniquement ascii avec String#ascii_only?
(pas UTF-8 car HTML est supposé utiliser uniquement ascii, les entités peuvent être utilisées autrement). Si ces deux tests réussissent, vous pouvez utiliser scan
en toute sécurité.