这是交易。在我的 android 应用程序中,我正在使用 Jsoup 进行一些网络抓取。现在它工作正常,但它太慢了。我在我的代码中所做的是:
- 在Jsoup中通过POST方式登录页面;
- 获取 cookie;
- 通过重复使用 cookie,我浏览了 6 页(POST 和 GET)并抓取它们(主要是表格和很多行。我的意思是很多......所以,真的有很多 foreach 循环);
- 将所有需要的数据写入 SQLiteDatabase;
现在的问题是速度太慢了。我的意思是,在按下登录按钮后的应用程序登录屏幕中,用户必须在 3G 中等待长达 10 秒,在 WiFi 中等待约 8-10 秒(取决于 WiFi 速度)。当他尝试检查数据更新时,它会执行相同的算法 + 比较 SQLiteDatabase 表数据。
那么,是否有任何替代方法来执行此 HTML 解析 - 在 android 中抓取东西以使其更快?附言遗憾的是,我无法访问数据库。
编辑:
由于您询问了我正在抓取的内容,这里是您无需登录即可访问的几个页面的一个示例(与其他页面相比,它并不是一个真正的大表):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 秒。
最佳答案
由于您的问题含糊不清,并且您没有提供代码,也没有提供您正在解析的 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 行需要 2.6 秒
:这没办法。你必须忍受这一点,因为它是延迟满足你的请求的服务器。毕竟你只拿了一次 cookies 然后重复使用它们。
但是这部分获取页面
可以在一定程度上进行优化。如果您使用我发布的代码,您仍然会有再次发出 http 请求的开销(这是无法避免的,它与 cookie 一样是服务器延迟),但您只会解析您需要的部分,而不是整个响应。这会给你带来一些改善,但我认为不会很大。也许这甚至不值得。但是你可以尝试只改变这部分,如果你看到任何改进,请告诉我。
Document document = Jsoup.connect(modules_url)
.cookie(cookie_id, cookie)
.get();
此外,如果您确实需要速度,则必须使用某种形式的并发(多线程)。 像这样的东西会产生真正的不同:
- 在父线程中检索 cookie(仅在开始时一次)。
- 为每个页面创建一个新线程并传递 cookie 和 url 作为论点。
- 每个线程都会解析分配给它的页面。
- 所有数据都在父线程中收集。
Check this关于如何使您的 http 请求并发的选择答案
关于Android HTML Jsoup解析速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25537595/