实现北京114挂号平台自动挂号功能

前言

先吐槽一下北京市预约挂号统一平台,这个平台是为了方便更多的人进行挂号,不用去排长长的队伍,但是!但是!9:30 号出来了,服务器失去了响应,等可以响应了,号已经没了,甚是痛苦。因此突发奇想,是不是可以做一个自动挂号的东西呢?虽然违背原则,但是,谁让我会写程序呢?哈哈!

实现思路

使用程序进行模拟用户登录,之后获取到相应医院,科室,医生,订单的ID,进行多线程操作,即可实现自动登录功能。ps:说的简单,搞了一个周末。

准备工作

软件: eclipsePostmanBurp Suite Community Editionfirefox

Jar包:commons-logging-1.2.jarhttpclient-4.5.3.jarhttpcore-4.4.6.jarjson-20160810.jarjsoup-1_10_2.jarlog4j-1.2.17.jar

网站分析

网站的功能较为简单,由医院、科室、医生构成。当下订单的时候还有一个订单号。当然还有个在线支付的功能,我使用的医院没有采用线上支付。因此这部分不做考虑。

获取医生ID、订单ID的网址为:yyghwx.bjguahao.gov.cn/common/dutysource/appoint/1,200041160.htm?dutyDate=2017-12-27&departmentName=%25E7%2589%25B9%25E9%259C%2580%25E6%2594%25BE%25E5%25B0%2584%25E7%25A7%2591%25E9%2597%25A8%25E8%25AF%258A1&jingdu=&weidu= 打开这个网址,发现医生对应的行是一个<a>标签,即可获取到医生ID,订单ID。
订单信息确认URL:yyghwx.bjguahao.gov.cn/common/order/confirm/1/200041160/201122658/50169528.htm

编程实践

- 构建自己的HttpClient,让自己的访问一直是在同一个session下

