前言

在最近的互联网项目开发中,需要获取用户的访问ip信息进行统计的需求,用户的访问方式可能会从微信内置浏览器、Windows浏览器等方式对产品进行访问。

当然,获取这些关于ip的信息是合法的。但是,这些ip信息我们采用了其它第三方的服务来记录,并不在我们的数据库中。

这些ip信息是分组存放的,且每个分组都都是分页(1页10条)存放的,如果一次性访问大量的数据,API很有可能会报错。

怎样通过HTTP的方式去获取到信息,并且模拟浏览器每页每页获取10条的信息,且持久到数据库中,就成了当下亟需解决的问题。

通过以上的分析,可以有大致以下思路:

1、拿到该网页http请求的url地址,同时获取到调用该网页信息的参数(如:header、param等);

2、针对分页参数进行设计,由于需要不断地访问同一个接口,所以可以用循环+递归的方式来调用;

3、将http接口的信息进行解析,同时保证一定的访问频率(大部分外部http请求都会有访问频率限制)让返回的数据准确;

4、整理和转化数据,按照一定的条件,批量持久化到数据库中。

一、模拟http请求

我们除了在项目自己写API接口提供服务访问外,很多时候也会使用到外部服务的API接口,通过调用这些API来返回我们需要的数据。

如:

钉钉开放平台https://open.dingtalk.com/document/orgapp/user-information-creation、

微信开放平台https://open.weixin.qq.com/cgi-bin/index?t=home/index&lang=zh_CN等等进行开发。

这里分享一下从普通网页获取http接口url的方式,从而达到模拟http请求的效果:

以谷歌chrome浏览器为例,打开调式模式,根据下图步骤:



尤其注意使用Fetch/XHR,右键该url—>copy—>copy as cURL(bash):



随后粘贴到Postman中,模拟http请求:



粘贴的同时,还会自动带上header参数(以下这4个参数比较重要,尤其是cookie):



这样,就可以访问到所需的数据了,并且数据是json格式化的,这便于我们下一步的数据解析。

二、递归+循环设计

有几个要点需要注意:

1、分内外两层,内外都需要获取数据,即外层获取的数据是内层所需要的;

2、每页按照10条数据来返回,直到该请求没有数据,则访问下一个请求;

3、递归获取当前请求的数据,注意页数的增加和递归终止条件。

废话不多说,直接上代码:

点击查看代码
public boolean getIpPoolOriginLinksInfo(HashMap<String, Object> commonPageMap, HashMap<String, String> headersMap,String charset){
//接口调用频率限制
check(commonAPICheckData);
String linkUrl = "https://xxx.xxx.com/api/v1/xxx/xxx";
HashMap<String, Object> linkParamsMap = new HashMap<>();
linkParamsMap.put("page",commonPageMap.get("page"));
linkParamsMap.put("size",commonPageMap.get("size"));
String httpLinkResponse = null;
//封装好的工具类,可以直接用apache的原生httpClient,也可以用Hutool的工具包
httpLinkResponse = IpPoolHttpUtils.doGet(linkUrl,linkParamsMap,headersMap,charset);
JSONObject linkJson = null;
JSONArray linkArray = null;
linkJson = JSON.parseObject(httpLinkResponse).getJSONObject("data");
linkArray = linkJson.getJSONArray("resultList");
if (linkArray != null){
//递归计数
if (!commonPageMap.get("page").toString().equals("1")){
commonPageMap.put("page","1");
}
// 每10条urlId,逐一遍历
for (Object linkObj : linkJson.getJSONArray("resultList")) {
JSONObject info = JSON.parseObject(linkObj.toString());
String urlId = info.getString("urlId");
// 获取到的每个urlId,根据urlId去获取ip信息(即内层的业务逻辑,我这里忽略,本质就是拿这里的参数传到内层的方法中去)
boolean flag = getIpPoolOriginRecords(urlId, commonPageMap, headersMap, charset);
if (!flag){
break;
}
}
Integer page = Integer.parseInt(linkParamsMap.get("page").toString());
if (page <= Math.ceil(Integer.parseInt(linkJson.getString("total"))/10)){
Integer newPage = Integer.parseInt(linkParamsMap.get("page").toString()) + 1;
linkParamsMap.put("page", Integer.toString(newPage));
// 递归分页拉取
getIpPoolOriginLinksInfo(linkParamsMap,headersMap,charset);
}
else {
return true;
}
}
return true;
}

