服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 半小时实现Java手撸网络爬虫框架(附完整源码)

半小时实现Java手撸网络爬虫框架(附完整源码)

2021-09-22 00:52冰 河 Java教程

最近在做一个搜索相关的项目,需要爬取网络上的一些链接存储到索引库中,自己写了一个简单的网络爬虫,感兴趣的可以了解一下

最近在做一个搜索相关的项目,需要爬取网络上的一些链接存储到索引库中,虽然有很多开源的强大的爬虫框架,但本着学习的态度,自己写了一个简单的网络爬虫,以便了解其中的原理。今天,就为小伙伴们分享下这个简单的爬虫程序!!

首先介绍每个类的功能:

  • DownloadPage.java的功能是下载此超链接的页面源代码.
  • FunctionUtils.java 的功能是提供不同的静态方法,包括:页面链接正则表达式匹配,获取URL链接的元素,判断是否创建文件,获取页面的Url并将其转换为规范的Url,截取网页网页源文件的目标内容。
  • HrefOfPage.java 的功能是获取页面源代码的超链接。
  • UrlDataHanding.java 的功能是整合各个给类,实现url到获取数据到数据处理类。
  • UrlQueue.java 的未访问Url队列。
  • VisitedUrlQueue.java 已访问过的URL队列。

下面介绍一下每个类的源代码:

DownloadPage.java 此类要用到HttpClient组件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.sreach.spider;
 
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
 
/**
 * @author binghe
 */
public class DownloadPage {
 
 /**
  * 根据URL抓取网页内容
  *
  * @param url
  * @return
  */
 public static String getContentFormUrl(String url) {
  /* 实例化一个HttpClient客户端 */
  HttpClient client = new DefaultHttpClient();
  HttpGet getHttp = new HttpGet(url);
 
  String content = null;
 
  HttpResponse response;
  try {
   /* 获得信息载体 */
   response = client.execute(getHttp);
   HttpEntity entity = response.getEntity();
 
   VisitedUrlQueue.addElem(url);
 
   if (entity != null) {
    /* 转化为文本信息 */
    content = EntityUtils.toString(entity);
 
    /* 判断是否符合下载网页源代码到本地的条件 */
    if (FunctionUtils.isCreateFile(url)
      && FunctionUtils.isHasGoalContent(content) != -1) {
     FunctionUtils.createFile(
       FunctionUtils.getGoalContent(content), url);
    }
   }
 
  } catch (ClientProtocolException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   client.getConnectionManager().shutdown();
  }
 
  return content;
 }
 
}

FunctionUtils.java 此类的方法均为static方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package com.sreach.spider;
 
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author binghe
 */
public class FunctionUtils {
 
 /**
  * 匹配超链接的正则表达式
  */
 private static String pat = "http://www\\.oschina\\.net/code/explore/.*/\\w+\\.[a-zA-Z]+";
 private static Pattern pattern = Pattern.compile(pat);
 
 private static BufferedWriter writer = null;
 
 /**
  * 爬虫搜索深度
  */
 public static int depth = 0;
 
 /**
  * 以"/"来分割URL,获得超链接的元素
  *
  * @param url
  * @return
  */
 public static String[] divUrl(String url) {
  return url.split("/");
 }
 
 /**
  * 判断是否创建文件
  *
  * @param url
  * @return
  */
 public static boolean isCreateFile(String url) {
  Matcher matcher = pattern.matcher(url);
 
  return matcher.matches();
 }
 
 /**
  * 创建对应文件
  *
  * @param content
  * @param urlPath
  */
 public static void createFile(String content, String urlPath) {
  /* 分割url */
  String[] elems = divUrl(urlPath);
  StringBuffer path = new StringBuffer();
 
  File file = null;
  for (int i = 1; i < elems.length; i++) {
   if (i != elems.length - 1) {
 
    path.append(elems[i]);
    path.append(File.separator);
    file = new File("D:" + File.separator + path.toString());
 
   }
 
   if (i == elems.length - 1) {
    Pattern pattern = Pattern.compile("\\w+\\.[a-zA-Z]+");
    Matcher matcher = pattern.matcher(elems[i]);
    if ((matcher.matches())) {
     if (!file.exists()) {
      file.mkdirs();
     }
     String[] fileName = elems[i].split("\\.");
     file = new File("D:" + File.separator + path.toString()
       + File.separator + fileName[0] + ".txt");
     try {
      file.createNewFile();
      writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(file)));
      writer.write(content);
      writer.flush();
      writer.close();
      System.out.println("创建文件成功");
     } catch (IOException e) {
      e.printStackTrace();
     }
 
    }
   }
 
  }
 }
 
 /**
  * 获取页面的超链接并将其转换为正式的A标签
  *
  * @param href
  * @return
  */
 public static String getHrefOfInOut(String href) {
  /* 内外部链接最终转化为完整的链接格式 */
  String resultHref = null;
 
  /* 判断是否为外部链接 */
  if (href.startsWith("http://")) {
   resultHref = href;
  } else {
   /* 如果是内部链接,则补充完整的链接地址,其他的格式忽略不处理,如:a href="#" rel="external nofollow"  */
   if (href.startsWith("/")) {
    resultHref = "http://www.oschina.net" + href;
   }
  }
 
  return resultHref;
 }
 
 /**
  * 截取网页网页源文件的目标内容
  *
  * @param content
  * @return
  */
 public static String getGoalContent(String content) {
  int sign = content.indexOf("<pre class=\"");
  String signContent = content.substring(sign);
 
  int start = signContent.indexOf(">");
  int end = signContent.indexOf("</pre>");
 
  return signContent.substring(start + 1, end);
 }
 
 /**
  * 检查网页源文件中是否有目标文件
  *
  * @param content
  * @return
  */
 public static int isHasGoalContent(String content) {
  return content.indexOf("<pre class=\"");
 }
}

