我在Java中实现了一个Web scraper。在与我将要抓取的网站进行一些游戏之后,我想在Java中使用最佳实践进行并发HTTP连接。我目前正在使用Jsoup's连接方法。我想知道是否有可能创建线程并在这些线程内建立与HttpAsyncClient类似的连接。
答案 0 :(得分:2)
Jsoup不使用HttpAsyncClient。 Jsoup的Jsoup.connect(String url)
方法使用了阻止URL.openConnection()
方法。
如果要异步使用Jsoup,可以并行执行所有Jsoup.connect()
。在Java 8中,您可以使用并行流来执行此操作。我们假设您有一个要并行扫描的URL列表。看一下下面的例子:
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
public class ConcurrentJsoupExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
final List<String> urls = Arrays.asList(
"https://google.com",
"https://stackoverflow.com/questions/48298219/is-there-a-difference-between-httpasyncclient-and-multithreaded-jsoup-connection",
"https://mvnrepository.com/artifact/org.jsoup/jsoup",
"https://docs.oracle.com/javase/7/docs/api/java/net/URL.html#openConnection()",
"https://docs.oracle.com/javase/7/docs/api/java/net/URLConnection.html"
);
final List<String> titles = urls.parallelStream()
.map(url -> {
try {
return Jsoup.connect(url).get();
} catch (IOException e) {
return null;
}
})
.filter(Objects::nonNull)
.map(doc -> doc.select("title"))
.map(Elements::text)
.peek(it -> System.out.println(Thread.currentThread().getName() + ": " + it))
.collect(Collectors.toList());
}
}
此处我们定义了5个网址,这个简单应用程序的目标是从这些网站获取<title>
HTML标记的文本值。会发生什么是我们使用URL列表创建并行流,并将每个URL映射到Jsoup的Document
对象 - .get()
方法抛出已检查的异常,因此我们必须尝试捕获它并且如果发生异常我们返回null
值。所有null
值都会被.filter(Objects::nonNull)
过滤掉,之后我们可以提取我们需要的元素 - 在这种情况下,<title>
标记的文本值。我还添加了.peek()
来打印提取的值以及它运行的线程名称。示例性输出可能如下所示:
ForkJoinPool.commonPool-worker-1: java - Is there a difference between HttpAsyncClient and multithreaded Jsoup connection class? - Stack Overflow
main: Maven Repository: org.jsoup » jsoup
ForkJoinPool.commonPool-worker-4: URL (Java Platform SE 7 )
ForkJoinPool.commonPool-worker-2: URLConnection (Java Platform SE 7 )
ForkJoinPool.commonPool-worker-3: Google
最后,我们调用.collect(Collectors.toList())
来终止流,执行所有转换并返回标题列表。
这只是一个简单的例子,但它应该给你一个如何并行使用Jsoup的提示。
或者,如果类似功能的方法无法说服您,则可以使用url.parallelStream().forEach()
:
urls.parallelStream().forEach(url -> {
try {
final Document doc = Jsoup.connect(url).get();
final String title = doc.select("title").text();
System.out.println(Thread.currentThread().getName() + ": " + title);
// do something with extracted title...
} catch (IOException e) {
e.printStackTrace();
}
});