网站首页 > java教程 正文
大家都知道,想要爬取某宝的商品,如果只是用HttpURLConnection发个请求,失败率是很高的。一般想要保证成功率的话,都会选择真实的浏览器去抓取。
以前常用的解决方案是selenium或phantomjs,但是它两的环境配置太麻烦了,对程序员极度不友好,自从谷歌推出Puppeteer后,puppeteer迅速流行起来,获得大家一致称赞。它是一个NodeJS库,但今天并不是要使用它来爬取某宝商品,而是使用Java语言写的Jvppeteer,Jvppeteer与Puppeteer是同样的实现原理。
思路:
- 使用多线程,一个线程负责一个页面的爬取,接下来的内内容会使用page代替页面
- 创建与线程池线程数相同的page队列,放在LinkedBlockingQueue队列里,每当有爬取任务时,就从队列里取出一个page,爬取任务完成时,将page放回队列的后面。这样做的原因是重复利用page,减少页面的创建频率,但是要注意的是一个页面不能利用太久或者次数太多,防止出现crash的情况
- 拦截图片和多媒体资源的加载,多媒体资源和图片的加载会极大影响页面的加载速度,从而影响爬虫效率,所以要拦截(可选)。
- 我们选择获取整个页面内容,然后解析得到商品信息
代码实现
1.启动
//指定启动路径,启动浏览器
String path = new String("F:\\java教程\\49期\\vuejs\\puppeteer\\.local-chromium\\win64-722234\\chrome-win\\chrome.exe".getBytes(), "UTF-8");
ArrayList<String> argList = new ArrayList<>();
LaunchOptions options = new OptionsBuilder().withArgs(argList).withHeadless(false).withExecutablePath(path).build();
argList.add("--no-sandbox");
argList.add("--disable-setuid-sandbox");
Browser browser = Puppeteer.launch(options);
2.page队列
//启动一个线程池多线程抓取
int threadCount = 5;
ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount, 30, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
CompletionService service = new ExecutorCompletionService(executor);
//打开5个页面同时抓取,这些页面可以多次利用,这样减少创建网页带来的性能消耗
LinkedBlockingQueue<Page> pages = new LinkedBlockingQueue<>();
for (int i = 0; i < threadCount; i++) {
Page page = browser.newPage();
//拦截请求,可选
// page.onRequest(request -> {
// if ("image".equals(request.resourceType()) || "media".equals(request.resourceType())) {
// //遇到多媒体或者图片资源请求,拒绝,加载页面加载
// request.abort();
// } else {//其他资源放行
// request.continueRequest();
// }
// });
// page.setRequestInterception(true);
pages.put(page);//往队列后面放,阻塞
}
3.定义爬取线程
static class CrawlerCallable implements Callable<Object> {
private LinkedBlockingQueue<Page> pages;
public CrawlerCallable(LinkedBlockingQueue<Page> pages) {
this.pages = pages;
}
@Override
public Object call() {
Page page = null;
try {
page = pages.take();
PageNavigateOptions navigateOptions = new PageNavigateOptions();
navigateOptions.setWaitUntil(Arrays.asList("domcontentloaded"));
System.out.println("gotothreadName:"+Thread.currentThread().getName());
page.goTo("https://item.taobao.com/item.htm?id=541605195654", navigateOptions);
String content = page.content();
//解析商品
return parseItem(content);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (page != null) {
try {
pages.put(page);//把已经抓取完的网页放回队列里
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return null;
}
}
4.关闭线程池,获取结果
//结果集
List<Future<Object>> futures = new ArrayList<>();
//抓取100次
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
Future<Object> future = service.submit(new CrawlerCallable(pages));
futures.add(future);
}
//关闭线程池
executor.shutdown();
//获取结果
int i = 0;
for (Future<Object> result : futures) {
Object item = result.get();
i++;
System.out.println(i + ":" + Constant.OBJECTMAPPER.writeValueAsString(item));
}
long end = System.currentTimeMillis();
System.out.println("时间:" + (end - start));
在俺电脑测试,100个爬取任务只需要15s,速度还是非常的快。速度的快慢受到根据电配置以及带宽的影响,配置越好,速度越快。
猜你喜欢
- 2024-09-09 Java 爬虫遇上数据异步加载,试试这两种办法
- 2024-09-09 实战Java爬虫课程-猿天地(java爬虫入门教程)
- 2024-09-09 如何编写一个Python网络爬虫?点进来,我教你!
- 2024-09-09 Java编写爬虫抓取今日头条内容(java抓取今日头条数据)
- 2024-09-09 一款分布式爬虫管理平台,支持多种语言和框架
- 2024-09-09 编写Java网络爬虫(java 写爬虫)
- 2024-09-09 三个Python爬虫版本,带你轻松入门爬虫!
- 2024-09-09 App 爬虫必备技能:三步完成 Android 逆向
- 2024-09-09 Python 3 学习笔记:爬虫基础入门(python爬虫:入门+进阶)
- 2024-09-09 来来来!带你了解Python爬虫的方方面面!
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)