1. 概述

FormatImporter 用于将一些常见格式的外部数据导入到神策分析,目前支持读取 CSV 表格、Nginx 日志、MySQL 数据库、Oracle 数据库,以及符合数据格式的 JSON 数据。

2. 使用方法

2.1. 运行环境

此工具支持在 Linux 环境下运行,也支持在 windows 环境下运行,需要 Python3.4 或更高版本。另外如果需要导入 MySQL、Oracle 数据库中的数据的话需要确保机器上包含相关客户端的程序包。

windows 环境下部署时,需要注意文件编码问题,具体注意事项参考本文的 4.8 。

2.2. 下载工具

下载请点击此链接,脚本下载后是一个压缩包,解压后即可使用。

2.3. 获取数据接收地址

获取数据接收地址:

2.4. 导入 CSV 格式的数据

2.4.1. 导入事件

假设有 CSV 文件描述了以下用户行为 ( 参考代码包下 examples/events.csv ):

user_id,action,time,item_id,item_name,item_cate
bug29,view,2018-05-12 13:01:11,13245,男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲,男装
bug29,buy,2018-05-12 13:05:03,13245,男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲,男装
小武,view,2018-05-13 10:20:32,23421,New Order Technique 2CD豪华版 欧版行货 全新未拆,音像
菠菜,view,2018-05-13 20:42:53,3442,NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型,母婴
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID,time 列作为事件发生的时间,action 列作为事件名称,只导入 item_id 和 item_name 作为事件属性:

python3 format_importer.py csv_event \
--url 'http://localhost:8106/sa?project=xxx' \ 
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--timestamp_from 'time' \
--event_from 'action' \
--filename './examples/events.csv' \
--property_list 'item_id,item_name' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

注:--url 设置的是数据接收地址,具体的获取方法可以参照本文 2.3 节或 数据接入引导 

2.4.2. 用户属性

假设有 CSV 文件描述了以下用户属性 ( 参考代码包下 examples/profiles.csv ):

user_id,gender,is_member,score
bug29,男,true,131
小武,女,false,
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID:

python3 format_importer.py csv_profile \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--filename './examples/profiles.csv' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.4.3. 导入物品数据

假设有 CSV 文件描述了以下物品数据 ( 参考代码包下 examples/item.csv ):

item_type,item_id,item_name,item_cate,action
view,13245,男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲,男装,买买买
buy,13245,男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲,男装,缺货
view,23421,New Order Technique 2CD豪华版 欧版行货 全新未拆,音像,缺货
view,3442,NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型,母婴,买买
CODE

将这些数据导入神策系统中,--item_type 和 --item_id 配置分别指定物品数据的 item_type 和 item_id 字段值:

python3 format_importer.py csv_item \
--url 'http://localhost:8106/sa?project=xxx' \
--item_type 'item_type' \
--item_id 'item_id' \
--property_list 'item_name,item_cate,action' \
--filename './examples/item.csv' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.4.4.  导入用户关联关系

注意:导入用户关联关系需要 v1.13.5 及以上版本

假设有 CSV 文件描述了以下用户关联关系 ( 参考代码包下 examples/signup.csv ):

user_id,device_id
小武,ac0eadfb-cd5d-44b6-8a21-079862773c11
菠菜,2903f1d4-e20d-4866-8614-66d9101a3bd3
bug29,0c0c93f5-c747-4c1a-acfc-e75279720da1
CODE

将这些数据导入到神策系统中,--login_id_from 和 --anonymous_id_from 配置分别指定用户关联关系的登录 ID 与匿名 ID:

python3 format_importer.py csv_signup \
--url 'http://localhost:8106/sa?project=xxx' \
--login_id_from 'user_id' \
--anonymous_id_from 'device_id' \
--filename './examples/signup.csv' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.5. 导入 Nginx 日志

2.5.1. 导入事件

假设有 Nginx 日志描述了以下用户行为 ( 参考代码包下 examples/events.log ):

123.4.5.6 - [12/May/2018:13:01:11 +0800] "GET /item?id=13245&action=view&cate=%e7%94%b7%e8%a3%85" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲" "bug29"
123.4.5.6 - [12/May/2018:13:05:03 +0800] "GET /item?id=13245&action=buy&cate=%e7%94%b7%e8%a3%85" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲" "bug29"
123.4.5.7 - [13/May/2018:10:20:32 +0800] "GET /item?id=23421&action=view&cate=%e9%9f%b3%e5%83%8f" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "New Order Technique 2CD豪华版 欧版行货 全新未拆" "小武"
123.8.5.7 - [13/May/2018:20:42:53 +0800] "GET /item?id=&action=view&cate=%e6%af%8d%e5%a9%b4" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型" "菠菜"
CODE

对应 Nginx 配置的格式如下:

log_format compression '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$title" "$user_id"';
access_log /data/nginx_log/access.log compression;
CODE

将这些数据导入神策系统中,以 $user_id 作为用户 ID,以 $time_local 作为事件发生的时间,$request 解析后的参数 action 值作为事件名,只导入两个事件属性:$request 解析后的 id 作为 item_id,自定义的变量 $title 作为 item_name:

python3 format_importer.py nginx_event \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--timestamp_from 'time_local' \
--timestamp_format '%d/%b/%Y:%H:%M:%S %z' \
--event_from '__request_param_action' \
--filename './examples/events.log' \
--log_format '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$title" "$user_id"' \
--property_list '__request_param_id,title' \
--property_list_cnames 'item_id,item_name' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.5.2.  导入用户属性

