先前在《爬蟲系列之數據質量監控(二):監控系統設計》 一文中,對採集中數據解析部分可能出現的各種異常,進行了大概的總結。比如:標題或內容中包含亂碼、css樣式、JavaScript代碼等。
由於出現的異常可能千奇百怪,我們不可能提前想到所有現象。此時,就需要根據目前已經發現的問題,總結出一套能夠靈活應對不同情況的規則庫。
其目的就是在數據持久化接口處,對接收的所有數據,依據信源系統中配置的規則進行校驗,以判斷採集到的數據的準確性,便於改進採集器或腳本,優化數據質量,提高產品的用戶體驗。
一. 規則庫必須是抽象的規則,而不是具體表象。
通過對《爬蟲系列之數據質量監控(二):監控系統設計》中描述的各類規則進行抽象,大致可以總結出以下規則。
如下表所示:
序號 |
分類 |
規則細則 |
1 |
校驗規則 |
A字段值長度小於閥值A |
2 |
校驗規則 |
A字段的值是否包含CSS樣式 |
3 |
校驗規則 |
A字段的值是否有亂碼 |
4 |
校驗規則 |
A字段值中漢字長度小於閥值A |
5 |
校驗規則 |
A字段值是否符合yyyy-MM-dd HH:mm:ss時間格式 |
6 |
校驗規則 |
A字段值等於閥值A |
7 |
校驗規則 |
A字段值大於閥值A |
8 |
校驗規則 |
A字段值長度大於閥值A |
9 |
校驗規則 |
A字段值長度等於閥值A |
10 |
校驗規則 |
A字段的值是否包含JavaScript代碼 |
11 |
校驗規則 |
A字段值與字段B值相同 |
12 |
校驗規則 |
A字段值包括規則庫中配置閥值,或包括接口配置閥值A |
13 |
校驗規則 |
A字段值以規則庫中配置閥值結尾,或以接口中閥值A結尾 |
14 |
校驗規則 |
A字段值是否包含日期 |
15 |
清洗規則 |
A字段值內容格式化 |
16 |
清洗規則 |
A字段值包含閥值A時,則刪除A字段值中閥值A字符串 |
17 |
清洗規則 |
A字段值包含閥值A字符時,直接丟棄 |
18 |
清洗規則 |
A字段值轉義字符還原 |
19 |
矯正規則 |
A字段時間大於B字段時間,則A字段值=B字段值 |
20 |
矯正規則 |
A字段值包含閥值A,則:B字段值=閥值B |
21 |
矯正規則 |
A字段值包含閥值A,則A字段值中的閥值A替換爲閥值B |
目前整理的上述14條數據質量校驗規則,基本上可以應對80%以上的異常。
至於清洗和矯正規則,則尚需要根據實際的業務規則,進行相應的補充。
二. 規則庫的邏輯實現
在抽象出相應的規則庫以後,需要根據規則庫的描述,進行後端編碼的邏輯實現,把文字描述用代碼進行實現。具體實現邏輯類似下述兩個規則:
1.如規則1(A字段值長度小於閥值A)
代碼實現:
public BooleanisALengthLtB(MonitorRule mr, MonitorRuleRelation mrr,Object oneData) {
//判斷A字段及A閥值不爲空
if(!StringUtils.isNotBlank(mrr.getInterAField())||!StringUtils.isNotBlank(mrr.getThresholdA()))
returnfalse;
ObjectaFieldValue = Reflect.getObjectXField(oneData, mrr.getInterAField());
//閥值A必須爲數字;
if (!BooleanRegular.isNumber(mrr.getThresholdA()))
return false;
//判斷字段A的值不爲空;
if(!StringUtils.isNotBlank(aFieldValue)) return false;
Doublevalue = Double.parseDouble(mrr.getThresholdA());
if(aFieldValue.toString().length() < value.intValue())
returntrue;
returnfalse;
}
使用場景:如判斷解析的標題或正文必須大於某個長度,否則認爲解析異常。
2.如矯正規則19(A字段時間大於B字段時間,則A字段值=B字段值)
代碼實現:
public Object aGTb(MonitorRulemr, MonitorRuleRelation mrr, Object oneData) {
if (!StringUtils.isNotBlank(mrr.getInterAField())||!StringUtils.isNotBlank(mrr.getInterBField()))
return oneData;
Objecta = Reflect.getObjectXField(oneData, mrr.getInterAField());
Objectb = Reflect.getObjectXField(oneData, mrr.getInterBField());
if (!StringUtils.isNotBlank(a)|| !StringUtils.isNotBlank(b)) // 不爲空
return oneData;
if (!BooleanRegular.isDate(a.toString()) || !BooleanRegular.isDate(b.toString()))
return oneData;
// 必須是19位時間格式;
if (a.toString().length() == 19&& b.toString().length() == 19) {
long aLong = DateUtil.stringToLong(a.toString(),
DateUtil.year_month_day_hour_mines_seconds);
long bLong = DateUtil.stringToLong(b.toString(),
DateUtil.year_month_day_hour_mines_seconds);
if (aLong > bLong) {
oneData= Reflect.setObjectXField(oneData,mrr.getInterAField(), b);
}
}
return oneData;
}
使用場景:如解析出的發佈時間大於採集時間,則使用採集時間填充發布時間
三. 規則庫與kafka統一接口的關係處理
規則庫最終是用在kafka統一接口處,以便對接收的數據進行校驗,找出異常情況。那麼,他們如何進行關聯呢?主要有以下兩步:
1.Kafka統一接口與ES索引庫進行關聯
由於kafka的每一個對外服務接口,均對應一個唯一的ES索引庫,所以接口接收的數據屬性字段,必須與索引庫一致。所以,在信源系統中的接口列表處,添加與ES索引對應屬性信息。如下圖客戶端接口的配置信息:
Kafka統一接口中,數據類型爲客戶端的數據推送接口如下:
接口與ES索引對應的字段信息如下:
2. 給接口字段添加校驗規則
比如需要給網站推送接口的標題字段添加清洗規則,則可以如下圖操作。
或者添加矯正規則:
最終添加完畢以後如下圖所示:
四. kafka統一接口的校驗處理
由於信源系統中已經配置了接口和規則庫之間的關係,其中二者是通過接口方法名稱和規則庫處理邏輯(規則庫處理邏輯:是規則庫後臺處理邏輯的方法名稱)進行關聯。如下圖所示:
然後,在接口方法中通過類全路徑,以及規則處理邏輯方法名,通過反射的方式進行動態調用。這樣就可以根據字段配置的處理規則,靈活地進行各種規則的校驗。
具體處理的代碼類似下面:
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class DynamicTask implementsCallable<Object> {
// 該參數爲待調用的類名和方法名;格式:java.tools.executors.dynamic.TestClass.test2
String classMethon;
// 該參數爲爲調用classMethon方法需要傳入的參數集合;
Object[] arguments;
/**
* @param classMethon
* 類包.方法名。如:fy.java.tools.executors.dynamic.TestClass.test2(
* 調用TestClass的test2方法)
* 注意:classPackage包路徑中不能有'_'、'-'等特色字符,否則無法執行;
* @param arguments 待調用方法需要的參數,參數順序必須和方法中的額參數順序相同;
*/
public DynamicTask(StringclassMethon, Object[] arguments) {
this.classMethon =classMethon;
this.arguments =arguments;
}
public Object call() throwsException {
String classPackage = this.classMethon.substring(0,this.classMethon .lastIndexOf("."));// 類名
String methodName = this.classMethon.substring(this.classMethon.lastIndexOf(".") + 1);// 方法名;
Class<?> service =Class.forName(classPackage);
Object result = null;
Class<?>[]parameterTypes = null; // 獲得參數的類型
try {
Method[] methods =service.getMethods();
for (Methodmethod : methods) {
StringmName = method.getName();
if(methodName.equals(mName)) {
parameterTypes= method.getParameterTypes();
break;
}
}
/**
* service是服務器端提供服務的對象,但是,要通過獲取到的調用方法的名稱,
* 參數類型,以及參數來選擇對象的方法,並調用。獲得方法的名稱
*/
try {
// 通過反射機制獲得方法
Methodmethod = service.getMethod(methodName, parameterTypes);
// 通過反射機制獲得類的方法,並調用這個方法
result =method.invoke(service.newInstance(), arguments);
} catch(Throwable e) {
e.printStackTrace();
System.out.println(arguments.toString());
}
} catch (Throwable e){ e.printStackTrace() ;}
return result;
}
}
上面就是數據質量校驗前後臺的大致處理邏輯,希望對各位有一定的參考意義。
更多內容請關注公衆號:十點數據。大家一起學習,一起進步。
本文分享自微信公衆號 - 十點數據(crawler-small-gun)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。