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

网友投稿 243 2023-01-01

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

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

首先介绍每个类的功能:

DownloadPage.java的功能是下载此超链接的页面源代码.

FunctionUtils.java 的功能是提供不同的静态方法,包括:页面链接正则表达式匹配,获取URL链接的元素,判断是否创建文件,获取页面的Url并将其转换为规范的Url,截取网页网页源文件的目标内容。

HrefOfPage.java 的功能是获取页面源代码的超链接。

UrlDataHanding.java 的功能是整合各个给类,实现url到获取数据到数据处理类。

UrlQueue.java 的未访问Url队列。

VisitedUrlQueue.java 已访问过的URL队列。

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

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

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方法

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

fihttp://le = 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://oschina.net" + href;

}

}

return resultHref;

}

/**

* 截取网页网页源文件的目标内容

*

* @param content

* @return

*/

public static String getGoalContent(String content) {

int sign = content.indexOf("

String signContent = content.substring(sign);

int start = signContent.indexOf(">");

int end = signContent.indexOf("

return signContent.substring(start + 1, end);

}

/**

* 检查网页源文件中是否有目标文件

*

* @param content

* @return

*/

public static int isHasGoalContent(String content) {

return content.indexOf("

}

}

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

package com.sreach.spider;

/**

* @author binghe

*

*/

public class HrefOfPage {

/**

* 获得页面源代码中超链接

*/

public static void getHrefOfContent(String content) {

System.out.println("开始");

String[] contents = content.split("

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接口

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队列

package com.sreach.spider;

import java.util.LinkedList;

/**

* @author binghe

*

*/

public class UrlQueue {

/** 超链接队列 */

public static LinkedList urlQueue = new LinkedList();

/** 队列中对应最多的超链接数量 */

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刚好符合这个要求

package com.sreach.spider;

import java.util.HashSet;

/**

* 已访问url队列

* @author binghe

*

*/

public class VisitedUrlQueue {

public static HashSet visitedUrlQueue = new HashSet();

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 此类为测试类

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://oschina.net/code/explore/achartengine/client/androidManifest.xml";

String url1 = "http://oschina.net/code/explore";

String url2 = "http://oschina.net/code/explore/achartengine";

String url3 = "http://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来配置。

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接口

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队列

package com.sreach.spider;

import java.util.LinkedList;

/**

* @author binghe

*

*/

public class UrlQueue {

/** 超链接队列 */

public static LinkedList urlQueue = new LinkedList();

/** 队列中对应最多的超链接数量 */

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刚好符合这个要求

package com.sreach.spider;

import java.util.HashSet;

/**

* 已访问url队列

* @author binghe

*

*/

public class VisitedUrlQueue {

public static HashSet visitedUrlQueue = new HashSet();

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 此类为测试类

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://oschina.net/code/explore/achartengine/client/androidManifest.xml";

String url1 = "http://oschina.net/code/explore";

String url2 = "http://oschina.net/code/explore/achartengine";

String url3 = "http://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来配置。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java实现LeetCode(螺旋矩阵)
下一篇:圆通快递物流查询单号跟踪电话(圆通快递查询单号 查询 跟踪)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~