基于java 生成打车网约车订单测试数据

背景

该需求在笔者本科的毕业设计过程中产生,需要生成一定量的数据用于聚类分析测试数据集。
笔者经过大量的搜索,依然没有找到能符合项目需要的数据集,最终决定通过代码的方式生成若干订单数据。为了方便其他人查看,笔者将查询到的数据集统一放在这里。

滴滴盖亚数据https://outreach.didichuxing.com/ 需要申请
UberMovementhttps://link.zhihu.com/?target=https%3A//movement.uber.com/ 国际网约车巨头,笔者需要国内的数据,pass
阿里天池数据集https://tianchi.aliyun.com/dataset?spm=5176.14154004.J_3941670930.21.31fe5699J4PqSP 可以搜索到滴滴公布的数据,搜索关键字”滴滴“
知乎作者总结https://www.zhihu.com/question/24335537比较全面

尽管存在诸多真实的数据集,但都不满足笔者做聚类分析的基本需求,其中最有可能满足的是2020_ccf滴滴时空预测_训练数据集,但是数据没有任何描述信息,直接从数据入手无法理解。

基本目标数据

字段类型
订单开始时间DATE
订单结束时间DATE
起点(经纬度)VARCHAR
终点(经纬度)VARCHAR

成果截图

ODinA0.png美团热点地区 ODFF56.png抓包数据
ODFWZR.pngpostman转java代码ODANEn.png热点地区数据
Orc9Dx.png生成的订单数据

代码实现

地点选择

为了尽可能保证数据真实性,作为打车地点应该选择为该城市热门地点。以哈尔滨为例,通过美团进行搜索,结果即可认为是该城市热门地点。

  1. 抓包找到数据请求路径
curl "https://apimobile.meituan.com/group/v4/poi/pcsearch/105?uuid=6961927388a74eba9ec9.1652337967.1.0.0&userid=1699913514&limit=32&offset=32&cateId=-1&q=^%^E5^%^93^%^88^%^E5^%^B0^%^94^%^E6^%^BB^%^A8&token=Im7ycrPXlIs7yI8TXXXXXXXXXXXX
  -H "Accept: */*" ^
  -H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" ^
  -H "Connection: keep-alive" ^
  -H "Cookie: uuid=6961927388a74eXXXXXXXXXXXXXXXXXXXXX
  -H "Origin: https://hrb.meituan.com" ^
  -H "Referer: https://hrb.meituan.com/" ^
  -H "Sec-Fetch-Dest: empty" ^
  -H "Sec-Fetch-Mode: cors" ^
  -H "Sec-Fetch-Site: same-site" ^
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36" ^
  -H "sec-ch-ua: ^\^" Not A;Brand^\^";v=^\^"99^\^", ^\^"Chromium^\^";v=^\^"101^\^", ^\^"Microsoft Edge^\^";v=^\^"101^\^"" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "sec-ch-ua-platform: ^\^"Windows^\^"" ^
  --compressed

发现请求通过设置offset 参数为分页查询limit 偏移量,所以可以通过设置该值(0,32,64,96,···)完成对全部数据的查询。如上图抓包数据所示

  1. 通过postman 将浏览器抓的包导入并转换成java代码,如上图postman转java代码所示
