Commit 8a4ddee7 by ClassmateWang

2021-10-21 11:55 更新\JAVA\网络\4)获取web数.md

parent c110ed6b
# 4)获取web数 # 4)获取web数
...@@ -278,9 +278,259 @@ public class URLConnectionTest { ...@@ -278,9 +278,259 @@ public class URLConnectionTest {
![image-20211019175549567](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019175549567.png) ![image-20211019175549567](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019175549567.png)
## 三、提交表单数据
> 这一节的侧重点在两个:
>
> - 请求与响应的过程
> - **如何编写客户端程序使之与服务器端程序进行交互**
![image-20211020093543331](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211020093543331.png)
这里有两个概念上的很明显的区分,当表单数据提交到HTTP服务器之后,其实是会去**调用服务器端的程序**,而常见的编写服务器端程序的方式有这么几个:
- Java Servlet
- JavaServer Face
- 微软ASP Active Server Pages
- CGI Common Gateway Interface 通用网关接口
<u>这里就体现了,服务器端程序的作用其实是处理表单数据,并生成另一个HTML页,该页会被web服务器发回给浏览器,发回给浏览器的响应页可以包含新的信息或者仅仅只是一个确认,而之后web浏览器会显示响应页。</u>
> 客户端向服务器发送请求到得到响应的过程:
>
> 1. 往往通过两种命令向服务器发送请求:
> - GET
> - 将参数附着在URL的结尾处即可传递数据
> - 每个参数都具有“名字 = 值”的形式,而这些参数之间用& 字符分隔开,参数值遵循下面的规则,使用URL编码模式进行编码:
> - 保留字符A~Z,a~z和0~9 以及.-~_
> - 用+代替所有的空格
> - 将其他字符编码为UTF-8,并将每个字节都编码为%后面紧跟一个两位的十六进制数
> - POST
> - URL的大小有限制,Post用于处理具有大量数据的表单
> - 传递参数的时候并不会将数据附着到URL而是从URLConnection 中获取输出流,并将名/值 写入到输出流中
### 重定向
> 将Post 数据发送给服务器的时候,服务器端程序产生的响应可能是redirect ,后面跟着一个完全不同URL,该URL被用于调用获取实际的信息。
>
> 服务器是可以这样做的,因为这些信息位于他处,**<u>HttpUrlConnection 类在大多数情况下自动的可以处理这种重定向。</u>**
>
> 重定向有两种方式:
>
> 1. 自动重定向配置
> 2. 必须手动进行重定向
如果**Cookie**需要在重定向中从从一个站点发送给另一个站点,就可以**配置一个全局的cookie 处理器**
```java
CookieHandler.setDefault(new CookieManager(null,CookiePolicy.ACCEPT.ALL));
```
重定向往往是自动处理的,但是有些情况下,客户端需要自己完成重定向,比如说在http 和 https之间的自动重定向因为安全原因而不被支持,还有一些细微的原因:比如说邮政编码服务器在User-agent 中包含字符串Java 时无法工作
> **User Agent**中文名为**用户代理**,是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA。它是一个特殊字符串头,**是一种向访问网站提供你所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识**。通过这个标识,用户所访问的网站可以显示不同的排版从而为用户提供更好的体验或者进行信息统计;例如用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的 UA来判断的。UA可以进行伪装。
在这些情况下,可以人工实现重定向。在连接到服务器之前,将自动重定向关闭:
connection.setInstanceFollowRedirects(false);
发送请求后获取响应码:
int responseCode = connection.getResponseCode();
检查它是不是以下的值之一
HttpURLConnection.HTTP_MOVED_PERM
HttpURLConnection.HTTP_MOVED_TEMP
HttpURLConnection.HTTP_MOVED_OTHER
如果是这些值之一,就获取Location 响应头,以获取重定向的URL,然后断开连接,建立到新的URL连接上:
```java
String location = connection.getHeaderField("Location");
if(location != null){
URL base = connection.getURL();
connection.disConnect();
connection = (HttpURLConnection) new URL(base,location).openConnection();
...
}
```
当需要从某个web 站点查询信息时,该程序所展示出的处理技术就会显得很有用(比如说用Java 编写爬虫的时候),只需要找出发送的参数 ,然后从回复信息中剔除HTML 和其他不必要的信息即可
示例:
```java
package Socket.post;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
/**
* @BelongsProject: JavaLearnAll
* @BelongsPackage: Socket.post
* @Author: Wang Haipeng
* @CreateTime: 2021-10-21 08:47
* @Description: 编写客户端与服务器交互,向服务器发送请求并输出
*/
public class PostTest {
public static void main(String[] args) throws IOException {
/*获取数据配置文件的*/
String propsFilename = args.length >0 ? args[0] : "F:\\Code\\JavaLearnAll\\src\\main\\java\\Socket\\post\\post.properties";
Properties props = new Properties();
try(InputStream in = Files.newInputStream(Paths.get(propsFilename))) {
props.load(in);
} catch (IOException e) {
e.printStackTrace();
}
/*访问的URL*/
String urlString = props.remove("url").toString();
/*用户端浏览器类型*/
Object userAgent = props.remove("User-Agent");
/*重定向次数*/
Object redirects = props.remove("redirects");
/*设置全局的Cookie 处理器*/
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
String result = doPost(new URL(urlString),props,
userAgent == null ? null : userAgent.toString(),
redirects == null ? -1 : Integer.parseInt(redirects.toString()));
System.out.println(result);
}
/**
*
* @param url 用于请求服务器的URL
* @param nameValuePairs 连接/请求信息
* @param userAgent 用户客户端类型
* @param redirects 响应次数
* @return 打印服务器的请求
* @Description: 向服务器发送请求,如果有重定向则处理重定向,如果没有重定就将服务器的的响应数据打印到控制台上
*
*/
public static String doPost(URL url, Map<Object,Object> nameValuePairs, String userAgent, int redirects) throws IOException {
/*所有满足URL类的协议都可以通过URLConnection 来封装请求或者响应的对象*/
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (userAgent != null){
connection.setRequestProperty("User-agent",userAgent);
}
if (redirects >0){
/*HttpURLConnection.setFollowRedirects 设置所有的http 连接自动处理重定向*/
/*设置本次连接自动处理重定向*/
connection.setInstanceFollowRedirects(false);
}
/*设置请求头的属性*/
connection.setRequestProperty("Content-Type","application/json");
/*HTTP协议要求必须发送下请求的的长度*/
/*
这一段设置请求长度的代码必须放到PrintiWriter 建立之前,因为PrintWriter 对象建立就一定会建立连接
而请求头必须在PrintWrter 前发送给服务器
*/
connection.setRequestProperty("Content-Length","1024");
/*开启响应输出*/
connection.setDoOutput(true);
/*将数据简单封装为Json 数据传递给服务器*/
try(PrintWriter out = new PrintWriter(connection.getOutputStream())) {
StringBuilder request = new StringBuilder();
request.append("{");
for (Map.Entry<Object,Object> pair : nameValuePairs.entrySet()){
String name = pair.getKey().toString();
String value = pair.getValue().toString();
String result = '\"'+name+'\"'+':'+'\"'+value+'\"'+',';
request.append(result);
}
request.append("}");
request.deleteCharAt(request.length()-2);
System.out.println(request.toString());
out.print(request.toString());
}
String encoding = connection.getContentEncoding();
if (encoding == null){
encoding = "UTF-8";
}
/*
* 处理无法进行自动重定向的情况
* */
if (redirects > 0){
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
){
/*重定向的目标位置会放置在响应头的Location 字段*/
String location = connection.getHeaderField("Location");
if (location != null){
/*返回当前的Connection 建立连接的URL对象*/
URL base = connection.getURL();
/*断开连接*/
connection.disconnect();
return doPost(new URL(base,location), nameValuePairs, userAgent, redirects-1);
}
}
}else if (redirects == 0){
throw new IOException("Too many redirects!");
}
/**
* 处理从服务器读数据过程中服务器端出错的情况
* 当读取响应过程中服务器端出现错误,那么在调用connection.getInputStream()
* 时会抛出一个错误页面,为了捕获这个错误页,可以通过getErrorStream方法:
*/
StringBuilder response = new StringBuilder();
try(Scanner in = new Scanner(connection.getInputStream(),encoding)) {
while (in.hasNextLine()){
response.append(in.nextLine());
response.append("\n");
}
}catch(IOException e){
InputStream err = connection.getInputStream();
/*如果服务器不存在错误的响应页,那么就抛出这个错误客户端不去处理,一般会有响应页*/
if (err == null){
throw e;
}
try(Scanner in = new Scanner(err)) {
response.append(in.nextLine());
response.append("\n");
}
}
return response.toString();
}
}
```
> HttpUrlConnection 如何区分是Get 请求还是Post 请求,是否开启输出流,
>
> ![image-20211021114913644](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211021114913644.png)
>
> 开启输出流后会将Get 请求修改为Post 请求,默认是Get 请求
## 三、涉及API ## 三、涉及API
![image-20211019181137260](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019181137260.png) ![ ](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019181137260.png)
![image-20211019181151159](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019181151159.png) ![image-20211019181151159](https://wangnotes.oss-cn-beijing.aliyuncs.com/notesimage/image-20211019181151159.png)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment