JAVA 多线程爬虫实例详解
程序员文章站
2024-02-28 21:18:46
java 多线程爬虫实例详解
前言
以前喜欢python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到java的强大。java有一个很好...
java 多线程爬虫实例详解
前言
以前喜欢python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到java的强大。java有一个很好的机制,就是多线程。而且java的代码效率执行起来要比python快很多。这份博客主要用于记录我对多线程爬虫的实践理解。
线程
线程是指一个任务从头至尾的执行流。线程提供了运行一个任务的机制。对于java而言,可以在一个程序中并发地启动多个线程。这些线程可以在多处理器系统上同时运行。
runnable接口
任务类必须实现runnable接口,它只包含一个run方法。需要实现这个方法来告诉系统线程将如何运行。
thread类
包含为任务而创建的线程的构造方法,以及控制线程的方法。
synchronized关键字
为避免竞争状态,防止多个线程同时进入程序的某个特定部分,即临界区,以便一次只有一个线程可以访问临界区。
利用加锁同步
java可以显式加锁,一个锁是一个lock接口的实例,它定义了加锁和释放锁的方法。
线程池
线程池是管理开发执行任务个数的理想方法。java提供executor接口来执行线程池中的任务,提供executorservice接口管理和控制任务。
使用线程池的方法获取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)); // 添加任务到线程池 } 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); // 启动多线程任务 } } 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是它的内部类,addurl实现了runnable的接口,主要启动多线程服务运行url的addlist方法。而url是最内核的部分 ,他提供addlist任务和多线程的共享区域urllist,所以在实现添加url的步骤中,需要对urllist加锁。
- 线程池主要有两种类型,一个是固定线程池,即newfixedthreadpool;另一个是newcachedthreadpool,这个主要利用了缓冲机制,能动态地添加线程。在上述代码中,我主要使用了newcachedthreadpool.
使用线程池的方法根据url列表爬取网页元素
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); } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
下一篇: Linux利用UDF库实现Mysql提权