用httpclient开发的在线自动抢订火车票系统(已上传源代码)

标签: httpclient 开发 在线 | 发表时间:2012-01-08 19:30 | 作者:
出处:http://www.iteye.com
前两天女友要在线订火车在票,一直都没有办法订到票,最后没办法,便给她写了个自动抢票的脚本,可周边的朋友听她说通过软件订到票了,都先后向她要软件,可原来的脚本只是基于控制台输入,这样给别人也没法用,兴趣一起,就花了一天的时间做了个WEB界面,然后分享给朋友用。先上几张图,看大家看看。

这个是登录界面,要使用前先设置一下常用的邮箱和登录密码,自动抢票过程中,如需要再输入登录验证码和订单验证码,或订单成功时,将通过此email通知您,建议使用qq邮箱,这样只要您在电脑时开打QQ,收到邮件时,qq会弹出窗口通知您,您可以即时响应。  
第一次使用时,要先把在火车票官网注册的资料填在这里:

这资料都必码是已在官方上注册过的,在自动订票时,需要用到这些资料。



第一次登录后需要求先填写官网上的登录验证码和订单提交验证码,只要填写正确后,在填票过程中就一直可以使用,这也是官网的BUG,应该是他们在较正验证码后,没有让当前验证码失效,这样就只要不去刷新验证码,就可以一直使用第一次输入的正确验证码。


填写一下订票任务,主要就填写订票人资料(当然这个也是要在官网上添加过的),和订票信息,什么时候从哪到哪,从几列车等,保存完后,点开始,就则可以自动登录,找票,然后订票,订票成功后发邮件通知。


执行过程中,会时间更新执行的结果信息。




留言板功能,收集问题的反馈。


网友提供http代理,官方网站对一个IP的单位时间内访问次数据有限制,超过这个超制值IP会被封锁一段时间。
因为使用的人多,所以系统需要使用代理来向官网发送请求。

看到系统的整体内容后,再来介绍一下相关的技术
这个自动发送请求,提交数据,都依赖于httpclient4,火车票订票官方是使用ssl加密,这里是启用httpclient的ssl功能
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{easyTrustManager}, null);
SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
Scheme sch = new Scheme("https", sf,443);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);


大家都知道,这个官网的反映速度有时实在是惨不忍睹,所以我们也要设计一下请求等待的最长时间(30秒):
 httpclient.getParams().setIntParameter("http.socket.timeout",30000);//毫秒


官方网站对一个IP的单位时间内访问次数据有限制,超过这个超制值IP会被封锁一段时间。
因为使用的人多,所以系统需要使用代理来向官网发送请求,这个代理需要支持ssl,所以先网上找到一批代理IP过来,存在一个txt文字,格式为 ip:端口\n,以下读取IP代理,并且进行速试测试
	public void run(){
		  try {
			System.out.println("-----设置代理服务器----");
			String proxyFileString0 = FileUtils.readFileToString(new File(ApplicationUtils.getWebrootDir()+"/order/proxy_add.txt"), "UTF-8");
			String proxyFileString1 = FileUtils.readFileToString(new File(ApplicationUtils.getWebrootDir()+"/order/proxy_enable.txt"), "UTF-8");
			String proxyFileString = proxyFileString0+"\n"+proxyFileString1;
			for(String proxyString : StringUtils.split(proxyFileString, "\n")){
				proxyString = proxyString.trim();
	        	if(StringUtils.isNotEmpty(proxyString)){
	        		System.out.print("-----测试代理服务器: "+proxyString);
	        		String[] proxyInfo = proxyString.split(":");
	        		
		        	HttpHost httpHost = new HttpHost(proxyInfo[0], Integer.valueOf(proxyInfo[1])); 
		        	long start = System.currentTimeMillis();
		        	if(HttpUtils.testProxy(httpHost)){
		        		long usetime = System.currentTimeMillis()-start;
		        		if(usetime > 10000){
		        			System.out.println(" 不使用,响应时间太长时间:"+usetime+"毫秒----");
		        		}else{
		        			System.out.println(" 可使用,使用时间:"+usetime+"毫秒----");
		        			httpHostList.add(httpHost);
		        		}
		        	}
	        	}
	        }
			System.out.println("-----设置代理服务器成功,总数:"+httpHostList.size()+"----");
		} catch (Exception e) {
		}
	}