构造自己的Httpclient

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.json.JSONObject;
public class HttpClient_Mine {
    private static final Logger LOG = LogManager.getLogger(HttpClient_Mine.class);
    public static CloseableHttpClient httpClient = null;
    public static HttpClientContext context = null;
    public static CookieStore cookieStore = null;
    public static RequestConfig requestConfig = null;
    static {
        init();
    }
    private static void init() {
        context = HttpClientContext.create();
        cookieStore = new BasicCookieStore();
        // 配置超时时间
        requestConfig = RequestConfig.custom().setConnectTimeout(120000).setSocketTimeout(60000).setConnectionRequestTimeout(60000).build();
        // 设置默认跳转以及存储cookie
        httpClient = HttpClientBuilder.create().setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                .setRedirectStrategy(new DefaultRedirectStrategy()).setDefaultRequestConfig(requestConfig)
                .setDefaultCookieStore(cookieStore).build();
    }
    /**
     * http get
     * 
     * @param url
     * @return response
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static CloseableHttpResponse get(String url) throws ClientProtocolException, IOException {
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = httpClient.execute(httpget, context);
        try {
            cookieStore = context.getCookieStore();
            List<Cookie> cookies = cookieStore.getCookies();
            for (Cookie cookie : cookies) {
                System.out.println("key:" + cookie.getName() + "  value:" + cookie.getValue());
                LOG.debug("key:" + cookie.getName() + "  value:" + cookie.getValue());
            }
        } finally {
            response.close();
        }
        return response;
    }

    /**
     * http post
     * @param url
     * @param parameters
     *            form表单
     * @return response
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static CloseableHttpResponse post(String url, String parameters)
            throws ClientProtocolException, IOException {
        HttpPost httpPost = new HttpPost(url);
        List<BasicNameValuePair> nvps = toNameValuePairList(parameters);
        httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost, context);
        try {
            cookieStore = context.getCookieStore();
            List<Cookie> cookies = cookieStore.getCookies();
            for (Cookie cookie : cookies) {
                LOG.debug("key:" + cookie.getName() + "  value:" + cookie.getValue());
            }
        } finally {
            response.close();
        }
        return response;
    }

    @SuppressWarnings("unused")
    private static List<BasicNameValuePair> toNameValuePairList(String parameters) {
        List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
        if(parameters == null || "".equals(parameters)) {
            return nvps;
        }
        String[] paramList = parameters.split("&");
        for (String parm : paramList) {
            int index = -1;
            for (int i = 0; i < parm.length(); i++) {
                index = parm.indexOf("=");
                break;
            }
            String key = parm.substring(0, index);
            String value = parm.substring(++index, parm.length());
            nvps.add(new BasicNameValuePair(key, value));
        }
        System.out.println(nvps.toString());
        return nvps;
    }
    /**
     * 模拟AJAX post/json 方式进行提交。
     * @param <PostMethod>
     * @param url
     * @param parameters
     * @return response
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static CloseableHttpResponse postAjax(String url, String parameters)
            throws ClientProtocolException, IOException {
        //设置代理
        //HttpHost proxy = new HttpHost("127.0.0.1", Integer.valueOf(8080));  
        //RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
        HttpPost httpPost = new HttpPost(url);
        //httpPost.setConfig(config);
        //parameters = toJsonString(parameters);
        StringEntity entity = new StringEntity(parameters, "utf-8");// 处理中文乱码问题。
        entity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "*/*");
        //设置post请求方式
        CloseableHttpResponse response = httpClient.execute(httpPost);
        if (response.getStatusLine().getStatusCode() == 200) {
            HttpEntity he = response.getEntity();
            String s = EntityUtils.toString(he, "UTF-8");
            System.out.println(s);
            if(s.contains("成功")) {
                Proxy.status = "200";
            }
        }
        try {
            cookieStore = context.getCookieStore();
            List<Cookie> cookies = cookieStore.getCookies();
            for (Cookie cookie : cookies) {
                LOG.debug("key:" + cookie.getName() + "  value:" + cookie.getValue());
            }
        } finally {
            response.close();
        }
        return response;
    }

    /**
     * 手动增加cookie
     * 
     * @param name
     * @param value
     * @param domain
     * @param path
     */
    public static void addCookie(String name, String value, String domain, String path) {
        BasicClientCookie cookie = new BasicClientCookie(name, value);
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookieStore.addCookie(cookie);
    }

    /**
     * 把结果console出来
     * 
     * @param httpResponse
     * @throws ParseException
     * @throws IOException
     */
    public static void printResponse(HttpResponse httpResponse) throws ParseException, IOException {
        // 获取响应消息实体
        HttpEntity entity = httpResponse.getEntity();
        // 响应状态
        System.out.println("status:" + httpResponse.getStatusLine());
        System.out.println("headers:");
        HeaderIterator iterator = httpResponse.headerIterator();
        while (iterator.hasNext()) {
            System.out.println("\t" + iterator.next());
        }
        // 判断响应实体是否为空
        if (entity != null) {
             String responseString = EntityUtils.toString(entity);
             System.out.println("response length:" + responseString.length());
             System.out.println("response content:" + responseString.replace("\r\n", ""));
        }
        System.out.println("------------------------------------------------------------------------------------------\r\n");
    }

    /**
     * 把当前cookie从控制台输出出来
     * 
     */
    public static void printCookies() {
        System.out.println("headers:");
        cookieStore = context.getCookieStore();
        List<Cookie> cookies = cookieStore.getCookies();
        for (Cookie cookie : cookies) {
            System.out.println("key:" + cookie.getName() + "  value:" + cookie.getValue());
        }
    }

    /**
     * 检查cookie的键值是否包含传参
     * 
     * @param key
     * @return
     */
    public static boolean checkCookie(String key) {
        cookieStore = context.getCookieStore();
        List<Cookie> cookies = cookieStore.getCookies();
        boolean res = false;
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(key)) {
                res = true;
                break;
            }
        }
        return res;
    }

    /**
     * 直接把Response内的Entity内容转换成String
     * 
     * @param httpResponse
     * @return
     * @throws ParseException
     * @throws IOException
     */
    public static String toString(CloseableHttpResponse httpResponse) throws ParseException, IOException {
        // 获取响应消息实体
        HttpEntity entity = httpResponse.getEntity();
        if (entity != null)
            return EntityUtils.toString(entity);
        else
            return null;
    }

- 获取医生索引网页基本信息

