아래는 패키징을 WAR로 작업한 스프링부트 서버를 도커에 올리는 과정이다. 

개인적인 기록을 위한 것으로 간단하게 명령어만 서술하려 한다.

 

- 필자는 Window10 버전을 사용한다.

- 도커가 설치되어있다고 가정한다.

- Gradle을 사용하여 빌드한다

- Dockerfile을 사용하여 도커에 올린다

 

참고 : https://zzang9ha.tistory.com/360

 

 


1. Dockerfile 작성

프로젝트 루트 폴더에 Dockerfile을 생성한다.

 

파일 내부에는 아래와 같이 작성한다

 

FROM openjdk:17-jdk-alpine
ARG JAR_FILE=build/libs/*.war
COPY ${JAR_FILE} app.war
ENTRYPOINT ["java","-jar","app.war"]

 

필자는 java 17버전을 사용하고 있어서 jdk:17로 baseimage를 불러온다. 

ARG는 변수를 선언하는 명령어이다. 빌드를 하면 build/libs/ 폴더에 .war 형태로 저장된다. 이 파일들을 변수에 저장한다.

app.war 파일에 빌드된 war 파일들을 저장한다.

ENTRYPOINT는 도커를 실행할 때 실행하는 스크립트이다.

찾아보니, java -jar 형태로도 war 파일을 실행할 수 있었다.

 

2. 프로젝트 빌드

 

gradlew build -x test

 

-x test 는 test를 생략한다는 명령이다.

빌드를 진행하면 build/libs 폴더가 생성되며 그 안에 war 파일 2개가 저장된다.

 

 

3. 도커 이미지 생성

 

docker build --build-arg DEPENDENCY=build/dependency -t 이미지이름 .

 

이미지이름은 dockerTest처럼 원하는 이름으로 지정하면 된다.

마지막에 점(.) 은 필수이므로 잊지 말 것.

 

이미지가 잘 생성됐는지 확인해보고 싶으면 다음 명령어를 입력하면 된다.

 

docker images

 

4. 도커 컨테이너 실행

이제 생성된 도커 이미지로 도커 컨테이너를 실행한다. 

 

docker run -d -p 8080:8080 이미지이름

-d : 백그라운드(데몬)으로 컨테이너를 실행하라는 명령어

-p : 포트를 지정, 연결해주는 명령어. 외부의 8080포트와 컨테이너 내부 8080 포트를 연결한다.

 

컨테이너를 실행하면 컨테이너 ID 하나만 띄우고 터미널 실행이 끝나는데, 제대로 돌아가고 있는지를 확인하려면 다음 명령어를 입력한다.

 

docker ps

 

위 명령어를 입력하면 현재 실행중인 모든 컨테이너를 띄운다.

실행을 시켰는데도 아무것도 뜨지 않는다면, 무언가 문제가 생겨 컨테이너가 종료된 것이다.

종료된 컨테이너까지 모두 보려면 마지막에 -a 를 붙이면 된다.

 

docker ps -a

 

종료된 컨테이너의 경우 STATUS에 Exited라고 뜰 것이다.

오류를 확인하려면 로그를 봐야하니 다음 명령어를 입력해보자.

 

docker logs 컨테이너ID

 

ps -a 명령어로 확인한 컨테이너 ID를 logs 옆에 작성하면 로그가 뜬다.

이 로그에서 문제가 뭔지를 파악하면 된다.

반응형

이번 게시물에서는 response에서 원하는 데이터를 추출하는 방식을 소개한다.

 

Scrapy 튜토리얼1 - Scrapy를 선택한 이유

Scrapy 튜토리얼2 - 네이버 검색 결과 크롤링(크롤러 설정)


 

1. 데이터 추출

 

위 조건을 다 맞춰 spider를 만들었다면 parse함수에서 받아온 response로 원하는 값을 가져오자.

 

 

크롤링을 하기 위해서는 기본적으로 내가 원하는 데이터가 들어있는 태그를 파악해야한다.

F12키를 눌러 개발자 도구를 열고 왼쪽 위에 커서모양 버튼을 누르면 내가 원하는 데이터의 태그를 쉽게 찾을 수 있다.

 

 

scrapy는 class명과 id명, xpath 등을 활용할 수 있는데 간단히 class명으로 데이터를 받아온다.

 

response.css() 함수를 사용하면 class명과 id명을 이용해 데이터를 받아올 수 있다.

클래스는 .클래스명 으로 앞에 점을 하나 붙여야 하고 id는 #아이디명 으로 #을 붙여줘야 동작한다.

 

그 뒤에는 get()함수나 getall() 혹은 extract() 등을 사용해야 비로소 데이터를 추출해온다.

get() 함수는 해당하는 여러 태그 중 1개만 추출하고, getall()이나 extract()는 해당하는 태그가 여러개일 경우 리스트로 반환한다.

getall()과 extract()는 큰 차이는 없지만 getall()은 무조건 리스트로 반환하는 반면 extract() 결과 개수에 따라 리스트 혹은 str로 반환된다. 

 

selector 함수에 대한 더 자세한 사항은 공식문서를 참고하자.

 

 

 

위 코드를 실행한 결과는 다음과 같다.

<a href="https://blog.naver.com/kizaki56?Redirect=Log&amp;logNo=222665532164" class="api_txt_lines total_tit" target="_blank" onclick="return goOtherCR(this, 'a=blg*a.iblg&amp;r=1&amp;i=90000003_0000000000000033D7E66304&amp;u='+urlencode(this.href))"><mark>강남</mark> 한우 <mark>맛집</mark> 장위동유성집 특선 메뉴 추천</a>

 

 

 

2. 텍스트 추출

 

scrapy도 beautifulSoup의 get_text()함수처럼 텍스트만 추출해오는 함수가 존재한다.

이번에는 결과에서 텍스트만 추출해보자

 

    def parse(self, response):
        result = response.css('.api_txt_lines::text').get()
        print(result)

css 선택자에서 텍스트만 추출하도록 하려면 class명 뒤에 ::text 를 붙이면 된다.

 

 

추출된 결과를 보자

 

한우

 

 

분명 제목 전체를 가져오기를 원했는데 결과가 조금 이상하다. 그 이유는 html파일 구조를 보면 알 수 있다.

 

 

::text는 하위태그 내의 text까지는 접근할 수 없고, 텍스트 중 첫번째 텍스트만 가져오는 단점이 있다.

하위 태그까지 포괄한 모든 텍스트를 가져오고자 한다면 xpath를 사용해야한다.

 

 

    def parse(self, response):
        result = response.css('.api_txt_lines').xpath('string(.)').get()
        print(result)

 

xpath는 함수 xpath()를 사용하여 접근하면 된다.

일반적인 사용 예시는 'response.xpath('//*[@id="sp_blog_1"]/div/div/a').get()' 처럼 활용하면 된다.

 

여기서 주목해야할 것은 string()이다.

xpath 링크를 string()으로 감싸면 현재 태그가 감싸고 있는 모든 텍스트를 전부 반환하는 상당히 편리한 함수이다. 

 

한가지 더 설명하자면, string() 함수 안에 있는 .은 현재 위치를 의미한다. css로 접근한 뒤 xpath로 재접근을 하는 것이기 때문에 css로 접근한 현재위치인 a태그에서 모든 텍스트를 가져오는 것을 뜻한다. 만약 그 안쪽으로 더 접근하고자 한다면 .xpath('./div') 등으로 활용할 수 있다. 

 

 

결과를 살펴보자

 

강남 한우 맛집 장위동유성집 특선 메뉴 추천

 

원하는 결과를 잘 추출한 것을 볼 수 있다.

반응형

이번 게시글에서는 Scrapy를 파이썬 스크립트에서 어떻게 호출하고 사용하는지에 대해 소개하려 한다.

 

Scrapy는 프레임워크이기 때문에 쉘에서 사용하는 것이 일반적이지만, 필자는 서버에서 사용해야 하기 때문에 파이썬 스크립트에서 활용하였다.

 

튜토리얼은 네이버 블로그 검색 결과를 가져오는 방법을 소개한다.

 

 

Scrapy 튜토리얼1 - Scrapy를 선택한 이유

Scrapy 튜토리얼3 - 네이버 검색 결과 크롤링(데이터 추출)


 

1. 설치

 

우선 Scrapy를 사용하기 위해 파이썬 프로젝트에 설치해보자

pip install Scrapy

 

 

 

2. Spider 클래스 생성

 

설치가 완료되었다면 우선 Spider라는 것을 만들어야 한다. 용어가 조금 생소한데, 거미줄을 쳐서 html에서 원하는 정보만 걸러 가져온다.. 라는 발상에서 나온 용어이지 않나 싶다.

 

Scrapy는 이 Spider라는 클래스를 호출해 크롤링을 실행한다.

필자는 크롤링을 실제로 진행하는 함수들을 모아둔 클래스 정도로 이해했다.

 

import scrapy

class NaverSpider(scrapy.Spider):
    name = "naver"

    # 처음 크롤링을 시작하는 함수
    def start_requests(self):
    	# '강남 맛집'으로 검색한 네이버 블로그 결과 링크
        url = "https://search.naver.com/search.naver?query=강남 맛집&nso=&where=blog&sm=tab_opt"

        yield scrapy.Request(url=url, callback=self.parse)

    # 크롤링 결과를 받는 callback함수
    def parse(self, response):
    	# 해당 코드는 다음 게시물에서 설명합니다
        result = response.css('.api_txt_lines').get()
        print(result)

 

spider 클래스를 만드는 것에는 몇 가지 제약 조건이 있다.

 

 

 

1. 클래스는 scrapy.Spider를 상속받아야 한다.

 

클래스명은 어떤 것이든 관계 없다.

 

 

2. 클래스 멤버변수 name을 생성한다.

 

Scrapy가 spider를 사용하여 크롤링을 시작할 때, spider를 구분하기 위해 name 멤버변수를 사용한다. 

name은 말 그대로 spider를 식별할 수 있는 이름으로 어떤 이름을 사용하든 상관없다.

 

 

3. start_requests(self)를 생성한다.

 

Scrapy는 이 start_requests 함수를 가장 먼저 호출한다. 함수명은 변경하면 안된다.

함수 안에는 반드시 'yield scrapy.Request(url=링크, callback=콜백함수)'가 선언되어 있어야한다.

return을 쓰면 TypeError: 'Request' object is not iterable  라는 에러가 발생한다.

(yield은 return과 달리 여러번 결과를 반환할 수 있다.)

 

yield로 인해 scrapy는 해당 링크로 요청을 보내고 콜백함수를 호출해 응답을 사용할 수 있도록 한다.

for문을 통해 여러개의 링크로 request를 보낼수도 있다.

 

#예시코드

class NaverSpider(scrapy.Spider):
    name = "naver"

    def start_requests(self):
        urls = [링크1, 링크2, 링크3]

        for url in urls :
        	yield scrapy.Request(url=url, callback=self.parse)

 

 

4. scrapy.Request의 콜백함수를 만든다.

 

콜백함수의 함수명은 어떤것이든 관계 없다. 그러나 매개변수로 response는 반드시 들어가있어야 한다.

scrapy가 콜백함수로 response를 보내주기 때문이다.

 

 

 

 

3. CrawlerProcess 호출

 

spider를 만들었다면 이제 scrapy에서 spider를 호출해야한다.

 

파이썬 스크립트에서 scrapy를 사용하는 방법은 2가지이다.

 

  1.  CrawlerRunner()
  2.  CrawlerProcess()

 

두 함수의 차이는 호출 방식도 있지만 메인 스레드가 아닌 다른 스레드에서 호출이 가능하냐 아니냐의 차이이다.

 

CrawlerRunner는 메인 스레드가 아닌 서브스레드, 서브 프로세스에서도 동작이 가능하다.

CrawlerProcess는 오직 메인 스레드에서만 동작이 가능하다.

CrawlerRunner여러개의 spider를 동시에 실행할 수 있다는 장점도 있다.

 

두 함수의 호출 방식을 살펴보자.

 

from scrapy.crawler import CrawlerRunner

if __name__ == '__main__':

    configure_logging({'LOG_FORMAT': '%(levelname)s: %(message)s'})
    runner = CrawlerRunner()
    runner.crawl(NaverSpider)  # 이 부분에 자신이 정의한 spider 클래스를 넣는다
    # runner.crawl(DaumSpider)   # 여러개의 spider를 동시에 실행할 수도 있다.
    crawler = runner.join()
    crawler.addBoth(lambda _: reactor.stop())
    reactor.run()  # the script will block here until the crawling is finished

 

위 코드는 공식문서 튜토리얼 코드 그대로이다. 필자도 특별히 이 부분에 대해 건드린 것은 없다.

reactor.run()이 실행되면 크롤링이 모두 끝날때까지 다음 코드로 이동하지 않고 기다려준다.

 

단 주의해야할 사항은 reactor는 한번 실행되면 해당 프로세스가 끝날때까지 재실행이 불가능하다.

 

 

from scrapy.crawler import CrawlerProcess

if __name__ == '__main__' :
    process = CrawlerProcess()
    process.crawl(NaverSpider)  # 이 부분에 자신이 정의한 spider 클래스를 넣는다
    process.start()

 

CrawlerProcess는 호출 방식이 더 심플하다.

reactor와 달리 여러번 호출이 가능하고, process.start() 역시 크롤링이 다 끝날때까지 기다려준다. 

 

 

다음 포스트에서는 크롤링하여 받아온 response에서 데이터를 추출하는 작업을 진행하도록 하겠다

반응형

+ Recent posts