模拟登录+cookie保持+数据爬取——中国铁塔的爬虫之旅

2023-11-01 20:51

本文主要是介绍模拟登录+cookie保持+数据爬取——中国铁塔的爬虫之旅,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        先简单交代一下背景:我表哥在铁塔公司做维护工人,一旦铁塔出现故障就会有专人派单然后被派到单的人就需要在一个小时之内接单,否则就算工作失误,会扣钱,但是有一点让人非常不理解,铁塔公司的电脑端虽然有一个单子提醒的功能,但是目前看来就是一个摆设,设置了也不会起到任何作用,换句话说,如果你想知道自己有没有单子可以接,就必须自己手动定时刷新,坐在电脑旁边天天点鼠标玩,这是一件非常痛苦的事情,而且有很多单子会在晚上出来,明摆着就是想让你接不到单子扣工资嘛。所以这就找上我了。。。

        那么我所需要做的就是模拟登录进入系统,然后查询有没有单子可以接,有的话提醒表哥,需求听着很简单,但是毕竟中国铁塔,爬这个网站还是费了一番功夫。

        话不多说,进入正题,首先我要到了网站的登陆地址:中国铁塔维护系统 ,用户名以及密码

        然后进入登录页面,开启f12,如下

        

        比较不幸,有验证码,不过问题不大,先看看验证码的验证机制是怎么样的,边输验证码边查看network

        

        额,很明显了,验证码的输入框绑定了onchange事件,每次发生变化都会请求后台,后来我查看了response响应体,发现验证码输入正确会返回1,然后出现 中国铁塔,欢迎您 的字样,错误会返回0。至于验证码获取就是一个固定的地址,返回不同的图片,查看页面元素可以看的很清楚,在这里就不多说了。

        到了这一步,已经明了了自己第一步需要完成的工作 获取验证码图片-》解析验证码-》检查验证码-》获取响应结果

        话不多说,上代码,代码写的比较赶,没怎么注意规范。。。在这里是直接把验证码下载到桌面肉眼识别然后手输的,也可以使用tess4j,识别率还可以

        HttpGet getCheckCode = new HttpGet(getCheckCodeUrl);CloseableHttpResponse responseGet = null;String desktopDir =               FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();File imageFile = new File(desktopDir, "checkCode.jpg");try {responseGet = httpClient.execute(getCheckCode);FileOutputStream outputStream = new FileOutputStream(imageFile);HttpEntity entity = responseGet.getEntity();InputStream inputStream = entity.getContent();byte[] b = new byte[1024];int i = 0;while ((i = inputStream.read(b)) != -1) {outputStream.write(b, 0, i);}outputStream.flush();outputStream.close();EntityUtils.consume(entity);System.out.println("验证码获取成功,已经下载到桌面,请查看并输入验证码:");} catch (Exception e) {System.out.println("获取验证码失败!请重新运行程序!");return;} finally {try {responseGet.close();} catch (IOException e) {e.printStackTrace();}}Scanner scanner = new Scanner(System.in);String checkCode = scanner.next();HttpGet checkCodeGet = new HttpGet(checkCodeUrl + checkCode);checkCodeGet.addHeader("Accept", "text/plain, */*; q=0.01");checkCodeGet.addHeader("Referer", loginUrl);checkCodeGet.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");checkCodeGet.addHeader("X-Requested-With", "XMLHttpRequest");CloseableHttpResponse responseCheckCode = null;try {responseCheckCode = httpClient.execute(checkCodeGet);HttpEntity entity = responseCheckCode.getEntity();String codeStatus = EntityUtils.toString(entity);// 如果为1 则说明验证码正确,否则错误if (!"1".equals(codeStatus)) {System.out.println("验证码解析失败!请重新运行");return;}System.out.println("验证码解析成功!");EntityUtils.consume(entity);} catch (Exception e) {e.printStackTrace();} finally {try {responseCheckCode.close();} catch (IOException e) {e.printStackTrace();}}

运行效果

j经过测试,没有问题,现在可以进行模拟登录了,首先在网站进行登录,查看network的请求

好吧,居然是302重定向,猜想应该是请求带了问号后面的东西导致的重定向,去掉之后再次登录,发现状态码变成了200,ok,就用它了,如下

 

查看一下请求体,发现是form-data,此外发现除了用户名和密码两个参数之外,还有其他三个参数

最后经过调试,发现这几个参数在访问登录页面的时候在页面上会动态生成到隐藏域中一起传到后台,那我们也要首先访问一下获取这几个动态参数,否则登录一直是失败的。

上代码

        // 全局请求设置RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).setSocketTimeout(50000).setConnectTimeout(50000).setConnectionRequestTimeout(50000).build();// 创建cookie store的本地实例CookieStore cookieStore = new BasicCookieStore();// 创建HttpClient上下文HttpClientContext context = HttpClientContext.create();context.setCookieStore(cookieStore);// 创建一个HttpClientCloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).setDefaultCookieStore(cookieStore).build();CloseableHttpResponse response = null;String lt = "";String execution = "";String _eventId = "";// 先访问一下登录页面HttpGet getLoginPage = new HttpGet(loginUrl);try {response = httpClient.execute(getLoginPage);HttpEntity entity = response.getEntity();System.out.println("获取登录所需参数中...");String str = EntityUtils.toString(entity);lt = regex("\"lt\" value=\"([^\"]*)\"", str)[0];execution = regex("\"execution\" value=\"([^\"]*)\"", str)[0];_eventId = regex("\"_eventId\" value=\"([^\"]*)\"", str)[0];EntityUtils.consume(entity);} catch (Exception e1) {e1.printStackTrace();} finally {try {response.close();} catch (IOException e) {e.printStackTrace();}}/*** 通过正则表达式获取内容* * @param regex 正则表达式* @param from  原字符串* @return*/public static String[] regex(String regex, String from) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(from);List<String> results = new ArrayList<String>();while (matcher.find()) {for (int i = 0; i < matcher.groupCount(); i++) {results.add(matcher.group(i + 1));}}return results.toArray(new String[] {});}

regex方法使用正则表达式可以帮助解析出页面对应参数的值,然后进行模拟登录,上代码

		CloseableHttpResponse responseLogin = null;HttpPost httppost = new HttpPost(loginUrl); // 登录地址List<NameValuePair> nvps = new ArrayList<NameValuePair>();nvps.add(new BasicNameValuePair("username", "用户名"));nvps.add(new BasicNameValuePair("password", "密码"));nvps.add(new BasicNameValuePair("lt", lt));nvps.add(new BasicNameValuePair("execution", execution));nvps.add(new BasicNameValuePair("_eventId", _eventId));nvps.add(new BasicNameValuePair("submit", "登录"));httppost.addHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");httppost.addHeader("Accept-Encoding", "gzip, deflate");httppost.addHeader("Connection", "keep-alive");httppost.addHeader("Host", "180.153.49.81:18989");httppost.addHeader("Origin", "http://180.153.49.81:18989");httppost.addHeader("Content-Type", "application/x-www-form-urlencoded");httppost.addHeader("Referer", loginUrl);httppost.addHeader("Upgrade-Insecure-Requests", "1");httppost.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");HttpEntity reqEntity = new UrlEncodedFormEntity(nvps, Consts.UTF_8);httppost.setEntity(reqEntity);try {responseLogin = httpClient.execute(httppost);// 设置响应码,后面用int statusCode = responseLogin.getStatusLine().getStatusCode();if (statusCode != 200) {System.out.println("登录失败!请重新运行程序,如多次失败,请联系作者!");return;}System.out.println("登录成功,即将自动监控工单...");System.out.println(EntityUtils.toString(responseLogin.getEntity()));} catch (Exception e) {e.printStackTrace();} finally {try {responseLogin.close();} catch (IOException e) {e.printStackTrace();}}

 

运行,查看结果

非常感动,模拟登录成功!接下来访问查询界面,查询界面的url试了好一会,找了大半天,发现访问查询界面cookie会发生变化,所以直接访问不可取,查看其他请求,发现有一个请求是这样的:http://180.153.49.81:18989/SSO/login?service=http%3A%2F%2F180.153.49.216%3A9000%2Flayout%2Findex.xhtml

原来必须在请求路径后面加上查询的url才可以获取最新的cookie

shit,原来跨域了。。。访问之后获取到最新的cookie,然后带着最新的cookie去访问真正的查询界面,最后终于成功了

