使用java实现网络爬虫

接着上面一篇对爬虫需要的java知识,这一篇目的就是在于网络爬虫的实现,对数据的获取,以便分析。----->

爬虫实现原理

网络爬虫基本技术处理

网络爬虫是数据采集的一种方法,实际项目开发中,通过爬虫做数据采集一般只有以下几种情况:

1)搜索引擎

2)竞品调研

3)舆情监控

4)市场分析

网络爬虫的整体执行流程:

1)确定一个(多个)种子网页

2)进行数据的内容提取

3)将网页中的关联网页连接提取出来

4)将尚未爬取的关联网页内容放到一个队列中

5)从队列中取出一个待爬取的页面,判断之前是否爬过。

6)把没有爬过的进行爬取,并进行之前的重复操作。

7)直到队列中没有新的内容,爬虫执行结束。

这样完成爬虫时,会有一些概念必须知道的:

1)深度(depth):一般来说,表示从种子页到当前页的打开连接数,一般建议不要超过5层。

2)广度(宽度)优先和深度优先:表示爬取时的优先级。建议使用广度优先,按深度的层级来顺序爬取。

Ⅰ  在进行网页爬虫前,我们先针对一个飞机事故失事的文档进行数据提取的练习,主要是温习一下上一篇的java知识,也是为了下面爬虫实现作一个热身准备。

 首先分析这个文档,

,关于美国历来每次飞机失事的数据,包含时间地点、驾驶员、死亡人数、总人数、事件描述,一共有12列,第一列是标题,下面一共有5268条数据。

 现在我要对这个文件进行数据提取,并实现一下分析:  

根据飞机事故的数据文档来进行简单数据统计。

1)哪年出事故次数最多

2)哪个时间段(上午 8 –12,下午 12 –18,晚上 18 –24,凌晨 0 –8 )事故出现次数最多。

3)哪年死亡人数最多

4)哪条数据的幸存率最高。

代码实现:(一切知识从源码获取!)

package com.plane;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
 * 飞机事故统计
 * @author k04
 *sunwengang
 *2017-08-11
 */
public class planeaccident {
    //数据获取存取链表
    private static List<String> alldata=new ArrayList<>();

    public static void main(String args[]){
      getData("飞行事故数据统计_Since_1908.csv");
      alldata.remove(0);
      //System.out.println(alldata.size());
      //死亡人数最多的年份
      MaxDeadYear();
      //事故发生次数最多的年份
      MaxAccidentsYear();
      //事故各个时间段发生的次数
      FrequencyPeriod();
      //幸村率最高的一条数据
       MaximumSurvival();
    }

