原生Java使用Socket制作爬虫重复造轮子系列-一步步带-1

10 篇文章 0 订阅
2 篇文章 0 订阅

Hello,大家好,我是Shendi, 这次给大家带来爬虫系列, 网上大部分都是使用了框架的,这次我用单纯的Socket来制作爬虫

纯手码,如果有地方有点小错误请指出

先说一下思路:既然要做爬虫,那么就需要 知道http协议

先使用Socket创建TCP连接

Socket socket = new Socket("www.baidu.com",80);

就这一行代码,我们就将我们的socket指向百度的主机,但因为是http协议的,我们乱发数据没用,所以服务端不会返回任何数据

所以我们需要告诉那边的服务端, 我要访问你, 也就是获取输出流OutputStream发送指定的协议内容

输出指定的协议内容来与服务端进行通信

OutputStream output = socket.getOutputStream();
output.write("GET / HTTP/1.1\r\n".getBytes());
output.write(("HOST: www.baidu.com\r\n").getBytes());
output.write("\r\n".getBytes());

解释一下上面输出的数据

GET / HTTP/1.1\r\n 固定的(GET请求 获取/路径,关于访问子项目以及类型的下次说)

HOST: www.baidu.com\r\n 代表访问的主机

注意,我们访问的只能是根域名或者直接是ip 不能像www.baidu.com/index.php 这个地方只能填ip或根域名

\r\n换行 固定写法

我们发送指定的数据过去,服务端就会回应数据,我们现在就需要接收数据 使用InputStream

接收服务端返回的数据

InputStream input = socket.getInputStream();
int len = -1;
byte[] data = new byte[1024];
while ((len = input.read()) != -1) {
    System.out.print(new String(data,0,len));
}

这样我们就已经看到了百度的信息头与信息数据了 但是程序没结束,最后报超时错,这个下面会讲

思路大概就这样,接下来开始写项目(算是对自己的一次总结吧)

创建一个GetHttp类(运行测试类)

public class GetHttp {
	public static void main(String[] args) throws MalformedURLException, IOException {
		HttpSocketUtils socket = new HttpSocketUtils("www.hackshendi.club/App",80);
		HttpUtils http = socket.openHttpConn();
		System.out.println("------状态");
		System.out.println(http.getState());
		System.out.println("------状态信息");
		System.out.print(http.getStateInfo());
		System.out.println("------头");
		System.out.print(http.getHead());
		System.out.println("------body");
		System.out.print(http.getBody());
		socket.close();
	}
}

创建一个接口 Protocol 协议接口(后面协议可能不止一种)

/**
 * -协议接口
 * @author <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>Shendi</a>
 */
public interface Protocol {
    /**
	 * -打开http连接
	 * @return 返回一个HttpUtils
	 */
	public HttpUtils openHttpConn();
}

接下来创建一个Socket 工具类 HttpSocketUtils (一步步来 下方代码讲解)

/**
 * -Socket用于Http的封装工具类 只要建立此类连接就已经打开
 * @author <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>Shendi</a>
 */
public class HttpSocketUtils implements Protocol {
	private Socket socket;
	private InputStreamUtils input;
	private OutputStream output;
	private String host;// 主机名
	private int port;// 端口
	private int outTime;// 超时
	private Protocol protocol;// 协议
	private String subitem = "";//子项
	
	/**
	 * -使用ip+端口建立连接
	 * @param host
	 * @param port
	 */
	public HttpSocketUtils(String host, int port) {
		// 默认五秒的超时时间
		this("GET",host, port, 5000);
	}

	/**
	 * 使用ip+端口+请求类型建立连接
	 * @param reqType GET or POST
	 * @param host
	 * @param port
	 */
	public HttpSocketUtils(String reqType,String host, int port) {
		// 默认五秒的超时时间
		this(reqType,host, port, 5000);
	}
	
