欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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);
  }
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!