이번 게시물에서는 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에서 데이터를 추출하는 작업을 진행하도록 하겠다

반응형

이번 프로젝트에서는 서버에서 블로그 본문 데이터를 크롤링해 온 다음 이를 분석하여 클라이언트에 반환하는 기능을 구현했다. 이 과정에서 Scrapy를 공부하고 사용한 내용을 기록하고자 한다.

 

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

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


 

웹 크롤링은 대표적으로 BeautifulSoup와 Selenium을 사용한다. 배우기 쉽고, 한국어 자료도 방대한 장점이 있다. 

 

다만 내가 프로젝트에서 두 라이브러리 대신 Scrapy를 사용한 이유는 3가지가 있다.

 

 

 

Webdriver를 호출하는 Selenium의 속도저하 문제

 

Selenium은 실제 웹페이지를 띄우고, 렌더링되는 시간을 기다려야 크롤링이 가능하다. 지도나 무한 스크롤같은 동적인 페이지를 크롤링해야 한다면 Selenium을 사용하는 것이 맞다.

그러나 나는 정적인 페이지를 크롤링하고, BeautifulSoup이나 Scrapy와 비교했을때 훨씬 많은 시간을 소요해 선택하지 않았다.

 

 

 

동기적으로 동작하는 BeautifulSoup

 

BeautifulSoup와 Scrapy는 둘 다 request를 보냈을 때 response를 그대로 받아온다는 점에서 동일하다.

이 둘의 차이점은 BeautifulSoup은 동기적으로, Scrapy는 비동기적으로 동작한다는 것이다.

쉽게 말해 BeautifulSoup은 페이지 크롤링하는 동안 어떤 코드도 수행할 수 없어 대기해야 한다. 그러나 Scrapy는 페이지를 크롤링하는 동안 다른 동작을 수행할 수 있다. 

 

따라서 시간적으로 봤을때 BeautifulSoup는 Scrapy보다 더 많은 시간을 소요한다. BeautifulSoup와 multiprocessing을 함께 사용하면 상당히 빨라지지만 프로젝트 특성상 이후에도 많은 멀티스레딩을 동원하기 때문에 추가적으로 멀티프로세스를 호출하는 것이 오버헤드 측면에서 부담스러웠다.

 

 

 

Scrapy의 확장성 및 편의

 

BeautifulSoup가 단순히 html에서 정보를 가져오는데 그친다면 Scrapy는 확장성 측면에서 상당히 유연하다. middleware를 사용자가 직접 손볼 수 있고 데이터를 다운로드 하거나 하는 등의 여러 확장이 자유롭다.

 

더불어, 필자는 블로그에서 본문데이터를 가져오는 작업을 진행했는데 BeautifulSoup보다는 Scrapy가 하위 태그에서 텍스트만 골라 가져오는 것이 더 편리했다.(이것은 개인적인 의견일 수도 있다.)

 

 

 

한국에서는 Scrapy를 잘 사용하지 않는지 한국어 자료보다는 영어자료가 훨씬 많았다. 다행스러운 점은 스택오버플로우 등 영어권에는 Scrapy 자료가 훨씬 방대하다.

 

 

이러저러한 이유로 결론은 Scrapy를 선택했다는 것이다.

Scrapy가 BeautifulSoup와 Selenium에 비해서 배우기 어렵고 한국어 자료도 많이 없는 단점은 있다.

그러나 공식문서 튜토리얼을 따라가며 배우다보면 대략적으로 어떻게 동작하는지 이해하는 데에는 오래걸리지 않는다.

 

 

다음 포스트에서 본격적으로 Scrapy를 어떻게 사용하는지에 대해 소개하도록 하겠다.

반응형

+ Recent posts