java 爬取快递100 快递信息

目前有两个查询方式。

1. 通过 快递单号自动匹配快递公司 信息。
2. 通过 快递公司编码 和 快递单号 获取物流信息。

Code

ExpressSearchUtils - 快递查询工具类

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zhou.demo.ExpressReturnData;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;

import java.nio.charset.Charset;
import java.util.ArrayList;

/**
 * 快递查询工具类
 *
 * @author zhou
 */
public class ExpressSearchUtils {

    private static final Logger logger = LoggerFactory.getLogger(ExpressSearchUtils.class);

    /**
     * 根据 快递单号 自动获取快递公司的 URL
     */
    private static final String AUTO_SEARCH_COMPANY_URL = "https://www.kuaidi100.com/autonumber/autoComNum";

    /**
     * 根据 快递公司编码 和 快递单号 查询物流信息 的 URL
     */
    private static final String SEARCH_EXPRESS_INFO_URL = "https://m.kuaidi100.com/query";

    /**
     * 根据 【快递单号】 获取 【快递公司】 (90% 的正确率)
     * (由于接口返回的快递公司有时候会有多个, 第一个概率是最高的, 就直接返回第一家公司的)
     *
     * @param number 快递单号
     * @return 快递公司
     */
    public static String getCompanyCodeByNumber(String number) {
        if (StringUtils.isBlank(number)) {
            return null;
        }
        try {
            HttpPost httpPost = new HttpPost(AUTO_SEARCH_COMPANY_URL);
            // 必须携带 User-Agent 请求头信息 (参数是直接从浏览器请求中 copy 的)
            httpPost.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");
            // 请求参数
            ArrayList parameters = new ArrayList<>();
            parameters.add(new BasicNameValuePair("resultv2", "1"));
            parameters.add(new BasicNameValuePair("text", number));
            HttpEntity httpEntity = new UrlEncodedFormEntity(parameters);
            httpPost.setEntity(httpEntity);

            HttpClient httpClient = HttpClients.createDefault();
            HttpResponse httpResponse = httpClient.execute(httpPost);
            if (null == httpResponse) {
                return null;
            }
            if (null == httpResponse.getEntity()) {
                return null;
            }
            String responseText = EntityUtils.toString(httpResponse.getEntity(), Charset.forName("utf-8"));
            if (StringUtils.isBlank(responseText)) {
                return null;
            }
            JSONObject resultObject = JSONObject.parseObject(responseText);
            if (null == resultObject) {
                return null;
            }
            // 请求无效, 被阻拦了
            if ("301".equals(resultObject.get("returnCode"))) {
                logger.info("请求被拦截, number = [{}]", number);
                return null;
            }
            // 没有查询到相关的快递公司, 该情况为 快递单号 无效
            if ("[]".equals(String.valueOf(resultObject.get("auto")))) {
                logger.info("快递单号无效, number = [{}]", number);
                return null;
            }
            // 获取快递公司列表
            JSONArray jsonArray = JSONArray.parseArray(String.valueOf(resultObject.get("auto")));
            // 获取第一个
            JSONObject jsonObject = JSONObject.parseObject(String.valueOf(jsonArray.get(0)));
            return jsonObject.getString("comCode");
        } catch (Exception e) {
            logger.info("查询快递公司错误, number = [{}], errorMsg: {} ", number, e.getMessage(), e);
        }
        return null;
    }

    /**
     * 根据 快递公司编码 和 快递单号 查询物流信息
     *
     * @param code 快递公司编码
     * @param number 快递单号
     * @return 快递物流信息
     */
    public static ExpressReturnData getExpressInfoByCodeAndNumber(String code, String number){
        if (StringUtils.isBlank(code) || StringUtils.isBlank(number)) {
            return null;
        }
        try {
            HttpPost httpPost = new HttpPost(SEARCH_EXPRESS_INFO_URL);

            // 请求头信息
            httpPost.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");
            httpPost.setHeader(HttpHeaders.COOKIE,
                    "csrftoken=rq0RJ1s5Me6iLVBgEKr1z6zYXURo3QyiTth9vVgq; WWWID=WWW9AA328B0FD91F0096AEAD0A533959B31; Hm_lvt_22ea01af58ba2be0fec7c11b25e88e6c=1578103600,1578104194,1578105292,1578105357; Hm_lpvt_22ea01af58ba2be0fec7c11b25e88e6c=1578106770");
            httpPost.setHeader(HttpHeaders.HOST, "m.kuaidi100.com");
            httpPost.setHeader(HttpHeaders.ORIGIN, "https://m.kuaidi100.com");
            httpPost.setHeader(HttpHeaders.REFERER, "https://m.kuaidi100.com/result.jsp?nu=" + number);

            // 请求参数
            ArrayList parameters = new ArrayList<>();
            parameters.add(new BasicNameValuePair("id", "1"));
            parameters.add(new BasicNameValuePair("type", code));
            parameters.add(new BasicNameValuePair("postid", number));
            parameters.add(new BasicNameValuePair("platform", "MWWW"));
            parameters.add(new BasicNameValuePair("temp", "0.860779312047079"));
            HttpEntity httpEntity = new UrlEncodedFormEntity(parameters);
            httpPost.setEntity(httpEntity);

            // 执行请求
            HttpClient httpClient = HttpClients.createDefault();
            HttpResponse httpResponse = httpClient.execute(httpPost);
            if (null == httpResponse) {
                return null;
            }
            if (null == httpResponse.getEntity()) {
                return null;
            }
            String responseText = EntityUtils.toString(httpResponse.getEntity(), Charset.forName("utf-8"));
            if (StringUtils.isBlank(responseText)) {
                return null;
            }
            return JSONObject.parseObject(responseText, ExpressReturnData.class);
        }catch (Exception e){
            logger.info("查询快递信息错误, code = [{}], number =[{}], errorMsg: {} ", code, number, e.getMessage(), e);
        }
        return null;
    }
    
