当前位置: 首页 > news >正文

为了媳妇的一张号,我与百度医生杠上了

  需求是催生项目和推进项目的不竭动力。

背景:

  最近,因为媳妇要做个B超检查,想着去大医院查查应该更放心,所以就把目标瞄准在A医院。早已耳闻A院一号难求万人空巷,所以把所有能接触到的机会都看了一遍,线下听传闻说早上徐亚5点左右去排队还未必能排上,线上主要有以下两个来源:

1.支付宝

  在支付宝的城市服务中,定位到指定城市,是能够看一些医院提供了预约挂号接口的,显然A医院当之无愧也在其中。

  简便易用的支付宝用户体验,即便是第一次来也好像是经常使用这项服务般熟练。找到A院,搜索妇产科,在list中有若干医生,有的标注无号源,有的标注可预约,大抵如下:

 

  于是满怀激动的点击"可预约",可是弹出来的却不是我想要的结果--!

 

 

  后来听到诸如凌晨12点会有号放出,但是最终也是收到同样冷冰冰的弹出窗口。对于用此招挂上号的我深表佩服,只是抛开这个不说,感觉A院在支付宝这块的投入太低,UI设计也很呆板,最主要的是抢不到号。

 

2.百度医生

  相对来说,百度医生要比上面做的实诚的多,有就是有,没有就是没有。不管是PC端还是app端,用户界面更加柔和smooth。

 

  于是我把希望寄托在这里,但是A院的号难抢,这是事实。而百度医生我觉得还有一块空白可以实现的就是监控机制,好比12306可以刷票一样,添加这个模块,相信app的下载量和使用量会提高一个"当量"。

很显然,这个功能,还没有,那我只能自己动手了~~~

 

我的思路

  1. 百度从医院拿数据,那我就从百度拿数据。我能够监控百度医生放出来的消息,就基本与医院同步了;

  2. 我要使用的是百度医生,但是这个需要登录,而且登录方式只有使用手机号+验证码的方式。这个方法可行,但是需要中间sleep以输入验证码,或者是调用短信接口,显然这两个都不是一个很好的途径,时间金钱成本高,用户体验差;

  3. 于是我想到,可以先登录百度账户(用户名+密码),然后利用这个账户进入百度医生,就免去了短信验证这个环节。

 

码前预热

1. 框架选择

毫无疑问,我们采用selenium,一款web测试应用工具,模拟我在浏览器上的操作。可以基于IE、FF、Chrome等等浏览器,实现启动关闭浏览器/页面,在页面上点击、定位元素等相应操作。关于selenium webdriver的背景知识就不做介绍,一篇博文显然难以阐述清楚。

 

2. 工具使用

在使用selenium过程中,页面元素的定位是个核心问题。我们可以使用By类下面的By.name,By.id,By.linkText分别获取获取name属性,id属性,超链接文本。这些方式的定位我们直接可以在DOM结构中看出来,其中比较复杂的是xpath,需要根据DOM结构实现定位,这时候我们需要一个工具可以实时的测试我们的xpath表达式是否能够正确定位到指定元素。

a.在Firefox浏览器中,我们可以安装插件FirePath

 

  这里我们通过字符串".//*[@id='su']"就能够定位到"百度一下"这个按钮

 

b.在chrome里面,我们可以下载插件XPath Helper,有关它的用户,请参看

http://www.chromein.com/crx_11654.html

 

 

思路有了,工具齐了,那就开始着手做吧

1.启动chrome浏览器

  需要下载chrome的驱动

System.setProperty ( "webdriver.chrome.driver" , "C:\\Users\\Administrator\\Downloads\\chromedriver_win32\\chromedriver.exe" );
WebDriver driver = new ChromeDriver();

 

2.定位元素执行动作

  如定位百度页面的登录按钮,并执行点击操作

WebElement loginLink = driver.findElement(By.xpath(".//*[@id='u1']/a[7]"));
loginLink.click();

  

3.等待页面加载完成

  有时候在driver.get()到某个页面时,如果页面没有加载完成,这时候去定位元素容易抛出异常,所以需要加入等待页面加载完成的功能。这里将其封装在一个函数中:

public static void waitForLoad(WebDriver driver) {
	    ExpectedCondition<Boolean> pageLoad= new
	        ExpectedCondition<Boolean>() {
	            public Boolean apply(WebDriver driver) {
	                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
	            }
	        };
	    WebDriverWait wait = new WebDriverWait(driver, 30);
	    wait.until(pageLoad);
	}

  

4.遇到异常重新启动机制

  监控的原理在于间歇性的点击相应的科室并监听页面中是否有想要的元素出现(如出现预约挂号),如果出现则点击进入相应界面,如果没有出现则一直监听。

  但是在实测过程中,发现持续点击某科室会出现页面一直加载的情况,这时候会抛出异常,程序无法正常进行。

  所以在这里捕获异常,通过递归调用的思想,关闭先前的driver对象,并调用自己重新生成一份监听driver对象,从而保证程序正常执行,提高了程序的健壮性。

        try {
        	WebDriverWait wait = new WebDriverWait(driver,7000);
        	wait.until(new ExpectedCondition<WebElement>(){
        		
        		public WebElement apply(WebDriver d) {
        			try {
        				Thread.sleep(2000);//为避免给baidu早成麻烦,每2秒监听一次
        			} catch (InterruptedException e) {
        				e.printStackTrace();
        			}
        			departLink.click();//departLink为妇产科的文本链接
        			monitoringTimes++;//监听的次数
        			System.out.println("第" + monitoringTimes + "次监控");
        			return d.findElement(By.xpath("//*[@id='doctor-info-list']/descendant::*[contains(text(), '预约挂号')][1]"));
        		}
        	});
        	waitForLoad(driver);
		} catch (Exception e) {
			ticket = true;
			System.out.println("抱歉,目前余号不足,请稍后再试");
		}finally{
			if(ticket){
				driver.close();
				startMonitor();//递归调用
			}			
		}

  

 

5.成果展示

  但从程序来说,可以现实自动抢号功能,已成功预约到非妇产科以外科室的号。

 

  运行到监控的页面并监听:

 

  实时监听过程:

 

 

难点和展望

  • dom结构变动,会导致无法正常定位;
  • 代码执行过程,有时会抛出异常,需要处理;
  • 频繁的登录,需要在登录界面输入验证码,验证码的识别可以采用OCR破解;
  • 最好做成一个客户端,提供给用户自定义输入医院和科室乃至指定时间段或医生

 

写此程序,纯属需求催生,仅为一乐,勿动邪念^^。

 

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

  

友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                          2. 微信

                      

 

转载于:https://www.cnblogs.com/bigdataZJ/p/hittest.html

相关文章:

  • zend studio 的注册码-php的编辑器
  • Android 权限大全中英对照
  • Nightmare --- 炸弹时间复位
  • HDU 5655 CA Loves Stick 水题
  • 跟着实例学习ZooKeeper的用法: Barrier
  • Partition4:增加分区
  • Oracle创建表空间
  • succ
  • 新技能,利用Reflector来修改dll引用
  • 溢出隐藏
  • .Net转前端开发-启航篇,如何定制博客园主题
  • NTFS For Mac 的特点有哪些
  • 第十次课作业(风险管理、项目收尾、知识产权)
  • 5.jenkins使用Email Extension Plugin插件配置邮件通知
  • Fouandation(NSString ,NSArray,NSDictionary,NSSet) 中常见的理解错误区
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • gf框架之分页模块(五) - 自定义分页
  • Laravel 中的一个后期静态绑定
  • MySQL几个简单SQL的优化
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • vue的全局变量和全局拦截请求器
  • 创建一种深思熟虑的文化
  • 看域名解析域名安全对SEO的影响
  • 老板让我十分钟上手nx-admin
  • 前端_面试
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 一些css基础学习笔记
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (1) caustics\
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (LeetCode) T14. Longest Common Prefix
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (一)Java算法:二分查找
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)ABI是什么
  • (转)socket Aio demo
  • (转)关于pipe()的详细解析
  • ..回顾17,展望18
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .form文件_SSM框架文件上传篇
  • .NET DataGridView数据绑定说明
  • .net mvc部分视图
  • .NET 药厂业务系统 CPU爆高分析
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET中使用Protobuffer 实现序列化和反序列化
  • .NET中统一的存储过程调用方法(收藏)