一、 任务
获取某网站的各个数据,其中该网站包含地方性数据
二、步骤
1. 遍历该网站中各个地方网址,获得有效地方ID并保存在文件中
2. 将地方ID放入队列中,由于该网站有两个不同的页面(功能),所以需要两个该队列
3. 用两个类分别从两个队列中获取网页数据,并分别保存到对应队列中
4. 用两个类分别从上一步的队列获取数据,通过布隆过滤器进行筛选,并对数据进行加工,将不全的数据获取完整,并统一JSon键值对中的键,把数据保存在一个队列中
5. 直接从上一步的队列中取数据保存到Hbase中
三、详细步骤
1. 获取地方ID
一般、几乎所有的有关地方的网址中有一串数字指的是代表该地方的ID,这个id就国家行政区划代码,如下图是我目标网站中有效的地方ID:
具体办法就是把所有的行政区划代码带入网址,查看网页是否存在,可以根据网站实际情况做优化。
该步骤基本上只需要做一次,除非某些地方加入了该网站
2. 读取有效id到队列
这一步最简单,就是直接把文件中的数据以JSon的格式放入队列中,不过因为我的部分代码是用的别人写的,所有目前还没有去了解代码是如何与redis队列进行数据交互的,有时间可以去了解一下Jedis操作redis工具类 JedisUtil
3. 获取网页数据
- 首先从队列中获取id来获取网页的地址,然后通过url获取对应网页【这里也是用别人写的代码(使用CloseableHttpClient进行http请求)】
- 再判断网页是否有数据,比如通过判断网页的长度、解析网页后是否有数据
- 根据具体网页解析网页获取有效数据
一般主要分为html和xhr
对于html页面最常见的是在获取正文内容时,直接可以通过标签获取数据,如代码:
//html为页面,url为页面链接
Document doc=Jsoup.parse(html,url);
Elements es=doc.getElementsByTag("p");
ret= es.text();//正文
对于xhr,可能比较复杂,一般是采用字符串匹配(startsWith(String)-判断字符串是否以字符串String开头;indexOf(String)、indexOf(String,int)-返回匹配字符串String的下标)+字符串剪切(substring(int)、substring(int,int))
另外,对于格式为”{},{},{}…{}”的字符串,若大括号中是键值对,直接将字符串转化为json格式则会只转换第一个大括号的内容,因此也可以利用这一特性去裁剪数据
JSONObject info = JSONObject.fromObject(String);
- 最后把获取到的数据放入队列中
4. 去重
这一步主要功能就是通过布隆过滤器,去掉重复的数据,并把数据添加完整(比如部分网页的正文需要跳转页面才能获取,部分时间的显示是数字、或者格式不统一),再把json的键值对的键统一名字
- 布隆过滤器:
简单来说就是通过多个不同的哈希算法来判断该值是否存在,因为多个不同哈希算法得出的值会有多个映射,根据判断每个映射位是否都为1,若存在一个映射不为1,则该值肯定不存在,若都为1,则该值可能存在也可能不存在;
所以一套布隆过滤器下来,数据中不会有重复的,只会把少量的不重复的当做重复的扔掉,即误算率;
当然,数据越多,误算率也就越高
5. 把数据保存到Hbase中
当然,Hbase部分的代码也不是我写的,我也不懂Hbase具体是什么,我目前只需要利用Hbase的接口设置好传入的数据格式即可;
在传数据到Hbase前首先要对数据进行判断处理,去除乱码、历史数据(主要是根据时间字段来判断该数据距今多久了)、字段缺失(即接口所需的部分数据为空或者缺失)
后两者好处理,主要问题是怎么识别乱码,同样我也是别人的代码(网上还挺多的)
四、注意事项
1. 第一个主要是主要文件的读取和写入,可能会存在编码不正确
2. 第二步还是文件读入编码可能存在问题
3. 第三步就是解析页面,特别是xhr页面,存在数据中出现”{“,”}”等界限符号,导致字符串截取错误的
4. 第四步就是注意布隆过滤器的使用,我感觉如果数据量太大的话,会丢失很多不重复的数据,我觉得以后想个优化的办法;还有就是部分的时间是以目前时间为基准的,比如“刚刚”、“3分钟前”、“昨日”,“8月8日”等,此为还要注意跨年的时间判断,我写的代码:
SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public String getDateString(String s_date) throws ParseException{
String ret=null;
boolean flag=true;
Long l=System.currentTimeMillis();
//Date date=new Date(Long.valueOf(l));
//System.out.println(format0.format(new Date(Long.valueOf(l))));
int i=0;
int d;
//刚刚
if("刚刚".equals(s_date)){
}
else if((i=s_date.indexOf("分钟"))!=-1){
d=Integer.valueOf(s_date.substring(0,i));
l=l-d*60*1000;
}
else if((i=s_date.indexOf("小时"))!=-1){
d=Integer.valueOf(s_date.substring(0,i));
l=l-d*60*60*1000;
}
else if("昨日".equals(s_date)){
l=l-24*60*60*1000;
}
else{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
String year=sdf.format(new Date(l));
sdf = new SimpleDateFormat("MM");
String month=sdf.format(new Date(l));
sdf = new SimpleDateFormat("dd");
String day=sdf.format(new Date(l));
sdf = new SimpleDateFormat("HH:mm:ss");
String time=sdf.format(new Date(l));
if((i=s_date.indexOf("月"))!=-1){
month=s_date.substring(0, i);
}
if((i=s_date.indexOf("日"))!=-1){
int j;
if((j=s_date.indexOf("月"))==-1){
j=i-3;
}
day=s_date.substring(j+1, i);
}
ret=year+"-"+month+"-"+day+" "+time;
if(format0.parse(ret).getTime()>l){
year=String.valueOf(Integer.valueOf(year)-1);
ret=year+"-"+month+"-"+day+" "+time;
}
flag=false;
//System.out.println(year+"-"+month+"-"+day+" "+time);
}
if(flag){
Date date=new Date(Long.valueOf(l));
ret=format0.format(date);
}
return ret;
}