【问题标题】:Why is website crawling taking forever?为什么网站爬行需要永远?
【发布时间】:2012-08-11 01:54:19
【问题描述】:
public class Parser {

    public static void main(String[] args) {
        Parser p = new Parser();
        p.matchString();
    }

    parserObject courseObject = new parserObject();
    ArrayList<parserObject> courseObjects = new ArrayList<parserObject>();
    ArrayList<String> courseNames = new ArrayList<String>();
    String theWebPage = " ";

    {
        try {
            URL theUrl = new URL("http://ocw.mit.edu/courses/");
            BufferedReader reader =
                new BufferedReader(new InputStreamReader(theUrl.openStream()));
            String str = null;

            while((str = reader.readLine()) != null) {
                theWebPage = theWebPage + " " + str;
            }
            reader.close();

        } catch (MalformedURLException e) {
            // do nothing
        } catch (IOException e) {
            // do nothing
        }
    }

    public void matchString() {
        // this is my regex that I am using to compare strings on input page
        String matchRegex = "#\\w+(-\\w+)+";

        Pattern p = Pattern.compile(matchRegex);
        Matcher m = p.matcher(theWebPage);

        int i = 0;
        while (!m.hitEnd()) {
            try {
                System.out.println(m.group());
                courseNames.add(i, m.group());
                i++;
            } catch (IllegalStateException e) {
                // do nothing
            }
        }
    }
}

我想用上面的代码实现的是获取 MIT OpencourseWare 网站上的部门列表。我正在使用与页面源中的部门名称模式匹配的正则表达式。我正在使用 Pattern 对象和 Matcher 对象并尝试 find() 并打印这些与正则表达式匹配的部门名称。但是代码需要永远运行,我不认为使用 bufferedReader 在网页中阅读需要那么长时间。所以我认为我要么做错了什么,要么解析网站需要很长时间。因此,如果有任何关于如何提高性能或纠正我的代码中的错误的意见,我将不胜感激。对于写得不好的代码,我深表歉意。

【问题讨论】:

  • 你可以为每个页面下载和解析分离线程
  • 是的,我考虑过,也许我可能不得不这样做。然而,这非常令人惊讶,因为这甚至不是一个完整的网站,只是我目前正在使用的一个网页,虽然内容很多,但它仍然不应该花那么长时间。
  • 您是否在没有 BufferedReader 的情况下绑定,只是将 InputStream 直接读取到字节数组中?

标签: java regex web-crawler


【解决方案1】:

问题出在代码上

while ((str = reader.readLine()) != null)
    theWebPage = theWebPage + " " +str;

变量theWebPage 是一个字符串,它是不可变的。对于读取的每一行,此代码创建一个 new 字符串,其中包含到目前为止已读取的所有内容的副本,并附加一个空格和刚刚读取的行。这是大量不必要的复制,这就是程序运行如此缓慢的原因。

我下载了相关网页。它有 55,000 行,大小约为 3.25MB。不会太大。但由于循环中的复制,第一行最终被复制了大约 15 亿 次(55,000 平方的 1/2)。该程序将所有时间都用于复制和垃圾收集。我在我的笔记本电脑(2.66GHz Core2Duo,1GB 堆)上运行了它,从本地文件读取时运行了 15 分钟(没有网络延迟或网络爬取对策)。

要解决此问题,请将theWebPage 改为StringBuilder,并将循环中的行更改为

    theWebPage.append(" ").append(str);

如果您愿意,可以在循环后使用toString()theWebPage 转换为字符串。当我运行修改后的版本时,只用了几分之一秒。

顺便说一句,您的代码在类中使用 { } 中的裸代码块。这是一个实例初始化器(与静态初始化器相对)。它在对象构建时运行。这是合法的,但很不寻常。请注意,它误导了其他评论者。我建议将此代码块转换为命名方法。

【讨论】:

  • 是的,当我用完堆空间时,我意识到我正在这样做。我确实使用了 stringBuilder,现在它的速度更快了。感谢您的帮助。
【解决方案2】:

这是你的整个程序吗? parserObject的声明在哪里?

另外,在调用 matchString() 之前,所有这些代码不应该都在您的 main() 中吗?

parserObject courseObject = new parserObject();
ArrayList<parserObject>  courseObjects = new ArrayList<parserObject>();
ArrayList<String> courseNames = new ArrayList<String>();
String theWebPage=" ";
{

    try {
            URL theUrl = new URL("http://ocw.mit.edu/courses/");
            BufferedReader reader = new BufferedReader(new InputStreamReader(theUrl.openStream()));
            String str = null;

            while((str = reader.readLine())!=null)
            {
                theWebPage = theWebPage+" "+str;
            }
            reader.close();

    } catch (MalformedURLException e) {

    } catch (IOException e) {

    }
}

您还捕获了异常并且没有显示任何错误消息。当遇到异常时,您应该始终显示错误消息并执行某些操作。例如,如果您无法下载页面,则没有理由尝试解析空字符串。

从您的评论中,我了解了类中的静态块(谢谢,不知道它们)。但是,根据我的阅读,您需要将关键字 static 放在块 { 的开头之前。此外,将代码放入您的main 可能会更好,这样您可以在收到 MalformedURLException 或 IOException 时退出。

【讨论】:

  • 不,我有另一个 parserObject 类,但我没有用它做任何事情。但是从 try 块开始的所有代码都在静态代码块中。静态块不是在任何方法之前执行吗?
  • 也许,我从未见过这样的程序。另外,你怎么知道你没有收到 MalformedURLException、IOException 或 IllegalStateException?
  • 不,你是对的,关于代码块和异常,我捕获它们但不处理它们。将尝试这些解决方案。感谢您的意见。
  • 问题是非法状态异常,非常感谢。
【解决方案3】:

当然,您可以使用有限的 JDK 1.0 API 解决此分配问题,然后遇到 Stuart Marks helped you solve in his excellent answer 的问题。

或者,您只需使用流行的事实上的标准库,例如Apache Commons IO,然后使用这样的简单方法将您的网站读入字符串:

// using this...
import org.apache.commons.io.IOUtils;

// run this...
try (InputStream is = new URL("http://ocw.mit.edu/courses/").openStream()) {
    theWebPage = IOUtils.toString(is);
}

【讨论】:

    猜你喜欢
    • 2020-04-03
    • 1970-01-01
    • 2014-09-03
    • 2018-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-16
    • 2020-06-24
    相关资源
    最近更新 更多