JAVA 多線程爬蟲實例詳解
JAVA 多線程爬蟲實例詳解
前言
以前喜歡Python的爬蟲是出于他的簡潔,但到了后期需要更快,更大規(guī)模的爬蟲的時候,我才漸漸意識到Java的強(qiáng)大。Java有一個很好的機(jī)制,就是多線程。而且Java的代碼效率執(zhí)行起來要比python快很多。這份博客主要用于記錄我對多線程爬蟲的實踐理解。
線程
線程是指一個任務(wù)從頭至尾的執(zhí)行流。線程提供了運行一個任務(wù)的機(jī)制。對于Java而言,可以在一個程序中并發(fā)地啟動多個線程。這些線程可以在多處理器系統(tǒng)上同時運行。
runnable接口
任務(wù)類必須實現(xiàn)runnable接口,它只包含一個run方法。需要實現(xiàn)這個方法來告訴系統(tǒng)線程將如何運行。
Thread類
包含為任務(wù)而創(chuàng)建的線程的構(gòu)造方法,以及控制線程的方法。
synchronized關(guān)鍵字
為避免競爭狀態(tài),防止多個線程同時進(jìn)入程序的某個特定部分,即臨界區(qū),以便一次只有一個線程可以訪問臨界區(qū)。
利用加鎖同步
Java可以顯式加鎖,一個鎖是一個Lock接口的實例,它定義了加鎖和釋放鎖的方法。
線程池
線程池是管理開發(fā)執(zhí)行任務(wù)個數(shù)的理想方法。Java提供Executor接口來執(zhí)行線程池中的任務(wù),提供ExecutorService接口管理和控制任務(wù)。
使用線程池的方法獲取url列表
import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 獲取京東評論url列表 */ public class MyThreading { private static String p_id = null; private static Url urls = null; public MyThreading(String p_id){ this.p_id = p_id ; // 京東商品的id urls = new Url(p_id); } public List<String> getUriList(){ ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0 ; i < 600 ; i ++){ executor.execute(new AddUrl(i)); // 添加任務(wù)到線程池 } executor.shutdown(); while (!executor.isTerminated()){} return urls.getList(); } public static class AddUrl implements Runnable{ int page; public AddUrl(int page){ this.page = page; } public void run(){ urls.addList(page); // 啟動多線程任務(wù) } } public static class Url { private static Lock lock = new ReentrantLock(); // 開啟顯式家鎖 private static List<String> urlList = new ArrayList(); private String p_id; public Url(String p_id ){ this.p_id = p_id ; } public List<String> getList(){ return urlList; } public void addList(int page){ lock.lock(); try{ String url = "http://club.jd.com/productpage/p-" + p_id + "-s-0-t-0-p-" + String.valueOf(page) + ".html"; // Thread.sleep(5); urlList.add(url); //添加url到url列表 }catch(Exception ex ){ } finally { lock.unlock(); // 解鎖 } } } public static void main(String[] args) { String p_id = "2441288"; MyThreading myThreading = new MyThreading(p_id); List <String> urlList = myThreading.getUriList(); for(String url : urlList){ System.out.println(url); } System.out.println(urlList.size()); } }
代碼分析
- 代碼的作用:獲取京東評論的url列表
- 類的說明:MyThreading是主類, AddUrl和Url是它的內(nèi)部類,AddUrl實現(xiàn)了runnable的接口,主要啟動多線程服務(wù)運行Url的addList方法。而Url是最內(nèi)核的部分 ,他提供addList任務(wù)和多線程的共享區(qū)域urlList,所以在實現(xiàn)添加url的步驟中,需要對urlList加鎖。
- 線程池主要有兩種類型,一個是固定線程池,即newFixedThreadPool;另一個是newCachedThreadPool,這個主要利用了緩沖機(jī)制,能動態(tài)地添加線程。在上述代碼中,我主要使用了newCachedthreadPool.
使用線程池的方法根據(jù)url列表爬取網(wǎng)頁元素
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ThreadingCrawel { private static Content content = null; private static List<String> urlList = null; public ThreadingCrawel(List<String> urlList){ this.urlList = urlList; content = new Content(); } public List<String> getContent(){ ExecutorService executor = Executors.newCachedThreadPool(); for (String url : urlList){ executor.execute(new AddContent(url)); } executor.shutdown(); while(!executor.isTerminated()){} return content.getContent(); } public static class AddContent implements Runnable{ String url; public AddContent(String url){ this.url = url; } public void run(){ content.addContent(url); } } public static class Content { private static Lock lock = new ReentrantLock(); private static List<String> contentList = new ArrayList(); public void addContent(String url){ String content = ""; BufferedReader in = null; try{ URL realUrl = new URL(url); URLConnection connection = realUrl.openConnection(); in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "gbk")); String line; while( (line = in.readLine()) != null){ content += line +"\n"; } }catch(Exception e){ e.printStackTrace(); } finally{ try{ if (in != null){ in.close(); } }catch(Exception e2){ e2.printStackTrace(); } } Pattern p = Pattern.compile("content\":\".*?\""); Matcher match = p.matcher(content); String tmp; lock.lock(); while(match.find()){ tmp = match.group(); tmp = tmp.replaceAll("\"", ""); tmp = tmp.replace("content:", ""); tmp = tmp.replaceAll("<.*?>", ""); contentList.add(tmp); try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } lock.unlock(); } public List getContent(){ return contentList; } } public static void main(String[] args){ long start = System.currentTimeMillis(); String p_id = "2441288"; MyThreading myThreading = new MyThreading(p_id); List <String> urlList = myThreading.getUriList(); ThreadingCrawel threadingCrawel = new ThreadingCrawel(urlList); List <String> contentList = threadingCrawel.getContent(); for(String content : contentList){ System.out.println(content); } long end = System.currentTimeMillis(); System.out.println(end - start); } }
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
Springboot啟動不檢查JPA的數(shù)據(jù)源配置方式
這篇文章主要介紹了Springboot啟動不檢查JPA的數(shù)據(jù)源配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08java9版本特性資源自動關(guān)閉的語法增強(qiáng)
這篇文章主要為大家介紹了java9版本特性資源自動關(guān)閉的語法增強(qiáng)的詳細(xì)使用說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03IDEA?設(shè)置?SpringBoot?logback?彩色日志的解決方法?附配置文件
這篇文章主要介紹了IDEA?設(shè)置?SpringBoot?logback?彩色日志(附配置文件)的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-12-12redis實現(xiàn)隊列的阻塞、延時、發(fā)布和訂閱
本文主要介紹了redis實現(xiàn)隊列的阻塞、延時、發(fā)布和訂閱,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06利用JSONObject.toJSONString()包含或排除指定的屬性
這篇文章主要介紹了利用JSONObject.toJSONString()包含或排除指定的屬性,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Java Swing JPasswordField密碼框的實現(xiàn)示例
這篇文章主要介紹了Java Swing JPasswordField密碼框的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12