【问题标题】:How is the best way to extract the entire content from a BufferedReader object in Java?从 Java 中的 BufferedReader 对象中提取全部内容的最佳方法是什么?
【发布时间】:2011-04-24 12:58:00
【问题描述】:

我正在尝试通过 URLConnection 获取整个网页。

最有效的方法是什么?

我已经这样做了:

URL url = new URL("http://www.google.com/");
URLConnection connection;
connection = url.openConnection();
InputStream in = connection.getInputStream();        
BufferedReader bf = new BufferedReader(new InputStreamReader(in));
StringBuffer html = new StringBuffer();
String line = bf.readLine();
while(line!=null){
    html.append(line);
    line = bf.readLine();
}
bf.close();

html 包含整个 HTML 页面。

【问题讨论】:

  • 定义什么是所说的“高效”。
  • mm,最好的方法。什么需要更少的时间和资源。我不是 IO 方面的专家,所以我很难评估替代方案。
  • 我认为这是最好的方法,使用 StringBuffer 非常好并且有很大帮助,因为它创建的对象更少,我没有什么不同的。
  • 您目前的瓶颈是什么?您想用 CPU 周期换取带宽吗?此方法不支持 gzip。
  • 如果我没记错的话,readLine() 去掉了行分隔符,所以如果你没有明确地把它们放回去,你的输出就会把它们全部去掉。此外,如果这是 Java 1.5 或更高版本,请考虑使用 StringBuilder 而不是 StringBuffer

标签: java performance bufferedreader


【解决方案1】:

我认为这最好的方法。页面的大小是固定的(“就是这样”),因此您无法提高内存。也许您可以在拥有内容后对其进行压缩,但在这种形式下它们并不是很有用。我想你最终会想要将 HTML 解析成 DOM 树。

您为并行读取所做的任何事情都会使解决方案过于复杂。

我建议使用默认大小为 2048 或 4096 的 StringBuilder。

为什么您认为您发布的代码不够用?您听起来好像对过早优化感到内疚。

带着你所有的东西跑,晚上睡觉。

【讨论】:

  • 哈哈,谢谢@duffymo。我只是想知道是否有更好的方法。谢谢。
【解决方案2】:

你想对获得的 HTML 做什么?解析它?很高兴知道一个有点体面的 HTML 解析器已经有一个构造函数或方法参数,它直接接受 URLInputStream,这样您就不必担心这样的流性能。

假设您想要做的所有事情都在your previous question 中进行了描述,例如Jsoup,您可以获得所有这些新闻链接,如下所示:

Document document = Jsoup.connect("http://news.google.com.ar/nwshp?hl=es&tab=wn").get();
Elements newsLinks = document.select("h2.title a:eq(0)");
for (Element newsLink : newsLinks) {
    System.out.println(newsLink.attr("href"));
}

这仅在几秒钟后就会产生以下结果:

http://www.infobae.com/mundo/541259-100970-0-Pinera-confirmo-que-el-rescate-comenzara-las-20-y-durara-24-y-48-horas http://www.lagaceta.com.ar/nota/403112/Argentina/Boudou-disculpo-con-DAIA-pero-volvio-cuestionar-medios.html http://www.abc.es/agencias/noticia.asp?noticia=550415 http://www.google.com/hostednews/epa/article/ALeqM5i6x9rhP150KfqGJvwh56O-thi4VA?docId=1383133 http://www.abc.es/agencias/noticia.asp?noticia=550292 http://www.univision.com/contentroot/wirefeeds/noticias/8307387.shtml http://noticias.terra.com.ar/internacionales/ecuador-apoya-reclamo-argentino-por-ejercicios-en-malvinas,3361af2a712ab210VgnVCM4000009bf154d0RCRD.html http://www.infocielo.com/IC/Home/index.php?ver_nota=22642 http://www.larazon.com.ar/economia/Cristina-Fernandez-Censo-indispensable-pais_0_176100098.html http://www.infobae.com/finanzas/541254-101275-0-Energeticas-llevaron-la-Bolsa-portena-ganancias http://www.telam.com.ar/vernota.php?tipo=N&idPub=200661&id=381154&dis=1&sec=1 http://www.ambito.com/noticia.asp?id=547722 http://www.canal-ar.com.ar/noticias/noticiamuestra.asp?Id=9469 http://www.pagina12.com.ar/diario/cdigital/31-154760-2010-10-12.html http://www.lanacion.com.ar/nota.asp?nota_id=1314014 http://www.rpp.com.pe/2010-10-12-ganador-del-pulitzer-destaca-nobel-de-mvll-noticia_302221.html http://www.lanueva.com/hoy/nota/b44a7553a7/1/79481.html http://www.larazon.com.ar/show/sdf_0_176100096.html http://www.losandes.com.ar/notas/2010/10/12/batista-siento-comodo-dieron-respaldo-520595.asp http://deportes.terra.com.ar/futbol/los-rumores-empiezan-a-complicar-la-vida-de-river-y-vuelve-a-sonar-gallego,a24483b8702ab210VgnVCM20000099f154d0RCRD.html http://www.clarin.com/deportes/futbol/Exigieron-Roman-regreso-Huracan_0_352164993.html http://www.el-litoral.com.ar/leer_noticia.asp?idnoticia=146622 http://www.nuevodiarioweb.com.ar/nota/181453/Locales/C%C3%A1ncer_mama:_200_casos_a%C3%B1o_Santiago.html http://www.ultimahora.com/notas/367322-Funcionarios-sanitarios-capacitaran-sobre-cancer-de-mama http://www.lanueva.com/hoy/nota/65092f2044/1/79477.html http://www.infobae.com/policiales/541220-101275-0-Se-suspendio-la-declaracion-del-marido-Fernanda-Lemos http://www.clarin.com/sociedad/educacion/titulo_0_352164863.html

