为什么网站会永远爬行?

时间:2012-08-11 01:54:19

标签: java regex web-crawler

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对象,并尝试查找()并打印与正则表达式匹配的这些部门名称。但代码正在运行,我不认为使用bufferedReader在网页上阅读需要花费很长时间。所以我认为我要么做一些可怕的错误,要么解析网站需要花费相当长的时间。所以如果有任何关于如何提高性能或纠正我的代码中的错误,我将不胜感激。我为编写糟糕的代码道歉。

3 个答案:

答案 0 :(得分:13)

问题在于代码

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

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

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

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

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

如果您愿意,可以在循环后使用theWebPagetoString()转换为字符串。当我运行修改后的版本时,花了不到一秒钟。

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

答案 1 :(得分:2)

这是你的整个计划吗? parserObject的声明在哪里?

此外,在调用main()之前,不应该将所有代码都放在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) {

    } catch (IOException e) {

    }
}

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

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

答案 2 :(得分:1)

当然,您可以使用有限的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);
}