	/**
	 * -使用ip+端口+超时时间+请求类型建立连接
	 * @param reqType GET or POST
	 * @param host
	 * @param port
	 * @param outTime
	 */
	public HttpSocketUtils(String reqType,String hostArg, int port, int outTime) {
		try {
			this.host = hostArg;
			this.port = port;
			this.outTime = outTime;
			//判断是否有子项目 有则获取子项目
			int index = host.indexOf('/');
			if (index != -1) {
				subitem = host.substring(index,host.length());
				this.host = host.substring(0,index);
			}
			//获取连接
			socket = new Socket(host, port);
			socket.setSoTimeout(outTime);
			// 初始化协议
			protocol = new Protocol() {
				public HttpUtils openHttpConn() {
					try {
						//输出http协议
						output = socket.getOutputStream();
						output.write((reqType+" " + subitem + "/ HTTP/1.1\r\n").getBytes());
						output.write(("HOST: "+ host +" \r\n").getBytes());
						output.write("\r\n".getBytes());
						//解析 获取结果
						input = new InputStreamUtils(socket.getInputStream());
						return new HttpUtils(input);
					} catch (IOException e) {
						e.printStackTrace();
						WebLog.printErr(e.getMessage());
					}
					return null;
				}
			};
		} catch (IOException e) {
			e.printStackTrace();
			WebLog.printErr(e.getMessage());
		}
	}

	/**
	 * -打开http连接根据地址
	 * @param host 域名或者ip
	 * @param port 端口
	 * @return 返回Http工具类 如果为null则代表获取失败
	 */
	public HttpUtils openHttpConn() {
		return protocol.openHttpConn();
	}

	public void close() {
		try {
			if (input != null)
				input.close();
		} catch (IOException e) {
			e.printStackTrace();
			WebLog.printErr(e.getMessage());
		} finally {
			try {
				if (output != null)
					output.close();
			} catch (IOException e) {
				e.printStackTrace();
				WebLog.printErr(e.getMessage());
			} finally {
				try {
					if (socket != null)
						socket.close();
				} catch (IOException e) {
					e.printStackTrace();
					WebLog.printErr(e.getMessage());
				}
			}
		}
	}
	
	public String getHost() {
		return host;
	}
	
	public int getOutTime() {
		return outTime;
	}	
	
	public int getPort() {
		return port;
	}
}

既然是Socket工具类,那么就肯定有Socket操作,所以我们需要创建Socket,我写在了构造函数里,实现了接口

主要的函数就是openHttpConn函数了,获取一个HttpUtils的对象, 这个调用指向的是构造 函数里实现里写的那个方法

 在此实现里,我做了url中带子项路径的操作,这个类主要是用于获取连接+输出信息头给服务端,如果不懂请详看上面

接下来就是获取数据操作的类了 HttpUtils;WebLog 类为日志打印类

/**
 * -Http协议的工具类 用于搭建与http通信
 * @author <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>Shendi</a>
 */
public class HttpUtils {
	private InputStreamUtils input;
	private String head;//数据头
	private String body;//数据
	private String state;//状态
	private String stateInfo;//状态信息
	public HttpUtils(InputStreamUtils input) throws IOException {
		if (input == null) {
			throw new IOException("input is null");
		}
		this.input = input;
		//初始化 获取数据头 数据
		Initialize();
	}

	private void Initialize() throws IOException {
		if (input == null) {
			throw new IOException("input is null");
		}
		//获取状态
		StringBuffer dataHead = new StringBuffer();
		String data = null;
		//第一行为状态
		data = input.readLine();
		if (data != null) {
			dataHead.append(data);
			String[] datas = data.split(" ");
			if (datas.length > 1) {
				state = datas[1];
				if (datas.length > 2) {
					stateInfo = datas[2].substring(0,datas[2].length()-2);
				}
			}
			//快速释放资源
			datas = null;
		}
		int dataBodySize = -1;
		//获取数据头
		while ((data = input.readLine()) != null) {
			dataHead.append(data);
			//取得长度后 获取数据
			String[] map = data.split(":");
			if (map.length > 0) {
				String key = map[0].trim();
				if (map.length > 1) {
					String value = map[1].trim();
					//到了数据头的尾部
					if ("Content-Length".equals(key)) {
						//在读一行 跳出
						input.readLine();
						dataBodySize = Integer.parseInt(value);
						break;
					}
				}
			} else {
				throw new IOException("no content length");
			}
		}
		//去掉换行符
		dataHead.substring(0,dataHead.length()-2);
		if (dataHead != null && !"".equals(dataHead.toString())) {
			head = dataHead.toString();
		}
		if (dataBodySize != -1) {
			//读取数据内容
			byte[] dataBody = new byte[dataBodySize];
			input.read(dataBody);
			body = new String(dataBody);
		}
		
	}
	