上代码,两分钟一刷,在这里是判断页面包不包含字符串来判断有没有单子,实际换上表哥的名字就行了,没有对页面进行深层解析,如果解析页面,可以使用jsoup,可以像原生js一样解析html,非常容易上手。值得一提的是,获取到最新的cookie之后,发现原来的cookie还在,需要去除之前的cookie,并且gc回收的时候cookiestore中的cookie有被回收的风险,需要在本地维护一份变量才行。

        List<Cookie> cookies = context.getCookieStore().getCookies();/** for (Cookie c : cookies) {* * cookie += c.getName() + "=" + c.getValue() + "; "; }*/// System.out.println(cookie);CloseableHttpResponse responseQueryWork = null;HttpGet httpGetQuery = new HttpGet(queryWorkUrl); // 查询地址String viewState = "";try {responseQueryWork = httpClient.execute(httpGetQuery);HttpEntity entity = responseQueryWork.getEntity();String str = EntityUtils.toString(entity);// System.out.println(str);viewState = regex("\"javax.faces.ViewState\" value=\"([^\"]*)\"", str)[0];// System.out.println(viewState);//获取现在的cookieList<Cookie> lastcookies = context.getCookieStore().getCookies();//移除之前的cookiefor (Cookie c : cookies) {lastcookies.remove(c);}// System.out.println(lastcookies);//清空cookiecontext.getCookieStore().clear();//设置最新的cookiefor (Cookie c : lastcookies) {context.getCookieStore().addCookie(c);// 保存到当前的cookie 避免gc引起cookie丢失//savedCookies 为全局静态变量savedCookies.add(c);}/** String lastCookie = ""; for (Cookie c :* context.getCookieStore().getCookies()) {* * lastCookie += c.getName() + "=" + c.getValue() + "; "; }*/// System.out.println(lastCookie);EntityUtils.consume(entity);} catch (Exception e) {e.printStackTrace();} finally {try {responseQueryWork.close();} catch (IOException e) {e.printStackTrace();}}String lastViewState = viewState;Timer checkTimer = new Timer();System.out.println("==开始监控工单,请一定不要关闭窗口,如有工单会有音乐提醒!!两分钟一刷,尽量把电脑音量调大,以免听不见!!==");// 两分钟一刷checkTimer.schedule(new TimerTask() {@Overridepublic void run() {// 判断当前的cookie是否被回收 如果被回收,把之前储存的cookie加入if (context.getCookieStore().getCookies().size() == 0) {for (Cookie c : savedCookies) {context.getCookieStore().addCookie(c);}}CloseableHttpResponse lastQueryResponse = null;HttpPost lastPost = new HttpPost(lastQueryUrl); // 查询地址List<NameValuePair> lastnvps = new ArrayList<NameValuePair>();lastnvps.add(new BasicNameValuePair("AJAXREQUEST", "_viewRoot"));lastnvps.add(new BasicNameValuePair("queryForm", "queryForm"));lastnvps.add(new BasicNameValuePair("queryForm:msg", "0"));lastnvps.add(new BasicNameValuePair("queryForm:queryBillId", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryBillSn", ""));lastnvps.add(new BasicNameValuePair("queryForm:isQueryHis", "N"));lastnvps.add(new BasicNameValuePair("queryForm:queryStationId", ""));lastnvps.add(new BasicNameValuePair("queryForm:deviceidText", ""));lastnvps.add(new BasicNameValuePair("queryForm:addOrEditAreaNameId", ""));lastnvps.add(new BasicNameValuePair("queryForm:aid", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryUnitId", ""));lastnvps.add(new BasicNameValuePair("queryForm:j_id48", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryDWCompany", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryDWCompanyName", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryAlarmId", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryAlarmName", ""));lastnvps.add(new BasicNameValuePair("queryForm:j_id58", ""));lastnvps.add(new BasicNameValuePair("queryForm:starttimeInputDate", "2019-06-20 15:00"));lastnvps.add(new BasicNameValuePair("queryForm:starttimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:endtimeInputDate", ""));lastnvps.add(new BasicNameValuePair("queryForm:endtimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:revertstarttimeInputDate", ""));lastnvps.add(new BasicNameValuePair("queryForm:revertstarttimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:revertendtimeInputDate", ""));lastnvps.add(new BasicNameValuePair("queryForm:revertendtimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:dealstarttimeInputDate", ""));lastnvps.add(new BasicNameValuePair("queryForm:dealstarttimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:dealendtimeInputDate", ""));lastnvps.add(new BasicNameValuePair("queryForm:dealendtimeInputCurrentDate", "06/2019"));lastnvps.add(new BasicNameValuePair("queryForm:sitesource_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:querystationstatus_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:billStatus_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:faultSrc_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:isHasten_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:alarmlevel_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:faultDevType_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:isOverTime_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:isReplyOver_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:subOperatorHid_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:operatorLevel_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:turnSend_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:sortSelect_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:faultTypeId_hiddenValue", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryCrewVillageId", ""));lastnvps.add(new BasicNameValuePair("queryForm:hideFlag", ""));lastnvps.add(new BasicNameValuePair("queryForm:queryCrewVillageName", ""));lastnvps.add(new BasicNameValuePair("queryForm:refreshTime", ""));lastnvps.add(new BasicNameValuePair("queryForm:panelOpenedState", ""));lastnvps.add(new BasicNameValuePair("javax.faces.ViewState", lastViewState));lastnvps.add(new BasicNameValuePair("queryForm:j_id133", "queryForm:j_id133"));lastnvps.add(new BasicNameValuePair("AJAX:EVENTS_COUNT", "1"));lastPost.addHeader("Content-Type", "application/x-www-form-urlencoded");lastPost.addHeader("Referer", lastQueryUrl);HttpEntity reqEntityQuery = new UrlEncodedFormEntity(lastnvps, Consts.UTF_8);lastPost.setEntity(reqEntityQuery);try {lastQueryResponse = httpClient.execute(lastPost);// System.out.println(lastQueryResponse.getStatusLine().getStatusCode());HttpEntity lastEntity = lastQueryResponse.getEntity();// System.out.println(EntityUtils.toString(lastEntity));String lastStr = EntityUtils.toString(lastEntity);//如果查询成功,页面必然有包站人这几个字if (lastStr.contains("包站人")) {System.out.println("有单子来了!!快去接单吧!!音乐连续放三次会自动停止。。进入下轮检查!");// 连续放3次/** for (int i = 0; i < 3; i++) { playMusic(); }*/}EntityUtils.consume(lastEntity);} catch (IOException e) {e.printStackTrace();} finally {try {lastQueryResponse.close();} catch (IOException e) {e.printStackTrace();}}}}, 0, 120000);public static void playMusic() {try {String desktopDir = FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();FileInputStream inputStream = new FileInputStream(desktopDir + "\\tip.mp3");Player player = new Player(new BufferedInputStream(inputStream));player.play();} catch (Exception e) {e.printStackTrace();}}

 