解析本页面中的 超链接标签 获取到链接标签之后,对其进行拆分,获得医院ID,部门ID,医生ID,订单ID。为后续做准备。
医生信息页面
获取方法:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public static void getAlldoctor(String strUrl) {
        try {
            URL url = new URL(strUrl);
            Document doc = Jsoup.parse(url, 20000);
            Elements element = doc.getElementsByClass("hyxzym_box");
            Elements enables;
            for (Element elem : element) {
                enables = elem.getElementsByClass("hyxzym_b_sj hy_wxyh");
                for (Element enable : enables) {
                    try {
                        String href = enable.getElementsByTag("a").first().attr("href");
                        href = href.substring(href.indexOf("http:"), href.indexOf(".htm") + 4);
                        href = href.substring(href.indexOf("confirm") + 8, href.lastIndexOf("."));
                        String[] split = href.split("/");
                        Map<String, String> map = new HashMap<String, String>();
                        map.put("hospitalId", split[0]);
                        map.put("departmentId", split[1]);
                        map.put("doctorId", split[2]);
                        map.put("orderId", split[3]);
                        //多线程问题
                        Proxy.changeDoctor(map);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("获取失败" + strUrl);
        }

    }

- 实现模拟登录功能

private static void login() {
        try {
            //处理登录状态:
            String parameters = "";
            parameters += "redirectUrl=http://yyghwx.bjguahao.gov.cn/u/usercenter.htm";
            parameters += "&mobileNo="+Proxy.userName;
            parameters += "&password="+Proxy.password;
            parameters += "&channel=ksdl";
            CloseableHttpResponse post = HttpClient_Mine.post("http://yyghwx.bjguahao.gov.cn/quicklogin.htm", parameters);
            HttpClient_Mine.printResponse(post);
        } catch (ClientProtocolException e) {
            System.err.println("登录失败啦,你重启一下吧。");
            // TODO Auto-generated catch block
        } catch (IOException e) {
            // TODO Auto-generated catch block
        } catch (ParseException e) {
            // TODO Auto-generated catch block
        } 
    }

- 发送短信方法

北京114挂号平台是通过你的登录信息发送的短信,因此只要登陆了,直接就可以发送post请求就可以进行登录了。

public static void sendMessage() {
        try {
            CloseableHttpResponse post = HttpClient_Mine.post("http://yyghwx.bjguahao.gov.cn/v/sendorder.htm","");
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }

- 实现自动挂号

挂号还需要验证码,需要进行手动输入。这个挂号平台最坑的地方是,必须要先浏览一下页面,然后还要等5s钟之后才能发送验证码,否则会提示你验证码不正确。

    public void guaHao(){
        try {
            String url = "http://yyghwx.bjguahao.gov.cn/common/order/confirm/"+this.hospitalId+"/"+this.departmentId+"/"+this.doctorId+"/"+this.dutySourceId+".htm";
            HttpClient_Mine.get(url);

            System.out.println("---------->获取订单页面成功!!\n"+url);
            Thread.sleep(5000);
            sendMessage();
            Scanner sc = new Scanner(System.in);
            System.out.println("输入验证码!");
            String smsMessage = sc.nextLine();
            //登录成功之后,进行挂号。
            String parameters = "dutySourceId="+this.dutySourceId;   //订单号,在url里面进行截取
            parameters += "&hospitalId="+ this.hospitalId;
            parameters += "&departmentId="+this.departmentId;
            parameters += "&doctorId="+this.doctorId;//医生号,在url里面进行截取
            parameters += "&patientId="+Proxy.patientId;//就诊人编号
            parameters += "&hospitalCardId=";//就诊卡,可传空
            parameters += "&medicareCardId=";  //医保卡,可传空
            parameters += "&reimbursementType=12";
            parameters += "&smsVerifyCode="+smsMessage;//验证码
            parameters += "&childrenBirthday=";//可传空!
            parameters += "&htype=1";
            parameters += "&openId=";//可传空!
            parameters += "&isShare=";//可传空!
            parameters += "&pubHospitalId=";//可传空!
            parameters += "&isAjax=true";
            HttpClient_Mine.postAjax("http://yyghwx.bjguahao.gov.cn/common/order/submit.htm", parameters);
            //HttpClient_Mine.printResponse(post2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

- 结语

将以上的方法结合起来,自动挂号功能也就实现了,当然还可以使用多线程,实现更大可能性的刷票。当然,以上方法自己动手组装吧,总会有十分有意思的事情发生。

  • 1
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值