亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用jsoup實現抓取圖片爬蟲

發布時間:2021-10-19 09:22:46 來源:億速云 閱讀:339 作者:iii 欄目:編程語言

本篇內容介紹了“怎么用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,多線程操作無疑會轉為重量鎖

怎么用jsoup實現抓取圖片爬蟲

那么想要記錄日志的話,最好是讓它們沒有線程競爭的情況下再去操作文件;

然后是優化多線程操作,相比于獲取url,下載圖片肯定是要比它更慢的,如果先統一獲取url,然后根據url再去下載圖片是否會更好?

第一次優化:

// 用于記錄所有url
Queue<String> queue = new ConcurrentLinkedQueue<String>();
// 用于記錄所有日志
Queue<String> logQueue = new ConcurrentLinkedQueue<String>();
// 所有任務
List<Consumer> 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<String> 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**************************************/


// 用于記錄所有url
Queue<String> queue = new ConcurrentLinkedQueue<String>();
// 用于記錄所有日志
Queue<String> logQueue = new ConcurrentLinkedQueue<String>();
// 所有任務
List<Consumer> 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<String> 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,則并行去下載

看著線程都用上了,感覺爽多了

怎么用jsoup實現抓取圖片爬蟲

即使在消費,queue中對象還是越來越多

怎么用jsoup實現抓取圖片爬蟲

“怎么用jsoup實現抓取圖片爬蟲”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

舒兰市| 卢氏县| 平江县| 青河县| 砀山县| 三亚市| 平塘县| 彭山县| 盱眙县| 鹿邑县| 永胜县| 云浮市| 棋牌| 齐河县| 滦平县| 铜山县| 玛曲县| 剑川县| 禄丰县| 广元市| 磴口县| 资溪县| 宜宾县| 桃园市| 北安市| 九江县| 巴中市| 密云县| 叙永县| 镇雄县| 西吉县| 遵义县| 郁南县| 巫溪县| 清水县| 诸城市| 嘉义市| 雅江县| 仪征市| 镇原县| 长海县|