cockroach[小強] 當時不知道爲啥選了這麼個名字,又長又難記,致使編碼的過程當中由於單詞的拼寫問題耽誤了好長時間。css
這個項目算是個人又一個坑吧,算起來挖的坑多了去了,多一個很少少一個很多。html
一個小巧、靈活、健壯的爬蟲框架,暫且叫作框架吧。java
簡單到什麼程度呢,幾句話就能夠建立一個爬蟲。git
下面就逐點介紹一下:github
git clone https://github.com/zhangyingwei/cockroach.gitredis
cd cockroach數據庫
git installcookie
而後新建一個 maven 項目,在 pom 文件中引入框架
<dependency> <groupId>com.zhangyingwei.cockroach</groupId> <artifactId>cockroach</artifactId> <version>1.0-Alpha</version> </dependency>
在項目中新建一個測試類 App.java 並新建 main 方法。maven
public static void main(String[] args){ CockroachConfig config = new CockroachConfig() .setAppName("我是一個小強") .setThread(2); //爬蟲線程數 CockroachContext context = new CockroachContext(config); TaskQueue queue = TaskQueue.of(); context.start(queue); // 以上就是一個完整的爬蟲,下邊的代碼至關於一個生產者,往隊列裏邊寫任務,一旦寫入任務,爬蟲就會對任務進行爬取 new Thread(() -> { int i = 0; while(true){ i++; try { Thread.sleep(1000); String url = "http://www.xicidaili.com/wt/"+i; System.out.println(url); queue.push(new Task(url)); } catch (InterruptedException e) { e.printStackTrace(); } if (i > 1000) { break; } } }).start(); }
那靈活又體如今什麼方面呢
首先咱們嘗試一下自定義客戶端
public class SelfHttpClient implements HttpClient { public HttpClient setProxy(HttpProxy proxy){ //設置代理實現方法 } public TaskResponse doGet(Task task) throws Exception{ // get 請求實現方法 } public HttpClient proxy(){ // 應用代理到 http 客戶端 方法 } public TaskResponse doPost(Task task) throws Exception{ // post 請求實現方法 } public HttpClient setCookie(String cookie){ // 設置 cookie 實現方法 } public HttpClient setHttpHeader(Map<String, String> httpHeader){ // 設置 header 實現方法 } }
應用自定義 http 客戶端到爬蟲
CockroachConfig config = new CockroachConfig() .setAppName("我是一個小強") .setThread(2) //爬蟲線程數 .setHttpClient(SelfHttpClient.class)
自定義結果處理類
public class SelfStore implements IStore { @Override public void store(TaskResponse response) { System.out.println(response.getContent()); } }
這裏簡單的將結果打印了出來,在實際應用中,咱們能夠保存到數據庫或者保存到文件中等等。值得一說的是,若是結果是 html 網頁文本的話,咱們還提供了 select("css選擇器") 來對結果文本進行處理。
應用自定義 store 客戶端到爬蟲
CockroachConfig config = new CockroachConfig() .setAppName("我是一個小強") .setThread(2) //爬蟲線程數 .setHttpClient(SelfHttpClient.class) .setStore(SelfStore.class);
當 http 請求網頁出現錯誤的時候會統必定位到錯誤處理類,若是沒有自定義錯誤處理類,系統會默認使用 DefaultTaskErrorHandler ,此處理類會吧錯誤信息打印出來。具體實現代碼以下。
public class DefaultTaskErrorHandler implements ITaskErrorHandler { private Logger logger = Logger.getLogger(DefaultTaskErrorHandler.class); @Override public void error(Task task,String message) { logger.info("task error: "+message); } }
若是須要自定義錯誤處理類,能夠仿照以上代碼,實現 ITaskErrorHandler 接口,在 error 方法中實現本身的處理邏輯。
在自定義錯誤處理類以後,咱們須要把自定義類應用到爬蟲。
CockroachConfig config = new CockroachConfig() .setAppName("我是一個小強") .setThread(2) //爬蟲線程數 .setHttpClient(SelfHttpClient.class) .setStore(SelfStore.class) .setTaskErrorHandler(SelfTaskErrorHandler.class);
說到健壯,這裏主要體如今如下幾個方面:
應對IP封鎖
這裏咱們使用動態代理來解決這個問題。
CockroachConfig config = new CockroachConfig() .setAppName("我是一個小強") .setThread(2) //爬蟲線程數 .setHttpClient(SelfHttpClient.class) .setProxys("100.100.100.100:8888,101.101.101.101:8888")
如上所示,咱們能夠設置若干個代理 ip,最終將全部代理 ip 生成一個代理池,在爬蟲請求以前,咱們會從代理池中隨機抽取一個 ip 作代理。
應對 http 請求中的 user-agent 問題
程序中實現了一個 user-agent 池,每次請求都會隨機取出一個 user-agent 使用,目前在程序中集成了 17 種 user-agent,後續會考慮把這塊開放出來到配置中,自定義配置(有沒有意義呢?)。
程序中的異常處理問題
目前在異常處理這塊,自己也不是很是擅長,已經盡力把異常控制在一個可控的範圍內,程序中定義了不少自定義異常,這裏沒有什麼發言權,就不細說了,各位要是有意見建議,歡迎拍磚。
程序中並無現成的深度爬取實現,是由於通常狀況下我並不以爲深度爬取有什麼卵用,可是也不是沒有爲深度爬取留出來一席之地。咱們能夠本身提取出頁面中的連接並加入到任務隊列中。以達到深度爬取的效果。
public class DemoStore implements IStore { private String id = NameUtils.name(DemoStore.class); public DemoStore() throws IOException {} @Override public void store(TaskResponse response) throws IOException { List<String> urls = response.select("a").stream().map(element -> element.attr("href")).collect(Collectors.toList()); try { response.getQueue().push(urls); } catch (Exception e) { e.printStackTrace(); } } }
如今網上是個爬蟲就要搞一下分佈式,這令我很不爽。
實際上我看過幾個所謂的分佈式爬蟲源碼,他們所謂的分佈式,連僞分佈式都算不上!!!使用個 redis 作消息中間件就分佈式了嗎? 這就是所謂的分佈式??這根本就不是分佈式,原本我也準備使用 redis 作消息中間件來裝個分佈式的 B,可是寫了一半突然以爲有點噁心,遂刪除了代碼,還程序一個清靜,也還我本身一個安心。
分佈式這個坑確定是要挖的!!!
因此,個人分佈式將會包括:
因此,這個坑是愈來愈大了麼??我靠,有點怕怕