是否有人已经说过正则表达式绝对是解析 HTML 的错误工具? ;)

另见:

【讨论】:

  • 谢谢@BalusC 你直截了当。我之前没有这么说,因为我不想把想法搞砸。因为对“一般”答案很感兴趣。但这真的帮助了我。我一直在对不同的解析器进行基准测试。 Swing HTMLEditorKit 似乎是最好的。你有这方面的经验吗?你觉得JSoup更好吗?谢谢你的回答!!
  • 我发现 Jsoup 有一个更流畅的 API。我喜欢。使用类似 jQuery 的选择器在 Java 中选择 HTML 元素。性能差异在 IMO 可以忽略不计。
  • @BalusC 非常感谢。您对 API 的权利。它真的很整洁(我也在使用 HTMLParser,但并不那么简单)。最后一个问题。 JSoup.connect() 和 JSoup.parse() 之间有什么区别(在性能上)?在 parse() 文档中说应该使用 connect()。
  • 对,这是 1.3 API 中的新功能。是的,改用它。我会相应地更新答案。
  • 太棒了。我读过你的博客。你在委内瑞拉吗?我在阿根廷。萨卢多斯阿米戈!万分感谢!
【解决方案3】:

您的方法看起来不错,但是您可以通过避免为每一行创建中间 String 对象来提高效率。

这样做的方法是直接读入一个临时的 char[] 缓冲区。

这里是您的代码稍作修改的版本(为了清楚起见,减去所有错误检查、异常处理等):

        URL url = new URL("http://www.google.com/");
        URLConnection connection;
        connection = url.openConnection();
        InputStream in = connection.getInputStream();        
        BufferedReader bf = new BufferedReader(new InputStreamReader(in));
        StringBuffer html = new StringBuffer();

        char[] charBuffer = new char[4096];
        int count=0;

        do {
            count=bf.read(charBuffer, 0, 4096);
            if (count>=0) html.append(charBuffer,0,count);
        } while (count>0);
        bf.close();

如果要频繁调用此代码,您当然可以做一些额外的事情,例如预先分配字符数组和 StringBuffer。

【讨论】:

  • 附注您还可以直接从 InputStreamReader 读取 char[] 缓冲区。尚未测试这是否更好用,但值得考虑,因为您可能会消除不必要的缓冲层。
  • 谢谢!我会试试看。就一个问题。为什么是 4096?
  • 嗯 4096 只是一个“有根据的猜测”。太大会浪费内存,太小将需要对大文件进行太多单独的迭代/读取。如果愿意,您可以随时进行试验,看看是否可以为您的环境找到更好的价值。
  • html.append(charBuffer, 0, count);
  • @EJP 好地方 - 我不知何故错过了 JavaDocs 中的重载!修正了反映的答案。
【解决方案4】:

您可以尝试使用 apache 中的 commons-io (http://commons.apache.org/io/api-release/org/apache/commons/io/IOUtils.html)

new String(IOUtils.toCharArray(connection.getInputStream()))

【讨论】:

  • +1;只需为此使用非常稳定的 Apache Commons,您将节省大量代码和大量潜在错误。
  • 字符串数据 = IOUtils.toString(connection.getInputStream());似乎更容易
【解决方案5】:

有一些技术上的考虑。您可能希望使用 HTTPURLConnection 而不是 URLConnection。

HTTPURLConnection 支持分块传输编码,它允许您以块的形式处理数据,而不是在开始工作之前缓冲所有内容。这可以改善用户体验。

此外,HTTPURLConnection 支持持久连接。如果您要立即请求另一个资源,为什么要关闭该连接?保持与 Web 服务器的 TCP 连接打开可以让您的应用程序快速下载多个资源,而无需花费为每个资源建立新 TCP 连接的开销(延迟)。

告诉服务器您支持 gzip 并在 GZIPInputStream 周围包装一个 BufferedReader,如果响应标头显示内容已压缩。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 2020-05-04
    • 2010-11-25
    • 2012-03-31
    • 2012-02-29
    • 2020-05-21
    • 1970-01-01
    相关资源
    最近更新 更多