最近比较忙,所以第二篇文章稍微较晚了些,本篇文章将会对新浪体育的世界杯专题界面中的【积分】页面中的数据进行分析与数据采集,希望通过这个过程,可以帮助到需要的朋友们。

一、内容抓取

看过上一篇博客的朋友们都知道,我们通过对积分界面的相关分析,找到了请求积分页面页面数据的接口,通过该接口我们可以进行相关数据的获取,本文将从积分页面入手,进行相关的数据分析与解析。
以下为经过分析后,获取的积分页面的数据请求地址:
http://api.sports.sina.com.cn/?p=sports&s=sport_client&a=index&_sport_t_=football&_sport_s_=opta&_sport_a_=teamOrder&type=108&use_type=group&callback=cb_1da9fd2f_5595_4563_ab46_0570c721f313&dpc=1
从中可以看到请求的地址为:
http://api.sports.sina.com.cn/
请求的参数列表如下:

参数名称 参数值 参数含义
_sport_a_ teamOrder 请求的功能,即Action
_sport_s_ opta 未知
_sport_t_ football 足球
a index 未知
callback cb_1da9fd2f_5595_4563_ab46_0570c721f313 json
dpc 1 未知
p sports 用于API请求
s sport_client 用于API请求
type 108 未知
use_type group 用途,值为group时分组返回

有没有人会说,这个参数的含义是我是怎么知道的。

一般调用接口时,最简单的办法就是在测试接口时,不传递某个参数,然后观察返回的结果,在结合访问其他接口,观察几次,很容易发现某些参数的真实含义了。

例如:

不传递这个_sport_a参数,返回的数据提示信息为:

{"result":{"status":{"code":11,"msg":"sport action is invalid"},"data":[]}}

不传递这个_sport_s参数,返回的数据提示信息为:

{"result":{"status":{"code":11,"msg":"sport source is invalid"},"data":[]}}

传递use_type这个参数并且值为group后,返回的数据格式是这样的:

