如何使用 Java 进行网页抓取。
微信搜索关注《Java学研大本营》,加入读者群,分享更多精彩
动态网页抓取
我们现在正处于最激动人心的时刻!!例如,让我们抓取amazon.com上“猫粮”的所有产品和产品价格。
为此,首先,我们需要导航到 amazon.com,在搜索栏中搜索猫粮,然后从那里抓取产品名称和价格。
我们将如何渲染 JS?
认识Playwright——一个控制 Chrome、Firefox 和 Webkit 的跨语言库。Playwright 的 API 很简单,可以控制最流行的浏览器。
为了将其添加到您的项目中,将以下代码粘贴到您的 pom.xml 文件中的 dependencies 标记下。
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.25.0</version>
</dependency>
注意到一些奇怪的东西??是的,对于 playwright,它会自动下载 chromium 和所有必要的驱动程序,因此您无需维护它,这与 selenium 不同,selenium 需要通往 chromium 驱动程序二进制文件的路径。
第 1 步:编写导航和搜索 URL 的方法
public Document searchOnPage(String url ,String searchbarCSSSelectorQuery, String searchButtonSelectorQuery, String searchText){
try (Playwright playwright = Playwright.create()) {
final BrowserType chromium = playwright.chromium();
final Browser browser = chromium.launch();
final Page page = browser.newPage();
page.navigate(url);
page.fill(searchbarCSSSelectorQuery,searchText);
page.click(searchButtonSelectorQuery);
page.waitForTimeout(PAGELOADTIMEOUT);
Document doc = Jsoup.parse(page.content());
browser.close();
return doc;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
在上面的函数中,我将所有输入作为函数参数传递,但您应该创建一个单独的对象以提高代码的可重用性并遵循最佳软件开发实践。
第 2 步:抓取产品数据
String dynamicScrapingUrl = "https://amazon.com/";
String searchbarCSSSelectorQuery = "input#twotabsearchtextbox";
String searchButtonSelectorQuery ="input#nav-search-submit-button";
String searchText = "cat food";
DynamicScraper dynamicScraper = new DynamicScraper();
Document searchedHTMLResult = dynamicScraper.searchOnPage(dynamicScrapingUrl,searchbarCSSSelectorQuery,searchButtonSelectorQuery, searchText);
String prodcuctDataCSSSelector = "div.a-section.a-spacing-small.puis-padding-left-small.puis-padding-right-small";
List<Element> productDetails = dynamicScraper.getElementsByCSSQuery(searchedHTMLResult,prodcuctDataCSSSelector);
String productTextSelector ="span.a-size-base-plus.a-color-base.a-text-normal";
String productPriceSelector = "span.a-price-whole";
String productCurrencySelector = "span.a-price-symbol";
for(Element productDetail: productDetails){
String productName = productDetail.select(productTextSelector).text();
String productPrice = productDetail.select(productPriceSelector).text();
String currency = productDetail.select(productCurrencySelector).text();
System.out.println(productName + " - "+ currency+productPrice);
}
一旦我们运行它,我们就会得到预期的结果。
并行化:提高性能。
对于我们简单的抓取示例,我们并不真正关心性能,但对于复杂的爬虫,性能成为一个问题。
考虑一个爬行场景,我们爬到一个页面,然后爬到下一个页面,依此类推。如果要抓取的数据很大,则需要很长时间。我们可以通过使用多线程并行化任务来提高性能。
别担心,我们不会在 Java 中使用旧式的多线程,因为如果操作不当很容易出错,并且需要对线程通信有深入的了解。相反,我们将使用线程池和 FutureTasks(Java 11 + 的新功能)。
创建和使用线程池
// With Java 11+ we can create a thread pool like this -> this pool // has 10 threads
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// Async Function Implementation
public CompletableFuture<Document> searchOnPageAsync(String url ,String searchbarCSSSelectorQuery, String searchButtonSelectorQuery, String searchText){
CompletableFuture<Document> futureDocument;
// Asynchronously execute the query and then return the results
futureDocument = CompletableFuture.supplyAsync(()-> {
try {
return searchOnPage(url,searchbarCSSSelectorQuery,searchButtonSelectorQuery,searchText);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}, threadPool);
return futureDocument;
}
正如我们在现代 Java 特性中看到的那样,我们不必进行线程管理和通信,这一切都由本机Executor Service为我们完成。
我们可以使用这个函数再次抓取 amazon.com 产品,但是是异步的。
异步调用
// Async call
CompletableFuture future = dynamicScraper.searchOnPageAsync(dynamicScrapingUrl,searchbarCSSSelectorQuery,searchButtonSelectorQuery, searchText)
.thenApply(page -> dynamicScraper.getElementsByCSSQuery(page,prodcuctDataCSSSelector))
.thenAccept(products ->{
for(Element product: products){
String productName = product.select("span.a-size-base-plus.a-color-base.a-text-normal").text();
String productPrice = product.select("span.a-price-whole").text();
String currency = product.select("span.a-price-symbol").text();
System.out.println(productName + " - "+ currency+productPrice);
}
});
System.out.println("\nThis should be printed first as call above is async and is not a blocker\n");
future.join(); // joins the thread running
结论
Java Web Scraping 主题非常广泛,并且有多种用例。这篇文章的目的是提供 Scraping 的基础知识,我们只是触及了表面。代理、云驱动程序、特定于地理位置的搜索等更高级的主题超出了本文的范围。
附录:
Jsoup:https://jsoup.org/
推荐书单
《项目驱动零起点学Java》
《项目驱动零起点学Java》共分 13 章,围绕 6 个项目和 258 个代码示例,分别介绍了走进Java 的世界、变量与数据类型、运算符、流程控制、方法、数组、面向对象、异常、常用类、集合、I/O流、多线程、网络编程相关内容。《项目驱动零起点学Java》总结了马士兵老师从事Java培训十余年来经受了市场检验的教研成果,通过6 个项目以及每章的示例和习题,可以帮助读者快速掌握Java 编程的语法以及算法实现。扫描每章提供的二维码可观看相应章节内容的视频讲解。
《项目驱动零起点学Java》贯穿6个完整项目,经过作者多年教学经验提炼而得,项目从小到大、从短到长,可以让读者在练习项目的过程中,快速掌握一系列知识点。
马士兵,马士兵教育创始人,毕业于清华大学,著名IT讲师,所讲课程广受欢迎,学生遍布全球大厂,擅长用简单的语言讲授复杂的问题,擅长项目驱动知识的综合学习。马士兵教育获得在线教育“名课堂”奖、“最受欢迎机构”奖。
赵珊珊,从事多年一线开发,曾为国税、地税税务系统工作。拥有7年一线教学经验,多年线上、线下教育的积累沉淀,培养学员数万名,讲解细致,脉络清晰。
精彩回顾
微信搜索关注《Java学研大本营》
访问【IT今日热榜】,发现每日技术热点