    /**
     * 从源文件爬取数据
     * getData(String filepath)
     * @param filepath
     */
    public static void getData(String filepath){
      File f=new File(filepath);
      //行读取数据
      try{
        BufferedReader br=new BufferedReader(new FileReader(f));
        String line=null;
        while((line=(br.readLine()))!=null){
          alldata.add(line);
        }
        br.close();
      }catch(Exception e){
        e.printStackTrace();
      }
    }
    /**
     * 记录每年对应的死亡人数
     * @throws
     * 并输出死亡人数最多的年份,及该年死亡人数
     */
    public static void MaxDeadYear(){
      //记录年份对应死亡人数
      Map<Integer,Integer> map=new HashMap<>();
      //时间用date显示
      SimpleDateFormat sdf=new SimpleDateFormat("MM/dd/YYYY");
      //循环所有数据
      for(String data:alldata){
        //用逗号将数据分离,第一个是年份,第11个是死亡人数
        String[] strs=data.split(",");
        if(strs[0]!=null){
          //获取年份
          try {
            Date date=sdf.parse(strs[0]);
            int year=date.getYear();
            //判断map中是否记录过这个数据
            if(map.containsKey(year)){
              //已存在,则记录数+该年死亡人数
              map.put(year, map.get(year)+Integer.parseInt(strs[10]));
            }else{
              map.put(year, Integer.parseInt(strs[10]));
            }

          } catch (Exception e) {
            // TODO Auto-generated catch block

          }

        }
      }
      //System.out.println(map);

      //记录死亡人数最多的年份
      int max_year=-1;
      //记录死亡人数
      int dead_count=0;
      //用set无序获取map中的key值,即年份
      Set<Integer> keyset=map.keySet();
      //
      for(int year:keyset){
        //当前年事故死亡最多的年份,记录年和次数
        if(map.get(year)>dead_count&&map.get(year)<10000){
          max_year=year;
          dead_count=map.get(year);
        }
      }

      System.out.println("死亡人数最多的年份:"+(max_year+1901)+"  死亡人数:"+dead_count);
    }
    /**
     * 记录事故次数最多的年份
     * 输出该年及事故次数
     */
    public static void MaxAccidentsYear(){
      //存放年份,该年的事故次数
      Map<Integer,Integer> map=new HashMap<>();
      SimpleDateFormat sdf =new SimpleDateFormat("MM/dd/YYYY");
      //循环所有数据
      for(String data:alldata){
        String[] strs=data.split(",");
        if(strs[0]!=null){
          try {
            Date date=sdf.parse(strs[0]);
            //获取年份
            int year=date.getYear();
            //判断是否存在记录
            if(map.containsKey(year)){
              //已存在记录,+1
              map.put(year, map.get(year)+1);
            }else{
              map.put(year, 1);
            }
          } catch (Exception e) {
            // TODO Auto-generated catch block
          }
        }
      }
      //记录事故次数最多的年份
      int max_year=0;
      //该年事故发生次数
      int acc_count=0;
      //循环所有数据,获取事故次数最多的年份
      Set<Integer> keyset=map.keySet();
      for(int year:keyset){
        if(map.get(year)>acc_count){
          max_year=year;
          acc_count=map.get(year);
        }
      }
      //输出结果
      System.out.println("事故次数最多的年份"+(max_year+1901)+" 该年事故发生次数:"+acc_count);
    }
    /**
     * FrequencyPeriod()
     * 各个时间段发生事故的次数
     */
    public static void FrequencyPeriod(){
      //key为时间段,value为发生事故次数
      Map<String,Integer> map=new HashMap<>();
      //String数组存放时间段
      String[] strsTime={"上午(6:00~12:00)","下午(12:00~18:00)","晚上(18:00~24:00)","凌晨(0:00~6:00)"};
      //小时:分钟
      SimpleDateFormat sdf=new SimpleDateFormat("HH:mm");

      for(String data:alldata){
        String[] strs=data.split(",");
        //判断时间是否记录,未记录则忽略
        if(strs[1]!=null){
          try {
            Date date=sdf.parse(strs[1]);
            //取得小时数
            int hour=date.getHours();
            //判断小时数在哪个范围中
            int index=0;
            if(hour>=12&&hour<18){
              index=1;
            }else if(hour>=18){
              index=2;
            }else if(hour<6){
              index=3;
            }
            //记录到map中
            if(map.containsKey(strsTime[index])){
              map.put(strsTime[index], map.get(strsTime[index])+1);
            }else{
              map.put(strsTime[index], 1);
            }
          } catch (ParseException e) {
          }
        }

      }
      /*
      System.out.println("各时间段发生事故次数:");
      for(int i=0;i<strsTime.length;i++){
      System.out.println(strsTime[i]+" : "+map.get(strsTime[i]));
      }
      */
      // 记录出事故最多的时间范围
      String maxTime = null;
      // 记录出事故最多的次数
      int maxCount = 0;

      Set<String> keySet = map.keySet();
      for (String timeScope : keySet) {
        if (map.get(timeScope) > maxCount) {
          // 当前年就是出事故最多的年份,记录下年和次数
          maxTime = timeScope;
          maxCount = map.get(timeScope);
        }
      }
      System.out.println("发生事故次数最多的时间段:");
      System.out.println(maxTime+" : "+maxCount);
    }
    /**
     * 获取幸村率最高的一条数据的内容
     * 返回该内容及幸存率
     */
    public static void MaximumSurvival(){
      //存放事故信息以及该事故的幸村率
      Map<String,Float> map=new HashMap<>();
      //SimpleDateFormat sdf =new SimpleDateFormat("MM/dd/YYYY");
      //事故幸存率=1-死亡率,第十一个是死亡人数,第十个是总人数
      float survial=0;
      //循环所有数据
      for(String data:alldata){
        try{
        String[] strs=data.split(",");
        //计算幸存率
        float m=Float.parseFloat(strs[10]);
        float n=Float.parseFloat(strs[9]);
        survial=1-m/n;
        map.put(data, survial);
        }catch(Exception e){

        }
      }
      //记录事故次数最多的年份
      float max_survial=0;
      //幸存率最高的数据信息
      String this_data="null";
      //循环所有数据,获取事故次数最多的年份
      Set<String> keyset=map.keySet();
      for(String data:keyset){
        if(map.get(data)>max_survial){
          this_data=data;
          max_survial=map.get(data);
        }
      }
      System.out.println("幸存率最高的事故是:"+this_data);
      System.out.println("幸存率为:"+survial);
    }
}