HrefOfPage.java 此类为获取页面的超链接

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.sreach.spider;
/**
 * @author binghe
 *
 */
public class HrefOfPage {
 /**
  * 获得页面源代码中超链接
  */
 public static void getHrefOfContent(String content) {
  System.out.println("开始");
  String[] contents = content.split("<a href=\"");
  for (int i = 1; i < contents.length; i++) {
   int endHref = contents[i].indexOf("\"");
 
   String aHref = FunctionUtils.getHrefOfInOut(contents[i].substring(
     0, endHref));
 
   if (aHref != null) {
    String href = FunctionUtils.getHrefOfInOut(aHref);
 
    if (!UrlQueue.isContains(href)
      && href.indexOf("/code/explore") != -1
      && !VisitedUrlQueue.isContains(href)) {
     UrlQueue.addElem(href);
    }
   }
  }
 
  System.out.println(UrlQueue.size() + "--抓取到的连接数");
  System.out.println(VisitedUrlQueue.size() + "--已处理的页面数");
 }
}

UrlDataHanding.java 此类主要是从未访问队列中获取url,下载页面,分析url,保存已访问url等操作,实现Runnable接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.sreach.spider;
/**
 * @author binghe
 *
 */
public class UrlDataHanding implements Runnable {
 /**
  * 下载对应页面并分析出页面对应的URL放在未访问队列中。
  *
  * @param url
  */
 public void dataHanding(String url) {
  HrefOfPage.getHrefOfContent(DownloadPage.getContentFormUrl(url));
 }
 
 public void run() {
  while (!UrlQueue.isEmpty()) {
   dataHanding(UrlQueue.outElem());
  }
 }
}

UrlQueue.java 此类主要是用来存放未访问的URL队列

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.sreach.spider;
 
import java.util.LinkedList;
/**
 * @author binghe
 *
 */
public class UrlQueue {
 /** 超链接队列 */
 public static LinkedList<String> urlQueue = new LinkedList<String>();
 
 /** 队列中对应最多的超链接数量 */
 public static final int MAX_SIZE = 10000;
 
 public synchronized static void addElem(String url) {
  urlQueue.add(url);
 }
 
 public synchronized static String outElem() {
  return urlQueue.removeFirst();
 }
 
 public synchronized static boolean isEmpty() {
  return urlQueue.isEmpty();
 }
 
 public static int size() {
  return urlQueue.size();
 }
 
 public static boolean isContains(String url) {
  return urlQueue.contains(url);
 }
}

VisitedUrlQueue.java 主要是保存已访问过的URL,使用HashSet来保存,主要是考虑到每个访问过的URL是不同。HashSet刚好符合这个要求

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.sreach.spider;
 
import java.util.HashSet;
 
/**
 * 已访问url队列
 * @author binghe
 *
 */
public class VisitedUrlQueue {
 public static HashSet<String> visitedUrlQueue = new HashSet<String>();
 
 public synchronized static void addElem(String url) {
  visitedUrlQueue.add(url);
 }
 
 public synchronized static boolean isContains(String url) {
  return visitedUrlQueue.contains(url);
 }
 
 public synchronized static int size() {
  return visitedUrlQueue.size();
 }
}

Test.java 此类为测试类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.sql.SQLException;
 
import com.sreach.spider.UrlDataHanding;
import com.sreach.spider.UrlQueue;
/**
 * @author binghe
 *
 */
public class Test {
 public static void main(String[] args) throws SQLException {
  String url = "http://www.oschina.net/code/explore/achartengine/client/AndroidManifest.xml";
  String url1 = "http://www.oschina.net/code/explore";
  String url2 = "http://www.oschina.net/code/explore/achartengine";
  String url3 = "http://www.oschina.net/code/explore/achartengine/client";
 
  UrlQueue.addElem(url);
  UrlQueue.addElem(url1);
  UrlQueue.addElem(url2);
  UrlQueue.addElem(url3);
 
  UrlDataHanding[] url_Handings = new UrlDataHanding[10];
 
  for (int i = 0; i < 10; i++) {
   url_Handings[i] = new UrlDataHanding();
   new Thread(url_Handings[i]).start();
  }
 
 }
}

说明一下:由于我抓取的是针对oschina的,所以里面的url正则表达式不适合其他网站,需要自己修改一下。你也可以写成xml来配置。

到此这篇关于半小时实现Java手撸网络爬虫框架(附完整源码)的文章就介绍到这了,更多相关Java 网络爬虫框架内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/l1028386804/article/details/118324379

延伸 · 阅读

精彩推荐
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7472021-02-04
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18