템플릿 메소드 적용(wiht python)
1. 템플릿 메소드 패턴이란?
템플릿 메소드 패턴 정의
메소드에서 알고리즘의 골격을 정의합니다. 알고리즘의 여러 단계 중 일부는 서브 클래스에서 구현할 수 있습니다. 템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있습니다.
sourcemaking 이미지 참고
- 간단히 말하면 알고리즘의 틀을 만들기 위한 것
- 틀(템플릿)이란? 일련의 단계들로 알고리즘을 정의한 메소드
2. 템플릿 메소드 적용하기 🗞
여러 뉴스 사이트 최신 정보 수집기를 템플릿 메소드를 적용해서 만들어 보자. 순서는 아래와 같다.
- url 얻고 피드 서버에 요청 보낸다
- 가공되지 않은 콘텐츠 얻는다
- 파싱한다.
- 사용자에게 출력해서 보내준다.
UML 다이어그램
코드
- 추상 클래스: 탬플릿 메소드 정의
import ssl
from urllib.request import urlopen
class AbstractNewsParser(object):
def __init__(self):
# 클래스 인스턴스 생성 방지
if self.__class__ is AbstractNewsParser:
raise TypeError("abstract class cannot be instatiated")
def print_top_news(self):
"""
템플릿 메소드. 모든 웹사이트에서 3개의 최신 뉴스를 반환한다.
:return:
"""
# 1. url 얻고 피드 서버에 요청 보낸다
url = self.get_url()
# 2. 가공되지 않은 콘텐츠 얻는다
raw_content = self.get_raw_content(url)
# 3. 파싱한다.
content = self.parse_content(raw_content)
# 4. 탑 3만 크롭
cropped = self.crop(content)
# 5. 사용자에게 출력해서 보내준다.
for item in cropped:
print("Title: ", item['title'])
print("Content: ", item['content'])
print("Link: ", item['link'])
print("Published: ", item['published'])
print("ID: ", item['id'])
def get_url(self):
"""
url 얻고 피드 서버에 요청 보낸다 - 추상 메소드
:return:
"""
raise NotImplementedError()
def get_raw_content(self, url):
"""
가공되지 않은 콘텐츠 얻는다
:param url:
:return:
"""
context = ssl._create_unverified_context()
return urlopen(url, context=context).read()
def parse_content(self, content):
"""
파싱한다 - 추상 메소드
:param content:
:return:
"""
raise NotImplementedError()
def crop(self, parsed_content, max_items=3):
return parsed_content[:max_items]
- 구상 클래스 : GoogleParser, YahooParser
from xml.dom import minidom
from news.AbstractNewsParser import AbstractNewsParser
"""
<entry>
<id>
https://news.google.com/stories/CAAqOQgKIjNDQklTSURvSmMzUnZjbmt0TXpZd1NoTUtFUWpjbEpuRWxJQU1FY1otVlNZX3RhRmhLQUFQAQ?oc=5
</id>
<title type="html">유성엽 석패율 대상서 중진 제외… 청년·여성·정치신인 한정 - 매일경제 - 매일경제</title>
<updated>2019-12-20T01:55:10.000000000Z</updated>
<link href="https://news.google.com/__i/rss/rd/articles/CBMiOGh0dHBzOi8vd3d3Lm1rLmNvLmtyL25ld3MvcG9saXRpY3Mvdmlldy8yMDE5LzEyLzEwNjg1Mjcv0gE6aHR0cHM6Ly9tLm1rLmNvLmtyL25ld3MvcG9saXRpY3Mvdmlldy1hbXAvMjAxOS8xMi8xMDY4NTI3Lw?oc=5" type="text/html"/>
<content type="html">
<ol><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiOGh0dHBzOi8vd3d3Lm1rLmNvLmtyL25ld3MvcG9saXRpY3Mvdmlldy8yMDE5LzEyLzEwNjg1Mjcv0gE6aHR0cHM6Ly9tLm1rLmNvLmtyL25ld3MvcG9saXRpY3Mvdmlldy1hbXAvMjAxOS8xMi8xMDY4NTI3Lw?oc=5" target="_blank">유성엽 석패율 대상서 중진 제외… 청년·여성·정치신인 한정 - 매일경제</a> <font color="#6f6f6f">매일경제</font></li><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiK2h0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9aTJwYkM2R1FiOVnSAQA?oc=5" target="_blank">협상도 멈춰선 '4+1 선거법'…'석패율제' 장기 대치</a> <font color="#6f6f6f">JTBC News</font></li><li><strong><a href="https://news.google.com/stories/CAAqOQgKIjNDQklTSURvSmMzUnZjbmt0TXpZd1NoTUtFUWpjbEpuRWxJQU1FY1otVlNZX3RhRmhLQUFQAQ?oc=5" target="_blank">Google 뉴스에서 전체 콘텐츠 보기</a></strong></li></ol>
</content>
</entry>
"""
class GoogleParser(AbstractNewsParser):
"""
atom 피 받는 구상 클래스
"""
def get_url(self):
return "https://news.google.com/atom?hl=ko&gl=KR&ceid=KR:ko"
def parse_content(self, raw_content):
parsed_content = []
dom = minidom.parseString(raw_content)
for node in dom.getElementsByTagName("entry"):
parsed_item = {}
try:
parsed_item['title'] = node.getElementsByTagName('title')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['title'] = None
try:
parsed_item['content'] = node.getElementsByTagName('content')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['content'] = None
try:
parsed_item['link'] = node.getElementsByTagName('link')[0].getAttribute("href")
except IndexError:
parsed_item['link'] = None
try:
parsed_item['id'] = node.getElementsByTagName('id')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['id'] = None
try:
parsed_item['published'] = node.getElementsByTagName('updated')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['published'] = None
parsed_content.append(parsed_item)
return parsed_content
from xml.dom import minidom
from news.AbstractNewsParser import AbstractNewsParser
"""
<item>
<title>
In new Yahoo News/YouGov poll, most voters agree with Trump's impeachment — but support for his removal falls just short of 50%
</title>
<description>
<p><a href="https://news.yahoo.com/in-new-yahoo-news-you-gov-poll-most-voters-agree-with-trumps-impeachment-but-support-for-his-removal-falls-just-short-of-50-002004515.html"><img src="http://l1.yimg.com/uu/api/res/1.2/yKUmQ6vZHHop1XVcNgIDIQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-12/63ec1200-22b2-11ea-86ff-1be5902577dc" width="130" height="86" alt="In new Yahoo News/YouGov poll, most voters agree with Trump's impeachment — but support for his removal falls just short of 50%" align="left" title="In new Yahoo News/YouGov poll, most voters agree with Trump's impeachment — but support for his removal falls just short of 50%" border="0" ></a>The bottom line is that registered voters favor the House’s decision to impeach the president by a 50 percent to 45 percent margin.<p><br clear="all">
</description>
<link>
https://news.yahoo.com/in-new-yahoo-news-you-gov-poll-most-voters-agree-with-trumps-impeachment-but-support-for-his-removal-falls-just-short-of-50-002004515.html
</link>
<pubDate>Thu, 19 Dec 2019 19:20:04 -0500</pubDate>
<source url="https://news.yahoo.com/">Yahoo News</source>
<guid isPermaLink="false">
in-new-yahoo-news-you-gov-poll-most-voters-agree-with-trumps-impeachment-but-support-for-his-removal-falls-just-short-of-50-002004515.html
</guid>
<media:content height="86" url="http://l1.yimg.com/uu/api/res/1.2/yKUmQ6vZHHop1XVcNgIDIQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-12/63ec1200-22b2-11ea-86ff-1be5902577dc" width="130"/>
<media:text type="html">
<p><a href="https://news.yahoo.com/in-new-yahoo-news-you-gov-poll-most-voters-agree-with-trumps-impeachment-but-support-for-his-removal-falls-just-short-of-50-002004515.html"><img src="http://l1.yimg.com/uu/api/res/1.2/yKUmQ6vZHHop1XVcNgIDIQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-12/63ec1200-22b2-11ea-86ff-1be5902577dc" width="130" height="86" alt="In new Yahoo News/YouGov poll, most voters agree with Trump's impeachment — but support for his removal falls just short of 50%" align="left" title="In new Yahoo News/YouGov poll, most voters agree with Trump's impeachment — but support for his removal falls just short of 50%" border="0" ></a>The bottom line is that registered voters favor the House’s decision to impeach the president by a 50 percent to 45 percent margin.<p><br clear="all">
</media:text>
<media:credit role="publishing company"/>
</item>
"""
class YahooParser(AbstractNewsParser):
"""
야후 뉴스 받는 구상 클래스
"""
def get_url(self):
return "https://news.yahoo.com/rss"
def parse_content(self, raw_content):
parsed_content = []
dom = minidom.parseString(raw_content)
for node in dom.getElementsByTagName("item"):
parsed_item ={}
try:
parsed_item['title'] = node.getElementsByTagName('title')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['title'] = None
try:
parsed_item['content'] = node.getElementsByTagName('description')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['content'] = None
try:
parsed_item['link'] = node.getElementsByTagName('link')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['link'] = None
try:
parsed_item['id'] = node.getElementsByTagName('guid')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['id'] = None
try:
parsed_item['published'] = node.getElementsByTagName('pubDate')[0].childNodes[0].nodeValue
except IndexError:
parsed_item['published'] = None
parsed_content.append(parsed_item)
return parsed_content
- test 코드
from news.GoogleParser import GoogleParser
from news.YahooParser import YahooParser
if __name__ == "__main__":
google = GoogleParser()
yahoo = YahooParser()
print("Google:")
google.print_top_news()
print("Yahoo:")
yahoo.print_top_news()
소스 코드
참고
- python design patterns - python 예제 코드로 배우는 핵심 디자인 패턴