Mer*_*can 3 java multithreading web-scraping jsoup apache-httpasyncclient
我正在用 Java 实现一个网络爬虫。在玩了一些我要爬网的网站之后,我想使用 Java 中并发 HTTP 连接的最佳实践。我目前正在使用Jsoup 的连接方法。我想知道是否可以像HttpAsyncClient一样创建线程并在这些线程内建立连接。
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)
| 归档时间: |
|
| 查看次数: |
1025 次 |
| 最近记录: |