웹 크롤링(스크래핑) 이해하기
웹 크롤링(스크래핑)의 개념
- 웹 스크래핑(web scraping): 웹 사이트 상에서 원하는 부분에 위치한 정보를 컴퓨터로 하여금 자동으로 추출하여 수집하도록 하는 기술
- 웹 크롤링(web crawling): 자동화 봇(bot)인 웹 크롤러(web crawler)가, 정해진 규칙에 따라 복수 개의 웹 페이지를 브라우징하는 행위 (aka 웹 스파이더링(web spidering))
즉, 일반적으로 '웹 크롤링'이라고 알려진 과정은, 우리가 제작한 웹 크롤러가 웹 크롤링 과정에서 방문한 페이지들의 컨텐트를 저장한 뒤, 이에 웹 스크래핑 기술을 적용하여 원하는 부분의 정보만을 추출하여 데이터셋의 형태로 저장하는 과정을 모두 포괄합니다.
우리가 취급하는 모든 웹 페이지는 기본적으로 HTML과 CSS 기술에 근거하여 만들어져 있으며, 근래에 접어들면서 JavaScript 기술의 적용 부분도 확장되고 있습니다. 따라서 웹 크롤링 및 스크래핑을 무리 없이 수행하기 위해서는 HTML, CSS(, JavaScript) 등의 기술에 대한 기초적인 이해가 필요합니다.
만약 HTML과 CSS의 기초를 학습하지 않으신 분의 경우, Flearning의 프론트엔드 강좌를 먼저 수강해 주시길 바랍니다.
https://www.flearning.net/courses/1
웹 크롤링 및 스크래핑을 위한 라이브러리
- scrapy(스크래파이): 웹 데이터 분석을 위해, 웹 사이트를 크롤링하고 스크래핑을 통해 정보를 추출하여 데이터셋의 형태로 저장하는 데 특화된 Python 라이브러리
scrapy의 기초적인 기능만을 사용할 경우, 우리가 '웹 브라우저로 보고 있는' 화면을 그대로 스크래핑할 수 없는 상황이 발생합니다. 대표적인 두 가지 유형이 있습니다.
1. 동적으로 컨텐츠를 생성하는 '동적 웹 페이지(dynamic web page)'
동적 웹 페이지의 경우, 일단 HTML/CSS로만 작성된 정적(static) 웹 페이지를 띄운 뒤, 브라우저로 하여금 서버 측으로 추가적인 요청을 보내도록 한 뒤 이에 대한 응답으로 필요한 컨텐츠를 웹 페이지 상에 사후적으로 채워넣는 방식입니다.
- 예시: English Premier League 2015/16 시즌 최종 순위표
https://www.premierleague.com/tables?co=1&se=42&mw=-1&ha=-1
2. 웹 페이지에 대한 요청 시 로그인 정보 등을 함께 보내야 하는 웹 페이지
어느 웹 사이트 상에서 회원의 신분으로 로그인하여 회원에게만 제공되는 웹 페이지를 보고 싶을 경우, 웹 페이지를 요청할 때마다 매번 '내가 현재 회원 신분으로 로그인되어 있다'는 것을 입증할 수 있는 증거 자료(쿠키(cookie))를 서버 측으로 함께 제시해야 합니다.
- 예시: Flearning에서 제공하는
강좌 소개 페이지 -> 첫 번째 강의 시청하기
https://www.flearning.net/classes/15
위와 같은 유형의 웹 사이트에 대한 스크래핑을 제대로 수행하기 위해서는, HTML, CSS, JavaScript 및 HTTP 통신 등과 관련된 어느 정도 전문적인 이해가 필요합니다. 이런 이해가 갖춰져 있다면, scrapy의 고급 기능을 활용하여 위 유형의 웹 사이트에 대해 원하는 웹 페이지를 크롤링할 수 있습니다.
본 강의에서는 Python 자동화 브라우저 라이브러리인 'selenium(셀레늄)'의 webdriver 모듈을 추가로 사용하여, 웹 기술에 대한 이해가 없더라도 위 유형에 해당하는 웹 페이지들을 '웹 브라우저로 보고 있는' 모습 그대로 스크래핑할 수 있도록 하는 가장 간단한 꼼수를 안내할 것입니다.
scrapy 및 selenium 설치하기
scrapy 및 selenium 설치하기 (Windows)
scrapy 설치하기
현재 scrapy는 Windows의 Python 3에 대해서는 지원을 하고 있지 않으므로, conda를 사용하여 Python 2 가상 환경을 따로 구축해야 합니다. 크롤링을 하는 경우에 한해서만 Python 2 가상 환경을 사용하고, 데이터 분석을 하는 경우에는 원래의 Python 3 환경을 사용할 것입니다.
1. Python 2 가상 환경 생성 및 activate/deactivate
- 가상의 Python 2 환경 구성:
> conda create -n py27 python=2.7
- 가상의 Python 2 환경을 activate:
> activate py27
- 현재 Python의 버전을 확인하여, 어떤 버전의 Python이 activate 되어 있는지 확인:
> python --version
- 가상의 Python 2 환경을 deactivate:
> deactivate py27
이제 Python 2 가상 환경을 activate한 상태에서, 이어지는 과정을 수행합니다.
2. lxml 및 pywin32 설치
- lxml의 Windows용 wheel 파일 다운로드:
http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml
lxml-3.6.1-cp27-cp27m-win32.whl (32비트 운영체제의 경우) lxml-3.6.1-cp27-cp27m-win_amd64.whl (64비트 운영체제의 경우)
- 다운로드받은 wheel 파일을 사용하여 lxml 설치(64비트 운영체제 예시):
> pip install lxml-3.6.1-cp27-cp27m-win_amd64.whl
- pywin32 설치:
> pip install pypiwin32
3. Microsoft Visual C++ Compiler for Python 2.7 설치
(주의) 사용하시는 Windows 버전 혹은 이전의 업데이트 내역에 따라, Microsoft Visual C++ Compiler가 이미 설치되어 있는 경우도 있습니다. 따라서 2번 과정까지 완료하신 뒤 4번 과정(scrapy 설치)을 먼저 시도해 주시고, 그 과정에서 오류가 발생하는 경우에 한해서만 본 3번 과정을 진행해주시길 바랍니다.
- Microsoft Visual C++ Compiler for Python 2.7 다운로드 및 설치:
http://www.microsoft.com/en-us/download/details.aspx?id=44266
4. scrapy 설치
> pip install Scrapy
selenium 설치하기
> pip install selenium
ChromeDriver 다운로드 및 설치하기
ChromeDriver는, selenium과 같은 자동화 브라우저 라이브러리에서 크롬 브라우저를 사용하기 위해 필요로 하는 드라이버입니다.
- chromedriver_win32.zip 파일 다운로드:
https://sites.google.com/a/chromium.org/chromedriver/downloads
다운로드받은 파일의 압축을 해제한 후, 이를 홈 디렉터리로 이동하고 그 경로를 기록합니다.
예시: C:\Users\kilho\chromedriver.exe
위 경로를, selenium을 사용해서 크롬을 실행할 때마다 다음과 같이 입력할 것입니다:
browser = webdriver.Chrome("C:\Users\kilho\chromedriver.exe")
scrapy 테스트
cmd에서 다음을 실행합니다:
> scrapy shell "https://www.flearning.net/classes/15"
IPython이 실행되면, 다음을 실행합니다:
response.text
HTML 태그가 길게 표시된 경우, 해당 URL의 페이지를 제대로 스크래핑한 것으로 해석할 수 있습니다. 다음을 실행하여 종료합니다:
exit()
selenium 테스트
IPython을 실행한 뒤, 다음을 순서대로 실행합니다:
from selenium import webdriver
browser = webdriver.Chrome("C:\Users\kilho\chromedriver.exe")
browser.get("http://flearning.net")
browser.quit()
exit()
scrapy 및 selenium 설치하기
scrapy 및 selenium 설치하기 (Mac OS)
scrapy 설치하기
> pip install scrapy
selenium 설치하기
> pip install selenium
ChromeDriver 다운로드 및 설치하기
ChromeDriver는, selenium과 같은 자동화 브라우저 라이브러리에서 크롬 브라우저를 사용하기 위해 필요로 하는 드라이버입니다.
- chromedriver_mac64.zip 파일 다운로드:
https://sites.google.com/a/chromium.org/chromedriver/downloads
다운로드받은 파일의 압축을 해제한 후, 이를 홈 디렉터리로 이동하고 그 경로를 기록합니다.
예시: /Users/kilho/chromedriver
위 경로를, selenium을 사용해서 크롬을 실행할 때마다 다음과 같이 입력할 것입니다:
browser = webdriver.Chrome("/Users/kilho/chromedriver")
scrapy 테스트
터미널에서 다음을 실행합니다:
> scrapy shell "https://www.flearning.net/classes/15"
IPython이 실행되면, 다음을 실행합니다:
response.text
HTML 태그가 길게 표시된 경우, 해당 URL의 페이지를 제대로 스크래핑한 것으로 해석할 수 있습니다. 다음을 실행하여 종료합니다:
exit()
selenium 테스트
IPython을 실행한 뒤, 다음을 순서대로 실행합니다:
from selenium import webdriver
browser = webdriver.Chrome("C:\Users\kilho\chromedriver.exe")
browser.get("http://flearning.net")
browser.quit()
exit()
scrapy의 구조 및 웹 스크래핑 맛보기
scrapy의 기본 구조
Spider
Spider는 어떤 웹 사이트들을 어떠한 규칙에 의거하여 크롤링할 것인지 명시하고, 각각의 웹 페이지의 어떤 부분을 스크래핑할 것인지 등을 일괄적으로 명시하는 클래스입니다.
Selector
Spider 상에 웹 페이지 상의 어느 부분을 스크래핑할 것인지 명시하고자 할 때, 특정 HTML 요소를 간편하게 선택할 수 있도록 하는 메커니즘을 scrapy에서는 Selector 클래스로 구현하였습니다.
Selector를 사용하면 CSS 선택자를 직접 사용하여 특정 HTML 요소를 선택할 수도 있으나, HTML 상에서의 특정 요소를 선택하는 데 특화된 언어인 'XPath'를 사용하는 것이 더 권장됩니다.
HTML과 CSS의 기초를 잘 알고 있다면, XPath는 몇 개의 예제를 따라서 작성하는 것만으로 금방 학습할 수 있습니다. 만약 XPath에 대해 좀 더 자세하게 배우고 싶다면, 다음의 링크를 참조하시길 바랍니다.
http://www.w3schools.com/xsl/xpath_intro.asp
Item과 Item pipeline
Item은 scrapy에서 기본 제공하는 자료구조 클래스입니다. 새로운 Item 클래스를 정의하고 여기에 우리가 수집하고자 하는 정보들을 명시하면, Spider 상에서 실제 스크래핑을 수행한 결과물을 간편하게 관리할 수 있습니다.
Item pipeline 클래스를 새로 정의하고 여기에 각 Item들을 어떻게 처리할 것인지 명시하면, 해당 규칙에 의거하여 데이터를 가공하거나 혹은 외부 파일로 간편하게 저장할 수 있습니다.
Settings
Spider나 Item pipeline 등이 어떻게 동작하도록 할 지에 대한 세부적인 설정 사항을 Settings 상에 명시합니다.
거의 모든 웹 사이트에서는 크롤링을 수행하는 크롤러 봇들의 행동을 제한하고자 robots.txt라는 파일을 게시합니다.
https://www.flearning.net/robots.txt
예를 들어, 우리가 제작한 Spider가 이 robots.txt 파일에 명시된 규칙을 따를 것인지, 혹은 무시할 것인지 등을 Settings 상에서 설정할 수도 있습니다.
scrapy shell과 크롬 개발자 도구를 사용하여 웹 스크래핑 체험하기
scrapy shell
scrapy에서는 지정한 웹 페이지를 스크래핑한 결과를 인터랙티브하게 확인할 수 있도록 하는 기능인 scrapy shell을 제공합니다. 예를 들어 아래와 같은 URL의 웹 페이지를 스크래핑하고자 한다고 합시다.
http://www.dmoz.org/Computers/Programming/Languages/Python/Books
cmd 혹은 터미널 상에서 scrapy shell을 실행할 때, 다음과 같이 URL을 명시해줍니다.
> scrapy shell "http://www.dmoz.org/Computers/Programming/Languages/Python/Books"
그러면 IPython이 실행되면서, 해당 웹 페이지에 대해 스크래핑을 할 준비가 완료됩니다.
크롬 개발자 도구
크롬 개발자 도구(Developer Tools)는, 웹 페이지 상에서 원하는 요소의 XPath를 한 번에 찾아서 제공하는 기능을 지원합니다. 크롬 개발자 도구의 사용법과 관련해서는, 동영상의 내용을 참조해 주시길 바랍니다.
웹 스크래핑 체험하기
동영상 강의의 scrapy shell에서 실행한 코드를 순서대로 아래와 같이 나타내었습니다.
- 'Sites' 리스트 상의 하나의 아이템의 제목 텍스트 스크래핑하기:
response.xpath('//*[@id="site-list-content"]/div[1]/div[3]/a/div')
response.xpath('//*[@id="site-list-content"]/div[1]/div[3]/a/div/text()')
response.xpath('//*[@id="site-list-content"]/div[1]/div[3]/a/div/text()').extract()
response.xpath('//*[@id="site-list-content"]/div[1]/div[3]/a/div/text()').extract()[0]
- 'Sites' 리스트 상의 모든 아이템의 제목 텍스트 스크래핑하기:
titles = response.xpath('//*[@id="site-list-content"]/div')
title = titles[0]
title.xpath('./div[3]/a//div/text()')
for title in titles:
print(title.xpath('./div[3]/a//div/text()'))
본 강의에서 사용한 XPath 설명
//
: 기준이 되는 요소의 자손 요소들을 모두 살펴보고, 이 중에서 이어지는 조건을 만족하는 요소를 찾음/
: 기준이 되는 요소의 직속 자식 요소들만을 살펴보고, 이 중에서 다음에 이어지는 조건을 만족하는 요소를 찾음*
: 모든 종류의 태그에 해당하는 요소를 탐색[@id="site-list-content"]
: 탐색하고자 하는 요소의 id 값에 대한 조건을 명시[1]
: 명시한 조건을 만족하는 요소가 여러 개인 경우, 이들 중 몇 번째 요소를 가져올 것인지에 대한 인덱스(1부터 시작)/text()
: 해당 요소의 텍스트 부분만을 추출
정적 웹 페이지에서 데이터 추출하고 수집하기
영화 데이터 수집하기: rottentomatoes.com
- Rotten Tomatoes 2015년 TOP 100 영화 리스트 페이지:
https://www.rottentomatoes.com/top/bestofrt/?year=2015
해당 웹 페이지에서, 영화 제목 하나를 클릭하면 그 영화의 상세 정보를 나타내는 웹 페이지가 표시됩니다. 영화 제목이 여러 개 있으므로 이런 페이지들도 여러 개 얻어질 것인데, 우리는 각 페이지에서 영화 제목, 평점, 장르, 총평 등의 정보를 가져올 것이라고 가정합시다.
새 scrapy 프로젝트 생성
> scrapy startproject rt_crawler
rt_crawler 프로젝트 폴더 구성
rt_crawler/
scrapy.cfg
rt_crawler/ # 해당 프로젝트의 Python 모듈
__init__.py
items.py # 프로젝트 Item 파일
pipelines.py # 프로젝트 Item pipeline 파일
settings.py # 프로젝트 Settings 파일
spiders/ # Spider 저장 폴더
__init__.py
...
Item 클래스 정의
rt_crawler/items.py
파일을 연 뒤, 다음의 내용을 추가합니다:
class RTItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
score = scrapy.Field()
genres = scrapy.Field()
consensus = scrapy.Field()
scrapy.Item
클래스를 상속받는 RTItem
이라는 클래스를 생성하였는데, 이 클래스의 객체에 우리가 수집하고자 하는 정보들을 저장할 것입니다. 우리는 영화 제목, 평점, 장르, 총평 등의 정보를 수집할 것이므로 각각을 RTItem
클래스의 내부 변수로 정의하고, scrapy.Field()
함수를 실행하여 이들을 '필드(field)'의 형태로 생성합니다.
Spider 클래스 정의
rt_crawler/spiders/rt_spider.py
파일을 새로 생성하고, 다음의 내용을 추가합니다:
import scrapy
from rt_crawler.items import RTItem
class RTSpider(scrapy.Spider):
name = "RottenTomatoes"
allowed_domains = ["rottentomatoes.com"]
start_urls = [
"https://www.rottentomatoes.com/top/bestofrt/?year=2015"
]
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())
yield scrapy.Request(url, callback=self.parse_page_contents)
def parse_page_contents(self, response):
item = RTItem()
item["title"] = response.xpath('//*[@id="movie-title"]/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/div[2]/div[4]//span/text()').extract() # list of genre
consensus_list = response.xpath('//*[@id="all-critics-numbers"]/div/div[2]/p//text()').extract()[2:]
item["consensus"] = ' '.join(consensus_list).strip()
yield item
scrapy.Spider
클래스를 상속받는 RTSpider
클래스를 정의하고, name
, allowed_domains
, start_urls
변수를 순서대로 명시하였습니다. 각각의 변수는 다음과 같은 의미를 지닙니다.
name
: 해당 Spider의 이름. 웹 크롤링 및 스크래핑을 실행할 때 여기서 지정한 이름을 사용함.allowed_domains
: Spider로 하여금 크롤링하도록 허가한 웹 사이트의 도메인 네임.start_urls
: 웹 크롤링의 시작점이 되는 웹 페이지 URL. 해당 웹 페이지에서 출발하여 이어지는 웹 페이지들을 크롤링함.
start_urls
변수에 명시된 URL의 웹 페이지를 Spider가 서버에 요청하고, 이에 대한 응답을 response
라는 이름의 변수로 받으며, 이를 parse()
함수에서 처리됩니다. parse()
함수에서는 얻어진 시작 웹 페이지의 어떠한 부분을 스크래핑할지 명시해주는 부분입니다.
parse()
함수 내에서, 첫 페이지의 리스트 상의 전체 100개 제목의 하이퍼링크 URL을 얻은 뒤, 이들 각각에 대하여 scrapy.Request()
함수를 사용하여 웹 페이지 요청을 보내도록 하였고, 이에 대한 응답을 어떻게 처리할지 parse_page_contents()
함수에 명시하였습니다. 그리고 scrapy.Request()
함수의 callback
인자의 값으로 parse_page_contents
를 입력하였습니다.
parse_page_contents()
함수는 각 영화 페이지 상에서 스크래핑을 어떻게 수행할지 명시합니다. 영화 페이지가 여러 개 존재함에도 불구하고 이 함수를 하나만 정의해도 되는 이유는, 모든 영화 페이지가 거의 동일한 구조를 가지고 있기 때문입니다. 크롬 개발자 도구를 사용하여 영화 제목, 평점, 장르, 총평 각각의 요소의 XPath를 알아냅니다. 그리고 앞서 정의했던 RTItem
클래스의 객체를 item
이라는 이름으로 생성한 뒤, 앞서 정의한 필드들에 각 요소들을 대입합니다.
yield
키워드에 관해 궁금하신 분은, 다음의 링크를 참조하시길 바랍니다:
https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/
Spider 실행
> scrapy crawl RottenTomatoes
Spider가 수집한 정보를 CSV 파일로 저장하고 싶은 경우, -o
옵션을 추가합니다:
> scrapy crawl RottenTomatoes -o rt.csv
Item pipeline 정의
웹 스크래핑을 통해 수집된 데이터를 어떻게 가공하고 이를 외부 파일로 어떻게 저장할지 좀 더 구체적으로 명시하기 위해, Item pipeline을 정의할 수 있습니다. rt_crawler/pipelines.py
파일을 새로 생성하고, 다음의 내용을 추가합니다:
import csv
class RTPipeline(object):
def __init__(self):
self.csvwriter = csv.writer(open("rt_movies_new.csv", "w"))
self.csvwriter.writerow(["title", "score", "genres", "consensus"])
def process_item(self, item, spider):
row = []
row.append(item["title"])
row.append(item["score"])
row.append('|'.join(item["genres"]))
row.append(item["consensus"])
self.csvwriter.writerow(row)
return item
RTPipeline
클래스를 새로 정의한 뒤, 생성자 함수인 __init__()
함수에서 Python의 csv
모듈을 사용하여, 매 행을 rt_movies_new.csv 파일에 쓰도록 선언합니다.
process_item()
함수에서는, Spider를 통해 수집한 각 Item을 어떻게 처리할지 명시합니다. 여기에서 Python 리스트 하나를 생성하여 Item의 각 필드의 값을 append하는데, "genres" 필드의 경우 원래 Python 리스트 형태였으므로 해당 리스트 내 성분을 '|' 문자로 연결하여 새로운 문자열을 생성한 뒤 이를 append하였습니다. 맨 마지막 부분에서 csvwriter
를 통해 CSV 파일 상에 한 행을 쓰도록 하였습니다.
Item pipeline을 정의하였으면, 크롤링 실행 시 Settings 상에서 해당 Item pipeline을 사용하도록 설정해줘야 합니다. rt_crawler/settings.py
파일을 연 뒤, ITEM_PIPELINES
관련 부분의 내용을 다음과 같이 수정해줍니다.
ITEM_PIPELINES = {
'rt_crawler.pipelines.RTPipeline': 300,
}
'RottenTomatoes' Spider를 다시 한 번 실행하면, 수집된 Item들이 Item pipeline에 의해 rt_movies_new.csv 파일에 CSV 포맷으로 쓰여집니다.
> scrapy crawl RottenTomatoes
scrapy의 사용과 관련한 자세한 내용을 알고 싶으신 경우, scrapy의 공식 웹 페이지 문서를 참조하시길 바랍니다.
동적 웹 페이지에서 데이터 추출하고 수집하기
유럽 축구 데이터 수집하기: premierleague.com
- Premier League의 2015/16 시즌 최종 순위표:
https://www.premierleague.com/tables?co=1&se=42&mw=-1&ha=-1
해당 웹 페이지에서, 순위표 상에 나와있는 순위(Position), 팀명(Club), 경기수(Played), 승(Won), 무(Drawn), 패(Lost), 득점(GF), 실점(GA), 득실차(GD), 승점(Points) 등의 정보를 가져올 것이라고 가정합시다.
scrapy 프로젝트 생성, Item 클래스 정의
scrapy 프로젝트를 하나 생성합니다:
> scrapy startproject epl_crawler
Item 클래스를 하나 정의합니다. epl_crawler/items.py
파일을 연 뒤, 다음의 내용을 추가합니다:
class EPLItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
club_name = scrapy.Field()
position = scrapy.Field()
played = scrapy.Field()
won = scrapy.Field()
drawn = scrapy.Field()
lost = scrapy.Field()
gf = scrapy.Field()
ga = scrapy.Field()
gd = scrapy.Field()
points = scrapy.Field()
selenium webdriver를 사용하여 '눈에 보이는' 웹 페이지 스크래핑하기
Spider 클래스를 새로 정의하고, 그 내용을 명시합니다. 이 때, response
변수를 사용하여 스크래핑을 하는 대신, selenium webdriver 모듈을 사용하여 목표 웹 페이지 URL에 대한 요청을 다시 보내고, 이에 대한 응답을 selector
라는 새로운 변수로 받아 이를 스크래핑할 것입니다.
epl_crawler/spiders/epl_spider.py
파일을 새로 생성하고, 다음의 내용을 추가합니다:
import scrapy
from selenium import webdriver
from epl_crawler.items import EPLItem
class EPLSpider(scrapy.Spider):
name = "PremierLeague"
allowed_domains = ["premierleague.com"]
start_urls = [
"https://www.premierleague.com/tables?co=1&se=42&mw=-1&ha=-1"
]
def __init__(self):
scrapy.Spider.__init__(self)
self.browser = webdriver.Chrome("/Users/kilho/chromedriver")
def parse(self, response):
self.browser.get(response.url)
time.sleep(5)
html = self.browser.find_element_by_xpath('//*').get_attribute('outerHTML')
selector = Selector(text=html)
rows = selector.xpath('//*[@id="mainContent"]/div/div[1]/div[3]/div/div/table/tbody/tr[not(@class="expandable")]')
for row in rows:
item = EPLItem()
item["club_name"] = row.xpath('./td[3]/a/span[2]/text()')[0].extract()
item["position"] = row.xpath('./td[2]/span[1]/text()')[0].extract()
item["played"] = row.xpath('./td[4]/text()')[0].extract()
item["won"] = row.xpath('./td[5]/text()')[0].extract()
item["drawn"] = row.xpath('./td[6]/text()')[0].extract()
item["lost"] = row.xpath('./td[7]/text()')[0].extract()
item["gf"] = row.xpath('./td[8]/text()')[0].extract()
item["ga"] = row.xpath('./td[9]/text()')[0].extract()
item["gd"] = row.xpath('./td[10]/text()')[0].extract()
item["points"] = row.xpath('./td[11]/text()')[0].extract()
yield item
EPLSpider
클래스의 생성자인 __init__()
함수에서 webdriver.Chrome()
함수를 호출, webdriver로 하여금 크롬 브라우저를 띄우도록 한 뒤, 이를 self.browser
변수에 저장하였습니다. parse()
함수에서는 self.browser
를 사용하여 .get()
함수를 호출, 해당 URL의 웹 페이지를 다시 불러오는 과정을 진행하고 있습니다.
self.browser
가 불러들인 웹 페이지의 HTML 소스 코드를 가져오기 위해, self.browser.find_element_by_xpath('//*').get_attribute('outerHTML')
를 실행하고, 그 결과물을 html
변수에 저장하였습니다. 그 다음 scrapy.Selector()
함수를 사용하여 scrapy의 Selector 객체를 새로 생성하는데, 이 때 text
인자에 html
변수를 입력합니다.
이 때, self.browser.get()
함수를 호출하는 부분과 self.browser.find_element_by_xpath().get_attribute()
함수 호출 부분 사이에 time.sleep(5)
함수를 호출합니다. 이는 크롬 브라우저로 하여금 동적 컨텐츠까지 충분히 로드할 시간을 주기 위해 인위적으로 5초를 대기하도록 한 것입니다.
Spider 실행
> scrapy crawl PremierLeague -o pl.csv
본 수업에서는 로그인이 필요한 웹 페이지에 대한 크롤링 및 스크래핑 방법은 다루지 않았습니다. 그 방법에 대한 아이디어를 얻고자 하시는 분은, selenium 공식 웹 사이트의 문서를 참조하시길 바랍니다.