最后在每次请求时,选择一个代理
//设置代理对象 ip/代理名称,端口      
try{
	httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,ProxyHostUtils.next());
}catch(Exception e){}

每个用户在发送请求前,先从官网上读取两个验证码,一个是登录验证码,一个是提交订单验证码,一次性读取就可以了。
/**
	 * 读取登录验证码
	 * @throws Exception
	 */
	@Transient
	public void getRemoteLoginCode() throws Exception {
		File file = HttpUtils.doGetFile(OrderRunTask.loginCodeUrl+"&nocache="+Utils.getRandomString(10), cookieContext);
		if(file != null){
			File codeFile = new File(ApplicationUtils.getWebrootDir()+getLoginCodeUrl());
			FileUtils.copyFile(file, codeFile);
		}
	}
	
	/**
	 * 读取提交订单验证码
	 * @throws Exception
	 */
	@Transient
	public void getRemoteSumitCode() throws Exception {
		File file = HttpUtils.doGetFile(OrderRunTask.submitCodeUrl+"&nocache="+Utils.getRandomString(10), cookieContext);
		if(file != null){
			File codeFile = new File(ApplicationUtils.getWebrootDir()+getSubmitCodeUrl());
			FileUtils.copyFile(file, codeFile);
		}
		
	}


每个用户可以同时订多张火车票,系统为每张火车票启动一个线程,持续的运行,直到订票成功或者是被中断。
//建立线程池
public static ExecutorService runningTaskPool = Executors.newFixedThreadPool(1000);

//启动订票任务
OrderRunTask orderRunTask = new OrderRunTask(account, orderTask);
OrderRunTaskUtils.runningTaskMap.put(orderTask.getId(), orderRunTask);
Future<?> future = OrderRunTaskUtils.runningTaskPool.submit(orderRunTask);
orderRunTask.setFuture(future);

//中断任务
OrderRunTask orderRunTask = OrderRunTaskUtils.runningTaskMap.get(taskId);
try{
	orderRunTask.setStop();
	orderRunTask.getFuture().cancel(true);
}catch(Exception e){
	e.printStackTrace();
}

//判断是否订票成功,并且发送邮件
if(body.indexOf("45分钟") != -1){
	Date now = new Date();
	account.sendMail(orderTask.getQueryTrainDate()+"_订票成功",
		orderTask.getQueryTrainDate() +" "+
		orderTask.getFromStation()+"到"+orderTask.getToStation()+(StringUtils.isEmpty(orderTask.getTrainNo())?orderTask.getTrainNo():"")+" --订票时间:"
		+DateUtils.getDateTime("HH:mm:ss", now)
	);
	account.setReload(true);
									orderTask.setState(200);
									orderTask.setRuningTime(now);
									this.account.error(orderTask.toString()+": 订票成功。");
	break next;
}


这也只是本人兴趣开发,不使用在商品场合,以上分享的只是这个系统的设计思想,有时间再深入讨论。
顺便附上本机(随时变动的)的一个链接地址,地址已关闭,访问太多了,小小的笔记本,顶不住压力。
大伙这么好热情啊,现在已上传了控制台输入版本(jar包太大,没上传,需要的自己网上下载),欢迎大家讨论,至于web版本的,现在不好上传,里面有其他项目的内容,不好整理,这就不好意思了,见谅。

如果使用中报出javax.net.ssl.SSLException: hostname in certificate didn't match异常,是检查一下jar,httpclient4.0.1不会有这个问题,至少具体原因是没什么,还没有去调试。

如果报出java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
这个因为所有线程都共享一下httpclient,造成http发送请求的冲突,可以改造一下HttpUtils,为每个线程分配一个独立的httpclient,使用ThreadLocal;
可以更新一下HttpUtils.



作者: zxb_fish 
声明: 本文系ITeye网站发布的原创文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

已有 130 人发表回复,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [httpclient 开发 在线] 推荐:

Apache HttpClient 4.3开发指南

- - CSDN博客推荐文章
《Apache HttpClient 4.3开发指南》. 作者:chszs,转载需注明. 博客主页: http://blog.csdn.net/chszs. Apache HttpClient 4系列已经发布很久了,但由于它与HttpClient 3.x版本完全不兼容,以至于业内采用此库的公司较少,在互联网上也少有相关的文档资料分享.

