Java简单实现爬虫,抓取整个网站所有链接+图片+文件(思路+代码) - Go语言中文社区

Java简单实现爬虫,抓取整个网站所有链接+图片+文件(思路+代码)


写这个纯属个人爱好,前两天想玩爬虫,但是百度了一大圈也没发现有好一点的帖子,所以就自己研究了下,亲测小点的网站还是能随随便便爬完的
最新github地址:https://github.com/xietiansheng/spider-demo
(github为maven项目,拉取下来直接运行即可)

先上几张效果图

这里写图片描述
这里写图片描述
这里写图片描述

##需要用到的知识点

  • 网络请求(至于用哪个嘛,看个人喜好,文章用的是okhttp)
  • File文件读写
  • Jsoup框架(html解析器)

##需要的jar包

注意:okhttp内部依赖okio,别忘了同时导入okio


难点

  • 如图(随便弄了个草图)
    难点图
  • 要说技术难点的话,无非就是如何遍历整个网站了,首先,要考虑到的是抓取到一个链接后,这个链接里面肯定还有好几十甚至上百个链接,接下来这几十个链接里面又有链接,链接里面有链接一层一层嵌套,该如何去获取这些链接?

##实现思路

1.链接存储
使用文件操作储存所有链接,至于为什么不用集合存储,据博主了解,写爬虫基本都不用集合去存储数据,原因在于链接多了之后会报内存溢出错误。也就是集合里面存太多东西了,然后还要对它进行查找操作,所以不推荐使用集合进行储存。
2.链接读取
将每次读到的链接存入.txt文本文件中,这里要注意的是每次存入链接的时候要在后面加上rn(换行),也就是让每个链接各占一行,这样有利于后期以行的形式读取链接。
3.链接遍历
①、获取首页链接中的子链接,存入文件中,已行为单位存储。
②、定义一个变量num(默认为-1),用于记录当前读的是第几条链接,每次遍历完一条链接后 判断如果(num<链接文件行数 )则 num++。
③、遍历解析链接的方法,每一次遍历的目标链接等于 文件内的第num行

这样基本就实现了链接的遍历

举个栗子
假设index.html页面内有5个子链接分别对应 ae.html,解析index.html页面后将该页面中的5个链接存入文件中,num++(此时num=0),文件中的15行就分别对应这5个链接,第二次调用读取方法的时候用到的链接就是文件中的第num行,也就是a.html。
接着解析a.html,将a.html中的所有超链接追加进文件中。

上图:
这里写图片描述

图中的遍历方式似乎有点像一个横放着的wifi信号

接下来贴代码:

首先创建两个类
HttpUtil.java (网络请求类,用于获取网页源代码)
Spider.java (爬虫主代码)

###HttpUtil.java 类

import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by XieTiansheng on 2018/3/7.
 */

public class HttpUtil {
    private static OkHttpClient okHttpClient;
    private static int num = 0;
    
    static{
    	okHttpClient = new OkHttpClient.Builder()
    			.readTimeout(1, TimeUnit.SECONDS)
    			.connectTimeout(1, TimeUnit.SECONDS)
    			.build();
    }
    
    
    public static String get(String path){
    	//创建连接客户端
        Request request = new Request.Builder()
                .url(path)
                .build();
        //创建"调用" 对象
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();//执行
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (IOException e) {
        	System.out.println("链接格式有误:"+path);
        }
        return null;
    }
    
}

这个就不多写注释了 百度有一大堆okhttp教程


Spider.java 类

首先定义要爬的网站首页与储存链接的文件对象
:

	public static String path = "http://www.yada.com.cn/";	//雅达公司官网
	public static int num = -1,sum = 0;
	/**
	 * 定义四个文件类(链接存储,图片储存,文件存储,错误链接存储)
	 */
	public static File aLinkFile,imgLinkFile,docLinkFile,errorLinkFile;

解析html页面的方法
:

/**
	 * 
	 * @param path		目标地址
	 */
	public static void getAllLinks(String path){
		Document doc = null;
		try{
			doc = Jsoup.parse(HttpUtil.get(path));
		}catch (Exception e){
			//解析出的错误链接(404页面)
			writeTxtFile(errorLinkFile, path+"rn");	//写入错误链接收集文件
			num++;	
			if(sum>num){	//如果文件总数(sum)大于num(当前读取位置)则继续遍历
				getAllLinks(getFileLine(aLinkFile, num));
			}
			return;
		}
		//获取html代码中所有带有href属性的a标签,和图片
		Elements aLinks = doc.select("a[href]");
		Elements imgLinks = doc.select("img[src]");
		System.out.println("本次抓取的链接:"+path);
		for(Element element:aLinks){
			String url =element.attr("href");
			//判断链接是否包含这两个头
			if(!url.contains("http://")&&!url.contains("https://")){
				//不是则加上	例:<a href="xitongshow.php?cid=67&id=113" />
				//则需要加上前缀	http://www.yada.com.cn/xitongshow.php?cid=67&id=113
				//否则下次解析该链接的时候会报404错误            
				url = Spider.path+url;//网站首页加上该链接
			}
			//如果文件中没有这个链接,而且链接中不包含javascript:则继续(因为有的是用js语法跳转)
			if(!readTxtFile(aLinkFile).contains(url)
					&&!url.contains("javascript")){	
				//路径必须包含网页主链接--->防止爬入别的网站
				if(url.contains(Spider.path)){		
					//判断该a标签的内容是文件还是子链接
					if(url.contains(".doc")||url.contains(".exl")
							||url.contains(".exe")||url.contains(".apk")
							||url.contains(".mp3")||url.contains(".mp4")){
						//写入文件中,文件名+文件链接
						writeTxtFile(docLinkFile, element.text()+"rnt"+url+"rn");
					}else{
						//将链接写入文件
						writeTxtFile(aLinkFile, url+"rn");
						sum++;	//链接总数+1
					}
					System.out.println("t"+element.text()+":t"+url);
				}
			}
		}
		//同时抓取该页面图片链接
		for(Element element:imgLinks){
			String srcStr = element.attr("src");
			if(!srcStr.contains("http://")&&!srcStr.contains("https://")){//没有这两个头
				srcStr = Spider.path+srcStr;
			}
			if(!readTxtFile(imgLinkFile).contains(srcStr)){	
				//将图片链接写进文件中
				writeTxtFile(imgLinkFile, srcStr+"rn");
			}
		}
		num++;
		if(sum>num){    //如果文件总数(sum)大于num(当前读取位置)则继续遍历
			getAllLinks(getFileLine(aLinkFile, num));
		}
	}

该方法用于解析html页面,取到所有链接,存入文件

两个操作文件的方法(读/取)
:

/**
	 * 读取文件
	 * @param file	文件类
	 * @return	文件内容
	 */
	public static String readTxtFile(File file){
		String result = "";		//读取結果
		String thisLine = "";	//每次读取的行
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			try {
				while((thisLine=reader.readLine())!=null){
					result += thisLine+"n";
				}
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 写入内容
	 * @param file	文件类
	 * @param urlStr	要写入的文本
	 */
	public static void writeTxtFile(File file,String urlStr){
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(file,true));
			writer.write(urlStr);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

简单的文件操作方法,用于储存每次解析出来的链接

获取文件中的指定行内容
:

/**
	 * 获取文件指定行数的数据,用于爬虫获取当前要爬的链接
	 * @param file	目标文件
	 * @param num	指定的行数
	 */
	public static String getFileLine(File file,int num){
		String thisLine = "";
		int thisNum = 0 ;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			while((thisLine = reader.readLine())!=null){
				if(num == thisNum){
					return thisLine;
				}
					thisNum++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

这个方法很重要,用于获取文件中的第几条链接

下面是这个类的完整代码
:

package com.xietiansheng.shangmao.cn;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import okio.ForwardingTimeout;

public class Spider {
	
	public static String path = "http://www.yada.com.cn/";	//雅达公司官网
	public static int num = -1,sum = 0;
	/**
	 * 定义四个文件类(链接存储,图片储存,文件存储,错误链接存储)
	 */
	public static File aLinkFile,imgLinkFile,docLinkFile,errorLinkFile;
	/**
	 * 
	 * @param path		目标地址
	 */
	public static void getAllLinks(String path){
		Document doc = null;
		try{
			doc = Jsoup.parse(HttpUtil.get(path));
		}catch (Exception e){
			//接收到错误链接(404页面)
			writeTxtFile(errorLinkFile, path+"rn");	//写入错误链接收集文件
			num++;	
			if(sum>num){	//如果文件总数(sum)大于num(当前坐标)则继续遍历
				getAllLinks(getFileLine(aLinkFile, num));
			}
			return;
		}
		Elements aLinks = doc.select("a[href]");
		Elements imgLinks = doc.select("img[src]");
		System.out.println("开始链接:"+path);
		for(Element element:aLinks){
			String url =element.attr("href");
			//判断链接是否包含这两个头
			if(!url.contains("http://")&&!url.contains("https://")){
				//不是则加上	例:<a href="xitongshow.php?cid=67&id=113" />
				//则需要加上前缀	http://www.yada.com.cn/xitongshow.php?cid=67&id=113
				//否则404
				url = Spider.path+url;
			}
			//如果文件中没有这个链接,而且链接中不包含javascript:则继续(因为有的是用js语法跳转)
			if(!readTxtFile(aLinkFile).contains(url)
					&&!url.contains("javascript")){	
				//路径必须包含网页主链接--->防止爬入别的网站
				if(url.contains(Spider.path)){		
					//判断该a标签的内容是文件还是子链接
					if(url.contains(".doc")||url.contains(".exl")
							||url.contains(".exe")||url.contains(".apk")
							||url.contains(".mp3")||url.contains(".mp4")){
						//写入文件中,文件名+文件链接
						writeTxtFile(docLinkFile, element.text()+"rnt"+url+"rn");
					}else{
						//将链接写入文件
						writeTxtFile(aLinkFile, url+"rn");
						sum++;	//链接总数+1
					}
					System.out.println("t"+element.text()+":t"+url);
				}
			}
		}
		//同时抓取该页面图片链接
		for(Element element:imgLinks){
			String srcStr = element.attr("src");
			if(!srcStr.contains("http://")&&!srcStr.contains("https://")){//没有这两个头
				srcStr = Spider.path+srcStr;
			}
			if(!readTxtFile(imgLinkFile).contains(srcStr)){	
				//将图片链接写进文件中
				writeTxtFile(imgLinkFile, srcStr+"rn");
			}
		}
		num++;
		if(sum>num){
			getAllLinks(getFileLine(aLinkFile, num));
		}
	}
	
	/**
	 * 读取文件内容
	 * @param file	文件类
	 * @return	文件内容
	 */
	public static String readTxtFile(File file){
		String result = "";		//读取結果
		String thisLine = "";	//每次读取的行
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			try {
				while((thisLine=reader.readLine())!=null){
					result += thisLine+"n";
				}
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 写入内容
	 * @param file	文件类
	 * @param urlStr	要写入的文本
	 */
	public static void writeTxtFile(File file,String urlStr){
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(file,true));
			writer.write(urlStr);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取文件指定行数的数据,用于爬虫获取当前要爬的链接
	 * @param file	目标文件
	 * @param num	指定的行数
	 */
	public static String getFileLine(File file,int num){
		String thisLine = "";
		int thisNum = 0 ;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			while((thisLine = reader.readLine())!=null){
				if(num == thisNum){
					return thisLine;
				}
					thisNum++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}
	
	/**
	 * 获取文件总行数(有多少链接)
	 * @param file	文件类
	 * @return	总行数
	 */
	public static int getFileCount(File file){
		int count = 0;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			while(reader.readLine()!=null){	//遍历文件行
				count++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return count;
	}
	
	
	public static void main(String[] args) {
		aLinkFile = new File("D:/Spider/ALinks.txt");
		imgLinkFile = new File("D:/Spider/ImgLinks.txt");	
		docLinkFile = new File("D:/Spider/DocLinks.txt");
		errorLinkFile = new File("D:/Spider/ErrorLinks.txt");
		//用数组存储四个文件对象,方便进行相同操作
		File[] files = new File[]{aLinkFile,imgLinkFile,docLinkFile,errorLinkFile};
		try {
			for(File file: files){
				if(file.exists())	//如果文件存在
					file.delete();	//则先删除
				file.createNewFile();	//再创建
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		long startTime = System.currentTimeMillis();    //获取开始时间
		Spider.getAllLinks(path);	//开始爬取目标内容
		System.out.println(""
				+ "——————————————————爬取结束——————————————————"
				+ "n目标网址:"+path
				+ "n链接总数:"+sum+"条"
				+ "n图片总数:"+getFileCount(imgLinkFile)+"张"
				+ "n文件总数:"+getFileCount(docLinkFile)+"份");
		writeTxtFile(aLinkFile, "链接总数:"+getFileCount(aLinkFile)+"条");
		writeTxtFile(imgLinkFile, "图片总数:"+getFileCount(imgLinkFile)+"张");
		writeTxtFile(docLinkFile, "文件总数:"+getFileCount(docLinkFile)+"份");
		writeTxtFile(errorLinkFile, "问题链接总数:"+getFileCount(errorLinkFile)+"条");
		long endTime = System.currentTimeMillis();    //获取结束时间
		System.out.println("n程序运行时间:" + (endTime - startTime) + "ms");    //输出程序运行时间
	}
}

结束


纯属娱乐
有问题可以给我留言或者在下面评论
可以用于服务端于安卓客户端结合达到想要的效果

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_38777773/article/details/79533393
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