Ⅱ  接下来我们就可以在网页的数据上下手了。

下面先实现一个单网页数据提取的功能。

使用的技术可以有以下几类:

1)原生代码实现:

  a)URL类

2)使用第三方的URL库

  a)HttpClient库

3)开源爬虫框架

  a)Heritrix

  b)Nutch

【一】

先使用URL类,来将当当网下搜索机械表的内容提取出来。

package com.exe1;
/**
 * 读取当当网下机械表的数据,并进行分析
 * sunwengang  2017-08-13 20:00
 */
import java.io.*;
import java.net.*;

public class URLDemo {
  public static void main(String args[]){
    //确定爬取的网页地址,此处为当当网搜机械表显示的网页
    //网址为    http://search.dangdang.com/?key=%BB%FA%D0%B5%B1%ED&act=input
    String strurl="http://search.dangdang.com/?key=%BB%FA%D0%B5%B1%ED&act=input";
    //建立url爬取核心对象
    try {
      URL url=new URL(strurl);
      //通过url建立与网页的连接
      URLConnection conn=url.openConnection();
      //通过链接取得网页返回的数据
      InputStream is=conn.getInputStream();

      System.out.println(conn.getContentEncoding());
      //一般按行读取网页数据,并进行内容分析
      //因此用BufferedReader和InputStreamReader把字节流转化为字符流的缓冲流
      //进行转换时,需要处理编码格式问题
      BufferedReader br=new BufferedReader(new InputStreamReader(is,"UTF-8"));

      //按行读取并打印
      String line=null;
      while((line=br.readLine())!=null){
        System.out.println(line);
      }

      br.close();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

  }
}

结果显示:

【二】

下面尝试将这个网页的源代码保存成为本地的一个文本文件,以便后续做离线分析。

如果想根据条件提取网页中的内容信息,那么就需要使用Java的正则表达式。

正则表达式

Java.util包下提供了Pattern和Matcher这两个类,可以根据我们给定的条件来进行数据的匹配和提取。

通过Pattern类中提供的规则字符或字符串,我们需要自己拼凑出我们的匹配规则。

正则表达式最常用的地方是用来做表单提交的数据格式验证的。

常用的正则表达式规则一般分为两类:

1)内容匹配

  a)\d:是否是数字

  b)\w:匹配 字母、数字或下划线

  c).:任意字符

  d)[a-z]:字符是否在给定范围内。

2)数量匹配

  a)+:1个或以上

  b)*:0个或以上

  c)?:0或1次

  d){n,m}:n-m次

匹配手机电话号码:

规则:1\\d{10}

匹配邮件地址:

规则:\\w+@\\w+.\\w+(\\.\\w+)?