用httpclient开发的在线自动抢订火车票系统(已上传源代码)

- - ITeye论坛最新精华讨论帖
前两天女友要在线订火车在票,一直都没有办法订到票,最后没办法,便给她写了个自动抢票的脚本,可周边的朋友听她说通过软件订到票了,都先后向她要软件,可原来的脚本只是基于控制台输入,这样给别人也没法用,兴趣一起,就花了一天的时间做了个WEB界面,然后分享给朋友用. 这个是登录界面,要使用前先设置一下常用的邮箱和登录密码,自动抢票过程中,如需要再输入登录验证码和订单验证码,或订单成功时,将通过此email通知您,建议使用qq邮箱,这样只要您在电脑时开打QQ,收到邮件时,qq会弹出窗口通知您,您可以即时响应.

HttpClient 与 Close_Wait

- - 互联网 - ITeye博客
服务器A需要通过HttpClient去连接另一个系统B提供的服务,运行一段时间后抛出以下异常:. 在服务器B上运行netstat命令,发现大量连接处于 CLOSE_WAIT 状态. 简单来说CLOSE_WAIT数目过大是由于被动关闭连接处理不当导致的. 我说一个场景,服务器A会去请求服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,连接状态我们可以看到是TIME_WAIT.

Httpclient远程调用WebService示例(Eclipse+httpclient)

- - 企业架构 - ITeye博客
我们将Web Service发布在Tomcat或者其他应用服务器上后,有很多方法可以调用该Web Service,常用的有两种:.       1、通过浏览器HTTP调用,返回规范的XML文件内容.       2、通过客户端程序调用,返回结果可自定义格式.       接下来,我利用Eclipse作为开发工具,演示一个Httpclient调用WebService的简单示例.

HttpClient使用详解

- - CSDN博客推荐文章
HttpClient:是一个接口. 首先需要先创建一个DefaultHttpClient的实例. 先创建一个HttpGet对象,传入目标的网络地址,然后调用HttpClient的execute()方法即可:. 创建一个HttpPost对象,传入目标的网络地址:. 通过一个NameValuePair集合来存放待提交的参数,并将这个参数集合传入到一个UrlEncodedFormEntity中,然后调用HttpPost的setEntity()方法将构建好的UrlEncodedFormEntity传入:.

Android HttpURLConnection及HttpClient选择

- - Trinea
介绍Android中Http请求方式的选择、区别及几个常用框架对API的选择. Android Http请求API主要分两种:. 第一种是Java的HttpURLConnection,默认带gzip压缩. 第二种Apache的HttpClient,默认不带gzip压缩. 两种方式请求connection都是keep alive,默认User-Agent不同.

HttpClient 连接池管理

- - IT瘾-dev
随着微服务的流行,服务之间的http调用越来越多. 在java里面我们可以使用httpclient这个开源工具类来进行处理,但若使用不当,可能性能会比较差,尤其是连接池是否能正常使用. 接下来会详细分析下httpclient的连接池原理. 使用httpclient的好处. 1)、降低延迟:如果不采用连接池,每次连接发起Http请求的时候都会重新建立TCP连接(经历3次握手),用完就会关闭连接(4次挥手),如果采用连接池则减少了这部分时间损耗.

配置使用连接池的httpClient

- - 企业架构 - ITeye博客
httpClient4.3开始弃用了DefaultHttpClient和PoolingClientConnectionManager.原来这样配置的连接工厂. 现在改用spring推荐的类,配置如下:.   配置带有连接池的org.springframework.web.client.RestTemplate.

HttpUrlconnection 、Httpclient get 、post 请求核心代码

- - CSDN博客推荐文章
HttpURLConnection的使用  . * URL请求的类别分为二类,GET与POST请求. * a:) get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet, . * b:) post与get的不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内.

Java中httpClient中三种超时设置

- - CSDN博客推荐文章
本文章给大家介绍一下关于Java中httpClient中的三种超时设置小结. 在Apache的HttpClient包中,有三个设置超时的地方:. /* 从连接池中取连接的超时时间*/ ConnManagerParams.setTimeout(params, 1000); /*连接超时*/ HttpConnectionParams.setConnectionTimeout(params, 2000); /*请求超时*/ HttpConnectionParams.setSoTimeout(params, 4000);.