반응형

아본의 엘시노 소드 앞에서. 저 엘시노 소드가 참 갖고싶었는데.

 

마비노기는 내 게임 인생 중 가장 오래한 PC 온라인 게임이라고 봐도 무방할 정도이다.

 

 


고2때 TV에서 틀어준 CF를 시작으로 마비노기를 시작했으며, 한창 디씨인사이드를 많이 했었던지라 디씨 마갤에 상주하면서 군대가기 직전 까지, 그리고 군대를 다녀오고 사회인이 되어서까지 오래오래 즐긴 게임이다.
마비노기의 흥망성쇠를 다 알고 있다고 한다면 당연히 거짓이겠지만, 나름 찬란했던 시절과 암흑기로 넘어가던 시절까지 즐긴 편이니 이정도면 상당히 오래한거는 맞다.

 

이 넓은 대륙을 엘로드 들고 걸어다닌 적도 있다;

마비노기를 시작하고 가장 재미있었던 때는 아무래도 새로운 종족인 엘프와 자이언트가 나오기 직전까지였다.
누적레벨과 스킬레벨로 대표되는 초창기의 마비노기는 잦은 환생으로 어빌리티 포인트를 모으고, 스킬을 수련하여 어빌리티 포인트를 소모하여 스킬 레벨업을 이루는 방식으로 되어있으며 장비들을 직접 제작, 숙련도를 통한 개조, 인챈트로 대표되는 장비 시스템과 극초기에만 호황(?)이었던 아르바이트, 나름 체계적인 시나리오 등으로 내 입맛에 쏙 들었었다.

 

축포 한가방 꽉채우는것은 국롤

성당 아르바이트를 하게 되면 축복의 포션을 구할 수 있었고 이 축포를 사용하게 되면 해당 장비의 내구도 소모량이 줄어드는 효과가 있었는데, 장비의 수리확률이 일부를 제외하고는 100%가 아니다보니 축포는 게임 내 필수적인 아이템이나 다름없었다.
쪼렙때는 부캐의 부캐를 돌려가며 축포 노가다를 했었고, 이걸 팔아치우면서 돈을 벌기도 했었다.
디씨 마갤러들을 만나며 장비도 허접하게 맞추기를 시작했고, 군대를 다녀오고나서는 아예 친구가 되었고 이 친구들은 10년이 지난 지금까지도 안부인사를 묻는 관계가 되었다. 게임에서 친구를 실제로 사귀다니, 아주 놀라울 정도.

 

한창 열심히 할 때의 스킬

마비노기는 직업 이라는 개념이 없었으며 (추후에는 재능 뭐시기 업데이트를 통해 직업적인 개념이 정립되긴 했다) 근접 스킬 / 활 스킬 / 마법 스킬 등 어떤것을 우선시하여 스킬레벨을 올리느냐에 따라 직업으로서의 개념이 존재했다.
거기에 스텟을 보정해주는 스킬들로는 생활 스킬이 있었는데 그 중에서 방직 스킬과 제련 스킬은 진짜 토나올 정도로 난이도가 빡세고 노가다도 엄청났던 기억이 난다. 뭐, 요즘은 엄청 쉬워졌다지만.

 

친구들과 아본 던전을 돌 때.

가장 기억에 남는 던전은 이멘마하의 룬다던전으로 몽환적인 BGM이 아주 인상적이었다. 극초기(이리아가 나오기 전)에는 그저 저승에 가서 빨간구슬 던전 노가다를 뛰는것이 최고라곤 했는데 그럴바에야 사람들 틈에 껴서 알비 상급 던전이나 마스 상급 심심하면 페카 던전에 꼽사리 껴서 가는 것이 그렇게나 재밌을수가 없었다.
대학생이 되고 나서는 월정액권을 결제하며 게임을 즐겼지만, 용돈이 그마저도 여의치 않을 때에는 일일 2시간 플레이를 수행한 후, 접속이 끊기기 직전에 던전에 쳐박아두고 아는 사람들과 수다떠는것이 그나마 유일한 낙이었다.
이 당시의 이 행동을 '나과장이 잡으러 온다'는 식으로 밈화 해서 이야기 했던 것도 있었다.

 

이 펫은 1살에 60레벨을 달성했다. 가히 최강의 펫.