通过Pattern和Matcher的配合,我们可以把一段内容中匹配我们要求的文字提取出来,方便我们来处理。

例如:将一段内容中的电话号码提取出来。

public class PatternDemo {

  public static void main(String[] args) {
    Pattern p = Pattern.compile("1\\d{10}");

    String content = "<div><div class='jg666'>[转让]<a href='/17610866588' title='手机号码17610866588估价评估_值多少钱_归属地查询_测吉凶_数字含义_求购转让信息' class='lj44'>17610866588</a>由 张云龙 300元转让,联系电话:17610866588</div><div class='jg666'>[转让]<a href='/17777351513' title='手机号码17777351513估价评估_值多少钱_归属地查询_测吉凶_数字含义_求购转让信息' class='lj44'>17777351513</a>由 胡俊宏 888元转让,QQ:762670775,联系电话:17777351513,可以小砍价..</div><div class='jg666'>[求购]<a href='/15019890606' title='手机号码15019890606估价评估_值多少钱_归属地查询_测吉凶_数字含义_求购转让信息' class='lj44'>15019890606</a>由 张宝红 600元求购,联系电话:15026815169</div><div class='jg666'>";

    Matcher m = p.matcher(content);
    // System.out.println(p.matcher("sf@sina").matches());
    Set<String> set = new HashSet<>();
    // 通过Matcher类的group方法和find方法来进行查找和匹配
    while (m.find()) {
      String value = m.group();
      set.add(value);
    }
    System.out.println(set);
  }
}

通过正则表达式完成超连接的连接匹配和提取

对爬取的HTML页面来说,如果想提取连接地址,就必须找到所有超连接的标签和对应的属性。

超连接标签是<a></a>,保存连接的属性是:href。

<a href=”…”>…</a>

规则:

<a .*href=.+</a>

广度优先遍历

需要有一个队列(这里直接使用ArrayList来作为队列)保存所有等待爬取的连接。

还需要一个Set集合记录下所有已经爬取过的连接。

还需要一个深度值,记录当前爬取的网页深度,判断是否满足要求

此时对当当网首页分类里的图书进行深度为2的网页爬取,参照上述对机械表单网页的爬取,利用递归的方式进行数据获取存到E:/dangdang_book/目录下:

package com.exe1;
/**
 * 读取当当网下首页图书的数据,并进行分析
 * 爬取深度为2
 * 爬去数据存储到E:/dangdang_book/目录下,需自行创建
 * sunwengang  2017-08-13 20:00
 */
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;

public class URLDemo {
  //提取的数据存放到该目录下
  private static String savepath="E:/dangdang_book/";
  //等待爬取的url
  private static List<String> allwaiturl=new ArrayList<>();
  //爬取过的url
  private static Set<String> alloverurl=new HashSet<>();
  //记录所有url的深度进行爬取判断
  private static Map<String,Integer> allurldepth=new HashMap<>();
  //爬取得深度
  private static int maxdepth=2;

