본문 바로가기

Python

Scrapy 데이터 추출하고 수집하기, 파이참에서

Anaconda Prompt에서 scrapy startproject crawler_test 입력해서 새 프로젝트 생성.

파이참을 켜고 File->Open에서 명령창의 홈 경로에 있는 crawler_test 열기.

items.py 파일을 열고 다음과 같이 입력

import scrapy

class CrawlerTestItem(scrapy.Item): #수집하고자 하는 정보 저장

title = scrapy.Filed()
score = scrapy.Field()
genres = scrapy.Field()
consensus = scrapy.Field()

spiders 폴더에 가서 새 파이썬 파일 생성 rt_spider.py

import scrapy

from crawler_test.items import CrawlerTestItem #crawler_test라는 프로젝트에 있는 items 파일의 클래스 임포트

class RTSpider(scrapy.Spider): #Spider 클래스 상속 받음
name = "RottenTomatoes" #RTSpider 클래스를 식별해주는 스파이더 이름
allowed_domains = ["rottentomatoes.com"]#웹 사이트 도메인 네임
# 여기 있는 웹 페이지와 링크되어 있는 페이지 크롤링
# 서버에 url request하고 응답을 response라는 변수로 받고 parse라는 함수에서 처리
start_urls = ["https://www.rottentomatoes.com/top/bestofrt/?year=2017"]

def parse(self,response):#어떤 부분 스크래핑할 지 명시

명령창에서 얻고자하는 url 입력 scrapy shell "https://www.rottentomatoes.com/top/bestofrt/?year=2017" 입력

개발자 도구를 써서 xpath 얻기, 원하는 부분에 대한 xpath를 얻고 scrapy shell에서 진짜로 내가 원하는 부분이 잘 나오는지 확인해야 한다. 제대로 안나오면 말단의 자식 태그부터 제거하면서 나올 때까지 반복한다.


response.xpath('//*[@id="top_movies_main"]/div/table/tr[1]/td[3]/a/@href/text()')[0].extract() 입력하면

u'/m/lady_bird' 이게 나온다.


본래 url에 위의 sub url을 붙이기 위해 urljoin()을 사용한다.

url = response.xpath('//*[@id="top_movies_main"]/div/table/tr[1]/td[3]/a/@href/text()')[0].extract()

response.urljoin(url)

을 하면 u'https://www.rottentomatoes.com/m/lady_bird' 이게 출력된다.


반복되는 제목, 평점 등의 정보를 뽑아내기 위해 부모 태그를 찾을 필요가 있다.


for tr in response.xpath('//*[@id="top_movies_main"]/div/table/tr'):

href = tr.xpath('./td[3]/a/@href')

url = response.urljoin(href[0].extract())

print(url)

이렇게 입력하면 페이지에 있는 url을 얻을 수 있다. 이 url의 웹페이지를 요청하기 위해 parse함수를 작성한다.


import scrapy

from crawler_test.items import CrawlerTestItem #crawler_test라는 프로젝트에 있는 items 파일의 클래스 임포트

class RTSpider(scrapy.Spider): #Spider 클래스 상속 받음
name = "RottenTomatoes" #RTSpider 클래스를 식별해주는 스파이더 이름
allowed_domains = ["rottentomatoes.com"]#웹 사이트 도메인 네임
# 여기 있는 웹 페이지와 링크되어 있는 페이지 크롤링
# 서버에 url request하고 응답을 response라는 변수로 받고 parse라는 함수에서 처리
start_urls = ["https://www.rottentomatoes.com/top/bestofrt/?year=2017"]

def parse(self,response):#어떤 부분 스크래핑할 지 명시
for tr in response.xpath('//*[@id="top_movies_main"]/div/table/tr'):
href = tr.xpath('./td[3]/a/@href')
url = response.urljoin(href[0].extract())
# url이 함수의 인자로 들어감. 그 url을 어떻게 스크래핑할 지 parse_page_contents 함수에 작성
yield scrapy.Request(url, callback=self.parse_page_contents)