// 执行抓包代码
package com.example.point.predict;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.point.domain.mapper.HotAreaDao;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class ExecutorTask {

	// hotAreaDao 基于HotArea 的持久层接口,使用MP框架,继承BaseMapper接口
    @Autowired
    private HotAreaDao hotAreaDao;

    public void getHotArea() {
        int index = 0;
        while (index++ <= 36) {
            List<HotArea> list = doGetHotArea(String.valueOf(32 * index));
            assert list != null;
            // 持久化到数据库中
            list.forEach(a -> hotAreaDao.insert(a));

        }
    }

    private List<HotArea> doGetHotArea(String offset) {
        HttpUrl url = HttpUrl.parse("https://apimobile.meituan.com/group/v4/poi/pcsearch/105?uuid=6961927388a74eba9ec9.1652337967.1.0.0&userid=1699913514&limit=32&cateId=-1&q=%E5%93%88%E5%B0%94%E6%BB%A8&token=Im7ycrPXlIs7yI8TnP3eLq2MH8UAAAAAuxEAALrOIZCcFyKVfLB6YYHG4mtoojKPoeQebjDm6R80cgwPID2K83VeJZ9-A9VoUDwc5A&sort=default")
                .newBuilder()
                .addQueryParameter("offset", offset)
                .build();
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        Request request = new Request.Builder()
                .url(url)
                .method("GET", null)
                .addHeader("Accept", "*/*")
                .addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
                .addHeader("Connection", "keep-alive")
                .addHeader("Cookie", "uuid=6961927388a74eba9ec9.1652337967.1.0.0; _lx_utm=utm_source%3Dbing%26utm_medium%3Dorganic; _lxsdk_cuid=180b70485d4c8-06c7b6d70175c7-12333272-e1000-180b70485d4c8; ci=105; mtcdn=K; userTicket=SULzbnlKOnLeJcncXywbuPQZSqyBbqEFvtOYsQMx; _yoda_verify_resp=1ovN6a48LK40Lon%2BFF2pB9OP9nhfGcUiJEjzL%2FSrhFyDao4sU7K4toyhPpT3WnmWNj4%2Fm3M87qwvZTfv7fQw96fksSiW6OR94P6rXB7RfTPoRCGBxUrRev%2FrWv0OEWQDzuRKsp%2BWhmKlKKSF2gbVhiU%2FxcSaAtH26KdlwUPotsQB4lkYvduG3ofYO%2BhRkTMEJaSoGgqwO6uf%2B0v2NB6vVQZkWD5JJK0pSx2CEZcb0%2FnEWywKHKD7gCvRw2FbrBvZPEVxsCfq8H71Gzd7NwMr7NI%2BvcjfU345F9tN%2FAJHl8GAOQ%2FfrJgjcloer1ET3X%2FeVepex3txcxkzjPbHt4tixqcn%2F8jXHHhXMrkluYWfW6E7%2BoBNyoHMScijYJmDr3EH; _yoda_verify_rid=15289e4b3a40b014; u=1699913514; n=ccr763202343; lt=Im7ycrPXlIs7yI8TnP3eLq2MH8UAAAAAuxEAALrOIZCcFyKVfLB6YYHG4mtoojKPoeQebjDm6R80cgwPID2K83VeJZ9-A9VoUDwc5A; mt_c_token=Im7ycrPXlIs7yI8TnP3eLq2MH8UAAAAAuxEAALrOIZCcFyKVfLB6YYHG4mtoojKPoeQebjDm6R80cgwPID2K83VeJZ9-A9VoUDwc5A; token=Im7ycrPXlIs7yI8TnP3eLq2MH8UAAAAAuxEAALrOIZCcFyKVfLB6YYHG4mtoojKPoeQebjDm6R80cgwPID2K83VeJZ9-A9VoUDwc5A; token2=Im7ycrPXlIs7yI8TnP3eLq2MH8UAAAAAuxEAALrOIZCcFyKVfLB6YYHG4mtoojKPoeQebjDm6R80cgwPID2K83VeJZ9-A9VoUDwc5A; unc=ccr763202343; firstTime=1652338204933; _lxsdk_s=180b70485d5-ced-43f-b8c%7C%7C129")
                .addHeader("Origin", "https://hrb.meituan.com")
                .addHeader("Referer", "https://hrb.meituan.com/")
                .addHeader("Sec-Fetch-Dest", "empty")
                .addHeader("Sec-Fetch-Mode", "cors")
                .addHeader("Sec-Fetch-Site", "same-site")
                .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36")
                .addHeader("sec-ch-ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"")
                .addHeader("sec-ch-ua-mobile", "?0")
                .addHeader("sec-ch-ua-platform", "\"Windows\"")
                .build();
        try {
            Response response = client.newCall(request).execute();
            String json = response.body().string();
            JSONObject jsonObject = JSONObject.parseObject(json).getJSONObject("data");
            List<HotArea> list = new ArrayList<>();
            JSONArray array = jsonObject.getJSONArray("searchResult");
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                HotArea hotArea = new HotArea.HotAreaBuilder()
                        .city("哈尔滨")
                        .sourceId(73856242)
                        .title(item.getString("title"))
                        .latitude(item.getString("latitude"))
                        .longitude(item.getString("longitude"))
                        .showtype(item.getString("showType"))
                        .backcatename(item.getString("backCateName"))
                        .areaname(item.getString("areaname"))
                        .originJson(item.toJSONString())
                        .img(item.getString("imageUrl"))
                        .build();
                list.add(hotArea);
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

// HotArea 实体类,也就是具体将美团上获取的 所需要的数据封装成类
package com.example.point.predict;

import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * hot_area
 * @author 
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HotArea implements Serializable {
    /**
     * 主键id
     */
    @TableId
    private Integer id;

    /**
     * 城市
     */
    private String city;

    /**
     * 源id
     */
    private Integer sourceId;

    private String title;

    /**
     * 经度
     */
    private String latitude;

    /**
     * 维度
     */
    private String longitude;

    /**
     * 地点类型(学校、景点···)
     */
    private String showtype;

    private String backcatename;

    /**
     * ___路(学府路,和平路···)
     */
    private String areaname;

    /**
     * 原始json
     */
    private String originJson;
   
    private String img;

    private static final long serialVersionUID = 1L;
}
// HotArea 的数据持久化接口,xml不再粘贴
package com.example.point.domain.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.point.predict.HotArea;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface HotAreaDao extends BaseMapper<HotArea> {
    int deleteByPrimaryKey(Integer id);

    int insert(HotArea record);

    int insertSelective(HotArea record);

    HotArea selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(HotArea record);

    int updateByPrimaryKey(HotArea record);
}

通过创建一个Test 方法,执行ExecutorTask的getHotArea 方法即可得到热点地区信息。如上图热点地区数据所示.

  1. 生成测试订单数据集
    订单数据表
字段名类型 描述信息
or_idbigint 主键id
orginvarchar起点
destinationvarchar终点
begin_timedatetime开始时间
endtimedatetime结束时间
nodevarchar备注信息
costdouble费用
client_idvarchar乘客id
driver_idvarchar司机id
statusvarchar订单状态
fromvarchar起点经纬度(以逗号分割)
tovarchar终点经纬度(以都好分割)
distancedouble距离
durationbigint花费时间

在HotArea表中,随机获取两个不同的地址,作为一条订单记录的起点终点,并随机一个2022-5-1 到当前的一个时间作为订单开始时间。通过腾讯地图开放api 批量距离计算(矩阵)来计算。调用该接口可以返回行车距离行车时间。这样即可完成表中关键数据的生成。

package com.example.point.predict;

import com.alibaba.fastjson.JSONObject;
import com.example.point.app.MapService;
import com.example.point.domain.entity.PoOrder;
import com.example.point.domain.mapper.HotAreaDao;
import com.example.point.domain.mapper.PoOrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Random;


@Service
public class GeneralOrderData {
    @Autowired
    private HotAreaDao hotAreaDao;
    @Autowired
    private PoOrderDao orderMapper;

    public void generalData() throws IOException, ParseException {
    	// hotArea 表中存在992 条数据,随机0-992 之间的数并以主键id查询
        int fromIndex = (int) (Math.random() * 993);
        int toIndex = (int) (Math.random() * 993);
        // 避免重复
        while (fromIndex == toIndex) {
            toIndex = new Random(992).nextInt();
        }
        HotArea fromHotArea = hotAreaDao.selectByPrimaryKey(fromIndex);
        HotArea toHotArea = hotAreaDao.selectByPrimaryKey(toIndex);
        // 获取行车距离,行车时间;MapService 内部通过OkHttp3完成对Tencent 地图api的访问
        String json = new MapService().getDistance("driving", fromHotArea.getLatitude().concat(",").concat(fromHotArea.getLongitude()), toHotArea.getLatitude().concat(",").concat(toHotArea.getLongitude()));
        // 通过fastjson进行解析,接下来的distance和duration即为距离和时间,单位米和秒
        JSONObject jsonObject = JSONObject.parseObject(json)
                .getJSONObject("result")
                .getJSONArray("rows")
                .getJSONObject(0)
                .getJSONArray("elements")
                .getJSONObject(0);
        String distance = jsonObject.getString("distance");
        String duration = jsonObject.getString("duration");
		
		// 随机2022-5-1 之后的一个时间戳,作为订单开始时间。并与duration相加计算可得到结束时间
        long day = 864000;
        long startTime = (long) (Math.random() * (day + 1)) + 1651334400;
        long endTime = startTime + Long.parseLong(duration);
        
        // 订单信息封装类
        PoOrder poOrder = PoOrder.builder()
                .beginTime(new Date(startTime * 1000))
                .endtime(new Date(endTime * 1000))
                .cost(new MapService().getCost(Double.parseDouble(distance)))
                .orgin(fromHotArea.getTitle())
                .destination(toHotArea.getTitle())
                .from(fromHotArea.getLatitude().concat(",").concat(fromHotArea.getLongitude()))
                .to(toHotArea.getLatitude().concat(",").concat(toHotArea.getLongitude()))
                .driverId(String.valueOf(1000 + (int)(Math.random() * 101)))
                .clientId(String.valueOf(2000 + (int)(Math.random() * 101)))
                .status("完成")
                .distance(Double.valueOf(distance))
                .duration(Long.valueOf(duration))
                .build();
        // 持久化订单记录
        orderMapper.insert(poOrder);
    }
}


// 订单封装类
package com.example.point.domain.entity;

import java.io.Serializable;
import java.util.Date;

import lombok.Builder;
import lombok.Data;

/**
 * po_order
 * @author 
 */
@Data
@Builder
public class PoOrder implements Serializable {
    private Long orId;

    /**
     * 起点
     */
    private String orgin;

    /**
     * 终点
     */
    private String destination;

    /**
     * 开始时间
     */
    private Date beginTime;

    /**
     * 结束时间
     */
    private Date endtime;

    /**
     * 备注信息
     */
    private String node;

    /**
     * 费用
     */
    private Double cost;

    /**
     * 乘客id
     */
    private String clientId;

    /**
     * 司机id
     */
    private String driverId;

    /**
     * 订单状态
     */
    private String status;

    /**
     * 起点经纬度(以逗号分割)
     */
    private String from;

    /**
     * 终点经纬度(以都好分割)
     */
    private String to;

	 /**
     * 行车距离
     */
    private Double distance;
    
	 /**
     * 行车时间)
     */
    private Long duration;

    private static final long serialVersionUID = 1L;
}
// 订单持久化接口,BaseMapper 是Mybatis Plus 提供的mapper接口,其实并不需要理解这个类,实际上就是将一条订单保存到数据库中,可以有很多种方法
package com.example.point.domain.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.point.domain.entity.PoOrder;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PoOrderDao extends BaseMapper<PoOrder> {
    int deleteByPrimaryKey(Long orId);

    int insert(PoOrder record);

    int insertSelective(PoOrder record);

    PoOrder selectByPrimaryKey(Long orId);

    int updateByPrimaryKeySelective(PoOrder record);

    int updateByPrimaryKey(PoOrder record);
}
// 批量执行 生成订单数据
package com.example.point.predict;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.text.ParseException;


@SpringBootTest
class GeneralOrderDataTest {

    @Autowired
    private GeneralOrderData generalOrderData;

    @Test
    void generalData() {
        try {
            for (int i = 1; i < 999; i++) {
                System.out.println(i);
                if (i % 50 == 0) {
                    System.out.println("休眠  " + i);
                    // 休眠是因为地图api访问频度太高会拒绝
                    Thread.sleep(10000);
                }
                generalOrderData.generalData();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果如上图 生成的订单数据 所示

总结

通过该项目基本完成了所需要的订单数据生成,但是思考发现数据距离真实数据还有很大差别。

  • 订单起点终点:选择仍然只是理论上的热点,实际上可能存在大量在小区、医院、公司的订单数据。
  • 订单开始时间:代码中只做了随机,实际上在早晚高峰也是打车的高峰期,单纯随机并不能反映出这一定。
  • 订单真实性:发现某些数据的里程数在40公里及以上,订单价格以每公里1.9 元计算最高达到了900元,实际上这种订单大概率不会发生。
  • 优化手段:可以通过出租车gps 数据进行分析,获取到打车热力图并以此作为订单起点。对时间进行加权随机,对公里数做出限制,如果发现公里数大于30则进行筛选,仅生成1/4 的大公里数据。

数据及源码

热点城市数据(HotArea)992条 hot_area.sql
订单数据(PoOrder)约1500条 po_order.sql
代码 稍后总结
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值