三、解析数据(调用频率限制)

一般来说,调用外部API接口会有调用频率的限制,即一段时间内不允许频繁请求接口,否则会返回报错,或者禁止调用。基于这样的限制,我们可以简单设计一个接口校验频率的方法,防止请求的频率太快。

废话不多说,代码如下:

点击查看代码
/**
* 调用频率校验,按照分钟为单位校验
* @param commonAPICheckData
*/
private void check(CommonAPICheckData commonAPICheckData){
int minuteCount = commonAPICheckData.getMinuteCount();
try {
if (minuteCount < 2){
//接近频率限制时,休眠2秒
Thread.sleep(2000);
}else {
commonAPICheckData.setMinuteCount(minuteCount-1);
}
} catch (InterruptedException e) {
throw new RuntimeException("-------外部API调用频率错误!--------");
}
}

CommonAPICheckData类代码:

点击查看代码
@Data
public class CommonAPICheckData { /**
* 每秒调用次数计数器,限制频率:每秒钟2次
*/
private int secondCount = 3; /**
* 每分钟调用次数计数器,频率限制:每分钟100次
*/
private int minuteCount = 100; }

四、数据持久化

爬出来的数据我这里是按照一条一条入库的,当然也可以按照批次进行batch的方式入库:

点击查看代码
/**
* 数据持久化
* @param commonIpPool
*/
private boolean getIpPoolDetails(CommonIpPool commonIpPool){
try {
//list元素去重,ip字段唯一索引
commonIpPoolService.insert(commonIpPool);
} catch (Exception e) {
if (e instanceof DuplicateKeyException){
return true;
}
throw new RuntimeException(e.getMessage());
}
return true;
}

五、小结

其实,爬取数据的时候我们会遇到各种各样的场景,这次分享的主要是分页递归的相关思路。

有的时候,如果网页做了防爬的功能,比如在header上加签、访问前进行滑动图片校验、在cookie上做加密等等,之后有时间还会接着分享。

代码比较粗糙,如果大家有其它建议,欢迎讨论。

如有错误,还望大家指正。

【Java爬虫】如何通过 API 递归分页爬取网页数据的更多相关文章

  1. 爬虫系列4:Requests+Xpath 爬取动态数据

    爬虫系列4:Requests+Xpath 爬取动态数据 [抓取]:参考前文 爬虫系列1:https://www.cnblogs.com/yizhiamumu/p/9451093.html [分页]:参 ...

  2. 使用webdriver+urllib爬取网页数据(模拟登陆,过验证码)

    urilib是python的标准库,当我们使用Python爬取网页数据时,往往用的是urllib模块,通过调用urllib模块的urlopen(url)方法返回网页对象,并使用read()方法获得ur ...

  3. python之爬取网页数据总结(一)

    今天尝试使用python,爬取网页数据.因为python是新安装好的,所以要正常运行爬取数据的代码需要提前安装插件.分别为requests    Beautifulsoup4   lxml  三个插件 ...

  4. Java两种方式简单实现:爬取网页并且保存

    注:如果代码中有冗余,错误或者不规范,欢迎指正. Java简单实现:爬取网页并且保存 对于网络,我一直处于好奇的态度.以前一直想着写个爬虫,但是一拖再拖,懒得实现,感觉这是一个很麻烦的事情,出现个小错 ...

  5. 另类爬虫:从PDF文件中爬取表格数据

    简介   本文将展示一个稍微不一样点的爬虫.   以往我们的爬虫都是从网络上爬取数据,因为网页一般用HTML,CSS,JavaScript代码写成,因此,有大量成熟的技术来爬取网页中的各种数据.这次, ...

  6. python爬虫——爬取网页数据和解析数据

    1.网络爬虫的基本概念 网络爬虫(又称网络蜘蛛,机器人),就是模拟客户端发送网络请求,接收请求响应,一种按照一定的规则,自动地抓取互联网信息的程序.只要浏览器能够做的事情,原则上,爬虫都能够做到. 2 ...

  7. 吴裕雄--天生自然PYTHON爬虫:安装配置MongoDBy和爬取天气数据并清洗保存到MongoDB中

    1.下载MongoDB 官网下载:https://www.mongodb.com/download-center#community 上面这张图选择第二个按钮 上面这张图直接Next 把bin路径添加 ...

  8. Java爬虫系列四:使用selenium-java爬取js异步请求的数据

    在之前的系列文章中介绍了如何使用httpclient抓取页面html以及如何用jsoup分析html源文件内容得到我们想要的数据,但是有时候通过这两种方式不能正常抓取到我们想要的数据,比如看如下例子. ...

  9. Java爬虫学习(1)之爬取新浪微博博文

    本次学习采用了webmagic框架,完成的是一个简单的小demo package com.mieba.spiader; import us.codecraft.webmagic.Page; impor ...

  10. 使用 Python 爬取网页数据

    1. 使用 urllib.request 获取网页 urllib 是 Python 內建的 HTTP 库, 使用 urllib 可以只需要很简单的步骤就能高效采集数据; 配合 Beautiful 等 ...

