JSoup字符编码问题

23

我正在使用JSoup解析来自http://www.latijnengrieks.com/vertaling.php?id=5368的内容。 这是一个第三方网站,没有指定正确的编码方式。 我正在使用以下代码加载数据:

public class Loader {

    public static void main(String[] args){
        String url = "http://www.latijnengrieks.com/vertaling.php?id=5368";

        Document doc;
        try {

            doc = Jsoup.connect(url).timeout(5000).get();
            Element content = doc.select("div.kader").first();
            Element contenttableElement = content.getElementsByClass("kopje").first().parent().parent();

            String contenttext = content.html();
            String tabletext = contenttableElement.html();

            contenttext = Jsoup.parse(contenttext).text();
            contenttext = contenttext.replace("br2n", "\n");
            tabletext = Jsoup.parse(tabletext.replaceAll("(?i)<br[^>]*>", "br2n")).text();
            tabletext = tabletext.replace("br2n", "\n");

            String text = contenttext.substring(tabletext.length(), contenttext.length());
            System.out.println(text);


        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }    

}

这将产生以下输出:

Aeneas dwaalt rond in Troje en zoekt Cre?sa. Cre?sa is echter op de vlucht gestorven Plotseling verschijnt er een schim. Het is de schim van Cre?sa. De schim zegt:'De oorlog woedt!' Troje is ingenomen! Cre?sa is gestorven:'Vlucht!' Aeneas vlucht echter niet. Dan spreekt de schim:'Vlucht! Er staat jou een nieuw vaderland en een nieuw koninkrijk te wachten.' Dan pas gehoorzaamt Aeneas en vlucht.

有没有办法让输出中的问号标记变回原来的(ü)?


Jsoup 可能正在正确解析内容。你是如何可视化输出的?在控制台窗口中吗?还是作为写入文件的文本? - Hovercraft Full Of Eels
最终输出将在Android TextView中,但这是一个控制台窗口,而Android Logcat给出了相同的结果。 - Hihaatje
4个回答

52
HTTP响应的Content-Type头中缺少charset属性。在解析HTML时,Jsoup将会使用平台默认的字符集。Document.OutputSettings#charset()不能起到作用,因为它仅用于展示(在html()和text()上),而不是用于解析数据(换句话说,现在已经太迟了)。

您需要将URL读取为InputStream,并在Jsoup#parse()方法中手动指定字符集。

String url = "http://www.latijnengrieks.com/vertaling.php?id=5368";
Document document = Jsoup.parse(new URL(url).openStream(), "ISO-8859-1", url);
Element paragraph = document.select("div.kader p").first();

for (Node node : paragraph.childNodes()) {
    if (node instanceof TextNode) {
        System.out.println(((TextNode) node).text().trim());
    }
}

这里的结果是

Aeneas dwaalt rond in Troje en zoekt Creüsa.
Creüsa is echter op de vlucht gestorven
Plotseling verschijnt er een schim.
Het is de schim van Creüsa.
De schim zegt:'De oorlog woedt!'
Troje is ingenomen!
Creüsa is gestorven:'Vlucht!'
Aeneas vlucht echter niet.
Dan spreekt de schim:'Vlucht! Er staat jou een nieuw vaderland en een nieuw koninkrijk te wachten.'
Dan pas gehoorzaamt Aeneas en vlucht.

1
那就是我要找的答案!再次感谢Balus,如果可以的话,给你5+分! - Hovercraft Full Of Eels
@Hovercraft:不用谢。顺便说一下,Jonathan在即将发布的Jsoup 1.6.2中添加了Element#textNodes(),这应该可以使instanceof检查变得多余。你只需要这样做:for (TextNode node : paragraph.textNodes())。另请参见https://dev59.com/rWw05IYBdhLWcg3wkik-#7164518 - BalusC
1
实际上,当使用Jsoup.parse(someText)方法调用时,“Content-Type”存在有效字符集也不会改变Jsoup的行为。您需要调用Jsoup.parse(inputStream, null, baseUrl)或类似的方法才能使Jsoup检测到字符集。 - Tristan
这个答案可以解决包含其他字符的URL。太好了!@BalusC - malajisi
能否以这种方式添加参数?类似地,Jsoup.connect(url).data(params)。 - Reva Junior
"Jsoup.parse(inputStream, null, baseUrl)" 这个也是一个很好的解决方案。谢谢@Tristan - Md. Sajedul Karim

17

好的,我找到了另一种方法。在我的情况下,我有一个Jsoup连接对象,并且我想从使用“ISO-8859”编码的网站的post()请求中检索html响应。由于JSOUP的默认编码是UTF-8,响应中的内容(html)会用�替换某些字母。我需要将其转换为ISO-8859-15。为此,我创建了连接。

Connection connectionTest = Jsoup.connect("URL")
.cookie("cookiereference", "cookievalue")
.method(Method.POST);

接下来,我创建了一个响应文档来保存帖子的答案。由于在Jsoup中如何设置响应的编码不清楚,所以我选择执行帖子并将响应保存为字节,保留编码属性。之后,我创建了一个新的字符串,传递这个字节数组和必须应用的正确编码。然后,文档将使用正确的编码创建。

Document response = Jsoup.parse(new String(
connectionTest.execute().bodyAsBytes(),"ISO-8859-15"));

因此,在我们使用response.html()时,修改前后都要进行返回。

修改前:

62.09-1-00 - Suporte t�cnico, manuten��o e outros servi�os em tecnologia da informa��o

修改后:

62.09-1-00 - Suporte técnico, manutenção e outros serviços em tecnologia da informação


你不能将上述方法作为通用方法使用。如果你访问的网站采用另一种编码方式,该方法会如何工作?我们能否将其变成通用方法?或者具体指定,如果网站采用ISO-8859编码,则运行此代码,否则运行默认代码(Jsoup.parse(execute.body(), url))。 - Asad Rao

7

Jsoup文档中指出,在读取文档时,应该自动检测正确的字符集,但是由于某些原因,它对我不起作用。然后我尝试使用outputSettings().charset(...)手动设置文档的字符集:

doc.outputSettings().charset("ISO-8859-1");

但这种方式仍然不起作用,也许是我做错了(我刚学习Jsoup)。

一个可行的解决方法是,使用设置了字符集的Scanner读取网页:

     String charset = "ISO-8859-1";

     URL myUrl = new URL(url);
     Scanner urlScanner = new Scanner(myUrl.openStream(), charset);
     StringBuilder sb = new StringBuilder();
     while (urlScanner.hasNextLine()) {
        sb.append(urlScanner.nextLine() + "\n");
     }
     urlScanner.close();

     doc = Jsoup.parse(sb.toString());

但是我会继续关注这个帖子,看看是否有更好的建议,不需要使用另一个类来读取HTML。


1
在我的情况下,我使用UTF-8来处理中文文本!无论如何感谢您! - Phuong

-1

我使用了:

public static String charset = "UTF-8";
doc = Jsoup.parse(new URL(theURL).openStream(), charset, theURL);

另外,将该类保存为UTF-8格式


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接