이후 이리아가 업데이트 되면서 새로운 종족인 자이언트와 엘프가 새로 생겼고 별의별 스킬과 펫들이 본격적으로 대두되기 시작했다.
엘프는 활 스킬에 이점이 있는 종족이었고, 자이언트는 근접공격에 이점이 있는 종족이었다. 인간의 양손검은 자이언트에게 한손검으로 취급된다거나, 엘프는 걸음걸이가 빠르며 활 공격이 두 발씩 나간다거나 하는 소소한 차이점이 존재했다.

 

반신화

이 즈음부터는 본격적으로 마비노기 유저들에게서 돈을 빨아먹었던 때로, 어마어마하게 넓은 이리아 대륙을 그냥 다니기가 어려우니 날아다니는 펫이나 말 펫들이 엄청나게 출시되었다. 거기에 나중에는 공격력이 상당한 펫들 뿐만 아니라, 소환시 경직 공격을 주는 펫 까지 등장하여 이 펫이 없으면 파티에 안껴줄 정도의 밸런스파괴를 불러일으키기도 했다.
그리고 이리아 업데이트를 시작으로 마비노기의 시나리오는 산으로 가기 시작했고, 결국 나중에는 대체 뭔소릴 씨부리는것인지 도저히 이해하기 어려운 스토리들이 연이어 나오기도 했다. 그래서 브류나크가 어쨌고 반신이 어쨌고... 그래서 세익스피어는 왜 나온거더라? 아직도 그들의 관계는 이해할수가 없네.

초기의 G1~G3의 스토리는 각각의 연결고리와 반전 스토리가 숨어있는 등 치밀하고 탄탄한 스토리가 아주 좋았으나 후반부로 갈 수록 개연성도 떨어지고 설득력도 떨어지는 스토리들로 그냥 보상이나 구하려는 메인 스토리가 된 것이 참으로 아쉽다.

 

초기에는 빨구던전이 있었다면, 내가 접기 직전까지는 탈틴의 정찰하드가 있었다. 탈틴이라는 지역의 퀘스트(?) 중에, 정찰병 구출이라는 몬스터 리젠 퀘스트가 있었는데 여기 하드 난이도가 빠른 시간 내에 끝나고 보상도 상당히 좋은(우아한 건틀렛)편이었다. 허구언날 여기서 주구장창 수다떨며 놀기도 했던 아련한 추억이 있다.

 

그나마 이정도가 내가 가장 잘 꾸민거라거나..

나름 꾸미기가 메인 컨텐츠인만큼 귀엽고 아기자기한 그래픽들 사이에서 여캐 비율이 극도로 높은 편이었으나 나는 그러한 고만고만한 씹덕취향이 너무나도 내 스타일이 아니라서 대놓고 신기하게 생긴(?) 캐릭터들을 주로 만들었다. 또는 예쁜 반짝이 염색약들 보다는 어디서 아무도 쓰지 않는 염색약들을 잔뜩 모아다가 모든 파트에 발라서 나이트 사이키 조명과도 같은 모습으로 다닌다거나. 오히려 이러한 점이 내 마음에 들었을지도 모르겠다.

게임을 접기 직전에는 온갖 추악한 업데이트가 많았는데, 대놓고 파티플을 유도하는 교역이라거나 돈벌어먹을 상술로 대표되는 각종 캐쉬템들이 그러했다.
특히 세공이라는 것이 나왔을 때에는 사회적인 이슈가 될 정도로 문제가 많고 게임 내에서의 밸런스도 상당히 문제였을듯. 이후 게임을 접어서 지인을 통해 마비노기의 근황을 엿듣고는 하는데, 내가 했을 때와는 전혀 다른 게임이 되어버려서 대체 누굴 위한 게임인가, 싶을정도이긴 하다.
그나마 이제는 그 구닥다리 플라나리아 엔진인가 플레이오네 엔진을 언리얼로 바꾼다고 하니, 이제는 좀 더 나은 게임 환경이 되지 않을까 싶긴 하다.

말은 이렇게 했어도 게임 인생 중 가장 재미있게 즐겼던 게임임에는 틀림없다.
길드 정모도 몇 번이나 있었고, 지금도 연락하고 결혼식에 와줄 친구들을 만들었다거나 어디가서 술안주로 게임이야기를 하면 빠질 수 없는 추억을 만들어줬으니까.
지금 생각해보면 저런 게임을 다시는 못하겠지 싶긴 하다. 이 즈음부터, 우리나라의 게임 대세는 MMORPG가 아니라 한두시간 안에 끝날만한 게임들 위주로 대세가 기울기도 했고 말이다.

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

