HttpAsyncClient 和多线程 Jsoup 连接类有区别吗?

Mer*_*can 3 java multithreading web-scraping jsoup apache-httpasyncclient

我正在用 Java 实现一个网络爬虫。在玩了一些我要爬网的网站之后,我想使用 Java 中并发 HTTP 连接的最佳实践。我目前正在使用Jsoup 的连接方法。我想知道是否可以像HttpAsyncClient一样创建线程并在这些线程内建立连接。

Szy*_*iak 5

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",
                "/sf/ask/3380875361/",
                "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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我们定义了 5 个 URL,这个简单应用程序的目标是<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
Run Code Online (Sandbox Code Playgroud)

最后我们调用.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();
    }
});
Run Code Online (Sandbox Code Playgroud)