java网络编程

Okhttp3工具

  • 需要安装依赖:
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
 <groupId>com.squareup.okhttp3</groupId>
 <artifactId>okhttp</artifactId>
 <version>4.1.0</version>
</dependency>

抓取网站内容

在这里插入图片描述

//实例化Okhttp3
OkHttpClient okHttpClient = new OkHttpClient();
//实例化Request对象
Request request = new Request.Builder().url(url).build();
//调用构建对象
Call call = okHttpClient.newCall(request);
//执行调用
String result = call.execute().body().string();
    

post操作提交表单数据

  • 通过Map<String,String>存放表单

  • 需要构建一个FormBody对象来存放表单

Builder builder = new FormBody.Builder();
//通过Map的遍历将表单信息存入builder,再通过FormBody对象提交
for(){
    builder.add("", "");
}
FormBody formBody = builder.build();

Request request = new Request.Builder().url(url).post(formBody).build();

POST JSON数据

  • 添加依赖
<!-- JSON 操作库 -->
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.62</version>
</dependency>

JOSN数据是通过RequestBody对象提交的

在这里插入图片描述

//创建JOSN数据类型,并定义为常量
private static final MediaType JOSN_TYPE = MediaType.prase("application/json; charset=utf-8");
//将数据转化成为JSON格式的字符串
String prarm = JSON,toJSONString(Map<String,String>);
RequestBody requestBody = RequestBody.create(JOSN_TYPE,prarm);

Request request = new Request.Builder().url(url).post(RequestBody).build();

Response获取响应码

获取响应码的语句为:

call.execute().code()

我们再读取相应内容时已经执行过一次请求,不能重复执行,因此代码应该优化为:

import okhttp3.Response;

// 执行请求并储存为Response对象
Response rep = call.execute();
// 获取响应状态码
int code = rep.code();
// 获取响应内容
String content = rep.body().string();

Response - 非文本文件

当我们调取的api是图片或表格等二进制编码内容,我们获取的内容也应当使用byte[]类型来存取。

在这里插入图片描述

Response - JSON反序列化

把爬取到的JSON对象通过

JSON.parseObject(content,Mapclass)

转化成Map对象

在这里插入图片描述

解析JSON对象

当我们将JSON对象转化为Map对象后,就可以通过解析数据获取我们想要的value值。{}解析为Map对象并获取键值,[]对象解析为list并获取索引。

{
  "code": 0,
  "data": {
    "ip": "117.89.35.58",
    "country": "中国",
    "area": "",
    "region": "江苏",
    "city": "南京",
    "county": "XX",
    "isp": "电信",
    "country_id": "CN",
    "area_id": "",
    "region_id": "320000",
    "city_id": "320100",
    "county_id": "xx",
    "isp_id": "100017"
  }
}

多次取出嵌套的Map对象(取出城市名)

Map contentObj = JSON.parseObject(content, Map.class);
Map dataObj = (Map)contentObj.get("data");
String city = (String)dataObj.get("city");
  • Map与List对象结合

    {
      "code": 0,
      "data": {
        "keyword": "西海情歌",
        "priority": 0,
        "qc": [
          
        ],
        "semantic": {
          "curnum": 0,
          "curpage": 1,
          "list": [],
          "totalnum": 0
        },
        "song": {
          "curnum": 50,
          "curpage": 1,
          "list": [
            {
              "albumid": 14880,
              "albummid": "0024RXz94OSKCa",
              "albumname": "刀郎Ⅲ",
    

取出多层嵌套(albumname)

在这里插入图片描述

addHeader

1.User-Agent

​ 有些时候我们在浏览器中输入IP地址详情信息查询的API是可以查看到结果的,但是在Java程序中调用却不行,这是因为一些大型的网站会对请求进行校验,如果API服务器认为请求不是来自于真正的浏览器,就会拒绝掉。

​ 判断请求是否来源于一个真正的浏览器,需要通过HTTP消息头(Header)中取得的User-Agent信息来判断,其作用是告诉服务端浏览器类型,版本等信息。

点击阅读

HTTP 标头(header)

User-Agent

模拟一个win7+chrome的环境:

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1

使用时只需在构件Request对象时调用addHeader()方法即可:

Request request = new Request.Builder()
    .url(url)
    .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1")
    .build();
2.Referer

​ 有些时候我们申请图片之类的内容,网站会有更加严格的检查:图片防盗链。

在这里插入图片描述

​ 图片无法正常显示,是因为在网页请求中,Http的Headers中存在Referer信息,表示请求的来源,当网站发现其并不是自己网站的内部来源时便会拒绝访问。因此我们可以通过调用addHeader()方法添加Referer信息模仿网站访问来源:

Request request = new Request.Builder()
    .url(url)
    .addHeader("Referer", "https://xxx.com/")
    .build();

可以通过浏览器的开发者工具寻找Referer值

3.Host

​ Host表示当前请求的域名。在一些特殊情况下(如url并不写域名而是写IP地址进行请求的时候),设置Host就非常有用。

点击阅读:Host文档

​ Host同样是Headers的数据字段之一:

Request request = new Request.Builder()
    .url(url)
    .addHeader("Host", "www.douban.com")
    .build();

注意:Host的值是不带协议头的域名

下载文件

​ 我们请求的图片、excel等文件无法在console上进行查看,所以请求结果必须写入文件。

​ 写入文件的步骤:

创建文件对象
   ↓
写入内容
   ↓
关闭写入操作(确保文件同时只能被一个程序写)
  • 写入文本文件

    import java.io.File;
    import java.io.FileWriter;
    
    // 文件对象
    File file = new File("foo.txt");
    
    // 写入内容(数据类型为String content)
    FileWriter fileWritter = new FileWriter(file.getName());
    fileWritter.write(content);
    
    // 关闭
    fileWritter.close();
    

File是文件类,FileWritter是文件写入内容类。

  • 写入二进制文件(图片,表格等)

    import java.io.File;
    import java.io.FileOutputStream;
    
    // 文件对象(excel表格类型)
    File file = new File("china-city-list.xlsx");
    
    // 写文件(数据类型为 byte[] data)
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(data);
    
    // 必须刷新并关闭
    fos.flush();
    fos.close();
    

解析excel

​ 正常情况下我们下载的excel表格需要office软件才能读取,但其实Java也有库(easyexcel)可以操作excel文件。

  • 导入依赖

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>easyexcel</artifactId>
      <version>3.1.1</version>
    </dependency>
    
  • 调用库

    excel文件是多sheet模式的,每个sheet都是一个表格,表格又分为行和列。

    所以数据的解析路径为:sheet -> 行-> 列

    例如我们解析一个xzq_201907.xlsx文件:

    import com.alibaba.excel.EasyExcel;
    import java.util.Map;
    import java.util.List;
    
    // 读取第一个sheet
    List<Map<Integer, String>> sheetDatas = EasyExcel.read("xzq_201907.xlsx").sheet(0).doReadSync();
    // List 中每个元素表示一行
    for (Map<Integer, String> rowData : sheetDatas) {
      // Map 中用序号指代每一列
      for (Integer index : rowData.keySet()) {
        // 列值
        String columnValue = rowData.get(index);
      }
    }
    

​ 首先调用Easyexcel,read()方法传入文件名称,调用sheet()方法设置参数为0(第一个sheet)最后的doReadSync()表示同步方式读取文件。将读取的内容返回到一个list集合中,集合中存放的map信息便是表格的一行数据(key是序列号,value是单元格值)如下图:

  • 自动转换为类

    我们使用Map存放每一列数据是因为我们不知道文件每一列的含义,当我们之道文件每一列的含义时,使用自定义类来表示会更加直观。

import com.alibaba.excel.EasyExcel;
import java.util.List;

// 读取第一个sheet
List<DemoData> sheetDatas = EasyExcel.read("xzq_201907.xlsx").head(DemoData.class).sheet(0).doReadSync();


// 属性定义的顺序必须与列顺序保持一致
public class DemoData {
  private String code1;
  private String city1;
  private String code2;
  private String city2;
  private String code3;
  private String city3;
    //返回值为List<DemoData.class>
}

关系图如下:

在这里插入图片描述

当我们想要输出内容的时候,便可以将我们存储的List转化为JSON对象输出。

List<DemoData> sheetDatas = EasyExcel.read("xzq_201907.xlsx").head(DemoData.class).sheet(0).doReadSync();

for(DemoData romdata:sheetDatas){
    System.out.println(JSON.toJSONString(romdata));
}

使用cookie完成登录

​ 当我们访问的API。网页需要登录才能访问时,我们便需要使用Cookie。

​ 下面以豆瓣为例,使用Java程序读取“我的豆瓣”页面内容。

在这里插入图片描述

​ 在我们登录后,来到“我的豆瓣”页面通过开发者工具即可获取登陆后的cookie。

对谷歌开发者工具不熟悉的可以点击查看使用教程

​ 把cookie拷贝下来后放到一个cookie.txt文件中,之后我们需要编码把cookie信息字符串转换成为Map类型的数据。(需要使用工具类)

在这里插入图片描述

使用session完成登录

​ 因为cookie是临时的,而且是存放在浏览器客户端的,登陆以后还想以登陆状态与服务器通讯,比较麻烦。

​ 这就要通过在程序中使用Session对象来解决问题。下面通过模拟登录时光网来学习应用Session对象。

浏览器中输入``http://www.mtime.com/` 打开时光网,点击右上角的登录按钮;

如果没有看到输入框,可以点击角落电脑图标切换:

时光网1

弹出登录对话框。

时光网1

或者直接 点此打开注册与登录页面,并点击“会员登录”

时光网2

如果没有注册的,先注册,再退出登录,并重复此步骤。

如果遇到电脑无法注册时光网的情况,麻烦下载时光网APP,进行注册。

收集信息

然后打开谷歌浏览器的开发者工具,如图所示,Preserve log 必须勾选。

对谷歌开发者工具不熟悉的,可以点击查看 使用教程

关键点

输入用户名和密码后,在右侧抓取到的网络请求中,(先打开开发者工具再登录)找到登录请求 API

hhttps://front-gateway.mtime.com/user/user/login.api

在表头中可以获取Header信息,后面的载荷中可获取我们的账号密码。拥有这些信息后便可以开始代码编写。

  • addHeader的信息都可以在表头复制,通过提交表单的方式把账号密码提交(注意获取的url是API而不是网站)。
 public static void main(String[] args) {
    PageLoginer asker = new PageLoginer();

    // 完成登录
    String url = "https://front-gateway.mtime.com/user/user/login.api";
    // 登录表单数据
    Map<String, String> formData = new HashMap();
    formData.put("name", "13938713947");
    formData.put("password", "2f093dd394eb131f41e01a31d6ce874b");

    String content = asker.postContent(url, formData);
    System.out.println("login result:");
    System.out.println(content);
 }
}
复用Session

​ 在我们登陆成功以后,在想要进入某些页面只需要复用Session对象再次发出请求即可。

​ 在下列匿名内部类代码中,我们不再将OkHttpClient定义为postContent()方法的变量,而是改为类变量private static final OkHttpClient okHttpClien,目的就是在整个类中使用同一个okHttpClient执行HTTP请求,提升效率。

// 用 CookieJar 实现 cookie 的存储,便于登录后请求其它 URL 可以复用
  private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
      .cookieJar(new CookieJar() {
        private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
          cookieStore.put("mtime.com", cookies);
          System.out.println("[saveFromResponse]url.host()=" + url.host());
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
          System.out.println("[loadForRequest]url.host()=" + url.host());
          List<Cookie> cookies = cookieStore.get("mtime.com");
          return cookies != null ? cookies : new ArrayList<>();
        }
      })
      .build();

​ 当我们需要再申请页面时,我们不需要重复执行excute,只需要把不同的参数Request传入就行。因此我们又进行了一次重构,把执行过程封装成了一个方法。完整代码如下:

package com.youkeda.test.http;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.FormBody.Builder;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;

public class PageLoginer {

  // 用 CookieJar 实现 cookie 的存储,便于登录后请求其它 URL 可以复用
  private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
      .cookieJar(new CookieJar() {
        private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
          cookieStore.put("mtime.com", cookies);
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
          List<Cookie> cookies = cookieStore.get("mtime.com");
          return cookies != null ? cookies : new ArrayList<>();
        }
      })
      .build();

  /**
   * 向指定的 URL 提交数据,并返回提交后的结果
   */
  public String postContent(String url, Map<String, String> formData) {
    //post方式提交的数据
    Builder builder = new FormBody.Builder();
    // 放入表单数据
    for (String key : formData.keySet()) {
      builder.add(key, formData.get(key));
    }
    // 构建 FormBody 对象
    FormBody formBody = builder.build();
    // 指定 post 方式提交FormBody
    Request request = new Request.Builder()
        .url(url)
        .post(formBody)
        .addHeader("User-Agent",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36")
        .addHeader("Referer",
            "http://www.mtime.com/")
        .addHeader("Host", "front-gateway.mtime.com")
        .build();

    return doExcute(request);
  }

  /**
   * 根据输入的url,读取页面内容并返回
   */
  public String getContent(String url) {
    // 定义一个request
    Request request = new Request.Builder()
        .url(url)
        .addHeader("User-Agent",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36")
        .addHeader("Referer","http://my.mtime.com/")
        .build();

    return doExcute(request);
  }

  private String doExcute(Request request) {
    // 返回结果字符串
    String result = null;
    try {
      result = okHttpClient.newCall(request).execute().body().string();
    } catch (IOException e) {
      // 抓取异常
      System.out.println("request " + request.url() + " error . ");
      e.printStackTrace();
    }

    return result;
  }

  public static void main(String[] args) {
    PageLoginer asker = new PageLoginer();

    // 完成登录
    String url = "https://front-gateway.mtime.com/user/user/login.api";
    // 登录表单数据
    Map<String, String> formData = new HashMap();
    formData.put("name", "13938713947");
    formData.put("password", "2f093dd394eb131f41e01a31d6ce874b");

    String content = asker.postContent(url, formData);
    System.out.println("login result:");
    System.out.println(content);

    // 请求个人设置页面
    String myUrl = "http://my.mtime.com/personal/personInfo";
    String content1 = asker.getContent(myUrl);
    System.out.println("login result:");
    System.out.println(content1);


  }
}

使用Java通过邮箱发送文件

  • 添加依赖
<dependency>
 <groupId>javax.mail</groupId>
 <artifactId>mail</artifactId>
 <version>1.4</version>
</dependency>
package com.youkeda.email;
import java.security.Security;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Mail {
    public static void main(String[] args) {
        try {
            final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

            //配置邮箱信息
            Properties props = System.getProperties();
            //邮件服务器
            props.setProperty("mail.smtp.host", "smtp.qq.com");
            props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
            props.setProperty("mail.smtp.socketFactory.fallback", "false");
            //邮件服务器端口
            props.setProperty("mail.smtp.port", "465");
            props.setProperty("mail.smtp.socketFactory.port", "465");
            //鉴权信息
            props.setProperty("mail.smtp.auth", "true");
            //建立邮件会话
            Session session = Session.getDefaultInstance(props, new Authenticator() {
                //身份认证
                protected PasswordAuthentication getPasswordAuthentication() {
                    //1.账户 授权码
                    return new PasswordAuthentication("854373800@qq.com", "ejomnxpjpsaabebi");
                }
            });
            //建立邮件对象
            MimeMessage message = new MimeMessage(session);
            //设置邮件的发件人
            message.setFrom(new InternetAddress("854373800@qq.com"));
            //2.设置邮件的收件人
            message.setRecipients(Message.RecipientType.TO, "854373800@qq.com");
            //设置邮件的主题
            message.setSubject("通过javamail发出!!!");
            //文本部分
            message.setContent("文本邮件测试", "text/html;charset=UTF-8");
            message.saveChanges();
            //发送邮件
            Transport.send(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值