总体运行效果:

大功告成,其实可以做成图形界面,但是有点麻烦,这样已经足够了,哈哈,打个jar包写个bat双击运行即可。

这篇关于模拟登录+cookie保持+数据爬取——中国铁塔的爬虫之旅的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/m0_37719874/article/details/93461347
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/325668

相关文章

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

Spring Boot 整合 Redis 实现数据缓存案例详解

《SpringBoot整合Redis实现数据缓存案例详解》Springboot缓存,默认使用的是ConcurrentMap的方式来实现的,然而我们在项目中并不会这么使用,本文介绍SpringB... 目录1.添加 Maven 依赖2.配置Redis属性3.创建 redisCacheManager4.使用Sp

Python模拟串口通信的示例详解

《Python模拟串口通信的示例详解》pySerial是Python中用于操作串口的第三方模块,它支持Windows、Linux、OSX、BSD等多个平台,下面我们就来看看Python如何使用pySe... 目录1.win 下载虚www.chinasem.cn拟串口2、确定串口号3、配置串口4、串口通信示例5

Python Pandas高效处理Excel数据完整指南

《PythonPandas高效处理Excel数据完整指南》在数据驱动的时代,Excel仍是大量企业存储核心数据的工具,Python的Pandas库凭借其向量化计算、内存优化和丰富的数据处理接口,成为... 目录一、环境搭建与数据读取1.1 基础环境配置1.2 数据高效载入技巧二、数据清洗核心战术2.1 缺失

Python处理超大规模数据的4大方法详解

《Python处理超大规模数据的4大方法详解》在数据的奇妙世界里,数据量就像滚雪球一样,越变越大,从最初的GB级别的小数据堆,逐渐演变成TB级别的数据大山,所以本文我们就来看看Python处理... 目录1. Mars:数据处理界的 “变形金刚”2. Dask:分布式计算的 “指挥家”3. CuPy:GPU

SpringBoot后端实现小程序微信登录功能实现

《SpringBoot后端实现小程序微信登录功能实现》微信小程序登录是开发者通过微信提供的身份验证机制,获取用户唯一标识(openid)和会话密钥(session_key)的过程,这篇文章给大家介绍S... 目录SpringBoot实现微信小程序登录简介SpringBoot后端实现微信登录SpringBoo

使用Vue-ECharts实现数据可视化图表功能

《使用Vue-ECharts实现数据可视化图表功能》在前端开发中,经常会遇到需要展示数据可视化的需求,比如柱状图、折线图、饼图等,这类需求不仅要求我们准确地将数据呈现出来,还需要兼顾美观与交互体验,所... 目录前言为什么选择 vue-ECharts?1. 基于 ECharts,功能强大2. 更符合 Vue

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

Python实现获取带合并单元格的表格数据

《Python实现获取带合并单元格的表格数据》由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,所以本文我们就来聊聊如何使用Python实现获取带合并单元格的表格数据吧... 由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,现将将封装成类,并通过调用list_exc