利用Selenium实现交互式WebService接口 --处理验证码
来源:互联网 发布:淘宝能卖视频吗 编辑:程序博客网 时间:2024/05/17 04:07
OverView
写于2017年7月25日,如有不当之处,贵请指导!
作者现在需要爬取一个某政务型 查询网站,实现WebService接口。本文为了方便叙述,把接口和调用接口的业务系统合并—统称为系统,如果不懂,忽略此句
简要概括就是:
- 系统填一个表单、并将验证码交给用户识别
- 用户填完验证码提交后系统继续填写页面表单
- 最后将抓取的结果返回给用户。
为了方便大家理解,画流程图如下:
一、准备
1、网站交互分析(F12)
大致了解需求后就是开发的过程,首先你做的就是网页交互分析,请按F12,在Network(网络)那一栏观察分析,务必!!!
如果能直接找到URL接口的,千万不要用Selenium方案!
以此政务网站为例,第一、第三行就系统交互:先填写发票代码,网页JS验证代码并Ajax获取验证码。这个验证码是不同颜色字符组成的,填写时会让你选择其中部分颜色的字,提交表单时这里的加密也需要提交。(PS:所以这里我们基本可以判定,必须Selenium)
。填写完验证码和其他数据后,我们便可以点击查验。
为了加速填写,表单填写采用了复制粘贴。可恰巧的是这个网站表单的个别box有限制粘贴,故采用粘贴与键盘输入结合。
2、系统分析
结合一开始的需求分析,WebService设计如下:
相比较以往抓取,WebService最大的问题就是将原本线性过程转变成分步的交互过程。所以必须暂存drive,暂存你的操作过程。
- 线性的抓取过程:写好代码逻辑,一步一步执行抓取并返回结果。
- Http 交互过程 :一个http请求完成一次逻辑并返回结果,每一次请求都是一次执行。
所以,我们必须将原来的过程断开,在每一次请求时分步执行。
Tips:为了提高响应速度,当验证码发送给用户填写时,后台已经在异步执行其他数据的填写操作了。当用户填好验证码发送至系统时,系统只需补充验证码便可以确认查验了。
二、系统搭建
环境准备
技术实现采用的Selenium+Java Servlet,目录如下:Tips:
Springboot好像不能操作系统的剪切板,故而简单的来。
- 关于selenium+Java基本准备,可以参照此篇文档:Selenium+Java基础 –此处默认大家都熟悉Selenium+Java环境搭建。
三、Coding
1、工具类PageUtil
/** * * @author minzhou<mingxuan.zhou@outlook.com> * @version 1.0 * Time 20170703 */public class PageUtils { /** * 获取IE的WebDriver * * @param page * @return */ public static WebDriver getWebDriver(String url) { System.setProperty("webdriver.ie.driver", "D:/Develop/WebCrawler/IEDriverServer_x64_3.4.0/IEDriverServer.exe"); DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer(); ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,true); WebDriver driver = new InternetExplorerDriver(); driver.get(url); return driver; } /** * 复制粘贴的两种方案,推荐第一个 * @param driver * @param str */ public static void CtrlC_V(WebDriver driver,String str) { StringSelection stringSelection = new StringSelection(str); Toolkit.getDefaultToolkit().getSystemClipboard() .setContents(stringSelection, null); new Actions(driver).sendKeys(Keys.chord(Keys.CONTROL+"v")).perform(); } public static void keyOperation(String str){ StringSelection stringSelection = new StringSelection(str); Toolkit.getDefaultToolkit().getSystemClipboard() .setContents(stringSelection, null); //系统级粘贴 Robot robot = null; try { robot = new Robot(); } catch (AWTException e1) { e1.printStackTrace(); } robot.keyPress(KeyEvent.VK_CONTROL); robot.keyPress(KeyEvent.VK_V); robot.keyRelease(KeyEvent.VK_V); robot.keyRelease(KeyEvent.VK_CONTROL); }}
2、获取验证码ImageServlet
原始图片的静态jpg文件,刷新后的图片为base64,依此,我们可以根据src的变化判断是否出现验证码,并在验证码出现后获取src,截取相应部分并将base64编码的图片的String以及提示信息发送到客户端即可。
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session=request.getSession(); WebDriver driver = null; Invoice invoice=new Invoice(); invoice.setInvoiceCode(request.getParameter("invoiceCode")); invoice.setInvoiceCheckSum(request.getParameter("invoiceCheckSum")); invoice.setInvoiceDate(request.getParameter("invoiceDate")); invoice.setInvoiceNum(request.getParameter("invoiceNum")); String url="https://inv-veri.chinatax.gov.cn/"; //Use session storage drive if (session.getAttribute("driver") == null) { driver = PageUtils.getWebDriver(url); session.setAttribute("driver", driver);//首次访问时,新建dirve对象 }else { driver =(WebDriver) session.getAttribute("driver");//非首次打开时,获取对象 if (driver.getCurrentUrl()==url) { driver.navigate().refresh();//URL为目标页,刷新 } else { driver.navigate().to(url);//不是目标页导航至目标页 } } //InvoiceCode==发票代码 WebElement invoiceCodeInput = driver.findElement(By.id("fpdm")); new Actions(driver).click(invoiceCodeInput).perform(); //new Actions(driver).moveToElement(invoiceCodeInput).click().perform(); //PageUtils.keyOperation(invoice.getInvoiceCode()); PageUtils.CtrlC_V(driver, invoice.getInvoiceCode()); //waiting for verification information try { new WebDriverWait(driver, 500).until(new ExpectedCondition<Boolean>(){ public Boolean apply(WebDriver d){ return d.findElement(By.id("yzm_img")) .getAttribute("src") .contains("base64"); } }); } catch (Exception e) { } WebElement codeImage = driver.findElement(By.id("yzm_img")); WebElement codeTips = driver.findElement(By.id("yzminfo")); try { request.setAttribute("codeImage", codeImage); request.setAttribute("codeTips", codeTips); /* // convert to json Gson gson=new Gson(); Map<String,String> map=new HashMap<>(); map.put("codeImage", codeImage.getAttribute("src")+""); map.put("codeTips", codeTips.getText()+""); String json = gson.toJson(map); //json信息返回 PrintWriter out = response.getWriter(); out.println(json); out.flush(); out.close();*/ } catch (JSONException e) { e.printStackTrace(); } request.getRequestDispatcher("/views/image.jsp").forward(request, response); }
3、填写验证码并解析
从session中取出drive并继续执行操作,并解析最终页面。
作者在操作时候发现不同浏览器对于selenium命令执行的结果是不一致的。比如IE,根据id定位对象的时候居然可以跨iframe,但是同样的代码chrome就不行。由于页面复杂,并没有注意到iframe存在,故而检查了好久。所以在此给大家提示:确认定位正确而找不到元素,可以利用F12在源码中搜索是否有iframe存在
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); HttpSession session = request.getSession(); WebDriver driver = (WebDriver) session.getAttribute("driver"); Invoice invoice = (Invoice) session.getAttribute("invoice"); //获取前台传来的验证码 invoice.setCode(request.getParameter("checkNum").trim()); //selenium操作 WebElement CodeInput = driver.findElement(By.id("yzm")); new Actions(driver).moveToElement(CodeInput).click().perform(); PageUtils.CtrlC_V(driver,invoice.getCode()); WebElement InvoiceSubmit = driver.findElement(By.id("checkfp")); //等待按钮可点 new WebDriverWait(driver, 1000).until(new ExpectedCondition<Boolean>(){ public Boolean apply(WebDriver d){ return d.findElement(By.id("checkfp")) .getAttribute("style") .contains("inline-block"); } }); //移动到上面点击,防止页面CSS遮挡 new Actions(driver).click(InvoiceSubmit).perform(); new WebDriverWait(driver, 1500).until(ExpectedConditions.presenceOfElementLocated(By.tagName("dialog"))); driver.switchTo().frame("dialog-body"); String example=driver.findElement(By.id("sbbh_dzfp")).getText(); System.out.println(example); }
至此,页面抓取完成。
关于此次抓取,三种浏览器都试了,就速度而言,IE最慢,非常不推荐使用。
- 利用Selenium实现交互式WebService接口 --处理验证码
- selenium自动处理验证码
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功能
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用
- Android 利用webservice 获取验证码图片
- xfire实现对WebService调用接口用户验证
- 利用selenium实现截图
- js函数(下)
- cookie与session的区别
- USB接口介绍
- 算法复杂度分析
- 餐饮小程序怎么制作
- 利用Selenium实现交互式WebService接口 --处理验证码
- 微信小程序movable-area
- 部署falsk到window服务器
- 解决AQDefaultDevice (173): skipping input stream 的输出问题
- Ubuntu安装更新失败
- java-面试体2
- 那个找不到工作的iOS程序猿
- 手把手教你做北邮操作系统小学期的实验一——Linux启动过程优化1
- linux memalign、valloc、realloc