假设有 Nginx 日志描述了以下用户属性( 参考代码包下 examples/profiles.log ):

123.4.5.6 - [12/May/2018:13:01:11 +0800] "POST /profile?user=bug29&is_member=true" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男" "131"
123.4.5.7 - [13/May/2018:10:20:32 +0800] "POST /profile?user=%e5%b0%8f%e6%ad%a6&is_member=false" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "女" ""
CODE

对应 Nginx 配置的格式如下:

log_format compression '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$gender" "$score"';
access_log /data/nginx_log/access.log compression;
CODE

将这些数据导入神策系统中,以 $reqeust 解析后的参数 user 作为用户 ID,导入三个用户属性: 自定义变量 $gender 和 $score ,以及 $request 解析后的参数 is_member:

python3 format_importer.py nginx_profile \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from '__request_param_user' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉--is_login
--filename './examples/profiles.log' \
--log_format '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$gender" "$score"' \
--property_list 'gender,score,__request_param_is_member' \
--property_list_cnames 'gender,score,is_member' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.5.3. 导入物品属性

假设有 Nginx 日志描述了以下物品数据 ( 参考代码包下 examples/item.log ):

123.4.5.6 - [12/May/2018:13:01:11 +0800] "GET /item?id=13245&action=view&cate=%e7%94%b7%e8%a3%85" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲" "bug29"
123.4.5.6 - [12/May/2018:13:05:03 +0800] "GET /item?id=13245&action=buy&cate=%e7%94%b7%e8%a3%85" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲" "bug29"
123.4.5.7 - [13/May/2018:10:20:32 +0800] "GET /item?id=23421&action=view&cate=%e9%9f%b3%e5%83%8f" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "New Order Technique 2CD豪华版 欧版行货 全新未拆" "小武"
123.8.5.7 - [13/May/2018:20:42:53 +0800] "GET /item?id=&action=view&cate=%e6%af%8d%e5%a9%b4" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型" "菠菜"
CODE

对应 Nginx 配置的格式如下:

log_format compression '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$title" "$user_id"';
access_log /data/nginx_log/access.log compression;
CODE

将这些数据导入神策系统中,--item_type 和 --item_id 配置分别指定物品数据的 item_type 和 item_id 字段值:

python3 format_importer.py nginx_item \
--url 'http://localhost:8106/sa?project=xxx' \
--item_id 'user_id' \
--item_type '__request_param_action' \
--filename './examples/item.log' \
--log_format '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$title" "$user_id"' \
--property_list '__request_param_id,title' \
--property_list_cnames '$gender,$score' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.5.4. 导入用户关联关系

注意:导入用户关联关系需要 v1.13.5 及以上版本

假设有 Nginx 日志描述了以下用户关联关系 ( 参考代码包下 examples/signup.log ):

123.4.5.6 - [12/May/2018:13:01:11 +0800] "POST /login?user_id=bug29&device_id=0c0c93f5-c747-4c1a-acfc-e75279720da1" 200 1127 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36" "http://fake_web.com/login.html" "男" "131"
123.4.5.7 - [13/May/2018:10:20:32 +0800] "POST /login?user_id=%e5%b0%8f%e6%ad%a6&device_id=ac0eadfb-cd5d-44b6-8a21-079862773c11" 200 1127 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "http://www.baidu.com?q=abc" "女" ""
CODE

将这些数据导入到神策系统中,--login_id_from 和 --anonymous_id_from 配置分别指定用户关联关系的登录 ID 与匿名 ID:

python3 format_importer.py nginx_signup \
--url 'http://localhost:8106/sa?project=xxx' \
--login_id_from '__request_param_user_id' \
--anonymous_id_from '__request_param_device_id' \
--filename './examples/signup.log' \
--log_format '$remote_addr [$time_local] "$request" $status $bytes_sent "$http_user_agent" "$http_referer" "$gender" "$score"' \
--property_list '__request_param_user_id,__request_param_device_id' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.6. 导入 MySQL 的数据

注意使用 MySQL 导入需要先安装相关库,请运行下面命令来安装 PyMySQL:

python3 -m pip install PyMySQL --upgrade
CODE

2.6.1. 导入事件

假设有 MySQL 数据库描述了以下用户行为 ( 参考代码包下 examples/events.sql ):