  public static void main(String args[]){
    //确定爬取的网页地址,此处为当当网首页上的图书分类进去的网页
    //网址为    http://book.dangdang.com/
//    String strurl="http://search.dangdang.com/?key=%BB%FA%D0%B5%B1%ED&act=input";
    String strurl="http://book.dangdang.com/";

    workurl(strurl,1);

  }
  public static void workurl(String strurl,int depth){
    //判断当前url是否爬取过
    if(!(alloverurl.contains(strurl)||depth>maxdepth)){
    //建立url爬取核心对象
    try {
      URL url=new URL(strurl);
      //通过url建立与网页的连接
      URLConnection conn=url.openConnection();
      //通过链接取得网页返回的数据
      InputStream is=conn.getInputStream();

      System.out.println(conn.getContentEncoding());
      //一般按行读取网页数据,并进行内容分析
      //因此用BufferedReader和InputStreamReader把字节流转化为字符流的缓冲流
      //进行转换时,需要处理编码格式问题
      BufferedReader br=new BufferedReader(new InputStreamReader(is,"GB2312"));

      //按行读取并打印
      String line=null;
      //正则表达式的匹配规则提取该网页的链接
      Pattern p=Pattern.compile("<a .*href=.+</a>");
      //建立一个输出流,用于保存文件,文件名为执行时间,以防重复
      PrintWriter pw=new PrintWriter(new File(savepath+System.currentTimeMillis()+".txt"));

      while((line=br.readLine())!=null){
        //System.out.println(line);
        //编写正则,匹配超链接地址
        pw.println(line);
        Matcher m=p.matcher(line);
        while(m.find()){
          String href=m.group();
          //找到超链接地址并截取字符串
          //有无引号
          href=href.substring(href.indexOf("href="));
          if(href.charAt(5)=='\"'){
            href=href.substring(6);
          }else{
            href=href.substring(5);
          }
          //截取到引号或者空格或者到">"结束
        try{
          href=href.substring(0,href.indexOf("\""));
        }catch(Exception e){
          try{
            href=href.substring(0,href.indexOf(" "));
          }catch(Exception e1){
            href=href.substring(0,href.indexOf(">"));
          }
        }
        if(href.startsWith("http:")||href.startsWith("https:")){
          //输出该网页存在的链接
          //System.out.println(href);
          //将url地址放到队列中
          allwaiturl.add(href);
          allurldepth.put(href,depth+1);
            }

          }

        }
      pw.close();
      br.close();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    //将当前url归列到alloverurl中
    alloverurl.add(strurl);
    System.out.println(strurl+"网页爬取完成,已爬取数量:"+alloverurl.size()+",剩余爬取数量:"+allwaiturl.size());
    }
    //用递归的方法继续爬取其他链接
    String nexturl=allwaiturl.get(0);
    allwaiturl.remove(0);
    workurl(nexturl,allurldepth.get(nexturl));
    }
}

控制台显示:

本地目录显示:

但是,仅是深度为2的也运行不短地时间,

如果想提高爬虫性能,那么我们就需要使用多线程来处理,例如:准备好5个线程来同时进行爬虫操作。

这些线程需要标注出当前状态,是在等待,还是在爬取。

如果是等待状态,那么就需要取得集合中的一个连接,来完成爬虫操作。

如果是爬取状态,则在爬完以后,需要变为等待状态。

多线程中如果想设置等待状态,有一个方法可以实现:wait(),如果想从等待状态唤醒,则可以使用notify()。

因此在多个线程中间我们需要一个对象来帮助我们进行线程之间的通信,以便唤醒其它线程。

多线程同时处理时,容易出现线程不安全的问题,导致数据出现错误。

为了保证线程的安全,就需要使用同步关键字,来对取得连接和放入连接操作加锁。

多线程爬虫实现

需要先自定义一个线程的操作类,在这个操作类中判断不同的状态,并且根据状态来决定是进行wait()等待,还是取得一个新的url进行处理。

package com.exe1;
/**
 * 读取当当网下首页图书的数据,并进行分析
 * 爬取深度为2
 * 爬去数据存储到E:/dangdang_book/目录下,需自行创建
 * 孙文刚  2017-08-13 20:00
 */
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;

public class URLDemo {
  //提取的数据存放到该目录下
  private static String savepath="E:/dangdang_book/";
  //等待爬取的url
  private static List<String> allwaiturl=new ArrayList<>();
  //爬取过的url
  private static Set<String> alloverurl=new HashSet<>();
  //记录所有url的深度进行爬取判断
  private static Map<String,Integer> allurldepth=new HashMap<>();
  //爬取得深度
  private static int maxdepth=2;
  //生命对象,帮助进行线程的等待操作
  private static Object obj=new Object();
  //记录总线程数5条
  private static int MAX_THREAD=5;
  //记录空闲的线程数
  private static int count=0;

