千家信息网

怎么用jsoup实现抓取图片爬虫

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,本篇内容介绍了"怎么用jsoup实现抓取图片爬虫"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!初版:
千家信息网最后更新 2024年11月25日怎么用jsoup实现抓取图片爬虫

本篇内容介绍了"怎么用jsoup实现抓取图片爬虫"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

初版:

ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200));for (int j = 1; j <= 总页数; j++) {    executor.execute(()->{        // 1.抓取网页,获得图片url        // 2.根据url保存图片        // 3.保存后记录成功和失败的信息到本地txt        });}

程序看起来没有什么问题,只开了6线程操作,开始没敢开太多线程,怕被网站拉黑。。

但是运行起来太慢了,一晚上只爬了10个多G,目前分析问题主要有两点:

1.并发操作本地txt,会拖慢单个任务执行的速度

2.线程没有充分利用

首先看下操作文件方法吧,所用方法来自NIO:

Files.write(log, attr.getBytes("utf8"), StandardOpenOption.APPEND);

通过查看源码发现,该方法会构造一个OutputStream去调用write方法,而write方法上有synchronized,多线程操作无疑会转为重量锁

那么想要记录日志的话,最好是让它们没有线程竞争的情况下再去操作文件;

然后是优化多线程操作,相比于获取url,下载图片肯定是要比它更慢的,如果先统一获取url,然后根据url再去下载图片是否会更好?

第一次优化:

// 用于记录所有urlQueue queue = new ConcurrentLinkedQueue();// 用于记录所有日志Queue logQueue = new ConcurrentLinkedQueue();// 所有任务List allTasks = new ArrayList<>();for (int j = 1; j <= 总页数; j++) {    allTasks.add(t ->{            // 获得url,放入queue中    });}// 使用ForkJoin并行执行记录url的任务BatchTaskRunner.execute(allTasks, taskPerThread, tasks -> {    tasks.forEach(t->t.accept(null));});// 将所有url并行执行下载List list = queue.stream().collect(Collectors.toList());BatchTaskRunner.execute(list, taskPerThread, tasks -> {    tasks.forEach(        // 1.下载文件        // 2.将url成功或失败放到logQueue中    );});// 最后再记录日志logQueue.forEach(    // 将所有日志保存到本地txt中);

这里主要分为三步:

1.并行执行任务,抓取url放入queue

2.并行执行下载,从queue中取url

3.从logQueue中保存日志到本地

分析:先是抓取所有url,然后再去并行执行保存;将保存日志放到最后,保存了图片后最后的日志反而无关紧要了,但是运行时候我发现还是存在问题:

我去,为什么一定要先放url再去处理啊!!放的同时也取任务,最后剩余的任务再并行执行不是更快!

好吧,有了这个想法,直接开干:

第二次优化:

/****************************第二次增加的逻辑start**************************************/// 控制主线程执行CountDownLatch countDownLatch = new CountDownLatch(totalPageSize);// 用于消费queue的线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(12, 12, 0, TimeUnit.SECONDS, new SynchronousQueue<>());// 用于自旋时的开关volatile boolean flag = false;/****************************第二次增加的逻辑end**************************************/// 用于记录所有urlQueue queue = new ConcurrentLinkedQueue();// 用于记录所有日志Queue logQueue = new ConcurrentLinkedQueue();// 所有任务List allTasks = new ArrayList<>();for (int j = 1; j <= 总页数; j++) {    allTasks.add(t ->{            // 获得url,放入queue中    });}// 开了一个线程去执行,主要是为了让它异步去操作new Thread(()->{    // 使用ForkJoin并行执行记录url的任务    // finally中调用countDownLatch.countDown()    BatchTaskRunner.execute(allTasks, taskPerThread, tasks -> {        tasks.forEach(t->t.accept(null));    });}).start();// 一边抓取一边消费for (int i = 0; i < 12; i++) {    executor.execute(()->{            try {                    takeQueue(); // 从queue获得url并消费,如果信号量归零则将flag置为true            } catch (InterruptedException e) {                                                }    });}for(;;) {    if(flag) {            break;    }    Thread.sleep(10000);}countDownLatch.await();executor.shutdownNow();// 都取完了,就不必再去并行执行了if(queue.size() == 0) {    return;}// 将所有url并行执行下载List list = queue.stream().collect(Collectors.toList());BatchTaskRunner.execute(list, taskPerThread, tasks -> {    tasks.forEach(        // 1.下载文件        // 2.将url成功或失败放到logQueue中    );});// 最后再记录日志logQueue.forEach(    // 将所有日志保存到本地txt中);

其中的takeQueue方法逻辑:

   void takeQueue() throws InterruptedException {                for(;;) {                        long count = countDownLatch.getCount();                        // 未归零则一直去消费                        if(count > 0) {                                String poll = queue.poll();                                if(poll != null) {                                        consumer.accept(poll); // 根据url去下载                                }else {                                        Thread.sleep(3000);                                }                        } else {                                flag = true;                                return;                        }                }        }

大概撸了个逻辑,日志什么的已经不重要了。。。

主线程自旋,保存url同时去并发下载,如果保存url的逻辑执行完了队列中还有url,则并行去下载

看着线程都用上了,感觉爽多了

即使在消费,queue中对象还是越来越多

"怎么用jsoup实现抓取图片爬虫"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

日志 线程 任务 图片 方法 逻辑 消费 文件 成功 问题 爬虫 内容 同时 完了 情况 更多 知识 网站 还是 分析 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 自己买服务器搭建免流 秦淮区一站式软件开发排名靠前 质量指标 软件开发 广州粉象网络技术待遇 征途手游怎么更新服务器 银行网络安全员工作职责 网络安全顾问是什么工作 上海夺汇网络技术有限公司天眼查 数据库添删改查 教育类软件开发的摘要 码是指数据库中的表中的某个属性 高速公路服务器和交换机工作原理 汽车网络技术论文答辩 软件开发不招女生 网络安全节日 阿里云服务器启动失败 网络技术公司简介范文大全 全境 服务器无法使用 给予模型的软件开发流程 软件开发需求报价模板下载 远程管理服务器的常用工具 我的世界纯净服务器和自定义 开展网络安全培训 强化 中国中药专利数据库检索系统入口 网络暴力算不算网络安全 杭州足浴软件开发系统 东营计算机软件开发学校招生电话 为什么我的服务器登陆不了 西安网络安全学院招生 网络安全加班多吗
0