code4craft / webmagic

A scalable web crawler framework for Java.
http://webmagic.io/
Apache License 2.0
11.42k stars 4.18k forks source link

使用setCharSet()后无法自动推测网页编码,导致网页乱码 #1101

Closed keatonLiu closed 1 year ago

keatonLiu commented 1 year ago

建议setCharSet()在通过响应推断编码失败时才使用,或者增加一个setDefaultCharSet(),当推断编码失败时使用该默认编码

sutra commented 1 year ago

现在的 Site.charset 就是这么用的吧:

request.getCharset() != null ? request.getCharset() : task.getSite().getCharset()

如果请求中没有获得 charset,就用 site 设置的 charset。

https://github.com/code4craft/webmagic/blob/develop/webmagic-core/src/main/java/us/codecraft/webmagic/downloader/HttpClientDownloader.java#L84

keatonLiu commented 1 year ago

请求中是获取不到charset的,charset是在响应里面获取,也就是在handleResponse()里面获取,这里面一段代码获取chaset:

https://github.com/code4craft/webmagic/blob/80424b0bd7242ae3f92055baabcedbf6e4a5913b/webmagic-core/src/main/java/us/codecraft/webmagic/downloader/HttpClientDownloader.java#L118-L120

keatonLiu commented 1 year ago

只有request.getCharset() != null ? request.getCharset() : task.getSite().getCharset()返回结果为null的时候他才会自动推断charset

sutra commented 1 year ago

现在的代码里的 charset 的使用顺序是, request charset, site charset, html charset, jvm default charset. 你的意思是,需要再来一个 default charset, 按照这个顺序: request charset, site charset, html charset, default charset, jvm default charset?

keatonLiu commented 1 year ago

是,差不多这个意思,我是想的是这个site charset只有在自动推断失效才会使用,这样就不用default charset了。 现在的情况是,只有request charset, site charset同时为null才会使用自动推断,而且会发出警告,如果推断失败,回退到的默认编码也只有utf-8。

keatonLiu commented 1 year ago

我的想法是,首先让这个函数推断失败时直接返回null:

https://github.com/code4craft/webmagic/blob/80424b0bd7242ae3f92055baabcedbf6e4a5913b/webmagic-core/src/main/java/us/codecraft/webmagic/downloader/HttpClientDownloader.java#L134-L141

然后把使用默认编码的判断放在这里:

protected Page handleResponse(Request request, String charset, HttpResponse httpResponse, Task task) throws IOException {
    byte[] bytes = IOUtils.toByteArray(httpResponse.getEntity().getContent());
    String contentType = httpResponse.getEntity().getContentType() == null ? "" : httpResponse.getEntity().getContentType().getValue();
    Page page = new Page();
    page.setBytes(bytes);
    if (!request.isBinaryContent()) {
        String detectedCharset = this.getHtmlCharset(contentType, bytes);
        if (detectedCharset == null) {
            if (charset == null){
                charset = Charset.defaultCharset().name();
                logger.warn("Charset autodetect failed, use {} as charset. Please specify charset in Site.setCharset()", Charset.defaultCharset());
            }
        } else {
            charset = detectedCharset;
        }

        page.setCharset(charset);
        page.setRawText(new String(bytes, charset));
    }

    page.setUrl(new PlainText(request.getUrl()));
    page.setRequest(request);
    page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
    page.setDownloadSuccess(true);
    if (this.responseHeader) {
        page.setHeaders(HttpClientUtils.convertHeaders(httpResponse.getAllHeaders()));
    }

    return page;
}