  public static void main(String args[]){
    //确定爬取的网页地址,此处为当当网首页上的图书分类进去的网页
    //网址为    http://book.dangdang.com/
//    String strurl="http://search.dangdang.com/?key=%BB%FA%D0%B5%B1%ED&act=input";
    String strurl="http://book.dangdang.com/";

    //workurl(strurl,1);
    addurl(strurl,0);
    for(int i=0;i<MAX_THREAD;i++){
      new URLDemo().new MyThread().start();
    }
  }
  /**
   * 网页数据爬取
   * @param strurl
   * @param depth
   */
  public static void workurl(String strurl,int depth){
    //判断当前url是否爬取过
    if(!(alloverurl.contains(strurl)||depth>maxdepth)){
      //检测线程是否执行
      System.out.println("当前执行:"+Thread.currentThread().getName()+" 爬取线程处理爬取:"+strurl);
    //建立url爬取核心对象
    try {
      URL url=new URL(strurl);
      //通过url建立与网页的连接
      URLConnection conn=url.openConnection();
      //通过链接取得网页返回的数据
      InputStream is=conn.getInputStream();

      //提取text类型的数据
      if(conn.getContentType().startsWith("text")){

      }
      System.out.println(conn.getContentEncoding());
      //一般按行读取网页数据,并进行内容分析
      //因此用BufferedReader和InputStreamReader把字节流转化为字符流的缓冲流
      //进行转换时,需要处理编码格式问题
      BufferedReader br=new BufferedReader(new InputStreamReader(is,"GB2312"));

      //按行读取并打印
      String line=null;
      //正则表达式的匹配规则提取该网页的链接
      Pattern p=Pattern.compile("<a .*href=.+</a>");
      //建立一个输出流,用于保存文件,文件名为执行时间,以防重复
      PrintWriter pw=new PrintWriter(new File(savepath+System.currentTimeMillis()+".txt"));

      while((line=br.readLine())!=null){
        //System.out.println(line);
        //编写正则,匹配超链接地址
        pw.println(line);
        Matcher m=p.matcher(line);
        while(m.find()){
          String href=m.group();
          //找到超链接地址并截取字符串
          //有无引号
          href=href.substring(href.indexOf("href="));
          if(href.charAt(5)=='\"'){
            href=href.substring(6);
          }else{
            href=href.substring(5);
          }
          //截取到引号或者空格或者到">"结束
        try{
          href=href.substring(0,href.indexOf("\""));
        }catch(Exception e){
          try{
            href=href.substring(0,href.indexOf(" "));
          }catch(Exception e1){
            href=href.substring(0,href.indexOf(">"));
          }
        }
        if(href.startsWith("http:")||href.startsWith("https:")){
          /*
          //输出该网页存在的链接
          //System.out.println(href);
          //将url地址放到队列中
          allwaiturl.add(href);
          allurldepth.put(href,depth+1);
          */
          //调用addurl方法
          addurl(href,depth);
            }

          }

        }
      pw.close();
      br.close();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      //e.printStackTrace();
    }
    //将当前url归列到alloverurl中
    alloverurl.add(strurl);
    System.out.println(strurl+"网页爬取完成,已爬取数量:"+alloverurl.size()+",剩余爬取数量:"+allwaiturl.size());
    }
    /*
    //用递归的方法继续爬取其他链接
    String nexturl=allwaiturl.get(0);
    allwaiturl.remove(0);
    workurl(nexturl,allurldepth.get(nexturl));
    */
    if(allwaiturl.size()>0){
      synchronized(obj){
        obj.notify();
      }
    }else{
      System.out.println("爬取结束.......");
    }

    }
  /**
   * 将获取的url放入等待队列中,同时判断是否已经放过
   * @param href
   * @param depth
   */
  public static synchronized void addurl(String href,int depth){
    //将url放到队列中
    allwaiturl.add(href);
    //判断url是否放过
    if(!allurldepth.containsKey(href)){
      allurldepth.put(href, depth+1);
    }
  }
  /**
   * 移除爬取完成的url,获取下一个未爬取得url
   * @return
   */
  public static synchronized String geturl(){
    String nexturl=allwaiturl.get(0);
    allwaiturl.remove(0);
    return nexturl;
  }
  /**
   * 线程分配任务
   */
  public class MyThread extends Thread{
    @Override
    public void run(){
      //设定一个死循环,让线程一直存在
      while(true){
        //判断是否新链接,有则获取
        if(allwaiturl.size()>0){
          //获取url进行处理
          String url=geturl();
          //调用workurl方法爬取
          workurl(url,allurldepth.get(url));
        }else{
          System.out.println("当前线程准备就绪,等待连接爬取:"+this.getName());
          count++;
          //建立一个对象,让线程进入等待状态,即wait()
          synchronized(obj){
            try{
              obj.wait();
            }catch(Exception e){

            }
          }
          count--;
        }
      }
    }

  }
}

控制台显示:

本地目录显示:

总结:

对于网页数据爬取,用到了线程,类集处理,继承,正则表达式等各方面的知识,从一个网页以深度为主,广度为基本进行爬取,获取每一个网页的源代码,并写入到一个本地的目录下。

1、给出一个网页链接,创建一个本地目录;

2、用URL类本地连接,用字符流进行读取,并写入到本地;

3、利用正则表达式在按行读取时获取该网页所存在的所有链接,以便进行深度+1的数据收集;

4、利用递归的方法,借助容器list,Set,Map来对链接进行爬取和未爬取得划分;

5、每次爬取一个网页时,所获得的所有链接在当前基础上深度+1,并且从未爬取队列中移除,加入到已爬取队列中;

6、为提升性能,在进行递归的时候,可以利用线程,复写Thread的run()方法,用多线程进行网页数据爬取;

7、直到爬取得网页深度达到你期望的深度时,爬取结束,此时可以查看本地目录生成的文件;

8、后续对本地生成的文件进行数据分析,即可获取你想要的信息。

借此,我们就可以对这些数据进行归约,分析,处理,来获取我们想要的信息。

这也是大数据数据收集的一个基础。

到此这篇关于使用java实现网络爬虫的文章就介绍到这了,更多相关java实现网络爬虫内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 网络爬虫新手入门详解

