java - 抓取非英文网站的编码问题

标签 java encoding utf-8 internationalization web-crawler

我试图将网页内容作为字符串获取,我发现这个问题解决了 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 header 中列出的字符集。

最佳答案

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

你需要结合使用

a) HTML META 内容类型标签:

<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 响应 header Content-Type(如果存在)
  2. 对响应内容字节使用编码检测器
  3. 使用 HTML META 内容类型

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

关于java - 抓取非英文网站的编码问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7615014/

相关文章:

Java SocketException 管道损坏

java - <init> 和 (Native Method) 是什么意思?

vba - VBA IDE 中长字符串的格式

php - 为什么 jQuery 使用 UTF-16 进行 POSTing?

java - 是否可以在 java 中动态地将 "add"指向类路径?

java - 如何让我的程序根据用户输入打开一个窗口?

c# - 以UTF-16编码格式反序列化xml文件时出现XmlException

c++ - 文件编码如何影响 C++11 字符串文字?

python - UTF-8 字符串作为字典中的键导致 KeyError

string - 如何生成一个由字母数字字符组成的随机字符串?