java实现网页爬虫的示例讲解

这一篇目的就是在于网页爬虫的实现,对数据的获取,以便分析。

目录:

1、爬虫原理

2、本地文件数据提取及分析

3、单网页数据的读取

4、运用正则表达式完成超连接的连接匹配和提取

5、广度优先遍历,多网页的数据爬取

6、多线程的网页爬取

7、总结

爬虫实现原理

网络爬虫基本技术处理

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

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实现网页爬虫的示例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java爬虫实战抓取一个网站上的全部链接

    前言:写这篇文章之前,主要是我看了几篇类似的爬虫写法,有的是用的队列来写,感觉不是很直观,还有的只有一个请求然后进行页面解析,根本就没有自动爬起来这也叫爬虫?因此我结合自己的思路写了一下简单的爬虫. 一 算法简介 程序在思路上采用了广度优先算法,对未遍历过的链接逐次发起GET请求,然后对返回来的页面用正则表达式进行解析,取出其中未被发现的新链接,加入集合中,待下一次循环时遍历. 具体实现上使用了Map<String, Boolean>,键值对分别是链接和是否被遍历标志.程序中使用了两个Map集

  • java 爬虫详解及简单实例

    Java爬虫 一.代码 爬虫的实质就是打开网页源代码进行匹配查找,然后获取查找到的结果. 打开网页: URL url = new URL(http://www.cnblogs.com/Renyi-Fan/p/6896901.html); 读取网页内容: BufferedReader bufr = new BufferedReader(new InputStreamReader(url.openStream())); 正则表达式进行匹配: tring mail_regex = "\\w+@\\w+

  • Java爬虫 信息抓取的实现

    今天公司有个需求,需要做一些指定网站查询后的数据的抓取,于是花了点时间写了个demo供演示使用. 思想很简单:就是通过Java访问的链接,然后拿到html字符串,然后就是解析链接等需要的数据.技术上使用Jsoup方便页面的解析,当然Jsoup很方便,也很简单,一行代码就能知道怎么用了: Document doc = Jsoup.connect("http://www.oschina.net/") .data("query", "Java") //

  • java正则表达式简单使用和网页爬虫的制作代码

    正则表达式是一种专门用于对字符串的操作的规则. 1.在String类中就有一些方法是对字符串进行匹配,切割. 判断字符串是否与给出的正则表达式匹配的:boolean matches( String regex); 按照给定的正则表达式对字符串进行切割的:String[]    split(String regex); 将符合正则表达式的字符串替换成我们想要的其他字符串:String  replaceAll(String  regex,String replacement) 2.下面介绍一下正则表

  • java实现网页爬虫的示例讲解

    这一篇目的就是在于网页爬虫的实现,对数据的获取,以便分析. 目录: 1.爬虫原理 2.本地文件数据提取及分析 3.单网页数据的读取 4.运用正则表达式完成超连接的连接匹配和提取 5.广度优先遍历,多网页的数据爬取 6.多线程的网页爬取 7.总结 爬虫实现原理 网络爬虫基本技术处理 网络爬虫是数据采集的一种方法,实际项目开发中,通过爬虫做数据采集一般只有以下几种情况: 1) 搜索引擎 2) 竞品调研 3) 舆情监控 4) 市场分析 网络爬虫的整体执行流程: 1) 确定一个(多个)种子网页 2) 进

  • Java DelayQueue实现任务延时示例讲解

    在项目中有使用到延时队列的场景,做个简单的记录说明:首先DelayQueue实现了BlockingQueue,加入其中的元素必须实现Delayed接口: 当生产者元素调用put往其中加入元素时,出发Delayed接口的compareTo方法进行排序,这个排序是按照时间的,按照计划执行的时间排序,先执行的在前面,后执行的排后面:消费者获取元素时,调用getDelay方法返回的值大于0,则消费者线程wait返回的这个时间后,再从队列头部取出元素:下面是个简单的例子 import org.jetbra

  • java 四舍五入使java保留2位小数示例讲解

    复制代码 代码如下: /* * 测试四舍五入 */package com.icer.test; /** * * @author Hanbin */public class MyRound {    public static void main(String[] args) {        double num = 3.23562;        double number = 0;        number = new MyRound().myRound(num,2);        Sy

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

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

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

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

  • 对Java ArrayList的自动扩容机制示例讲解

    注意: 不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力,ArrayList会增强自身的存储能力,已达到存储新元素的要求 ArrayList:本质通过内部维护的数组对象进行数据存储 ①:分析ArrayList的add(E)方法 public boolean add(E e) { ensureCapacityInternal(size + 1); /

  • Java实现的爬虫抓取图片并保存操作示例

    本文实例讲述了Java实现的爬虫抓取图片并保存操作.分享给大家供大家参考,具体如下: 这是我参考了网上一些资料写的第一个java爬虫程序 本来是想获取煎蛋网无聊图的图片,但是网络返回码一直是503,所以换了网站 import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStre

  • Node.js 利用cheerio制作简单的网页爬虫示例

    本文介绍了Node.js 利用cheerio制作简单的网页爬虫示例,分享给大家,具有如下: 1. 目标 完成对网站的标题信息获取 将获取到的信息输出在一个新文件 工具: cheerio,使用npm下载npm install cheerio cheerio的API使用方法和jQuery的使用方法基本一致 如果熟练使用jQuery,那么cheerio将会很快上手 2. 代码部分 介绍: 获取segment fault页面的列表标题,将获取到的标题列表编号,最终输出到pageTitle.txt文件里

  • Python 实现网页自动截图的示例讲解

    背景介绍 最近在为部门编写一个自动化测试工具,工具涉及到一个功能,即 将自动化测试生成的html报告截图,作为邮件正文,html文件上传到web服务器以链接形式添加到邮件中,最后发送邮件. 任务难点 之前从未接触过页面自动截图相关的方面,因此如何自动进行页面截图成为本地调研方向. 方案思考 在刚接到这个任务时,并不认同目前的方案.曾经一度认为,将html报告的内容写入邮件正文,即可通过html的形式发送邮件了.经过尝试后发现,邮件不支持带javascript的html.因此,选择了预览html并

随机推荐