随机推荐

  1. 错误记录-MariaDB连接异常

    简介: 问题: C#,VS2022,mariadb-10.11.5-winx64,using MySql.Data.MySqlClient; 在执行connection.Open()时抛出异常:Sys ...

  2. C++ MiniZip实现目录压缩与解压

    Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法.它最初由Jean-Loup Gailly和Mark Adler开发,旨在成为一个高效.轻量级的压缩库,其被广泛应用于许多领域,包括 ...

  3. C#中的类和继承

    公众号「DotNet学习交流」,分享学习DotNet的点滴. 类继承 通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展. 可以使用一个已经存在的类作为新类的基础.已存在的类称为基类(b ...

  4. Oracle ADG容灾端部署Rman备份的一些实践经验

    随着数据库中数据量的不断增加.业务的复杂性提高.各种政策颁布的系统容灾等级要求,数据库备份的工作及备份文件的有效性及备份文件的管理变得愈发重要.在Oracle数据库中提供了强大的备份和恢复工具,其中R ...

  5. .NET微信网页开发相关文章教程

    前言 今天我们主要总结一下.NET微信网页开发的相关文章教程. 微信网页开发详细文档可以看微信官方文档:https://developers.weixin.qq.com/doc/offiaccount ...

  6. 数据库系列:MySQL不同操作分别用什么锁?

    数据库系列:MySQL慢查询分析和性能优化 数据库系列:MySQL索引优化总结(综合版) 数据库系列:高并发下的数据字段变更 数据库系列:覆盖索引和规避回表 数据库系列:数据库高可用及无损扩容 数据库 ...

  7. VScode怎么实现c的运行,这里只讲述一些细节

    第一下载的Vscode要设置信任模式,否则你后面搞什么都没有用 第二下载minGw还是gcc 都行 第三安装插件,c,c++. 然后编译就行了,我搞了一天,主要弹出的是未找到exe文件,但是我告诉大家 ...

  8. 教你使用Prometheus-Operator进行K8s集群监控

    本文分享自华为云社区<Promethues-operator入门使用指导>,作者:可以交个朋友. 一. 背景 在非operator配置的普罗中我们监控k8s集群都是通过配置configma ...

  9. 解决win10的wifi打不开或无法搜索到周围wifi的问题

    今天笔者遇到了一个比较奇葩的问题,就是笔记本电脑的wifi打不开了,即使打开了也是搜索不到周围的wifi的.这个问题一开始笔者没有发现,因为在暑假期间都是使用笔记本连接自己的手机热点进行上网的.然而暑 ...

  10. Apache POI 操作Excel简单入门使用

    Apache POI简介 开发中经常会涉及到excel的处理,如导出Excel,导入Excel到数据库中,操作Excel目前有两个框架,一个是apache 的poi, 另一个是 Java Excel ...