Проблемы с кодированием при сканировании неанглийских сайтов - PullRequest
3 голосов
/ 30 сентября 2011

Я пытаюсь получить содержимое веб-страницы в виде строки, и я обнаружил, что этот вопрос касается , как написать базовый веб-сканер , который утверждает, что (и кажется) решает проблему кодированияоднако предоставленный там код, который работает для веб-сайтов на американском / английском языках, не может правильно обрабатывать другие языки.

Вот полный класс Java, демонстрирующий то, что я имею в виду:

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class I18NScraper
{
    static
    {
        System.setProperty("http.agent", "");
    }

    public static final String IE8_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; InfoPath.2)";

  //https://stackoverflow.com/questions/1381617/simplest-way-to-correctly-load-html-from-web-page-into-a-string-in-java
    private static final Pattern CHARSET_PATTERN = Pattern.compile("text/html;\\s+charset=([^\\s]+)\\s*");
    public static String getPageContentsFromURL(String page) throws UnsupportedEncodingException, MalformedURLException, IOException {
        Reader r = null;
        try {
            URL url = new URL(page);
            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setRequestProperty("User-Agent", IE8_USER_AGENT);

            Matcher m = CHARSET_PATTERN.matcher(con.getContentType());
            /* If Content-Type doesn't match this pre-conception, choose default and 
             * hope for the best. */
            String charset = m.matches() ? m.group(1) : "ISO-8859-1";
            r = new InputStreamReader(con.getInputStream(),charset);
            StringBuilder buf = new StringBuilder();
            while (true) {
              int ch = r.read();
              if (ch < 0)
                break;
              buf.append((char) ch);
            }
            return buf.toString();
        } finally {
            if(r != null){
                r.close();
            }
        }
    }

    private static final Pattern TITLE_PATTERN = Pattern.compile("<title>([^<]*)</title>");
    public static String getDesc(String page){
        Matcher m = TITLE_PATTERN.matcher(page);
        if(m.find())
            return m.group(1);
        return page.contains("<title>")+"";
    }

    public static void main(String[] args) throws UnsupportedEncodingException, MalformedURLException, IOException{
        System.out.println(getDesc(getPageContentsFromURL("http://yandex.ru/yandsearch?text=%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%BE%D0%B2&lr=223")));
    }
}

Какие выходы:

???????????&nbsp;&mdash; ??????: ??????? 360&nbsp;???&nbsp;???????

Хотя это должно быть:

Результатов&nbsp;&mdash; Яндекс: Нашлось 360&nbsp;млн&nbsp;ответов

Можете ли вы помочь мне понять, что я делаю неправильно?Попытки таких вещей, как форсирование UTF-8, не помогают, несмотря на то, что кодировка указана в источнике и заголовке HTTP.

Ответы [ 3 ]

2 голосов
/ 01 октября 2011

Определение правильной кодировки кодировки может быть сложным.

Вам необходимо использовать комбинацию

a) тег HTML META Content-Type:

<META http-equiv="Content-Type" content="text/html; charset=EUC-JP">

b) заголовок ответа HTTP:

Content-Type: text/html; charset=utf-8

c) Эвристика для обнаружения кодировки из байтов (см. этот вопрос )

Причина использования всех трех:

  1. (a) и (b) могут отсутствовать
  2. тип содержимого META может быть неправильным (см. этот вопрос )

Что делать, если (a) и (b) оба отсутствуют?

В этом случае вам нужно использовать некоторые эвристики для определения правильной кодировки - см. этот вопрос .

Я считаю эту последовательность наиболее надежной для надежной идентификации кодировки кодировки HTML-страницы:

  1. Использовать заголовок ответа HTTP Content-Type (если существует)
  2. Использовать детектор кодирования в байтах содержимого ответа
  3. использовать HTML META Content-Type

но вы можете поменять местами 2 и 3.

1 голос
/ 01 октября 2011

Проблема, с которой вы сталкиваетесь, заключается в том, что кодировка на вашем Mac не поддерживает кириллицу.Я не уверен, верно ли это для Oracle JVM, но когда Apple создавала свои собственные JVM, для Java по умолчанию использовалась кодировка символов MacRoman.

При запуске программы укажитесистемное свойство file.encoding для установки кодировки символов в UTF-8 (это то, что Mac OS X использует по умолчанию).Обратите внимание, что вы должны установить его при запуске: java -Dfile.encoding=UTF-8 ...;если вы установите его программно (с вызовом System.setProperty()), будет слишком поздно, и настройка будет проигнорирована.

Всякий раз, когда Java необходимо кодировать символы в байты - например, когда он преобразует текст в байтыдля записи в стандартный поток вывода или сообщения об ошибках - он будет использовать значение по умолчанию, если вы не укажете явно другое.Если кодировка по умолчанию не может кодировать определенный символ, заменяется подходящий символ замены.

Если кодировка может обрабатывать заменяющий символ Unicode, U + FFFD, (�), который используется.В противном случае знак вопроса (?) Является часто используемым символом замены.

0 голосов
/ 01 октября 2011

Apache Tika содержит реализацию того, что вы хотите здесь. Многие люди используют это для этого. Вы также можете заглянуть в Apache Nutch. С другой стороны, тогда вам вообще не придется реализовывать собственный сканер.

...