    这是 Java 网络爬虫系列文章的第一篇,如果你还不知道 Java 网络爬虫系列文章,请参看Java 网络爬虫基础知识入门解析.第一篇是关于 Java 网络爬虫入门内容,在该篇中我们以采集虎扑列表新闻的新闻标题和详情页为例,需要提取的内容如下图所示: 我们需要提取图中圈出来的文字及其对应的链接,在提取的过程中,我们会使用两种方式来提取,一种是 Jsoup 的方式,另一种是 httpclient + 正则表达式的方式,这也是 Java 网络爬虫常用的两种方式,你不了解这两种方式没关系,后面会有相应

  • Java 网络爬虫基础知识入门解析

    前言 说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬虫而且还能做的非常好,在开源社区中有不少优秀的 Java 网络爬虫框架,例如 webmagic .我的第一份正式工作就是使用 webmagic 编写数据采集程序,当时参与了一个舆情分析系统的开发,这里面涉及到了大量网站的新闻采集,我们就使用了 webmagic 进行采集程序的编写,由于当时不知

  • Java实现爬虫给App提供数据(Jsoup 网络爬虫)

    一.需求 最近基于 Material Design 重构了自己的新闻 App,数据来源是个问题. 有前人分析了知乎日报.凤凰新闻等 API,根据相应的 URL 可以获取新闻的 JSON 数据.为了锻炼写代码能力,笔者打算爬虫新闻页面,自己获取数据构建 API. 二.效果图 下图是原网站的页面 爬虫获取了数据,展示到 APP 手机端 三.爬虫思路 关于App 的实现过程可以参看这几篇文章,本文主要讲解一下如何爬虫数据. Android下录制App操作生成Gif动态图的全过程 :http://www