개발하는데 java 배치로 Windows Powershell을 파라미터 방식으로 호출할 일이 생겼다. 일단 powershell Script 코드는 다음과 같다.

 

1
2
echo 'TEST-1' $test
echo 'TEST-2' $userid
cs

대충 파라미터 두 개( test/userid)를 받아 echo로 찍어주는건데 기타 하위에는 물론 Azure Portal과의 통신이 있긴 하다. 근데 그게 중요한 것은 아니니까.

아무튼, java에서 파워쉘을 호출하는 소스코드는 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//중략
 
final Runtime rt = Runtime.getRuntime();
 
//powershell.exe 명령어를 통해서, 해당 경로의 ps1 파일을 실행함
String commands = String.format("powershell.exe \"F:\\powershell.ps1  ");
 
Process proc = null;
String s = null;
 
// PowerShell 명령 시도 및 메시지 출력
try {
    proc = rt.exec(commands);
 
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream());
    while (( s = stdInput.readLine()) != null ) {
        System.out.println(s);
    }
 
    BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream());
    while (( s = stdError.readLine()) != null ) {
        System.out.println(s);
    }
 
catch (IOException e) {
    e.printStacTrace();
 
}
 
//생략
cs

요기까지는 잘 했는데, 위의 powershell 코드와 같이 parameter를 넘기는 방법을 구글링해보아도 죄다 제각기 방법이 달라서, 츄라이를 해보니 다음과 같은 소스코드로 성공했다.

 

위의 java 소스에서 commands를 다음과 같은 방법으로 변경해주면 된다.

1
2
3
4
5
6
7
//중략
 
