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

一、内容抓取

看过上一篇博客的朋友们都知道,我们通过对积分界面的相关分析,找到了请求积分页面页面数据的接口,通过该接口我们可以进行相关数据的获取,本文将从积分页面入手,进行相关的数据分析与解析。
以下为经过分析后,获取的积分页面的数据请求地址:
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. 梯度的直观理解_梯度下降最直观的理解
  2. 请简述什么是spring的ioc和di_绿茶用什么茶叶罐储存?有6种茶叶罐适合
  3. 使用SQL如何把用逗号等字符隔开的字符串转换成列表,以及把列合并成符合隔开的字符串(转)...
  4. arg是什么函数_C 语言编程 — 函数
  5. 《信息安全系统设计基础》实验四 外设驱动程序设计
  6. 反汇编程序导致程序crash的解决思路
  7. 官宣!2020年,这5类程序员要过苦日子!网友:明年咋活?!
  8. 【学术】施一公分享自身经验:如何提高自己的专业英文文献阅读能力
  9. Python版学生管理系统源码分享【考试/作业必备】
  10. AdminLTE-2.4.10源码包
  11. 孙子算经 之 物不知数(韩信点兵)
  12. Linux文件编辑常用命令
  13. Lorenz系统、简单的Rossler系统和Chua电路系统的混沌吸引子——MATLAB实现
  14. QQ、微信、QQ浏览器UserAgent
  15. sqlserver2008已成功与服务器建立连接 但在登录过程中发生错误,指定的网络名不可再用(已解决)
  16. VBScript脚本运用(脚本程序与宿主程序的交互)
  17. Python 用 os.listdir() 获取文件列表和筛选特定格式文件
  18. 微星主板在有RAID的情况下在NVME的SSD上安装Win10
  19. 中职计算机英语视频,中职计算机微课视频一等奖(中职英语微课)
  20. spss安装剩下一个python_SPSSPython脚本在spss命令内部时停止并出现错误spss.提交()将创建一个警告...

热门文章

  1. 我有一个 APP 创意,如何将其实现?
  2. 算法三:判断该年该月有几天
  3. 内网IP使用Https小记
  4. 学习计算机基础必读的4本经典入门书籍,自学编程必备书单!
  5. Rust vs. Go:为什么他们在一起更好
  6. 滴滴涨价背后:市值缩水40亿美元,高峰期无人接单?
  7. 【Web渗透】信息收集篇——Google搜索引擎(二)
  8. 单片机(51) 什么是编码器?什么是译码器?
  9. 马化腾:谈谈我创办腾讯这些年
  10. web结课作业的源码 HTML5+CSS大作业——个人博客-功能齐全(48页) html大学生网站开发实践作业