def parse_page_contents(self, response):
item = CrawlerTestItem()
item["title"] = response.xpath('//*[@id="heroImageContainer"]/a/h1/text()')[0].extract().strip()
item["score"] = response.xpath('//*[@id="tomato_meter_link"]/span[2]/span/text()')[0].extract()
item["genres"] = response.xpath('//*[@id="mainColumn"]/section[3]/div/div[2]/ul/li[2]/div[2]//a/text()').extract()
consensus_list = response.xpath('//*[@id="all-critics-numbers"]/div/div[2]/p//text()').extract()[3:]
item["consensus"] = ' '.join(consensus_list).strip()
yield item

다른 페이지를 읽어들이기 위해 현재 사용하고 있는 명령창의 scrapy shell을 Ctrl+D를 눌러 종료한 뒤 

scrapy shell "https://www.rottentomatoes.com/m/lady_bird" 을 입력한다.

이 페이지의 제목을 따오기 위해 크롬 개발자 도구를 이용해 xpath를 얻는다.

scrapy shell에서 response.xpath('//*[@id="heroImageContainer"]/a/h1')를 입력하면 뭔가가 나온다.

response.xpath('//*[@id="heroImageContainer"]/a/h1/text()')를 입력하면 데이터에 문자가 나온다.

response.xpath('//*[@id="heroImageContainer"]/a/h1/text()').extract()를 입력하면 제목이 나오긴 하는데 개행과 공백이 표시된다. 이를 제거하기 위해

response.xpath('//*[@id="heroImageContainer"]/a/h1/text()')[0].extract().strip()를 사용한다. 이는 출력된 리스트의 첫번째 항목을 추출한 뒤 공백을 제거한다는 의미다. [0].extract()와 extract_first()는 같다.


어쨌든 위 문장은 제목을 얻어내는 데 사용된다.

영화의 신선도(참신함의 정도)를 얻기 위해선 아래와 같이 쓴다.

response.xpath('//*[@id="tomato_meter_link"]/span[2]/span/text()')[0].extract()



scrapy shell을 종료하고 cd crawler_test로 이동한다.

scrapy crawl RottenTomatoes 입력한다.


SyntaxError: Non-ASCII character 오류가 난다면

오류가 난 파일의 맨 위에 다음의 주석을 똑같이 입력한다. # -*- coding: utf-8 -*-


출력된 정보를 csv 파일로 저장하고 싶다면 

scrapy crawl RottenTomatoes -o rt.csv


수집된 데이터를 item pipline을 통해 정리해서 저장할 수 있다.

pipelines.py 파일에 들어가서

import csv

class CrawlerTestPipeline(object):
def __init__(self): #생성자
# 외부 파일에 쓰기
self.csvwriter = csv.writer(open("rt_movies_new.csv","w"))
# 아래 순서대로 csv에 값을 넣음
self.csvwriter.writerow(["title","score","genres","consensus"])

#spider를 통해 얻은 각 item을 어떻게 처리할 지 명세한다.
def process_item(self, item, spider):
row = []
row.append(item["title"])
row.append(item["score"])
row.append('|'.join(item["genres"])) # x 또는 y 장르라는 표현
row.append(item["consensus"])
self.csvwriter.writerow(row)
return item

위 item pipeline을 사용할 수 있도록 settings.py 파일로 이동해서 주석을 해제한다.

ITEM_PIPELINES = {
'crawler_test.pipelines.CrawlerTestPipeline': 300,
}

item pipeline으로 csv파일을 생성하라고 했기 때문에 -o 명령 없이

scrapy crawl RottenTomatoes를 입력한다.


홈 폴더(C:\Users\xx\crawler_test)로 이동해서 rt_movies_new.csv 파일을 메모장으로 열면 쉼표로 구분된 title score genres consensus에 맞게 레코드가 작성되어 있는 것을 확인할 수 있다.