String[] arr = {"test_msg" , "test_user"}
//powershell.exe 명령어를 통해서, 해당 경로의 ps1 파일을 실행함
String commands = String.format("powershell.exe \"F:\\powershell.ps1  " + arr[0+ " " + arr[1]);
 
//생략
cs

PowerShell 코드는 다음과 같이 수정해주면 된다.

1
2
3
4
5
$test = $args[0]
$userid = $args[1]
 
echo 'TEST-1' $test
echo 'TEST-2' $userid
cs

java 에서 공백문자열로 구분된 string 값들이 args로 잘 매핑이 된다.

 

테스트를 해보니 해당 ps1(powershell)을 호출할 때 각 String 형태의 parameter(arguments)들을 넘길 때, 그냥 공백문자열로 구분하는 것으로 보인다. 실제로 여러방법으로 테스트 해보니 잘 되기도 하고. 이로써 java로 powershell script를 parameter 포함하여 execute 하는것에 성공했다. 앞으로 근데, 이러한 플젝을 몇번이나 할 지는 모르겠지만 말이다 =_=;;

 

 

java - how pass string array as a parameters to a powershell 

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

한컴오피스의 한글파일(hwp)에서 텍스트 추출할 일이 생겼다.

대충 뒤져보니 대단하신 분 께서 한글문서 파서 라이브러리를 만드셨는데, 아직까지도 일부기능에 대해 개선작업을 진행중이신 것 같다.

뭐 표 라거나 그림파일 등에 대해서는 정상동작하지 않는 듯 하지만 나는 텍스트만 추출할 것이기 때문에 당장은 문제없이 사용가능할것으로 보인다.

 

자세한 지원범위는 이 분의 깃으로 들어가보면 될 듯.

https://github.com/neolord0/hwplib

 

GitHub - neolord0/hwplib: hwp library for java

hwp library for java. Contribute to neolord0/hwplib development by creating an account on GitHub.

github.com

아무튼, 대충 임포트하고 대충 써보기로 한다. 생각보다 텍스트 추출이 아주 잘 되어서 다행이다.

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/kr.dogfoot/hwplib -->
<dependency>
  <groupId>kr.dogfoot</groupId>
  <artifactId>hwplib</artifactId>
  <version>1.0.1</version>
</dependency>
cs

이렇게 잡아주고,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* hwplib hwp document text extraction example
*/
 
import kr.dogfoot.hwplib.object.HWPFile;
import kr.dogfoot.hwplib.reader.HWPReader;
import kr.dogfoot.hwplib.tool.textextractor.TextExtractMethod;
import kr.dogfoot.hwplib.tool.textextractor.TextExtractor;
 
// 중략
 
HWPFile hwpFile;
String hwpText;
try {
    hwpFile = HWPReader.fromFile("/Users/kimfish/DEV/java_workspace/"+"test.hwp");
    hwpText = TextExtractor.extract(hwpFile, TextExtractMethod.InsertControlTextBetweenParagraphText);
 
    System.out.println("===== hwp text extractor =====");
    System.out.println("hwpText = " + hwpText);
catch (Exception e) {
    e.printStackTrace();
cs

이렇게 쓰면 된다. 개꿀

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

일전의 포스트와 마찬가지로 pdf 에서도 텍스트를 추출할 일이 생겼다.

당연하겠지만 해당 pdf는 ocr이 된 pdf를 기준으로만 추출이 가능하다.

 

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
<dependency>
  <groupId>org.apache.pdfbox</groupId>
  <artifactId>pdfbox</artifactId>
  <version>2.0.24</version>
</dependency>
cs

이야 1년쯤 전에는 2.0.18 이었는데 그새 버전업했네. 아무튼 maven repository는 이렇게 잡아주고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* PDFBox library PDF text Extraction Example
*/
 
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
 
// 중략
 
try {
    File file = new File("/Users/kimfish/DEV/java_workspace/"+"/test.pdf");
    PDDocument document;
    document = PDDocument.load(file);
 
    PDFTextStripper s = new PDFTextStripper();
    String content = s.getText(document);
 
    System.out.println("===== docx text extractor =====");
    System.out.println(content); 
catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
cs

이렇게 하면 정상적으로 OCR 처리된 text가 추출된다.

 

 

 

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

아주 심플하게, 회사에서 해당 문서규격(xls/xlsx/doc/docx)에 대해 텍스트를 추출하는것이 필요했다. 일전에는 잠깐 해보았는데, 정리된 적이 없었고 이번을 계기로 좀 알게된 몇가지 사실들이 있기에 간단하게 기록해본다.

 

일단 메이븐에서 다음과 같이 라이브러리를 잡아준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-scratchpad</artifactId>
  <version>5.2.2</version>
</dependency>
 
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>5.2.2</version>
</dependency>
 
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>5.2.2</version>
</dependency>
 
cs

 

1. 워드 (doc/docx) 텍스트 추출

워드 2003 이전버전인 doc 와 이후 버전인 docx 를 추출하기 위해서는 두 가지만 기억하면 된다.

HWPFDocument 클래스는 Horrible Word Process Format 의 약자라는 점이며, poi 라이브러리만 상속받는것이 아닌 poi-scratchpad 라이브러리를 같이 상속받아야 한다. 즉, poi dependency만 잡아놓으면 HWPFDocument가 import 되지 않는다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* Word 2003 doc File Text Extraction Example
*/
 
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
// 중략
 
try {
    POIFSFileSystem poiFS = new POIFSFileSystem(
        new FileInputStream("/Users/kimfish/DEV/java_workspace/"+"/test.doc"));
    HWPFDocument hwp = new HWPFDocument(poiFS);
    WordExtractor we = new WordExtractor(hwp);
 
    String[] paragraphs = we.getParagraphText();
    System.out.println("===== doc text extractor ======");
    for (int i = 0; i < paragraphs.length; i++) {
        System.out.println(paragraphs[i]);
    }
 
catch (Exception e) {
    System.out.println(e); 
}
cs

 

docx 파일은 XWPFDocument를 import 받으며, docx의 text추출을 위해서는 poi 및 poi-ooxml 라이브러리에 대한 dependency가 필요하다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* Word 2007 docx File Text Extraction Example
*/
 
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
 
// 중략
 
try(
    FileInputStream fis = new FileInputStream
            ("/Users/kimfish/DEV/py_workspace/"+"/test.docx")) {  
    XWPFDocument file   = new XWPFDocument(OPCPackage.open(fis));  
    XWPFWordExtractor ext = new XWPFWordExtractor(file);  
 
    System.out.println("===== docx text extractor ======");
    System.out.println(ext.getText());  
}catch(Exception e) {  
    System.out.println(e);  
}  
cs

 

2. 엑셀 (xls/xlsx) 텍스트 추출

엑셀은 고려할 점이 상당히 많은데 각 셀/로우 별 텍스트 및 쉬트도 있는데다가 수식까지 가지고 있다. 현재는 샘플 토이 프로젝트라 수식이나 셀/로우 구분없이 텍스트만 가져오기를 수행해보았는데 일단 첫 쉬트에서는 잘 가져오고 있는듯. 좀 더 정교한 작업이 필요할 경우엔 공식 document 가 필요할것으로 보인다.

 

엑셀 2003 이전버전인 xls 같은 경우에는 poi Dependency 하나로도 잘 작동한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* Excel 2003 xls File Text Extraction Example
*/
 
import org.apache.poi.hssf.extractor.ExcelExtractor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
// 중략
 
try (
    FileInputStream fis = new FileInputStream("/Users/kimfish/DEV/java_workspace/"+"/test.xls")) {  
    
    String result = "";
 
    HSSFWorkbook wb = new HSSFWorkbook(fis);
    ExcelExtractor ee = new ExcelExtractor(wb);
    result = ee.getText();    
 
    System.out.println("===== xls text extractor =====");
    System.out.println(result);    
catch (Exception e) {
    //TODO: handle exception
}
cs

 

하지만 엑셀 2007 버전인 xlsx 같은 경우에는 워드2007과 마찬가지로 poi / poi-ooxml dependency 둘 다 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* Excel 2007 xlsx File Text Extraction Example
*/
 
import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
// 중략
 
try (
    
    FileInputStream fis = new FileInputStream("/Users/kimfish/DEV/py_workspace/"+"/test.xlsx")) {  
                
    // Workbook
    XSSFWorkbook wb = new XSSFWorkbook(fis);
            
    // Text Extraction
    XSSFExcelExtractor extractor = new XSSFExcelExtractor(wb);
    extractor.setFormulasNotResults(true);
    extractor.setIncludeSheetNames(false);
    
    System.out.println("===== xlsx text extractor =====");
    System.out.println( extractor.getText() );
catch (Exception e) {
    //TODO: handle exception
}
cs

 

일단은 이렇게 해서 정상적으로 출력되는것을 확인함. 어휴 poi 하나로 다 해놓든가 의존성을 걸어두던가. 왜 poi / poi-ooxml / poi-scratchpad 이렇게 세 개가 죄다 쪼개져있는지원;;

 

정리

xlsx - poi / poi-ooxml

xls - poi

docx - poi / poi-ooxml

doc - poi / poi-scratchpad

반응형

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[hwplib] hwplib을 이용한 한글파일 텍스트 추출  (1) 2022.07.06
[PDFBox] pdf 텍스트 추출  (0) 2022.07.05
[Tomcat 9.x] JNDI설정  (0) 2022.02.21
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return (2)  (0) 2021.07.06
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

파이썬 개발을 하면서 가장 헷갈리는 개념이 디렉토리 참조법이다. 자바에서는 그냥 대충 import package.directory.fileName; 과 같은 형식으로 지정해주면 바로 가져다 쓸 수 있는데 반해, 파이썬은 그렇지 않은가보다.

 

아무튼 세 가지의 케이스에 대해 정리/적용해보았고 그 내용은 다음과 같다.

 

패키지 구성은 다음과 같은 형식으로 되어있다고 가정한다.

project
⌞ A_folder
   - a_file_1.py
   - a_file_2.py
 B_folder
   - b_file_1.py
   ⌞ C_folder
       - c_file_1.py
   ⌞ D_folder
       - d_file_1.py

1. 동일 경로상의 import

A_folder 내부의 a_file_1.py 에서 a_file_2.py 파일을 import 하기위해서는 다음과 같이 작성한다.

1
from . import a_file_2.py
cs

또는

1
import a_file_2.py
cs

이거는 쉽다.

 

2. 하위 폴더의 import 

B_folder 내부의 b_file_1.py 에서 C_folder 내부의 c_file_1.py를 import 하기 위해서는 다음과 같이 작성한다.

1
from C_folder import c_file_1.py
cs

이정도는 크게 어렵지 않다.

 

3. 상위 폴더의 import

여기서부터 많이 헷갈리게 된다.

C_folder 내부의 c_file_1.py 에서 D_folder 내부의 d_file_1.py를 import 하기 위해서는 꽤 여러가지 방법이 있으나, 일단 내가 해결한 방법을 위주로 작성해본다.

1
2
3
4
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))
from D_folder import d_file_1.py
cs

여기에서 os.path.dirname(os.path.abspath( 는 한단계 상위를 의미하며, C_folder의 한단계 상위인 B_folder로 이동함을 의미한다. B_folder 내부에 D_folder가 위치해있으므로, from d_folder 을 해주는 것.

 

마찬가지로 c_file_1.py 에서 A_folder 를 참조하기 위해서는 os.path.dirname(os.path.abspath를 두 번 작성해주면 될 것이다.

 

 

java를 하다와서 그런지, from~ 쪽도 잘 이해가 안갔고 __file__과 같은 옵션네이밍(?)의 정의도 익숙하지 않다. 일단은 해결했으니까, 다음 개발로 넘어가야지...

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형
1
2
3
4
***** Usage Dev Env *****
= OS - macOS Monterey ver 12.3.1
= IDE - VSCode
= Lang - Python 3.10.4
cs

개발하고나서보니 아래의 내용 모두 다 무쓸모했다.

내가 크롤링하려던 사이트에서 스크립트의 오류인지 뭔지 chromedriver로는 정상적으로 로딩할 수 없었고, firefox를 이용한 geckodriver를 활용하니 단박에 모두 정리가 되었다.

 

아 크롬을 이렇게 버려야하나 ㅡㅡ;

파이썬으로 크롤링 개발을 하는데 요상한 에러가 발생했다. 사실 별 소스코드도 없이 파이썬에 셀레니움, 크롬드라이버를 붙여서 브라우저 하나 띄우는건데 여기서 저런 에러를 만날 줄이야 꿈에도 몰랐다.

 

대충 아래의 소스코드에서 오류가 발생했다.

1
2
3
4
5
6
7
8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
 
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=chrome_options)
driver.get('http://kimfish.co.kr')
driver.quit()
cs

 

그리고 발생오류상황은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  ~ /usr/local/bin/python3 /Users/kimfish/DEV/py_workspace/testChromeDriver.py
Traceback (most recent call last):
  File "/Users/kimfish/DEV/py_workspace/testChromeDriver.py", line 9, in <module>
    driver.get("https://bbs.ruliweb.com")
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 437, in get
    self.execute(Command.GET, {'url': url})
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 425, in execute
    self.error_handler.check_response(response)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/errorhandler.py", line 247, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: unknown error: session deleted because of page crash
from tab crashed
  (Session info: chrome=100.0.4896.88)
Stacktrace:
0   chromedriver                        0x0000000105383c34 chromedriver + 4406324
1   chromedriver                        0x000000010531d290 chromedriver + 3986064
2   chromedriver                        0x0000000104f7e71c chromedriver + 190236
=== 중략 ===
➜  ~ 
cs

 

Message: unknown error: session deleted because from tab crashed 와 같은 오류에 대해 검색을 해보니, 가상머신(대부분 도커)의 /dev/shm 메모리가 부족하다는둥 뭐라는둥 말이 많았는데 난 단순히 내 로컬환경에서 개발을 할 뿐이다 ㅠ

 

그래서 조금 더 찾아보니 chromedriver 호출시, 다음과 같은 옵션을 설정해주면 된다고 한다.

1
2
3
4
5
6
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--window-size=1420,1080')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--disable-gpu')
cs

적용 후 돌렸다! 그런데도 똑같은 오류가 발생한다!

 

환장하겄다 싶어 옵션을 다 빼보면서 테스트 하다가 결국 원인을 찾기는 했다. 바로, --headless 옵션이 문제였다.

해당 옵션은 UI가 지원되지 않는 서버들을 위해 작동하는 옵션으로 알고 있는데, 내 개발환경에서는 왜 오류가 발생하는지는 아직 잘 모르겠다. 일단 저 옵션을 제외하고는 잘 동작하니까 개발하겠지만... 조금 불안하다. 원인을 알면 해결법도 찾아야하는데.. 으으 

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

* M1 MacBookPro macOS Monterey 12.3.1

 

파이썬 + selenium을 이용하여 웹 크롤러를 만드려고 한다.

그전에 셀레니움을 사용하기 위해서는 크롬 디버그모드 활용툴과 같은 개념인 chromedriver를 설치, 사용해야 한다. 설치법은 크게 두 종류로 나뉘는 것으로 보이는데, 'homebrew install'과 '공식홈페이지 다운로드 설치' 두가지의 방법이다.

 

개인적으로는 버전관리가 간편한 homebrew 설치가 좋을것이라 생각한다. 괜히 공식 홈페이지 인스톨 했다가 크롬브라우저 버전업 하면 또 가서 다운받고 압축풀고 할라면 귀찮을듯.

1.  homebrew 로 설치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
➜  ~ brew install cask chromedriver
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> New Formulae
gops
==> Updated Formulae
Updated 2 formulae.
 
==> Downloading https://chromedriver.storage.googleapis.com/100.0.4896.60/chrome
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/366342d76a11f7c61ca4b54b8d00cf63f59917eb118f2d9f5745484daa0f8598--chromedriver_mac64_m1.zip
==> Installing Cask chromedriver
==> Linking Binary 'chromedriver' to '/opt/homebrew/bin/chromedriver'
🍺  chromedriver was successfully installed!
==> Downloading https://ghcr.io/v2/homebrew/core/cask/manifests/0.8.8
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/2b8b007815bfe6179d48bcdcbcce75cc494fd12d6c3dd831e60a05cb50229c7f--cask-0.8.8.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/cask/blobs/sha256:f50a59d4337bc
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/10cb17e2b53cf4b92c88efd42c0d6dcaf43570a3e7ba65939e070cb79335b3bd--cask--0.8.8.all.bottle.tar.gz
==> Pouring cask--0.8.8.all.bottle.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /opt/homebrew/share/emacs/site-lisp/cask
==> Summary
🍺  /opt/homebrew/Cellar/cask/0.8.813 files, 152KB
==> Running `brew cleanup cask`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Caveats
==> cask
Emacs Lisp files have been installed to:
  /opt/homebrew/share/emacs/site-lisp/cask
cs

한 번 지웠다가 재설치한 부분이 있어 로그가 좀 깔끔하지 않은듯 한데, 일단 설치 자체는 어렵지 않다.

그리고 이거 다른 사람들의 블로그에서 잘 다뤄주지 않던 이야기인데, 크롬 브라우저는 워낙에 버전업이 자주되다보니 소스코드가 잘 안돌아간다 싶으면 수시로 브라우저 버전과 크롬드라이버 버전이 일치하는지 확인해야 한다. 그럴 때 쓰는 버전 확인 명령어는 다음과 같다.

 

2. chromedriver 악성소프트웨어 검사

어라? 크롬드라이버 버전확인을 하려 했더니 다음과 같은 오류메시지가 발생하네?

 

‘chromedriver’() Apple에서 악성 소프트웨어가 있는지 확인할 없기 때문에 없습니다.

이는 권한 설정으로 해결할 수 있다. 다음과 같이 수행이 가능하다.

1
2
3
➜  bin git:(stable) pwd
/opt/homebrew/bin
➜  bin git:(stable) xattr -d com.apple.quarantine chromedriver
cs

주의할 점은 xattr -d 명령어 자체가 chromedriver 가 설치되어있는 디렉토리에서 수행해야 한다는 점이다.

brew 로 설치하는 경우에는 /opt/homebrew/bin 디렉토리 내부에 위치한다.

정상적으로 적용이 되어도 별다른 프롬프트가 나오지 않으니 당황하지 말고, 바로 버전 확인 명령어를 작성한다.

1
2
3
4
➜  bin git:(stable) pwd
/opt/homebrew/bin
➜  bin git:(stable) chromedriver -version
ChromeDriver 100.0.4896.60 (6a5d10861ce8de5fce22564658033b43cb7de047-refs/branch-heads/4896@{#875})
cs

현재 브라우저의 버전과 크롬드라이버 버전이 일치하는지는 이렇게 확인하면 된다.

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

얼마전에 맥북을 샀다. 그러면서 새롭게 개발환경을 설정했다. 이하는 OpenJdk를 설치하는 내용이다.

명령어 순서는 다음과 같다.

1
2
3
4
5
6
7
1. brew tap AdoptOpenJdk/openjdk
 
2. brew install --cask adoptopenjdk8
 
3. /usr/libexec/java_home -1.8
 
4. java -version
cs

 

1. brew tap AdoptOpenJdk/openjdk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kimfish@KimFish-MacBookPro ~ % brew tap AdoptOpenJdk/openjdk
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
Updated 4 formulae.
==> Tapping adoptopenjdk/openjdk
Cloning into '/opt/homebrew/Library/Taps/adoptopenjdk/homebrew-openjdk'...
remote: Enumerating objects: 1996done.
remote: Counting objects: 100% (60/60), done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 1996 (delta 44), reused 49 (delta 38), pack-reused 1936
Receiving objects: 100% (1996/1996), 372.27 KiB | 7.16 MiB/s, done.
Resolving deltas: 100% (1424/1424), done.
Tapped 47 casks (69 files, 521.8KB).
 

 

2. brew install --cask adoptopenjdk8 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kimfish@KimFish-MacBookPro ~ % brew install --cask adoptopenjdk8
==> Downloading https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/downl
==> Downloading from https://objects.githubusercontent.com/github-production-rel
######################################################################## 100.0%
==> Installing Cask adoptopenjdk8
==> Running installer for adoptopenjdk8; your password may be necessary.
Package installers may write to any location; options such as `--appdir` are ignored.
installer: Package name is AdoptOpenJDK
installer: Installing at base path /
installer: The install was successful.
package-id: net.adoptopenjdk.8.jdk
version: 1.8.0_292-b10
volume: /
location: 
install-time: 1649472633
🍺  adoptopenjdk8 was successfully installed!
cs

 

3.  /usr/libexec/java_home -v 1.8

1
2
kimfish@KimFish-MacBookPro ~ % /usr/libexec/java_home -1.8
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
cs

 

4. java -version

1
2
3
4
kimfish@KimFish-MacBookPro ~ % java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
cs

 

정상적으로 설치가 된 모습이다. 설치하고나니 AdoptOpenJdk가 아닌 RedhatOpenJdk를 설치할걸 그랬나 싶긴 하지만, 일단 사용 해 보고 문제가 발생하면 나중에 다시 설치하지뭐

반응형
블로그 이미지

김생선

세상의 모든것을 어장관리

,
반응형

뭔놈의 플젝이 SVN Update를 받으면 계속 에러가 나는건지. 이번에는 누가 JNDI 설정을 갈아치운건지 에러가 난다. 그래서 정리할 겸 작성함.

해당 프로젝트는 Tomcat 9.0버전대를 기준으로 JNDI설정이 되어있음.

 

1. Project - application.yml

1
2
3
spring:
  datasource:
  jndi-name: TESTDB_Pool
cs

별거는 없다. SpringBoot의 Application.yml 에서 JNDI를 다음과 같은 이름으로 사용하겠다고 명시적으로 알려주면 끝.

 

2. Tomcat - Context.xml

1
2
3
4
5
6
//중략
 
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
 
//application.yml에 작성한 spring.datasource.jndi-name과 동일하게 작성한
<ResourceLink global="TESTDB_Pool" name="TESTDB_Pool" type="javax.sql.Datasource"/>
cs

여기 또한 별거 없다. application.yml의 jndi-name과 3.Tomcat-Server.xml과 연결시켜주는 부분이다. 

 

3. Tomcat - Server.xml

1
2
3
4
5
6
7
8
9
10
11
//중략
<Resource auth="Container" description="User database that can be updated and saved"
  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
  name="UserDatabase" pathname="conf/tomcat-users.xml"
  type="org.apache.catalina.UserDatabase" />
 
//하단에 작성
<Resource auth="Container" driverClassName="com.tmax.tibero.jdbc.TbDriver"
  maxActive="5" maxIdle="5" name="TESTDB_Pool" type="javax.sql.DataSource"
  url="jdbc:tibero:thin:@000.000.000.000:65535" username="DB_ID" password="DB_PASSWORD"
  validationQuery="select 1 from dual" />
cs

가장 중요한 부분이다. DB커넥션에 관련된 모든 정보를 작성하는 곳으로, 기존의 application.yml에서 작성된 DB정보를 여기에 작성한다고 생각하면 된다.

 

 

이렇게 설정하고 재부팅 하면 된다.

반응형

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[PDFBox] pdf 텍스트 추출  (0) 2022.07.05
[poi] java poi 를 활용한 엑셀/워드 텍스트 추출  (2) 2022.07.04
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return (2)  (0) 2021.07.06
[xml] RestAPI XML Return  (0) 2021.07.02
블로그 이미지

김생선

세상의 모든것을 어장관리

,