真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)

本篇內(nèi)容介紹了“怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

在崇左等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)制作、網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站制作,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計(jì),營(yíng)銷型網(wǎng)站建設(shè),成都外貿(mào)網(wǎng)站建設(shè)公司,崇左網(wǎng)站建設(shè)費(fèi)用合理。

初版:

ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200));
for (int j = 1; j <= 總頁(yè)數(shù); j++) {
    executor.execute(()->{
        // 1.抓取網(wǎng)頁(yè),獲得圖片url
        // 2.根據(jù)url保存圖片
        // 3.保存后記錄成功和失敗的信息到本地txt
	});
}

程序看起來(lái)沒(méi)有什么問(wèn)題,只開(kāi)了6線程操作,開(kāi)始沒(méi)敢開(kāi)太多線程,怕被網(wǎng)站拉黑。。

但是運(yùn)行起來(lái)太慢了,一晚上只爬了10個(gè)多G,目前分析問(wèn)題主要有兩點(diǎn):

    1.并發(fā)操作本地txt,會(huì)拖慢單個(gè)任務(wù)執(zhí)行的速度

    2.線程沒(méi)有充分利用

首先看下操作文件方法吧,所用方法來(lái)自NIO:

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

通過(guò)查看源碼發(fā)現(xiàn),該方法會(huì)構(gòu)造一個(gè)OutputStream去調(diào)用write方法,而write方法上有synchronized,多線程操作無(wú)疑會(huì)轉(zhuǎn)為重量鎖

怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)

那么想要記錄日志的話,最好是讓它們沒(méi)有線程競(jìng)爭(zhēng)的情況下再去操作文件;

然后是優(yōu)化多線程操作,相比于獲取url,下載圖片肯定是要比它更慢的,如果先統(tǒng)一獲取url,然后根據(jù)url再去下載圖片是否會(huì)更好?

第一次優(yōu)化:

// 用于記錄所有url
Queue queue = new ConcurrentLinkedQueue();
// 用于記錄所有日志
Queue logQueue = new ConcurrentLinkedQueue();
// 所有任務(wù)
List allTasks = new ArrayList<>();
for (int j = 1; j <= 總頁(yè)數(shù); j++) {
    allTasks.add(t ->{
    	// 獲得url,放入queue中
    });
}
// 使用ForkJoin并行執(zhí)行記錄url的任務(wù)
BatchTaskRunner.execute(allTasks, taskPerThread, tasks -> {
    tasks.forEach(t->t.accept(null));
});
// 將所有url并行執(zhí)行下載
List list = queue.stream().collect(Collectors.toList());
BatchTaskRunner.execute(list, taskPerThread, tasks -> {
    tasks.forEach(
        // 1.下載文件
        // 2.將url成功或失敗放到logQueue中
    );
});
// 最后再記錄日志
logQueue.forEach(
    // 將所有日志保存到本地txt中
);

這里主要分為三步:

    1.并行執(zhí)行任務(wù),抓取url放入queue

    2.并行執(zhí)行下載,從queue中取url

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

分析:先是抓取所有url,然后再去并行執(zhí)行保存;將保存日志放到最后,保存了圖片后最后的日志反而無(wú)關(guān)緊要了,但是運(yùn)行時(shí)候我發(fā)現(xiàn)還是存在問(wèn)題:

我去,為什么一定要先放url再去處理啊??!放的同時(shí)也取任務(wù),最后剩余的任務(wù)再并行執(zhí)行不是更快!

好吧,有了這個(gè)想法,直接開(kāi)干:

第二次優(yōu)化:

/****************************第二次增加的邏輯start**************************************/
// 控制主線程執(zhí)行
CountDownLatch countDownLatch = new CountDownLatch(totalPageSize);
// 用于消費(fèi)queue的線程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(12, 12, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
// 用于自旋時(shí)的開(kāi)關(guān)
volatile boolean flag = false;
/****************************第二次增加的邏輯end**************************************/


// 用于記錄所有url
Queue queue = new ConcurrentLinkedQueue();
// 用于記錄所有日志
Queue logQueue = new ConcurrentLinkedQueue();
// 所有任務(wù)
List allTasks = new ArrayList<>();


for (int j = 1; j <= 總頁(yè)數(shù); j++) {
    allTasks.add(t ->{
    	// 獲得url,放入queue中
    });
}
// 開(kāi)了一個(gè)線程去執(zhí)行,主要是為了讓它異步去操作
new Thread(()->{
    // 使用ForkJoin并行執(zhí)行記錄url的任務(wù)
    // finally中調(diào)用countDownLatch.countDown()
    BatchTaskRunner.execute(allTasks, taskPerThread, tasks -> {
        tasks.forEach(t->t.accept(null));
    });
}).start();

// 一邊抓取一邊消費(fèi)
for (int i = 0; i < 12; i++) {
    executor.execute(()->{
    	try {
    		takeQueue(); // 從queue獲得url并消費(fèi),如果信號(hào)量歸零則將flag置為true
    	} catch (InterruptedException e) {
    				
    	}
    });
}
for(;;) {
    if(flag) {
    	break;
    }
    Thread.sleep(10000);
}
countDownLatch.await();
executor.shutdownNow();
// 都取完了,就不必再去并行執(zhí)行了
if(queue.size() == 0) {
    return;
}
// 將所有url并行執(zhí)行下載
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();
			// 未歸零則一直去消費(fèi)
			if(count > 0) {
				String poll = queue.poll();
				if(poll != null) {
					consumer.accept(poll); // 根據(jù)url去下載
				}else {
					Thread.sleep(3000);
				}
			} else {
				flag = true;
				return;
			}
		}
	}

大概擼了個(gè)邏輯,日志什么的已經(jīng)不重要了。。。

主線程自旋,保存url同時(shí)去并發(fā)下載,如果保存url的邏輯執(zhí)行完了隊(duì)列中還有url,則并行去下載

看著線程都用上了,感覺(jué)爽多了

怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)

即使在消費(fèi),queue中對(duì)象還是越來(lái)越多

怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)

“怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


網(wǎng)站標(biāo)題:怎么用jsoup實(shí)現(xiàn)抓取圖片爬蟲(chóng)
轉(zhuǎn)載來(lái)于:http://weahome.cn/article/jpcidj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部