	public String getHead() {
		return head;
	}
	
	public String getBody() {
		return body;
	}
	/**
	 * -状态
	 * @return
	 */
	public String getState() {
		return state;
	}
	/**
	 * -状态的信息
	 * @return
	 */
	public String getStateInfo() {
		return stateInfo;
	}
}

这个类用于InputStream操作,将数据处理好,爬出整个网页的html内容  

然后就是我造的轮子 InputStreamUtils类

/**
 * -读取的封装流 增强操作
 * @author <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>Shendi</a>
 */
public class InputStreamUtils {
	private InputStream input;
	public InputStreamUtils(InputStream input) throws IOException {
		if (input == null) {
			throw new IOException("input is null");
		}
		this.input = input;
	}
	
	/**
	 * -调用input的read方法
	 */
	public int read() throws IOException {
		return input.read();
	}
	/**
	 * -调用input的read方法
	 */
	public int read(byte[] b) throws IOException {
		return input.read(b);
	}
	
	/**
	 * -读取一行
	 * @return 如果没有数据 则返回null
	 * @throws IOException 
	 */
	public String readLine() throws IOException {
		int value = -1;
		//有效数据长度
		int len = 0;
		byte[] data = new byte[1024];
		while ((value = input.read()) != -1) {
			//如果数组长度不够则增长
			if (len >= data.length) {
				byte[] temp = data;
				data = new byte[len+1024];
				System.arraycopy(temp,0,data,0,temp.length);
			}
			data[len] = (byte) value;
			len++;
			//如果读到\n则读取完这一行
			if ((char)value == '\n') {
				break;
			}
		}
		//如果数组长度为0 则返回null
		if (len == 0) {
			return null;
		}
		return new String(data,0,len);
	}
	
	public void close() throws IOException {
		input.close();
	}
	
}

这个类主要是提供一个readLine方法,没啥好讲的

就这样,一个简单的爬虫写好了,能够获取数据头,数据体,状态,状态信息以及访问子项目等

 如果对你有帮助的话请点个赞吧~ 关注我,下节教你爬资源

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Java的HttpURLConnection类来发送application/x-www-form-urlencoded请求。以下是一个示例代码: ```java import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; public class HttpUrlConnectionExample { public static void main(String[] args) throws Exception { String url = "http://www.example.com"; URL obj = new URL(url); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); // 设置请求方法为POST con.setRequestMethod("POST"); // 设置请求头 con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 设置POST参数 String title = "test"; String sub1 = "1"; String sub2 = "2"; String sub3 = "3"; String urlParameters = "title=" + URLEncoder.encode(title, "UTF-8") + "&sub%5B%5D=" + URLEncoder.encode(sub1, "UTF-8") + "&sub%5B%5D=" + URLEncoder.encode(sub2, "UTF-8") + "&sub%5B%5D=" + URLEncoder.encode(sub3, "UTF-8"); // 发送POST请求 con.setDoOutput(true); DataOutputStream wr = new DataOutputStream(con.getOutputStream()); wr.writeBytes(urlParameters); wr.flush(); wr.close(); // 获取响应结果 int responseCode = con.getResponseCode(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); // 打印响应结果 System.out.println(response.toString()); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HackShendi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值