JAVA 多线程爬虫实例详解
JAVA多线程爬虫实例详解
前言
以前喜欢Python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到Java的强大。Java有一个很好的机制,就是多线程。而且Java的代码效率执行起来要比python快很多。这份博客主要用于记录我对多线程爬虫的实践理解。
线程
线程是指一个任务从头至尾的执行流。线程提供了运行一个任务的机制。对于Java而言,可以在一个程序中并发地启动多个线程。这些线程可以在多处理器系统上同时运行。
runnable接口
任务类必须实现runnable接口,它只包含一个run方法。需要实现这个方法来告诉系统线程将如何运行。
Thread类
包含为任务而创建的线程的构造方法,以及控制线程的方法。
synchronized关键字
为避免竞争状态,防止多个线程同时进入程序的某个特定部分,即临界区,以便一次只有一个线程可以访问临界区。
利用加锁同步
Java可以显式加锁,一个锁是一个Lock接口的实例,它定义了加锁和释放锁的方法。
线程池
线程池是管理开发执行任务个数的理想方法。Java提供Executor接口来执行线程池中的任务,提供ExecutorService接口管理和控制任务。
使用线程池的方法获取url列表
importjava.util.ArrayList;
importjava.util.List;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
/*
*获取京东评论url列表
*/
publicclassMyThreading{
privatestaticStringp_id=null;
privatestaticUrlurls=null;
publicMyThreading(Stringp_id){
this.p_id=p_id;//京东商品的id
urls=newUrl(p_id);
}
publicListgetUriList(){
ExecutorServiceexecutor=Executors.newCachedThreadPool();
for(inti=0;i<600;i++){
executor.execute(newAddUrl(i));//添加任务到线程池
}
executor.shutdown();
while(!executor.isTerminated()){}
returnurls.getList();
}
publicstaticclassAddUrlimplementsRunnable{
intpage;
publicAddUrl(intpage){
this.page=page;
}
publicvoidrun(){
urls.addList(page);//启动多线程任务
}
}
publicstaticclassUrl{
privatestaticLocklock=newReentrantLock();//开启显式家锁
privatestaticListurlList=newArrayList();
privateStringp_id;
publicUrl(Stringp_id){
this.p_id=p_id;
}
publicListgetList(){
returnurlList;
}
publicvoidaddList(intpage){
lock.lock();
try{
Stringurl="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(Exceptionex){
}
finally{
lock.unlock();//解锁
}
}
}
publicstaticvoidmain(String[]args){
Stringp_id="2441288";
MyThreadingmyThreading=newMyThreading(p_id);
ListurlList=myThreading.getUriList();
for(Stringurl: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列表爬取网页元素
importjava.io.BufferedReader;
importjava.io.InputStreamReader;
importjava.net.URL;
importjava.net.URLConnection;
importjava.util.ArrayList;
importjava.util.List;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
publicclassThreadingCrawel{
privatestaticContentcontent=null;
privatestaticListurlList=null;
publicThreadingCrawel(ListurlList){
this.urlList=urlList;
content=newContent();
}
publicListgetContent(){
ExecutorServiceexecutor=Executors.newCachedThreadPool();
for(Stringurl:urlList){
executor.execute(newAddContent(url));
}
executor.shutdown();
while(!executor.isTerminated()){}
returncontent.getContent();
}
publicstaticclassAddContentimplementsRunnable{
Stringurl;
publicAddContent(Stringurl){
this.url=url;
}
publicvoidrun(){
content.addContent(url);
}
}
publicstaticclassContent{
privatestaticLocklock=newReentrantLock();
privatestaticListcontentList=newArrayList();
publicvoidaddContent(Stringurl){
Stringcontent="";
BufferedReaderin=null;
try{
URLrealUrl=newURL(url);
URLConnectionconnection=realUrl.openConnection();
in=newBufferedReader(newInputStreamReader(connection.getInputStream(),"gbk"));
Stringline;
while((line=in.readLine())!=null){
content+=line+"\n";
}
}catch(Exceptione){
e.printStackTrace();
}
finally{
try{
if(in!=null){
in.close();
}
}catch(Exceptione2){
e2.printStackTrace();
}
}
Patternp=Pattern.compile("content\":\".*?\"");
Matchermatch=p.matcher(content);
Stringtmp;
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(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
lock.unlock();
}
publicListgetContent(){
returncontentList;
}
}
publicstaticvoidmain(String[]args){
longstart=System.currentTimeMillis();
Stringp_id="2441288";
MyThreadingmyThreading=newMyThreading(p_id);
ListurlList=myThreading.getUriList();
ThreadingCrawelthreadingCrawel=newThreadingCrawel(urlList);
ListcontentList=threadingCrawel.getContent();
for(Stringcontent:contentList){
System.out.println(content);
}
longend=System.currentTimeMillis();
System.out.println(end-start);
}
}
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!