  • hadoop中实现java网络爬虫(示例讲解)

    这一篇网络爬虫的实现就要联系上大数据了.在前两篇java实现网络爬虫和heritrix实现网络爬虫的基础上,这一次是要完整的做一次数据的收集.数据上传.数据分析.数据结果读取.数据可视化. 需要用到 Cygwin:一个在windows平台上运行的类UNIX模拟环境,直接网上搜索下载,并且安装: Hadoop:配置Hadoop环境,实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS,用来将收集的数据直接上传保存到HDFS,然后用MapReduce

  • 基于Java HttpClient和Htmlparser实现网络爬虫代码

    开发环境的搭建,在工程的 Build Path 中导入下载的Commons-httpClient3.1.Jar,htmllexer.jar 以及 htmlparser.jar 文件. 图 1. 开发环境搭建 HttpClient 基本类库使用 HttpClinet 提供了几个类来支持 HTTP 访问.下面我们通过一些示例代码来熟悉和说明这些类的功能和使用. HttpClient 提供的 HTTP 的访问主要是通过 GetMethod 类和 PostMethod 类来实现的,他们分别对应了 HTT

  • java实现一个简单的网络爬虫代码示例

    目前市面上流行的爬虫以python居多,简单了解之后,觉得简单的一些页面的爬虫,主要就是去解析目标页面(html).那么就在想,java有没有用户方便解析html页面呢?找到了一个jsoup包,一个非常方便解析html的工具呢. 使用方式也非常简单,引入jar包: <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.8.

  • java网络爬虫连接超时解决实例代码

    本文研究的主要是java网络爬虫连接超时的问题,具体如下. 在网络爬虫中,经常会遇到如下报错.即连接超时.针对此问题,一般解决思路为:将连接时间.请求时间设置长一下.如果出现连接超时的情况,则在重新请求[设置重新请求次数]. Exception in thread "main" java.net.ConnectException: Connection timed out: connect 下面的代码便是使用httpclient解决连接超时的样例程序.直接上程序. package da

  • 使用java实现网络爬虫

    接着上面一篇对爬虫需要的java知识,这一篇目的就是在于网络爬虫的实现,对数据的获取,以便分析.-----> 爬虫实现原理 网络爬虫基本技术处理 网络爬虫是数据采集的一种方法,实际项目开发中,通过爬虫做数据采集一般只有以下几种情况: 1)搜索引擎 2)竞品调研 3)舆情监控 4)市场分析 网络爬虫的整体执行流程: 1)确定一个(多个)种子网页 2)进行数据的内容提取 3)将网页中的关联网页连接提取出来 4)将尚未爬取的关联网页内容放到一个队列中 5)从队列中取出一个待爬取的页面,判断之前是否爬过

  • Java 实现网络爬虫框架详细代码

    目录 Java 实现网络爬虫框架 一.每个类的功能介绍 二.每个类的源代码 Java 实现网络爬虫框架 最近在做一个搜索相关的项目,需要爬取网络上的一些链接存储到索引库中,虽然有很多开源的强大的爬虫框架,但本着学习的态度,自己写了一个简单的网络爬虫,以便了解其中的原理.今天,就为小伙伴们分享下这个简单的爬虫程序!! 一.每个类的功能介绍 DownloadPage.java的功能是下载此超链接的页面源代码. FunctionUtils.java 的功能是提供不同的静态方法,包括:页面链接正则表达式

  • java编程实现简单的网络爬虫示例过程

    本项目中需要用到两个第三方jar包,分别为 jsoup 和 commons-io. jsoup的作用是为了解析网页, commons-io 是为了把数据保存到本地. 1.爬取贴吧 第一步,打开eclipse,新建一个java项目,名字就叫做 pachong: 然后,新建一个类,作为我们程序的入口. 这个作为入口类,里面就写一个main方法即可. public class StartUp { public static void main(String[] args) { } } 第二步,导入我们

随机推荐