    public static void main(String[] args) {
        // 查询物流公司
		// System.out.println(getCompanyCodeByNumber("快递单号"));
        // 查询物流信息
        // System.out.println(getExpressInfoByCodeAndNumber("快递公司编码", "快递单号"));
    }

}

getExpressInfoByCodeAndNumber - 该方法需要注意的地方
  1. 这段代码可能会失效,因为他的 Token 可能会过期,你直接去 快递100 上 copy 下来,然后再查询试试。
httpPost.setHeader(HttpHeaders.COOKIE,
                    "csrftoken=rq0RJ1s5Me6iLVBgEKr1z6zYXURo3QyiTth9vVgq; WWWID=WWW9AA328B0FD91F0096AEAD0A533959B31; Hm_lvt_22ea01af58ba2be0fec7c11b25e88e6c=1578103600,1578104194,1578105292,1578105357; Hm_lpvt_22ea01af58ba2be0fec7c11b25e88e6c=1578106770"); 
  1. 这个参数的数据是随机的,也会造成不可用,直接从 快递100 上 copy 下来即可。
parameters.add(new BasicNameValuePair("temp", "0.860779312047079"));

ExpressReturnData - 返回数据对象

import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.Date;
import java.util.List;

/**
 * 快递 100 返回数据对象
 *
 * @author zhou
 */
public class ExpressReturnData {

    /**
     * 消息体
     */
    private String message;

    /**
     * 单号
     */
    private String nu;

    /**
     * 是否签收标记,请忽略,明细状态请参考state字段
     */
    private Integer ischeck;

    /**
     * 快递单明细状态标记,暂未实现,请忽略
     */
    private String condition;

    /**
     * 快递公司编码,一律用小写字母
     */
    private String com;

    /**
     * 通讯状态, 请忽略
     */
    private String status;

    /**
     * 快递单当前状态,
     * 包括0在途,1揽收,2疑难,3签收,4退签,5派件,6退回等7个状态
     */
    private Integer state;

    /**
     * 最新查询结果,数组,包含多项,全量,倒序(即时间最新的在最前)
     */
    private List data;

    // Get / Set

    private class ExpressInfo {
        /**
         * 时间,原始格式
         */
        private String time;

        /**
         * 内容
         */
        private String context;

        /**
         * 格式化后时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date ftime;

        /**
         * 这个字段好像文档没给描述, 但是接口返回了
         */
        private String location;

        // Get / Set

    }
}

SupportDeliveryEnums - 快递编码枚举

/**
 * 快递编码
 *
 * @author zhou
 */
public enum SupportDeliveryEnums {

    EMS("ems", "邮政快递"),
    SHUNFENG("shunfeng", "顺丰快递"),
    SHENTONG("shentong", "申通快递"),
    YUANTONG("yuantong", "圆通快递"),
    YUNDA("yunda", "韵达快递"),
    JD("jd", "京东快递"),
    HUITONGKUAIDI("huitongkuaidi", "百世汇通"),
    ZHONGTONG("zhongtong", "中通快递"),
    ZHAIJISONG("zhaijisong", "宅急送")
    ;

    SupportDeliveryEnums(String code, String name) {
        this.code = code;
        this.name = name;
    }

    /**
     * 快递编码
     */
    private String code;
    /**
     * 快递名称
     */
    private String name;

    public String getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

}

存在问题的请评论我及时改进,如果帮到了你请点个赞。

你可能感兴趣的:(Java)