好久没更新了,今天办完房子装修手续,有点空咱们更新一下。
今天更新的内容是以前工作中采集数据的时候遇到的,目前我遇到的通过EventSource方式给前端提供数据的方式比较少,只见过这一种但是这种方式感觉还是有很多优势。
我们开始之前先看一下这几个问题。
- 数据爬取遇到EventStream是个什么东西?
在一次采集数据的过程中遇到这样一幕,一般我们的请求都是http,请求结束以后就释放连接,耗时也很低,大部分我们很容易发现调用的接口就直接获取数据了,但是我在本次采集中没有找到相关的数据接口,这就有些纳闷了,我就想她是如何提供数据的呢?接着我想他肯定是用了websocket或dwr技术推送给前端数据,我就查看相关页面源码最后也没找到,打开调试以后发现有一个特别耗时的链接,引起了我的注意。我就跟进去了,如下图:
EventSource方式获取数据
‘
正常http请求

看到这里我发现这里的返回是怎么不想其他的请求,我找了个http链接对比了一下发现返回内容在EventStream 中,恩这次采集果然和以前的都不通,以前要么是数据接口,要么解析页面,要么接通websocket,这次来了个EventStream ,在这之前我确实没见过,于是乎我网上开始百度了一下原来是html5里的EventSource。w3cshool介绍如下:
HTML5 服务器发送事件(Server-Sent Events)
HTML5 服务器发送事件(server-sent event)允许网页获得来自服务器的更新。
Server-Sent 事件 - 单向消息传递
Server-Sent 事件指的是网页自动获取来自服务器的更新。
以前也可能做到这一点,前提是网页不得不询问是否有可用的更新。通过服务器发送事件,更新能够自动到达。
例子:Facebook/Twitter 更新、股价更新、新的博文、赛事结果等。
通过描述我们可以知道他可以接受服务器向客户端推送消流,即完成数据的推送,那么数据推送我们可以用轮询、websocket、wdr、Comet, Ajax推送, 反向Ajax, HTTP流等等方式实现,SSE是不同的技术,Server-Sent的特性是单项接收,自动接收,与其他接收方式都不同。
- EventSource与websocket有何区别?
他们都能实现服务端向客户端推送数据,SSE用于web应用程序刷新数据,不需要用户做任何动作。WebSockets是实现服务端更加复杂的技术,但它是真的全双工socket, 服务端能推送数据到客户端,客户端也能推送数据回服务端。SSE工作于存在HTTP/HTTPS协议,支持代理服务器与认证技术。SSE是文本协议你能轻易的调试它。如果你需要发送大部二进制数据从服务端到客户端,WebSocket是更好的选择。如果要双向交互也选用websocket较好。w3cshool中描述,获取股价等信息,显然EventSource是更好的选择。
- ebSocket基于TCP协议,EventSource基于http协议。
- EventSource是单向通信,而websocket是双向通信。
- EventSource只能发送文本,而websocket支持发送二进制数据。
- 在实现上EventSource比websocket更简单。
- EventSource有自动重连接(不借助第三方)以及发送随机事件的能力。
- websocket的资源占用过大EventSource更轻量。
- websocket可以跨域,EventSource基于http跨域需要服务端设置请求头
- Java后台如何获取爬取数据并入库?
前面说了半天废话,总之EventSource的数据我们需要爬取,而且他能实现数据的推送知道这两点就可以了。关于前端怎么调用我们直接跳过,如果有和作者一样不知道怎么写的直接上w3cshool,上面有你想要的相关信息,我们接下来重点就看这个数据我们如何采集吧
我们要知道如何采集这个数据,就应该知道这个数据后台发送的原理是什么。首先他肯定是一个数据连接的请求,
从前端的链接代码我们可以看到她定义了一个后端的服务地址,也就是我们的请求地址,紧接着就会接收到后台源源不断推送过来的数据。明白了这点我们就直接通过代码发送模拟请求是不是就可以获取数据了?根据这个思路我们获取数据就可以了。
前端调用代码如下图

我们看到上图提示的代码是用php写的,我们用java写一个推送的,大概就是这样
response.setContentType("text/event-stream"); //这里最重要,定义数据格式
response.setCharacterEncoding("UTF-8");
try {
PrintWriter writer = response.getWriter();
writer.write("data: 测试数据,测试数据,测试数据 \n\n");//这里需要\n\n,必须要,不然前台接收不到值,键必须为data
writer.flush();
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
那么以上java代码就相当于我们模拟的一个推送数据服务,也就是说这个数据其实就是个数据输出流,只要进入服务就相当于一个长连接,服务端不关闭那么就可以一直推送数据,根据这个思路我们写一个接收程序:
思路:
1.创建请求
2.模拟前端请求,模拟请求验证参数
3.通过while循环读取数据
4.读取数据处理
5.数据入库,代码大概如下
URL realUrl = new URL(url);
realUrl.openConnection().setConnectTimeout(30*60*60*1000);
realUrl.openConnection().setReadTimeout(30*60*60*1000);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
conn.setRequestProperty("Host", "www.abc.cn");
conn.setRequestProperty("Pragma", "no-cache");
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setRequestProperty("Referer", "http://xxx/index.html");
conn.setRequestProperty("Accept-Encoding", " gzip, deflate, sdch");
conn.setRequestProperty("Cookie", "+cHCtySezP+hV83LD+/nIFk4h+DnX3JUe0B7f");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
String clum ="latitude,longitude,intens,slope,error,location,minisecond,province,"
+ "district,country,usedids,cg_ic,height,datetime,hour,minute,second";//获取字段
StringBuffer sb=new StringBuffer();
sb.append("insert into thunder ("+clum+")");
sb.append(" values("+BaseDao.buildQuestionMarkParam(clum.split(",").length)+")");
while ((line = in.readLine()) != null) {
String str=line.replace("data: ", "").toLowerCase();
str=new String(str.getBytes(),"UTF-8");
ThunderModel json = JSON.parseObject(str, ThunderModel.class);//解析数据
BaseDao.batch(sb.toString(), JSON.parseArray(JSON.toJSONString(json.getM().get(0).getA())), clum);//数据入库
System.out.println("采集成功");
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
System.out.println("正在尝试重启,如果启动失败,请手动启动");
startThreadData();//出错后重启服务
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
入库调用代码采用QueryRunner封装
