Captcha 보안문자가 있는 페이지의 크롤링 진행
1. Selenium Webdriver 초기세팅
2024.07.30 현재 개발은 완성된 상태이지만, 일정상 한번에 작성이 어려워 분리하여 작성중입니다.
2024.08.01대법원 스크래핑을 위한 다른 페이지를 확인 후 개발진행하여 스크래핑 진행 사이트가 다릅니다.
이미지나 내용을 수정하기 어려워 그대로 유지 중 입니다.
지원이 필요하시면 좌측 프로필의 메일로 연락 부탁드립니다.
서론
회사 내 프로젝트 개발간 사건정보 및 기일정도 자동조회 구현을 위하여
https://www.scourt.go.kr/portal/information/events/search/search.jsp 페이지의
https://safind.scourt.go.kr/sf/captchaImg?t=image 이미지 스크래핑이 필요함.
이에 아래 두가지 방안이 제시됨.
1. 캡챠 뚫기 (머신러닝 캡챠 뚫기)
2. 이미지를 가져와서 사용자에게 입력시키기
여기서 2번 방안으로 진행 요청을 주심.
Captcha 보안문자 이미지를 가져와서 사용자에게 입력시키기
Selenium 선택 사유
- CSR 기반의 동적 페이지 크롤링에 유용하며 가이드문서가 정말 잘 되어있음.
- 회사 유닛장님의 사용 추천과 이전 카카오톡, 디스코드 챗봇 개발경험
개발이슈
문제는 이 이미지가 실시간으로 변경이된다.
https://safind.scourt.go.kr/sf/captchaImg?t=image
이미지 URL링크에 접근해서 새로고침을 해보면 알겠지만
접근 할 때마다 이미지가 새로고침이되고
URL로 가져와 이미지화시키면 당연히 다른이미지로 호출되고
우측클릭해서 이미지를 저장시켜도 다른이미지가 저장되고..
일단 JAVA에 셀레늄부터 적용해보는걸 목표로 진행해본다.
셀레늄이란?
Web Application 테스트 및 자동화를 위한 자동화 도구입니다.
주로 웹 테스트를 위한 목적으로 QA분들이 많이 사용하나 웹 기반에서 스크래핑 등의 여러 자동화 작업을 하는 용도로도 많이 사용합니다.
End-To-End Test Automation Tool이라고도 합니다.
Java 프로젝트에 Selenium 라이브러리 추가
*Java에서 Selenium Webdriver를 사용하기 위한 목적으로 가이드 진행예정입니다.
https://www.selenium.dev/documentation/
Selenium JAVA Maven 설정하기
현 프로젝트 개발 기준에 맞춰 Maven세팅으로 selenium-java를 추가합니다.
<!-- pom.xml -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<version>${selenium.version}</version>
</dependency>
위와 같이 디펜던시 세팅을 하고 아래 코드로 테스트를해보면
WebDriver driver = new ChromeDriver();
driver.get("https://www.scourt.go.kr/portal/information/events/search/search.jsp");
driver.quit();
오류 (1) → The path to the driver executable must be set by the webdriver.chrome.driver system property; for more information
java.lang.IllegalStateException: The path to the driver executable must be set by the webdriver.chrome.driver system property; for more information, see https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver. The latest version can be downloaded from http://chromedriver.storage.googleapis.com/index.html
at com.google.common.base.Preconditions.checkState(Preconditions.java:847)
at org.openqa.selenium.remote.service.DriverService.findExecutable(DriverService.java:134)
at org.openqa.selenium.chrome.ChromeDriverService.access$000(ChromeDriverService.java:35)
at org.openqa.selenium.chrome.ChromeDriverService$Builder.findDefaultExecutable(ChromeDriverService.java:159)
at org.openqa.selenium.remote.service.DriverService$Builder.build(DriverService.java:355)
at org.openqa.selenium.chrome.ChromeDriverService.createDefaultService(ChromeDriverService.java:94)
at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:123)
at com.duzon.lulu.legal.controller.CodefController.getCodefTest(CodefController.java:40)
at com.duzon.lulu.legal.controller.CodefController$$FastClassBySpringCGLIB$$8ddcd761.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:97)
at com.duzon.lulu.legal.aop.SessionAop.checkSession(SessionAop.java:107)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
at com.duzon.lulu.legal.controller.CodefController$$EnhancerBySpringCGLIB$$a496a05c.getCodefTest(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.duzon.lulu.legal.filter.SwaggerFilter.doFilter(SwaggerFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.duzon.common.filter.P3PFilter.doFilter(P3PFilter.java:22)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:615)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1627)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
오류 (1) 해결방안
단순히 크롬창으로 크롤링을하니 크롬창을 띄울 크롬 드라이버 다운로드 및 설정이 필요했음.
Chrome Driver 다운로드 및 드라이버 실행
1. https://googlechromelabs.github.io/chrome-for-testing/#stable
2. 자신의 맥 버젼에 맞춰 우측 URL을 복사하여 주소창에 입력하여 다운로드 진행
2. 프로젝트에 등록
3. 크롬드라이버 위치 세팅
System.setProperty("webdriver.chrome.driver", "/.../driver/mac/chromedriver");
WebDriver driver = new ChromeDriver();
driver.get("https://www.scourt.go.kr/portal/information/events/search/search.jsp");
//url 크롤링을 위한 더 간편한 사이트 발견해서 변경예정
4. 실행
크롬웹드라이브를 통해 정상적으로 사이트가 실행된걸 확인할 수 있습니다.
TODO 캡챠(Captcha) 이미지 추출하기
https://gaetaeng.tistory.com/79