Android HTML Jsoup解析速度

4
这是我的安卓应用中使用Jsoup进行网页抓取的情况。现在它能正常工作,但速度太慢了。我在代码中所做的是:
  1. 通过Jsoup中的POST方法登录页面;
  2. 获取cookies;
  3. 重复使用cookies浏览6个页面(POST和GET),并对它们进行抓取(主要是表格和大量行。我的意思是很多...所以真的需要很多foreach循环);
  4. 将所有所需数据写入SQLiteDatabase中;
现在的问题是速度太慢了。我的意思是,在应用程序的登录界面上,用户按下登录按钮后,3G网络需要等待长达10秒,而WiFi网络需要等待8-10秒(这取决于WiFi速度)。当用户尝试检查数据更新时,它会执行相同的算法+比较SQLiteDatabase表数据。
那么,在安卓中是否有任何替代方法来加快HTML解析-抓取速度?附:遗憾的是,我没有访问数据库的权限。
编辑:由于您询问我正在抓取的内容,请参考以下无需登录即可访问的几个页面示例(与其他页面相比,这不是一个特别大的表):https://medeine.vgtu.lt/programos/programa.jsp?sid=F&fak=5&prog=87&rus=U&klb=en
现在,关于代码...我确实无法给您完整的代码,但这是我如何获取表格中每个单元格的示例:
document = Jsoup.connect(getContext().getString(R.string.url))
                    .cookie("JSESSIONID", cookie)
                    .get();

            Element table = document.select("table.duomenys").first();
            if (table != null) {
                databaseHandler.openDatabase();
                databaseHandler.getDatabase().beginTransaction();
                try {
                    for (Element row : table.select("tr.n, tr.l") {
                        Elements columns = row.select("td");
                        addItem(columns, DatabaseHandler.getTableName());
                    }
                    databaseHandler.getDatabase().setTransactionSuccessful();
                } finally {
                    databaseHandler.getDatabase().endTransaction();
                }
                databaseHandler.closeDatabase();
            }

以下是addItem()方法示例:

private void addItem(Elements columns, String tableName) {
    databaseHandler.addItem(new Item(
            columns.get(0).text(),
            columns.get(1).text(),
            columns.get(3).text(),
            columns.get(4).text()
    ), tableName);
}

这只是一个页面,总共有6个页面,其中一些页面非常大。当然,这是在AsyncTaskLoader的loadInBackground()方法中完成的。

编辑2:

Connection.Response response = Jsoup.connect("https://medeine.vgtu.lt/studentams/submit.jsp")
                .data("studKnNr", id, "asmKodas", password)
                .timeout(3000)
                .method(Connection.Method.POST)
                .execute();

        String cookie = response.cookie("JSESSIONID");

        Document document = Jsoup.connect(modules_url)
                .cookie(cookie_id, cookie)
                .get();

当我考虑这个问题时...可能不是解析慢,而是登录和通过6页重定向,如果是这种情况,我无能为力?现在我注意到,在Connection.Response中通过.execute()向服务器发送POST请求并获取cookie需要大约2.5秒钟。

将所有必需的数据写入SQLiteDatabase 我无法访问数据库 - greenapps
1
我正在将下载的数据写入自己的SQLiteDatabase。所谓没有访问权限,是指我无法访问该网站的数据库,因此我必须对其进行爬取。 非常明确地说,我正在爬取大学信息系统网站,但他们尚未授予我访问其数据库的权限。 - emilancius
如果你的代码运行速度太慢,那么你应该在这里发布你的代码,并将一个页面放在互联网上,这样我们就不必登录,可以进行一些测试。 - greenapps
1个回答

6
由于您的问题不明确,且未提供您的代码或解析的DOM示例,因此我将提供一般性的答案。
  • 优化您的jsoup查询。由于有很多数据(大型DOM),请尽可能高效地解析它们。
  • 减少循环。您确定在处理数据期间没有执行任何不必要的循环吗?
  • 如果您连接了大块字符串,请尝试使用StringBuilder而不是String
  • 尝试使用多个线程。

更新

您可以接收服务器的响应,操作消息体,然后使用Jsoup的解析器,以便最小化解析时间。

try {
    Connection.Response response = Jsoup.connect("ENTER_URL")
                                   .userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0")  
                                   .referrer("http://www.google.com")   
                                   .method(Method.GET) //or Method.POST
                                   .execute();

    String body = response.body();

    String table = body; //Manipulate the string, remove all the data you don't want.

    Document doc = Jsoup.parse(table);

    System.out.println(doc);

} catch(Exception e) {
    e.printStackTrace();
}

更新 2

Connection.Response line takes 2.6 seconds: 这是无法避免的。由于服务器延迟响应您的请求,您必须接受这一点。毕竟,您只需获取一次 cookie,然后重复使用它们。

但是,getting the page 这部分可以在某种程度上进行优化。如果您使用我发布的代码,仍然需要再次发起 http 请求(这不能避免,就像处理 cookies 一样会有服务器延迟),但您仅会解析所需的部分,而不是整个响应。这将带来一些改进,但我并不认为会很大。也许甚至不值得。但是您可以尝试仅更改此部分,并告诉我是否看到任何改进。

Document document = Jsoup.connect(modules_url)
                .cookie(cookie_id, cookie)
                .get();

此外,如果您确实需要速度,您将不得不使用某种并发形式(多个线程)。像这样做会有真正的差异:
  1. 在父线程中仅检索一次cookie(在开始时)。
  2. 为每个页面创建一个新线程,并将cookie和url作为参数传递。
  3. 每个线程解析分配给它的页面。
  4. 所有数据都在父线程中收集。
请参考此处选择的答案,了解如何使您的http请求并发。

为了提高DOM解析的效率:有没有办法只解析表格?因为Jsoup.connect().get()会解析整个HTML页面,然后再开始抓取数据。我只需要表格部分。 - emilancius
是的,但你看,当我登录时,我必须立即重定向到另一个页面,这是通过发送GET方法来实现的,所以无论如何都要使用.get()。请查看我的登录代码,请参见EDIT 2。而且我认为这个.body()代码只适用于非登录页面。 - emilancius
在EDIT2中,你需要做两件事情。首先,你要获取cookies,然后再获取页面(modules_url)。这两个步骤都需要2.5秒钟吗?还是只有接收cookies的部分需要2.5秒钟? - Alkis Kalogeris
Connection.Response 行需要 2.6 秒,response.cookie() 需要 1 毫秒。获取重定向页面需要 1.5 秒。因此,在获取页面内容之前总共需要 4.1 秒。 - emilancius
谢谢回答,我会的。 - emilancius
显示剩余3条评论

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