【问题标题】:Encoding issues crawling non-english websites抓取非英文网站的编码问题
【发布时间】:2011-11-28 17:22:59
【问题描述】:

我正在尝试将网页的内容作为字符串获取,我发现这个问题解决了how to write a basic web crawler,它声称(并且似乎)处理编码问题,但是那里提供的代码适用于美国/英语网站,无法正确处理其他语言。

这是一个完整的 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 标头中列出的字符集。

【问题讨论】:

  • 你试过Apache Http Client 4.x吗?我发现使用起来更加舒适和稳定。也应该处理大部分的编码问题——尽管下面提到的 &lt;meta&gt; 元素 Joel 的处理仍然取决于您,但 EntityUtils 有很长的路要走。
  • 你得到'?'的事实而不是 U+FFFD 在这里说明。也许对 ISO-8859-1 有一个隐含的解释。标准库的许多部分默认使用这种编码。
  • 你怎么知道解码发生错误,而不是调试输出的编码?在返回字符串之前,您应该打印字符的数值并查看它们作为检查。
  • 好吧好吧,看起来这是一个操作系统特定的问题。在我的 Mac 上运行它输出 ????但在我的 Linux 机器上运行就可以了。前几个字符是 10 1056 1077 1079 1091 1083 1100 1090 1072 1090 1086 1074 - 不知道如何解释,但它们实际上不是问号。

标签: java encoding utf-8 internationalization web-crawler


【解决方案1】:

确定正确的字符集编码可能很棘手。

你需要结合使用

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) 从字节中检测字符集的启发式方法(参见this question

三个都用的原因是:

  1. (a) 和 (b) 可能缺失
  2. META Content-Type 可能有误(请参阅this question

如果 (a) 和 (b) 都丢失了怎么办?

在这种情况下,您需要使用一些启发式方法来确定正确的编码 - 请参阅 this question

我发现这个序列对于可靠地识别 HTML 页面的字符集编码是最可靠的:

  1. 使用 HTTP 响应标头 Content-Type(如果存在)
  2. 对响应内容字节使用编码检测器
  3. 使用 HTML META 内容类型

但您可以选择交换 2 和 3。

【讨论】:

    【解决方案2】:

    Apache Tika 包含您想要的实现。许多人为此使用它。您还可以查看 Apache Nutch。另一方面,您根本不必实现自己的爬虫。

    【讨论】:

      【解决方案3】:

      您看到的问题是您的 Mac 上的编码不支持西里尔字母。我不确定在 Oracle JVM 上是否如此,但当 Apple 生产自己的 JVM 时,the default character encoding for Java was MacRoman.

      启动程序时,指定file.encoding 系统属性以将字符编码设置为UTF-8(这是Mac OS X 默认使用的)。请注意,您必须在启动时设置它:java -Dfile.encoding=UTF-8 ...;如果您以编程方式设置(调用System.setProperty()),则为时已晚,该设置将被忽略。

      每当 Java 需要将字符编码为字节时——例如,当它将文本转换为字节以写入标准输出或错误流时——它将使用默认值,除非您明确指定不同的值。如果默认编码无法对特定字符进行编码,则替换为合适的替换字符。

      如果编码可以处理所使用的 Unicode 替换字符 U+FFFD、(�)。否则,问号 (?) 是常用的替换字符。

      【讨论】:

      • 我用我的 iMac 进行了测试,在 java 版本“1.6.0_26”上,默认编码仍然是“MacRoman”。即使我的LANG 设置为“en_US.UTF-8”也是如此。
      • 添加系统属性标志输出如下:–†–µ–∑—É–ª—å—Ç–∞—Ç–æ–≤ ... –Ø–Ω–¥–µ–∫—Å:–ù–∞—à–ª–æ—Å—å 298 –º–ª–Ω –æ—Ç–≤–µ—Ç–æ–≤
      • 我们开始吧!我找到了ibm.com/developerworks/opensource/library/os-eclipse-osxjava,它描述了如何在 Eclipse 中设置 UTF-8。您提到的 -D 标志在命令行中正常工作。感谢您的帮助。
      猜你喜欢
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多