文章目录
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,Map。class)
转化成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信息来判断,其作用是告诉服务端浏览器类型,版本等信息。
点击阅读
模拟一个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/` 打开时光网,点击右上角的登录按钮;
如果没有看到输入框,可以点击角落电脑图标切换:
弹出登录对话框。
或者直接 点此打开注册与登录页面,并点击“会员登录”
如果没有注册的,先注册,再退出登录,并重复此步骤。
如果遇到电脑无法注册时光网的情况,麻烦下载时光网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();
}
}
}