{
"result":{
"status":{
"code":0,"msg":""
},
"data":{
"A":[
{"type_id":"4","league_id":"2017"},
{"type_id":"4","league_id":"2017"}]}
}

当不传递use_type这个参数以后,返回结构是这样的:

{
"result":{
"status":{
"code":0,"msg":""
},
"data":[
{"type_id":"4","league_id":"2017"},
{"type_id":"4","league_id":"2017"}]
}

我们知道当发送一个请求时,服务器返回来的基本都是字符串格式的数据,那么我们用HttpClient去请求这个地址后,返回来也是字符串结构,在未更改任何请求参数的情况下,我们可以看到服务器返回来的内容加上了一层包装,这对于我们的解析需要多处理一步,如下图所示:

尝试去掉callback    这个请求参数以后,发现时正常的JSON格式字符串的信息,如下所示:

以上是我们简单分析的一个过程。接下来写个程序采集下来,这个程序时去掉callback这个参数。程序用HttpClient发送请求,使用了一个已经封装好的工具类,主要代码如下所示:

public class TestFootball {public static final String CSS_URL = "http://n2.sinaimg.cn/products/worldcup2018/latest/css/base.css";public static final String PAGE_URL = "http://2018.sina.com.cn/scoreboard/page.shtml";public static final String PARAMS = "p=sports&s=sport_client&a=index&_sport_t_=football&_sport_s_=opta&_sport_a_=teamOrder&type=108&dpc=1";public static final String URL = "http://api.sports.sina.com.cn/?" + PARAMS;public static Map<String,String> NAME_MAP = new HashMap<String,String>();static {NAME_MAP.put("team_id", "球队ID");NAME_MAP.put("team_cn", "球队名称");NAME_MAP.put("team_order", "球队排名");NAME_MAP.put("count", "场次");NAME_MAP.put("win", "胜");NAME_MAP.put("lose", "负");NAME_MAP.put("draw", "平");NAME_MAP.put("goal", "进球");NAME_MAP.put("losegoal", "失球");NAME_MAP.put("truegoal", "净胜球");NAME_MAP.put("score", "积分");NAME_MAP.put("group", "小组");NAME_MAP.put("sl_id", "唯一标识ID");NAME_MAP.put("logo", "国家图片");}public static void main(String[] args) throws IOException {/*BasicHeader[] reqHeaders = new BasicHeader[2];reqHeaders[0] = new BasicHeader("If-Modified-Since", " Wed, 06 Jun 2018 04:28:24 GMT");reqHeaders[1] = new BasicHeader("Accept-Encoding", "gzip, deflate");*/List<Map<String,Object>> rList = new ArrayList<Map<String,Object>>();CloseableHttpResponse response = HttpClientRequestUtil.getHttpResponse(URL, "get", null, null);if(response == null){System.out.println("服务器无响应");return;}int statusCode = response.getStatusLine().getStatusCode();if(statusCode == 200){//打印网页的响应头的信息Header[] resHeaders = response.getAllHeaders();if(resHeaders != null){for(Header header : resHeaders){System.out.println(header.getName() + " : " + header.getValue());}}HttpEntity entity = response.getEntity();String returnResult = null;if (entity != null) {returnResult = EntityUtils.toString(entity);} else {return;}//根据返回结果进行处理JSONObject result = JSONObject.parseObject(returnResult);JSONObject rObj = result.getJSONObject("result");JSONObject statusObj = rObj.getJSONObject("status");int code = statusObj.getIntValue("code");if( code  == 0){//请求数据格式正确JSONArray datas = rObj.getJSONArray("data");Set<String> mapKeySet = NAME_MAP.keySet();if(datas != null && datas.size() > 0){for(int i = 0;i < datas.size();i++){JSONObject jsonObject = datas.getJSONObject(i);Map<String,Object> map = new HashMap<String,Object>();for(String key : mapKeySet){map.put(NAME_MAP.get(key), jsonObject.get(key));}rList.add(map);}}}}else if(statusCode == 304){//网页未修改System.out.println("网页未修改!");}System.out.println(JSON.toJSONString(rList));}
}

程序最后的输出结果如下:

[
{"进球":"3","球队排名":"1","净胜球":"2","唯一标识ID":"952","失球":"1","球队ID":"614","小组":"E","球队名称":"巴西","场次":"2","胜":"1","国家图片":"http://www.sinaimg.cn/lf/sports/logo85/952.png","负":"0","积分":"4","平":"1"},
{"进球":"4","球队排名":"1","净胜球":"1","唯一标识ID":"944","失球":"3","球队ID":"118","小组":"B","球队名称":"西班牙","场次":"2","胜":"1","国家图片":"http://www.sinaimg.cn/lf/sports/logo85/944.png","负":"0","积分":"4","平":"1"}
]

这样就可以把相关数据存储到数据库中了。由于我在请求时去掉了use_type=group这个参数,返回的data数据的顺序不是正常的数据顺序,如需排序需要在代码里面进行手动排序。

二、304状态码说明

有没有人发现我为什么要判断HTTP的状态吗等于304呢?其实,爬虫时或进行数据抽取时的一个典型的需求了,经常为了提高采集的效率,往往考虑增量去采集网页。而一般的增量采集的条件,则是根据某个时间点,去查询这个时间点之后产生的数据。
当做数据库的增量采集时,通常是在SQL中传入某一个时间点,而这个时间点从上一次的采集时间获取。
当做FTP文件数据的增量采集时,通常是根据文件在FTP服务器上的修改时间来判断是否是新增的文件。
那么对于网页来说,我们可以在下载网页时,记录网页的下载的某个时间点,增量采集这个网页时,把时间点传入到某个地方。
那么怎么知道网页是否进行修改了呢?
首先我们请求一下这个新浪体育的地址,并获取响应头的信息:
http://2018.sina.com.cn/news/
响应头信息如下:

Server : nginx
Date : Sat, 23 Jun 2018 14:48:31 GMT
Content-Type : text/html
Connection : keep-alive
Last-Modified : Sat, 23 Jun 2018 14:45:58 GMT
Vary : Accept-Encoding
X-Powered-By : shci_v1.03
Expires : Sat, 23 Jun 2018 14:48:57 GMT
Cache-Control : max-age=60
Age : 34
Via : http/1.1 cnc.beixian.ha2ts4.214 (ApacheTrafficServer/6.2.1 [cHs f ])
X-Cache : HIT.214
X-Via-CDN : f=Edge,s=cnc.beixian.ha2ts4.214,c=123.126.157.206
X-Via-Edge : 1529765311820ff09757bde9d7e7b101d14b9

从响应的结果中,我们可以看见【Last Modified】响应头,这个响应头代表了网页的修改时间,【Date】返回的是Web服务器的当前时间。

304这个请求我们在用浏览器调试工具去观测某个请求是,经常会发现他会出现304状态码的提示,如下图所示:

那么,他为什么会变成304状态呢?通过观察请求后我们会发现,当进行第二次请求时,浏览器会自动发送一个请求头【If-Modified-Since】,其中包含的时间是先前服务器发过来的【Last-Modified】最后修改的时间戳,这样让Web服务器端进行验证,通过这个时间戳判断爬虫上次抓过的页面是否有更新。如果有修改,则返回HTTP状态码200和新的内容。如果没有变化,则只返回HTTP状态码304,告诉爬虫没有变化,这样可以大大减少在网络上传输的数据,避免无所谓活多余的请求信息,同时也可以减轻被抓取服务器的负担。

例如我们可以用之前的代码去请求这个地址
String PAGE_URL = "http://2018.sina.com.cn/news/";
CloseableHttpResponse response = HttpClientRequestUtil.getHttpResponse(PAGE_URL, "get", null, null);

在未加【If-Modified-Since】这个请求头时,他的状态码是200。然后我们再请求时,如果未加上本次响应头里面的【Last-Modified】的值时,他还是200,若加上了【If-Modified-Since】这个请求头,且该请求头的值为上一次的【Last-Modified】的值时,他的状态就变成304了。
通过对304的判断,我们可以知道请求的网页是否做了修改,进而进行增量的数据的解析工作。后续我们可能去解析一下新浪体育世界杯新闻页面的数据。

我觉得爬虫是一项非常不错的学习内容,可以从中了解到HTTP协议的相关知识、各种JSON数据结构解析等。

本来计划用Jsoup去解析HTML格式获取世界杯数据的,不过目前这几个界面的数据都是发送JSON请求来获取数据的,后续会找一找相关的页面的数据。

总是觉得,这篇文章写的太简单了些。

2018年俄罗斯世界杯之Java数据爬虫(二)相关推荐

  1. ML之PDP:基于FIFA 2018 Statistics(2018年俄罗斯世界杯足球赛)球队比赛之星分类预测数据集利用DT决策树RF随机森林+PDP部分依赖图可视化实现模型可解释性之详细攻略

    ML之PDP:基于FIFA 2018 Statistics(2018年俄罗斯世界杯足球赛)球队比赛之星分类预测数据集利用DT决策树&RF随机森林+PDP部分依赖图可视化实现模型可解释性之详细攻 ...

  2. ML之shap:基于FIFA 2018 Statistics(2018年俄罗斯世界杯足球赛)球队比赛之星分类预测数据集利用RF随机森林+计算SHAP值单样本力图/依赖关系贡献图可视化实现可解释性之攻略

    ML之shap:基于FIFA 2018 Statistics(2018年俄罗斯世界杯足球赛)球队比赛之星分类预测数据集利用RF随机森林+计算SHAP值单样本力图/依赖关系贡献图可视化实现可解释性之详细 ...

  3. 同方威视安全护航2018年俄罗斯世界杯

    北京2018年7月24日电 /美通社/ -- 历时1个月的2018年俄罗斯世界杯落下帷幕,法国队4-2战胜克罗地亚队获得冠军,捧回大力神杯,时隔20年再次登上世界之巅.本届世界杯新人辈出,看点多多,使 ...

  4. 2018年俄罗斯世界杯对阵图(法国冠军杯![2018年 07月 12日 星期四 18:15:26 CST])

    A组 排名 球队 赛 胜 和 负 得 失 差 分 出线资格 1  俄罗斯 (H) 1 1 0 0 5 0 +5 3 晋级淘汰赛 2  乌拉圭 1 1 0 0 1 0 +1 3 3  埃及 1 0 0 ...

  5. 基于Java+数据爬虫+SSM架构实现手机购物网站前后台项目

    本基于SSM的手机购物网站系统,采用面向对象思想,选用javaweb开发技术,后台选用JavaSSM轻量级开发框架,使用tomcat8.0作为开发服务器,使用nginx实现反向代理,支持多台tomca ...

  6. Visa携手Zlatan Ibrahimović回归2018年FIFA俄罗斯世界杯

    Visa推出以足球超级明星为主角的全球营销活动,让球迷们不会错过今年夏天的精彩时刻 Visa将以创新的支付技术提升球迷们的比赛日体验 旧金山--(美国商业资讯)--国际足联(FIFA)官方支付服务合作 ...

  7. 2018俄罗斯世界杯冷门之夜!卫冕冠军德国0-1不敌墨西哥遭开门黑

    这次在俄罗斯开展的世界杯似乎为全世界的球迷了带来了许多惊喜.就在北京时间6月17日晚23时,在莫斯科卢日尼基球场开展的2018年俄罗斯世界杯F组中卫冕冠军德国竟不敌墨西哥给球迷爆了一个惊天大冷门. 比 ...

  8. Java网络爬虫--一步步使用Java网络爬虫技术实现豆瓣读书Top250数据的爬取,并插入数据库

    一步步使用Java网络爬虫技术实现豆瓣读书Top250数据的爬取,并插入数据库 目录 一步步使用Java网络爬虫技术实现豆瓣读书Top250数据的爬取,并插入数据库 第一步:创建项目,搭建项目结构 p ...

  9. 2018世界杯赛程PHP源码,PHP-ML机器学习预测2018俄罗斯世界杯比赛结果

    前言: 根据2014年巴西世界杯的小组赛比赛结果和赔率数据简单预测2018世界杯比赛结果,比赛的赔率我们可以事先知道,所以可以使用赔率作为预测数据 技术: PHP ML库 贝叶斯分类器 样本数据:20 ...

  10. 2018 俄罗斯世界杯赛程时间表

    2018 俄罗斯世界杯已经在 6月14日正式拉开了序幕,在这里小编为广大生信人奉上本次世界杯的赛程表(北京时间). 希望大家在工作学习之余,能够开开心心去享受这场足球盛宴,同时祝福大家端午节快乐! 3 ...

最新文章

  1. Spring4基础 学习笔记(5) ---- Spring与Dao
  2. JEECGJWT异常捕获强化处理
  3. 离线电脑搭建开发环境
  4. springboot+shiro+cas实现单点登录之cas server搭建
  5. matlab中的状态空间模型,状态空间模型matlab
  6. c语言看门狗的作用,AT89S52单片机看门狗C语言程序
  7. 新的开始——参加培训
  8. 版本控制系统(vcs)Git
  9. L1-6 烤地瓜 (15 分)
  10. pscp实现windows和linux之间互传文件
  11. 规范小区电瓶车充电桩的分布保障充电安全
  12. UKey税务系统开票及打票流程
  13. golang 的testing.T 和 testing.M 的区别
  14. 课时31 永久储存:腌制一缸美味的泡菜
  15. 中国人的数学为什么好,为什么不好
  16. 软件性能测试参数化数据准备,性能测试之如何准备测试数据
  17. 【Apache Spark 】第 6 章Spark SQL 和数据集
  18. 雷达的军事应用 军用 气象 航空航天
  19. CSP认证202209-3:防疫大数据
  20. 对症下药:删除顽固文件夹方法汇总

热门文章

  1. 读书印记 - 《南北战争三百年》
  2. 2020年北京地铁建设规划抢先看
  3. 高考是不是计算机投档,1:1高考投档是什么意思 填报志愿注意事项
  4. java超链接大全_JavaFX超链接
  5. Charles设置代理之后,为什么电脑和手机都不能上网
  6. 计算机科学 期刊点评,COMPUTER JOURNAL
  7. 十进制,二进制,八进制,十六进制的解意和详细转换
  8. Linux/软件 - 资源[国外站点]
  9. 【杂七杂八】虚拟机win中 腾讯会议视频黑屏
  10. AI人工智能基础自学(一)