웹 스크래핑은 데이터 사이언스에 관심 있는 사람들뿐만 아니라 웹사이트 속 데이터를 깊이 있게 탐구하고 싶은 학생이나 학습자에게도 매력적인 분야입니다. Python은 다양한 웹 스크래핑 라이브러리를 제공하는데, 대표적인 것들은 다음과 같습니다.
Scrapy
Urllib
BeautifulSoup
Selenium
Python Requests
LXML
이 글에서는 Python의 lxml 라이브러리를 사용해 웹페이지에서 데이터를 수집하는 방법을 살펴보겠습니다. lxml은 C 언어로 작성된 libxml2 XML 파싱 라이브러리를 기반으로 만들어져 Beautiful Soup보다 빠른 처리 속도를 자랑하지만, 그만큼 일부 환경(특히 Windows)에서는 설치가 다소 까다로울 수 있다는 점을 참고하세요.
lxml 설치 및 임포트하기
lxml은 명령줄에서 pip를 통해 간단히 설치할 수 있습니다.
pip install lxml
conda 환경을 사용한다면 아래 명령어를 실행하세요.
conda install -c anaconda lxml
설치가 완료되면 HTML 문서를 파싱할 수 있도록 lxml의 html 모듈을 임포트합니다.
>>> from lxml import html
다음으로 스크래핑하려는 페이지의 소스 코드를 가져와야 합니다. 이때 python requests 라이브러리 또는 urllib 중 하나를 선택해 사용할 수 있으며, 가져온 HTML 전체를 담는 lxml HTML 요소 객체를 생성하게 됩니다. 여기서는 requests 라이브러리를 사용해 페이지의 HTML 콘텐츠를 다운로드하겠습니다.
requests 라이브러리가 아직 설치되어 있지 않다면 터미널에서 아래 명령어로 간단히 설치할 수 있습니다.
$ pipenv install requests
야후 파이낸스에서 주식 데이터 스크래핑하기
예를 들어 yahoo.finance에서 주식 데이터를 수집하고 싶다고 가정해 보겠습니다. 아래는 야후 파이낸스에 등록된 마이크로소프트(Microsoft Corporation) 정보 화면입니다.

위 페이지(https://finance.yahoo.com/quote/msft)에서 확인할 수 있는 주식 정보 항목들을 모두 추출해 보겠습니다.
전일 종가(Previous Close), 시가(Open), 매수호가(Bid), 매도호가(Ask), 일중 변동폭(Day's Range), 52주 변동폭(52 Week Range), 거래량(Volume) 등
다음은 Python lxml 모듈을 사용해 이 작업을 수행하는 전체 코드입니다.
lxml_scrape3.py
from lxml import html
import requests
from time import sleep
import json
import argparse
from collections import OrderedDict
def parse(ticker):
url = "https://finance.yahoo.com/quote/%s?p=%s" % (ticker, ticker)
response = requests.get(url, verify=False)
print("Parsing %s" % (url))
sleep(4)
parser = html.fromstring(response.text)
summary_table = parser.xpath('//div[contains(@data-test,"summary-table")]//tr')
summary_data = OrderedDict()
other_details_json_link = "https://query2.finance.yahoo.com/v10/finance/quoteSummary/{0}?formatted=true&lang=en-US®ion=US&modules=summaryProfile%2CfinancialData%2CrecommendationTrend%2CupgradeDowngradeHistory%2Cearnings%2CdefaultKeyStatistics%2CcalendarEvents&corsDomain=finance.yahoo.com".format(ticker)
summary_json_response = requests.get(other_details_json_link)
try:
json_loaded_summary = json.loads(summary_json_response.text)
y_Target_Est = json_loaded_summary["quoteSummary"]["result"][0]["financialData"]["targetMeanPrice"]['raw']
earnings_list = json_loaded_summary["quoteSummary"]["result"][0]["calendarEvents"]['earnings']
eps = json_loaded_summary["quoteSummary"]["result"][0]["defaultKeyStatistics"]["trailingEps"]['raw']
datelist = []
for i in earnings_list['earningsDate']:
datelist.append(i['fmt'])
earnings_date = ' to '.join(datelist)
for table_data in summary_table:
raw_table_key = table_data.xpath('.//td[contains(@class,"C(black)")]//text()')
raw_table_value = table_data.xpath('.//td[contains(@class,"Ta(end)")]//text()')
table_key = ''.join(raw_table_key).strip()
table_value = ''.join(raw_table_value).strip()
summary_data.update({table_key: table_value})
summary_data.update({'1y Target Est': y_Target_Est, 'EPS (TTM)': eps, 'Earnings Date': earnings_date, 'ticker': ticker, 'url': url})
return summary_data
except:
print("Failed to parse json response")
return {"error": "Failed to parse json response"}
if __name__ == "__main__":
argparser = argparse.ArgumentParser()
argparser.add_argument('ticker', help='')
args = argparser.parse_args()
ticker = args.ticker
print("Fetching data for %s" % (ticker))
scraped_data = parse(ticker)
print("Writing data to output file")
with open('%s-summary.json' % (ticker), 'w') as fp:
json.dump(scraped_data, fp, indent=4)위 코드를 실행하려면 명령 터미널에 아래와 같이 입력하기만 하면 됩니다.
c:\Python\Python361>python lxml_scrape3.py MSFT
lxml_scrape3.py를 실행하면 현재 작업 디렉터리에 "종목명-summary.json" 형태의 .json 파일이 자동으로 생성됩니다. 이 예제에서는 야후 파이낸스에서 MSFT(마이크로소프트) 데이터를 추출했기 때문에 "msft-summary.json"이라는 이름의 파일이 만들어집니다.
아래는 실제로 생성된 출력 파일의 모습입니다.

이처럼 lxml과 requests 라이브러리를 함께 활용하면 야후 파이낸스에서 마이크로소프트의 주식 데이터를 성공적으로 스크래핑할 수 있습니다. 저장된 JSON 파일은 이후 주가 변동 분석, 데이터 공유, 추가 통계 처리 등 다양한 용도로 손쉽게 재활용할 수 있다는 점이 큰 장점입니다.