drop table if exists events;
create table events (
user_id varchar(100),
action varchar(100),
time timestamp,
item_id int,
item_name text,
item_cate varchar(100));
insert into events values('bug29', 'view', '2018-05-12 13:01:11', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('bug29', 'buy', '2018-05-12 13:05:03', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('小武', 'view', '2018-05-13 10:20:32', 23421, 'New Order Technique 2CD豪华版 欧版行货 全新未拆', '音像');
insert into events values('菠菜', 'view', '2018-05-13 20:42:53', 3442, 'NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型', '母婴');
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID,time 列作为事件发生的时间,action 列作为事件名称,只导入 item_id 和 item_name 作为事件属性。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py mysql_event \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--timestamp_from 'time' \
--event_from 'action' \
--user 'root' \
--password 'pass' \
--host 'localhost' \
--port 3307 \
--db 'test_db' \
--sql 'select user_id, action, time, item_id, item_name from events order by time;' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.6.2. 导入用户属性

假设有 MySQL 数据库描述了以下用户属性 ( 参考代码包下 examples/profiles.sql ):

drop table if exists profiles;
create table profiles (
user_id varchar(100),
gender varchar(20),
is_member bool,
score int);
insert into profiles values('bug29', '男', true, 131);
insert into profiles values('小武', '女', false, null);
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID,剩余列全部作为用户属性。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py mysql_profile \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--user 'root' \
--password 'pass' \
--host 'localhost' \
--port 3307 \
--db 'test_db' \
--sql 'select user_id, gender, is_member, score from profiles order by user_id;' \
--bool_property_list 'is_member' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.6.3. 导入物品属性

假设有 MySQL 数据库描述了以下物品数据 ( 参考代码包下 examples/events.sql ):

drop table if exists events;
create table events (
user_id varchar(100),
action varchar(100),
time timestamp,
item_id int,
item_name text,
item_cate varchar(100));
insert into events values('bug29', 'view', '2018-05-12 13:01:11', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('bug29', 'buy', '2018-05-12 13:05:03', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('小武', 'view', '2018-05-13 10:20:32', 23421, 'New Order Technique 2CD豪华版 欧版行货 全新未拆', '音像');
insert into events values('菠菜', 'view', '2018-05-13 20:42:53', 3442, 'NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型', '母婴');
CODE

将这些数据导入神策系统中,以表中的 item_id 列作为物品数据的 item_id,action 列作为 item_type 。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

format_importer.py mysql_item \
--url 'http://localhost:8106/sa?project=xxx' \
--item_type 'action' \
--item_id 'item_id' \
--user 'root' \
--password 'root1234' \
--host 'localhost' \
--port 3306 \
--db 'sa_item' \
--sql 'select user_id, gender, is_member, score from events order by user_id' \
--debug \ # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.6.4. 导入用户关联关系

注意:导入用户关联关系需要 v1.13.5 及以上版本

假设有 MySQL 数据库描述了以下用户关联关系 ( 参考代码包下 examples/signup.sql ):

drop table if exists users;
create table users (
    user_id varchar(100),
    device_id varchar(100));
insert into users values('bug29', '0c0c93f5-c747-4c1a-acfc-e75279720da1');
insert into users values('小武', 'ac0eadfb-cd5d-44b6-8a21-079862773c11');
insert into users values('菠菜', '2903f1d4-e20d-4866-8614-66d9101a3bd3');
CODE

将这些数据导入到神策系统中,--login_id_from 和 --anonymous_id_from 配置分别指定用户关联关系的登录 ID 与匿名 ID。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py mysql_signup \
--url 'http://localhost:8106/sa?project=xxx' \
--login_id_from 'user_id' \
--anonymous_id_from 'device_id' \
--user 'root' \
--password 'root1234' \
--host 'localhost' \
--port 3306 \
--db 'sa_user' \
--sql 'select user_id, device_id from users order by user_id;' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE


2.7. 导入 JSON 格式的日志

用户也可以将日志写入文件中,每行是一个符合数据格式 的数据。假设有日志描述了以下事件、用户属性、物品数据 ( 参考代码包下 examples/json_data.json )。

{"type":"track","time":1526101271000,"distinct_id":"bug29","properties":{"item_id":13245.0,"item_name":"\u7537\u58eb\u62a4\u8033\u4fdd\u6696\u9e2d\u820c\u76ae\u5e3d\u5e73\u9876\u516b\u89d2\u5e3d\u5934\u5c42\u725b\u76ae\u5e3d\u5b50\u65f6\u5c1a\u4f11\u95f2"},"event":"view","time_free":true}
{"type":"track","time":1526101503000,"distinct_id":"bug29","properties":{"item_id":13245.0,"item_name":"\u7537\u58eb\u62a4\u8033\u4fdd\u6696\u9e2d\u820c\u76ae\u5e3d\u5e73\u9876\u516b\u89d2\u5e3d\u5934\u5c42\u725b\u76ae\u5e3d\u5b50\u65f6\u5c1a\u4f11\u95f2"},"event":"buy","time_free":true}
{"type":"track","time":1526178032000,"distinct_id":"\u5c0f\u6b66","properties":{"item_id":23421.0,"item_name":"New Order Technique 2CD\u8c6a\u534e\u7248 \u6b27\u7248\u884c\u8d27 \u5168\u65b0\u672a\u62c6"}, "event":"view","time_free":true}
{"type":"track","time":1526215373000,"distinct_id":"\u83e0\u83dc","properties":{"item_id":3442.0,"item_name":"NUK\u5b89\u629a\u5976\u5634\u5b9d\u5b9d\u9632\u80c0\u6c14\u5b89\u6170\u5976\u5634\u4e73\u80f6\u8fea\u58eb\u5c3c\u5b89\u7761\u578b"},"event":"view","time_free":true}
{"type":"profile_set","time":1526263297951,"distinct_id":"bug29","properties":{"gender":"\u7537","is_member":true,"score":131.0},"time_free":true}
{"type":"profile_set","time":1526263297951,"distinct_id":"\u5c0f\u6b66","properties":{"gender":"\u5973","is_member":false},"time_free":true}
{"type":"item_set","properties":{"name":"yuejz","OrderPaid":12.1},"item_id":"item_id","time":1566022866941,"item_type":"item_type"}
{"type":"item_set","properties":{"name":"yuejz"},"item_id":"item_id","time":1566022866941,"item_type":"item_type"}
{"type":"item_set","time":1566023226152,"properties":{"OrderTime":"2019-07-01 12:02:36","OrderPaid":12.1},"item_id":"item_id","item_type":"item_type"}
{"type":"item_delete","item_id":"item_id","properties":{"OrderPaid":12.1,"OrderTime":"2019-07-01 12:02:36"},"item_type":"item_type"}
CODE

注意:若事件或用户属性数据中 distinct_id 为登录 ID,需在 properties 里添加 "$is_login_id":true 属性来标示。

将这些数据导入神策系统中:

python3 format_importer.py json \
--url 'http://localhost:8106/sa?project=xxx' \
--path './examples/json_data.json' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.8. 导入 Oracle 的数据

注意导入 Oracle 中的数据需要先安装相关库,请运行下面命令来安装 cx_Oracle 并需要确保机器上包含了 Oracle 客户端程序包:

python3 -m pip install cx_Oracle --upgrade
CODE

2.8.1. 导入事件

假设有 Oracle 数据库描述了以下用户行为 ( 参考代码包下 examples/events.plsql ):

drop table if exists events;
create table events (
user_id varchar(100),
action varchar(100),
time timestamp,
item_id int,
item_name text,
item_cate varchar(100));
insert into events values('bug29', 'view', '2018-05-12 13:01:11', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('bug29', 'buy', '2018-05-12 13:05:03', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('小武', 'view', '2018-05-13 10:20:32', 23421, 'New Order Technique 2CD豪华版 欧版行货 全新未拆', '音像');
insert into events values('菠菜', 'view', '2018-05-13 20:42:53', 3442, 'NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型', '母婴');
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID,time 列作为事件发生的时间,action 列作为事件名称,只导入 item_id 和 item_name 作为事件属性。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py oracle_event \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉 --is_login
--timestamp_from 'time' \
--event_from 'action' \
--user 'root' \
--password 'pass' \
--dsn '127.0.0.1/orcl' \
--sql 'select USER_ID as "user_id", ACTION as "action", TIME as "time", ITEM_ID as "item_id", ITEM_NAME as "item_name" from events order by time' \
--case_sensitive true
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

2.8.2. 导入用户属性

假设有 Oracle 数据库描述了以下用户属性 ( 参考代码包下 examples/profiles.plsql )。

drop table if exists profiles;
create table profiles (
user_id varchar(100),
gender varchar(20),
is_member bool,
score int);
insert into profiles values('bug29', '男', true, 131);
insert into profiles values('小武', '女', false, null);
CODE

将这些数据导入神策系统中,以 user_id 列作为用户 ID,剩余列全部作为用户属性。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py oracle_profile \
--url 'http://localhost:8106/sa?project=xxx' \
--distinct_id_from 'user_id' \
--is_login \ # 标示 distinct_id 为登录 ID,若 distinct_id 为匿名 ID,则去掉--is_login
--user 'root' \
--password 'pass' \
--dsn '127.0.0.1/orcl \
--sql 'select USER_ID as "user_id", GENDER as "gender", IS_MEMBER as "is_member", SCORE as "score" from profiles order by user_id' \
--case_sensitive true
--bool_property_list 'is_member' \
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.8.3. 导入物品数据

假设有 Oracle 数据库描述了以下物品数据 ( 参考代码包下 examples/events.plsql ):

drop table if exists events;
create table events (
user_id varchar(100),
action varchar(100),
time timestamp,
item_id int,
item_name text,
item_cate varchar(100));
insert into events values('bug29', 'view', '2018-05-12 13:01:11', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('bug29', 'buy', '2018-05-12 13:05:03', 13245, '男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲', '男装');
insert into events values('小武', 'view', '2018-05-13 10:20:32', 23421, 'New Order Technique 2CD豪华版 欧版行货 全新未拆', '音像');
insert into events values('菠菜', 'view', '2018-05-13 20:42:53', 3442, 'NUK安抚奶嘴宝宝防胀气安慰奶嘴乳胶迪士尼安睡型', '母婴');
CODE

将这些数据导入神策系统中,以数据库中的 item_id 列作为物品数据的 item_id,action 列作为物品数据的 item_type,只导入 item_cate 和 item_name 作为事件属性。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py oracle_item \
--url 'http://localhost:8106/sa?project=xxx' \
--item_id 'item_id' \
--item_type 'action' \
--user 'root' \
--password 'pass' \
--dsn '127.0.0.1/orcl' \
--sql 'select ITEM_ID as "item_id", ACTION as "action", ITEM_CATE as "item_cate", ITEM_NAME as "item_name" from events order by time' \
--case_sensitive true
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.8.4. 导入用户关联关系

注意:导入用户关联关系需要 v1.13.5 及以上版本

假设有 Oracle 数据库描述了以下用户关联关系 ( 参考代码包下 examples/signup.plsql ):

create table users (
    user_id varchar2(100),
    device_id varchar(200));
insert into users (user_id, device_id) values('bug29', '0c0c93f5-c747-4c1a-acfc-e75279720da1');
insert into users (user_id, device_id) values('小武', 'ac0eadfb-cd5d-44b6-8a21-079862773c11');
insert into users (user_id, device_id) values('菠菜', '2903f1d4-e20d-4866-8614-66d9101a3bd3');
commit;
CODE

将数据导入到神策系统中,--login_id_from 和 --anonymous_id_from 配置分别指定用户关联关系的登录 ID 与匿名 ID。最后的 ORDER BY 主要是保证多次查询数据顺序一致,这样假如导入一半失败了,可以在顺序不变的情况下用 --skip_cnt 配置跳过导入成功的行数:

python3 format_importer.py oracle_item \
--url 'http://localhost:8106/sa?project=xxx' \
--login_id_from 'USER_ID' \
--anonymous_id_from 'DEVICE_ID' \
--user 'root' \
--password 'pass' \
--dsn '127.0.0.1/orcl' \
--sql 'select USER_ID as "user_id",DEVICE_ID as "device_id" from users order by user_id' \
--case_sensitive true
--debug # 校验数据格式,不会导入数据,正式使用的时候去掉--debug
CODE

2.9. 从配置文件中导入

FormatImporter 支持从配置文件中读取参数,使用方法是在子命令之后增加 @<配置文件路径>。下载后的源码包的 conf 目录下就包含了默认的几个配置,可以修改配置的内容后使用。

以 2.2.1 为例,我们可以先把导入参数写到配置文件中 ( 参考代码包下 conf/csv_event.conf ):

url: http://localhost:8106/sa
distinct_id_from: user_id
is_login
event_from: action
timestamp_from: time
filename: ./examples/events.csv
property_list: item_id,item_name
debug # 校验数据格式,不会导入数据,正式使用的时候去掉 --debug
CODE

然后执行:

python3 format_importer.py csv_event @./conf/csv_event.conf
CODE

其他类型的数据都可以使用这种方法进行导入。

2.10. 注意事项

  1. 当导入数据到神策之前,请确认下数据中 distinct_id 是登录 ID 还是匿名 ID。若是登录 ID,则需在导入命令中添加参数 --is_login。
  2. 先选用部分数据,增加 --debug 选项测试通过后再正式导入。增加 --debug 后进入调试模式,不会真正导入数据。对于每条数据,若成功则发送的数据打到标准输出上,否则会打印出错信息。执行完毕之后打印读取多少条,出错多少条。
  3. 导入 CSV / Nginx 日志的时候,可以先用 head -1000 file_name > test_file 的方式先导入一部分数据到测试文件,然后使用测试文件导入。
  4. 导入 MySQL 数据的时候,可以在查询语句后面加上 LIMIT 1000 然后测试导入。
  5. 运行时在 format_importer 目录下会产生日志, 日志名为 format_importer.log,包含比输出更全的调试信息,如果增加 --debug 后屏幕上输出比较多,可以查看日志查找出错信息和调试信息。
  6. 由于参数比较复杂,建议使用配置文件的方式传递参数,具体配置文件样例可以解压后查看 conf 目录。
  7. 对于 CSV 日志导入,需要确保日志文件是有效的 CSV 格式,建议先阅读 本文常见问题中【CSV 如何转义】相关的内容。
  8. 由于 Nginx 的日志格式所限,导入的 property 的名字可能看起来并不具有可读性,比如:__request_param_action 等。强烈建议使用 property_list_cnames 来转化成可读性较强的属性名。
  9. 对于 MySQL 导入,如果 SQL 语句较长,容易出现 SQL 传递给程序后 shell 转义错误的问题,建议出错时查看在 format_impoter 目录下的日志,会在启动后第一条日志里面写上传递的参数,请仔细查看和传递的 SQL 是否一致。另外 SQL 较长的情况下,建议使用 --filename 的方式将 SQL 语句写入文件传递。
  10. 对于 MySQL 导入,如果 SQL 语句设计到两个表的 join 操作,那么指定列名时需要使用别名或者列名,而不是<表名>.<列名>。详细解释参见本文常见问题中【用 MySQL 将两张表 join 起来导入,明明有数据为什么提示我 distinct_id / timestamp / event 为空?】相关内容。
  11. 如果希望提高导入速度,建议先使用 --output_file 生成日志文件,然后使用 BatchImporter 或者 HDFSImporter 导入数据。

3. 使用详解

3.1. 子命令说明

子命令就是跟在执行脚本后的第一个参数,比如 2.1 中执行

python3 format_importer.py csv_event \
--url 'http://localhost:8106/sa?project=xxx' \
--event_default 'UserBuy' \
--distinct_id_from 'user' \
--timestamp_from 'buy_time' \
--filename 'buy.csv'
CODE

使用的子命令是 csv_event,表示将 CSV 格式文件转化成事件导入。目前支持以下 17 种子命令:

自命令名称解释
csv_profile将 CSV 格式文件转化成用户属性导入
csv_event将 CSV 格式文件转化成事件导入
csv_item将 CSV 格式文件转化成物品数据导入
csv_signup将 CSV 格式文件转化成用户关联关系导入
mysql_profile将 MySQL 数据库中数据转化成用户属性导入
mysql_event将 MySQL 数据库中数据转化成事件导入
mysql_item将 MySQL 数据库中数据转化成物品数据导入
mysql_signup将 MySQL 数据库中数据转化成用户关联关系导入
nginx_profile

将 Nginx 日志转化成用户属性导入

nginx_event将 Nginx 日志转化成事件导入
nginx_item将 Nginx 日志转化成物品数据导入
nginx_signup将 Nginx 日志转化成用户关联关系导入
json将 JSON 日志导入,注意日志不区分 event、profile、item、signup
oracle_profile将 Oracle 数据库中数据转化成用户属性导入
oracle_event将 Oracle 数据库中数据转化成事件导入
oracle_item将 Oracle 数据库中数据转化成物品数据导入
oracle_signup将 Oracle 数据库中数据转化成用户关联关系导入

如果想看单个子命令支持哪些参数,可以在子命令之后加 -h,将获取所有的参数和说明,如:

python3 format_importer.py csv_event -h
python3 format_importer.py json -h
CODE

3.2. 公共参数

通用的公共参数包括:

参数名别名是否必填解释
--url-l和 output_file 选一个必填数据接收地址
--output_file-O和 url 选一个必填输出的文件名,输出每行是一个符合格式的 JSON。
--project-j指定的 project 名,默认是 default
--skip_cnt-c第一次运行请忽略,如果运行失败,可以使用此配置指定跳过开头多少行
--debug-D如果指定了就是使用 debug 模式,不会导入数据,只在 stdout 显示数据,参见调试模式
--quit_on_error-Q如果选中,则出现一条错误日志就会退出
-- log_level-lv日志输出最小等级,默认为 DEBUG

此外,从 CSV 表格、Nginx 日志、MySQL 数据库、Oracle 数据库导入数据时,需要区分是导入 event、profile、item 还是 signup,四者有不同的公共参数;导入 JSON 日志时,只支持设置如上的公共参数。

3.3. 子命令公共参数

3.3.1. event 相关子命令

参数名别名选填/必填解释
--distinct_id_from-df必填指定列作为 distinct_id
--is_login
选填distinct_id 是否是登录 ID,默认为否
--event_from-ef和 event_default 选一个必填指定列作为事件名
--event_default-ed和 event_from 选一个必填指定固定字符串作为事件名
--timestamp_from-tf和 timestamp_default 选一个必填指定列作为 time
--timestamp_default-td和 timestamp_from 选一个必填指定固定时间字符串作为 time
--timestamp_format-tf选填和 timestamp_from 一起使用,通过此配置指定时间格式。默认是%Y-%m-%d %H:%M:%S

3.3.2. profile 相关子命令

参数名别名选填/必填解释
--distinct_id_from-df必填指定列作为 distinct_id
--is_login
选填distinct_id 是否是登录 ID,默认为否

3.3.3. item 相关子命令

参数名别名选填/必填解释
--item_type
必填指定列作为 item_type
--item_id
必填指定列作为 item_id

3.3.4. signup 相关子命令

参数名别名选填/必填解释
--login_id_from
必填指定列作为登录 ID
--anonymous_id_from
必填指定列作为匿名 ID

3.4. 导入 CSV 格式的其他参数

参数名别名是否必填 解释
--filename-fCSV 文件路径。
--property_list-pl用逗号分割选取的 property,举例 -p name,time 将会将 name 和 time 两列作为 property 导入。如果不填写则表示全部作为 property 导入。
--skip_identify-i对应的列将不会做自动类型判断,举例配置 --skip_identify name,id 后将会对 name 和 id 不做类型判断,完全作为 string 导入。如果不填写则表示全部的选中列都会自动做类型判断。
--ignore_value
指定某些值为空,比如指定 --ignore_value null 则所有的 null 都被认为是空值。
--csv_delimiter
CSV 文件的列分隔符,默认为 ',',只接受单字符参数,也可以传 \ + ascii的数字,比如 \9 表示是 \t 。
--csv_quotechar
CSV 文件的引用字符,用于指定CSV字符串的开始和结尾,默认为 '"',只接受单字符参数,也可以传 \ + ascii的数字,比如 \9 表示是 \t 。
--csv_prefetch_lines
CSV 文件预读行数,预读用于判断列的类型,默认为 -1,即预读整个文件。注意如果数据分布不均(比如前几行某个字段没有但是后面有)不要加这个参数。
--file_encoding
设置 CSV 文件编码格式,默认为 utf-8。
--list_type

指定属性为 list

用逗号分割选取的属性,举例--list_type list_a, list_b 将会将 list_a 和 list_b 两列作为 list 格式导入。list_type:list_a, list_b

list 格式数据需要以 | 分割,举例: 1|2|3

3.5. 导入 Nginx 日志的其他参数

参数名别名 是否必填 解释
--filename-fNginx 日志文件路径。
--log_format-FNginx 日志配置,类似 '"$remote_addr" "$time_local" "$http_refer" "$status"'。
--property_list-pl用逗号分割选取的 property 举例 --property_list http_refer,status 将会将 http_refer 和 status 两列作为 property 导入。
--skip_identify-i对应的列将不会做自动类型判断,举例 --skip_identify request_user,status 将会将 request_user, status 不做类型判断,完全作为 string 导入。如果不填写则表示全部的选中列都会自动做类型判断。
--url_fields-uf对应的列将作为 url 解析,用逗号分割。解析后会生成 __<字段名>_<解析内容> 这样命名的 property,解析内容包括 netloc, path, query, param_<参数名>。举例对于$my_url字段值为 http://www.abc.com/path/to/mine?k1=v1&k2=2 ,会解析为 {"__my_url_netloc": "www.abc.com","__my_url_path": "/path/to/mine", "__my_url_query":"k1=v1&k2=v", "__my_url_param_k1": "v1","__my_url_param_k2":2}。注意可以在 property_list 配置这些字段。默认是 "http_referer"。
--filter_path-fp过滤对应的 path,可多选。这里的 path 取的是 $request 的 path,支持正则。举例 --filter_path '.*\.gif' --filter_path '/index\.html' 将过滤对 gif 的请求和 index 的请求。
--ip_from-if只对 event 有效,哪个字段作为 ip,如果指定,则每条数据对应的 ip 为对应字段的值,默认是 $remote_addr。
--ignore_value
指定某些值为空,比如指定 --ignore_value null 则所有的null都被认为是空值。
--property_list_cnames
用逗号分割 property 的对应名称, 需要和--property_list 一一对应。

3.6. 导入 MySQL 数据的其他参数

参数名别名 是否必填解释
--user-u连接 MySQL 数据库所需的用户名。
--password-p连接 MySQL 数据库所需的密码。
--host-i连接 MySQL 数据库所需的地址。
--port-P连接 MySQL 数据库所需的端口。
--db-dMySQL 对应的数据库名,一次只能指定一个。
--sql-q和 filename 选一个必填查询语句,建议加 order by 等方式保证多次查询结果顺序一致。
--filename-f和 sql 选一个必填查询语句所在的文件路径,建议加 order by 等方式保证多次查询结果顺序一致。
--bool_property_list-bp逗号分割的 bool 类型属性列表,会将对应的属性值为 1 的转化为 true,0 转化为 false。
--case_sensitive-cs导入的属性名是否是大小写敏感,注意如果大小写不敏感会全部转化为大写,默认为 true。

3.7. 导入 JSON 日志的其他参数

参数名别名 是否必填解释
--path-p日志的文件/目录路径

注意导入 JSON 日志,如果传递了日志目录,那么会遍历该目录下一级的所有的文件,并且按照字母顺序导入。本参数不支持嵌套目录。

3.8. 导入 Oracle 数据的其他参数

参数名别名是否必填解释
--user-u连接 Oracle 数据库所需的用户名。
--password-p连接 Oracle 数据库所需的密码。
--dsn-dsn连接 Oracle 数据库所需的 dsn。
--sql-q和 filename 选一个必填查询语句,建议加 order by 等方式保证多次查询结果顺序一致。
--filename-f和 sql 选一个必填查询语句所在的文件路径,建议加 order by 等方式保证多次查询结果顺序一致。
--bool_property_list-bp逗号分割的 bool 类型属性列表,会将对应的属性值为 1 的转化为 true,0 转化为 false。
--case_sensitive-cs导入的属性名是否是大小写敏感,注意如果大小写不敏感会全部转化为大写,默认为 false。

4. 常见问题

1、CSV 的表头是中文是否可以支持

根据在数据格式里面的说明,property 的名称是不可以包含中文的,但是可以设置 property 在神策分析的属性显示名,通过配置 --add_cname 即可自动完成这一过程。例如 buy.csv 格式如下:

用户名,购买时间,商品id,商品名称,商品类别
小明,2015-01-20 10:35:22,13579,真皮帽子 男士护耳保暖鸭舌皮帽平顶八角帽头层牛皮帽子时尚休闲,男装
小芳,2015-07-13 23:12:03,24680,官方正品ZINO 3D透亮无瑕BB霜SPF30PA++ 防晒遮瑕美白 小样 3ml,护肤
小武,2015-04-03 20:30:01,31415,New Order Technique 2CD豪华版 欧版行货 全新未拆,音像
CODE

导入参数如下:

python3 format_importer.py csv_event \
--url 'http://localhost:8106/sa?project=xxx' \
--event_default 'UserBuy' \
--distinct_id_from '用户名' \
--is_login \
--timestamp_from '购买时间' \
--filename 'buy.csv' \
--add_cname \
--web_url 'http://localhost:8107' \
--username admin \
--password password
CODE

注意在不同的平台上对编码要求不同,需要保证默认编码和文件编码一致,具体请参考 Windows 下使用说明。

其中 web_url 与 url 基本一致,只有端口号不同,url 获取方式见上文。

2、如何配置 Nginx 可以过滤掉静态文件?

假设 Nginx 日志中包含了对 gif 文件,css 文件和 js 文件的请求,这些请求希望过滤掉,可以使用  --filter_path 来过滤。

python3 format_importer.py nginx_event \
--filter_path '.*\.gif' \
--filter_path '.*\.css' \
--filter_path '.*\.js' \
# 其他参数。。。
CODE


3、导入了一半出错了怎么办?

当出现解析数据出错时,导入工具会直接在终端打印错误原因和错误的行数,然后丢弃错误数据继续处理。打印日志类似:

2015-10-28 14:58:52,020 808 WARNING failed to parse line 12
2015-10-28 14:58:52,021 809 WARNING Traceback (most recent call last):
File "format_importer.py", line 804, in main
sa.track(distinct_id, event, properties)
File "/Users/padme/git/sa2/tools/format_importer/sensorsanalytics/sdk.py", line 118, in track
data = self._normalize_data(data)
File "/Users/padme/git/sa2/tools/format_importer/sensorsanalytics/sdk.py", line 149, in _normalize_data
raise SensorsAnalyticsIllegalDataException("property [distinct_id] must not be empty")
sensorsanalytics.sdk.SensorsAnalyticsIllegalDataException: property [distinct_id] must not be empty
CODE

在运行结束的时候会打印读取 (total_read) 了多少行,写入 (total_write) 多少行,出错 (error) 了多少行,跳过 (skip) 了多少行,类似:

2015-10-28 14:58:52,023 618 INFO end import nginx
2015-10-28 14:58:52,024 838 INFO counter = {'error': 3, 'skip': 0, 'total': 300, 'total_read': 100, 'total_write': 97}.
CODE

如果希望能够出错就提示,可以增加选项 --quit_on_error,这样的话出错了的日志如下:

2015-10-28 14:58:29,499 808 WARNING failed to parse line 12
2015-10-28 14:58:29,502 809 WARNING Traceback (most recent call last):
File "format_importer.py", line 804, in main
sa.track(distinct_id, event, properties)
File "/Users/padme/git/sa2/tools/format_importer/sensorsanalytics/sdk.py", line 118, in track
data = self._normalize_data(data)
File "/Users/padme/git/sa2/tools/format_importer/sensorsanalytics/sdk.py", line 149, in _normalize_data
raise SensorsAnalyticsIllegalDataException("property [distinct_id] must not be empty")
sensorsanalytics.sdk.SensorsAnalyticsIllegalDataException: property [distinct_id] must not be empty

2015-10-28 14:58:29,502 618 INFO end import nginx
2015-10-28 14:58:29,502 835 ERROR failed to import, please fix it and run with[--skip_cnt 11] again!
CODE

注意下方提示,说明已经成功导入了 11 行,修复第 12 行的数据后在之前的命令上再加上参数 --skip_cnt 11 即可。

需要特别说明的是,对于 MySQL,为了防止数据错误后不可恢复的问题,请务必保证查询 SQL 多次调用结果一致,即:

  1. 没有新数据在写入,比如通过增加 WHERE 保证只导入历史数据。
  2. 查询结果增加排序选项保证顺序一致,比如增加 ORDER BY。


4、用 MySQL 将两张表 join 起来导入,明明有数据为什么提示我 distinct_id / timestamp / event 为空?

注意如果 SQL 涉及到两个表 join 的操作,列名要么是唯一的,要么取别名。这里的列名一方面是表示使用 distinct_id_from, timestamp_from, event_from 的参数,另一方面也是导入后 property 的名字。

举例,SQL 如下:

SELECT a.uid, a.event, a.time, b.property1, b.property2
FROM a JOIN b ON a.action_id = b.action_id

那么运行参数需要指定为:

--distinct_id_from 'uid' \
--timestamp_from 'time' \
--event_from 'event'

导入的 property 的名字也分别是 (property1, property2) 而不是 (b.property1, b.property2)。

如果列名不是唯一的,另一种方法是取别名。举例sql如下:

SELECT a.u AS uid, a.e AS event, a.t AS time, b.property AS property1, a.property AS property2
FROM a JOIN b ON a.action_id = b.action_id

那么运行的参数指定为:

--distinct_id_from 'uid' \
--timestamp_from 'time' \
--event_from 'event'

导入的 property 的名字也分别是 (property1, property2)。


5、用 MySQL 导入的时候,如何将数值转化成文本/文本转化成数值导入?

MySQL 的 CAST 方法支持类型转化。

举例,MySQL 中有个类型为 int 的列 property1 和一个类型为 varchar(10) 的列为 property2,可以使用 sql:

SELECT CAST(property1 AS CHAR(10)) AS property1, CAST(property2 AS SIGNED) AS property2 FROM test_table;

将 property1 的值转化成 10 个字符长的文本,将 property2 的值转化为数值。


6、如何导入其他项目

如果没有显示指定 project,则导入默认的项目 default。

可以通过 --url 里面包含 project=<project_name> 来指定 project 名,也可以通过 --project 参数来指定。如果同时指定则采用 --project 的参数。

注意,如果 --url 的值是通过右上角 “账号” -> “数据接入” -> “复制数据接收地址” 来获取的话,则复制的接口里面自带 project 参数,不需要额外指定。

此外,如果是导入 JSON 日志,可以在 JSON 中增加 "project":"project名称" 来使用一份数据导入多个 project。日志里面的 project 字段优先级高于参数。


7、CSV 如何转义

CSV 是用逗号分割的文件格式,如果某一列的内容中包含逗号,需要用双引号分割开,否则 FormatImporter 将会报错。举例,CSV 文件内容如下:

col1,col2,col3
a,b,c,d

运行时将会报错:

csv error near line 1: content has 1 more fields than header

即提示内容的列数比头部的列数多一列,正确的做法是加上双引号:

col1,col2,col3
a,"b,c",d

将会被识别为:

col1col2col3
ab,cd

注意双引号可以跨行。举例,CSV 文件内容如下:

col1,col2,col3
a,"b,c
d,e",f

将会被识别为:

col1col2col3
ab,c,d,ef

因此如果某一列以双引号开头,那么 CSV 会一直找到下一个双引号结尾。如果两个双引号直接的字符过多,会报错列超长:

_csv.Error: field larger than field limit (131072)

解决方法有两个,一个是可以通过 --csv_quotechar 参数替换 CSV 默认的字符串边界符号,需要保证这个符号没有出现过。 另外就是对双引号转义,需要把一个双引号变成两个,并且对本列用双引号引住。举例,CSV 文件内容如下:

col1,col2,col3
a,"""b",c

将会识别为:

col1col2col3
a"bc


8、Windows下使用说明

在 Windows 下使用 FormatImporter 时,由于终端的语法不同,默认编码不同,有以下几个注意事项:

1. 尽量使用配置文件传递参数,而不是命令行传递,避免命令转义。注意上述示例中的命令都是基于 Unix 操作系统,如果直接在 Windows 下面运行可能会出现语法错误或转义。
2. CSV 文件默认支持 utf-8 编码,如果出现编码错误,可以先查看文件编码格式,然后使用 --file_encoding 命令设置编码格式
3. 如果需要在 windows 下访问 MySQL / Oracle,需要对编码做特殊处理,具体处理方式请咨询神策技术支持同学。


9、导入限制

导入数据时,FormatImporter 导入本身没有数据量上限,但是如果导入数据较大时,速度较慢,大约 1 分钟导入 500~700 条左右,且导入的速度和网络稳定性有关,如果导入超时,可以尝试下使用内网数据接收地址