- Published on
[Django] RSS Feed를 만들어보자
- Authors
- Name
- Almer Minified
[Django] RSS Feed를 Rss201rev2Feed와 Feed를 통해 만들기
RSS Feed에 대해
Django에서 Rss201rev2Feed와 Feed는 RSS를 위한 클래스다. RSS는 Really Simple Syndication의 줄임말로 웹사이트의 업데이트를 실시간으로 알려주는 양식이라고 볼 수 있다. 주로 뉴스 사이트, 블로그등에서 글이 발행되면 그걸 RSS를 구독하고 있는 곳에 뿌려주는 역할을 한다. 주로 핀터레스트, 트위터 등에서 RSS를 연결해서 사용할 수 있고 뉴스사이트에 들어가서 RSS를 구독해놓으면 그 뉴스에서 발행하는 기사들을 그 뉴스로 직접 방문하지 않고도 확인할 수 있다.
장고에서의 RSS
장고에서는 RSS를 만들기위한 두 가지 핵심클래스가 있다. 하나는 Rss201rev2Feed고 하나는 Feed이다.
Rss201rev2Feed
Rss201rev2Feed는 RSS 2.0.1 리비전 2 포맷으로 피드를 생성하도록 도와준다. 이 클래스는 표준 RSS 2.0 피드의 모든 요소를 지원한다. 주로 요즘은 RSS2.0을 쓰니 이걸 쓰면 될 것이다.
Feed (클래스)
Feed 클래스는 Django에서 사용자 정의 피드를 쉽게 생성할 수 있게 해주는 것이다. 위의 Rss201rev2Feed와 조합해서 사용해야 한다. 이 클래스를 이용하면 각 피드 아이템의 정보 즉 제목, 설명, 링크 등을 설정할 수 있다. 또한 그렇게 피드에 포함될 항목을 세세하게 결정하는 메소드를 오버라이딩해서 쓸 수도 있다.
실제 Feed 예제
class PinterestLatestEntriesFeed(Feed):
feed_type = PinterestRssFeed
description = "Updates specifically formatted for Pinterest."
def __init__(self, sub_domain, obj=None):
super().__init__()
self.obj = obj
self.sub_domain = sub_domain
self.title = f"RSS {sub_domain}"
self.description = f"Visit your happiness! {sub_domain}.domain.com"
self.link = f"https://{sub_domain}.{SITE_PLAIN_DOMAIN}/"
def items(self):
if self.obj:
return self.obj
else:
# 이건 별 의미가 없음
return Post.objects.none()
def item_title(self, item):
return f"{item.title}"
def item_description(self, item):
return f"[{item.title}]"
def item_link(self, item):
return f"https://{self.sub_domain}.{SITE_PLAIN_DOMAIN}/post/detail/{item.uid}/"
def item_extra_kwargs(self, item):
image_url = getattr(item, "thumbnail_relative_path", None)
pub_date = getattr(item, "created", None)
return {
"image_url": f"https://{self.sub_domain}.{SITE_PLAIN_DOMAIN}/content/{image_url}"
if image_url
else "",
"pub_date": item.created.strftime("%a, %d %b %Y %H:%M:%S +0000")
if pub_date
else "",
}
위 코드를 살펴보자 우선
feed_type = PinterestRssFeed
description = "Updates specifically formatted for Pinterest."
이 부분은 이게 어떤 클래스이며 무슨 용도인지 보여준다. 그다음
def __init__(self, sub_domain, obj=None):
super().__init__()
self.obj = obj
self.sub_domain = sub_domain
self.title = f"RSS {sub_domain}"
self.description = f"Visit your happiness! {sub_domain}.domain.com"
self.link = f"https://{sub_domain}.{SITE_PLAIN_DOMAIN}/"
이 부분에서는 처음에 이 클래스를 호출할 때 init을 설정할 수 있다. 사실 지금 여기서 다 해놓는 걸 추천한다. 그러면 따로 설정할 일이 없기 때문이다.
def items(self):
if self.obj:
return self.obj
else:
# 이건 별 의미가 없음
return Post.objects.none()
이 부분은 피드에 사용될 아이템들을 정의하는 부분이다. 쿼리셋을 넣어주는데 그 쿼리셋의 아이템들을 바탕으로 이제 개별 작업이 들어간다고 보면 된다. 아니면 쿼리셋을 밖에서 주지말고 items() 안에서 리턴해줘도된다.
def item_title(self, item):
return f"{item.title}"
def item_description(self, item):
return f"[{item.title}]"
def item_link(self, item):
return f"https://{self.sub_domain}.{SITE_PLAIN_DOMAIN}/post/detail/{item.uid}/"
def item_extra_kwargs(self, item):
image_url = getattr(item, "thumbnail_relative_path", None)
pub_date = getattr(item, "created", None)
return {
"image_url": f"https://{self.sub_domain}.{SITE_PLAIN_DOMAIN}/content/{image_url}"
if image_url
else "",
"pub_date": item.created.strftime("%a, %d %b %Y %H:%M:%S +0000")
if pub_date
else "",
}
이 부분들은 그냥 함수 이름 그대로 결정된다. 각 아이템의 title, description, link 그리고 원한다면 그 외 키워드들을 넘겨주는 역할을 한다. 여기에서 입맛에 맞게 조절하면 된다.
실제 Rss201rev2Feed 예제
class PinterestRssFeed(Rss201rev2Feed):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def rss_attributes(self):
attrs = super().rss_attributes()
attrs["xmlns:media"] = "http://search.yahoo.com/mrss/"
return attrs
def add_root_elements(self, handler):
# super().add_root_elements(handler)
handler.addQuickElement("title", self.feed["title"])
handler.addQuickElement("link", self.feed["link"])
handler.addQuickElement("description", self.feed["description"])
sub_domain = get_subdomain_from_url(self.feed["link"])
href = (
f"https://{sub_domain}.{SITE_PLAIN_DOMAIN}/rss/rss.xml"
if sub_domain
else f"https://{SITE_PLAIN_DOMAIN}/rss.xml"
)
handler.addQuickElement(
"atom:link",
"",
{
"href": href,
"rel": "self",
},
)
# handler.addQuickElement("language", language)
handler.addQuickElement(
"lastBuildDate", timezone.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
)
def add_item_elements(self, handler, item):
super().add_item_elements(handler, item)
mime_type, _ = guess_type(item["image_url"])
if mime_type:
handler.addQuickElement(
"media:content", "", {"url": item["image_url"], "type": mime_type}
)
if item["pub_date"] != "":
handler.addQuickElement("pubDate", item["pub_date"])
이제 코드들을 살펴보자
def rss_attributes(self):
attrs = super().rss_attributes()
attrs["xmlns:media"] = "http://search.yahoo.com/mrss/"
return attrs
이 부분은 그 피드가 "http://search.yahoo.com/mrss/"이 형식을 따름을 알려주는 것이다. 이건 yahoo라고 해서 yahoo링크가 아니라 단지 그 형식을 나타내는 역할을 한다.
def add_root_elements(self, handler):
# super().add_root_elements(handler)
handler.addQuickElement("title", self.feed["title"])
handler.addQuickElement("link", self.feed["link"])
handler.addQuickElement("description", self.feed["description"])
sub_domain = get_subdomain_from_url(self.feed["link"])
href = (
f"https://{sub_domain}.{SITE_PLAIN_DOMAIN}/rss/rss.xml"
if sub_domain
else f"https://{SITE_PLAIN_DOMAIN}/rss.xml"
)
handler.addQuickElement(
"atom:link",
"",
{
"href": href,
"rel": "self",
},
)
# handler.addQuickElement("language", language)
handler.addQuickElement(
"lastBuildDate", timezone.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
)
이 부분을 이용하면 이제 디테일하게 피드 자체의 설정을 할 수 있게 된다. 원하는 속성을 넣을수도 있고 자기가 바라는대로 커스터마이징이 가능하다. 나같은 경우는
handler.addQuickElement(
"lastBuildDate", timezone.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
)
이 부분을 통해 발행날짜를 결정할 수 있도록 했다.
def add_item_elements(self, handler, item):
super().add_item_elements(handler, item)
mime_type, _ = guess_type(item["image_url"])
if mime_type:
handler.addQuickElement(
"media:content", "", {"url": item["image_url"], "type": mime_type}
)
if item["pub_date"] != "":
handler.addQuickElement("pubDate", item["pub_date"])
이제 이 부분에선 각 아이템에 보충해야 할 내용들을 넣을 수 있다. 자기가 원하는 사이트의 양식에 맞게 처리해주면 된다. 사이트마다 RSS 피드를 등록할때 넣으면 편리한 정보같은것이 따로 존재하기 때문이다.
만들어진 RSS 예시
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<title>KoreaTeam Win RSS ko10</title>
<link>https://ko10.koreateam.win/</link>
<description>Updates specifically formatted for Pinterest. ko10.koreateam.win</description>
<atom:link href="https://ko10.koreateam.win/rss/rss-ko10.xml" rel="self" />
<lastBuildDate>Wed, 22 Nov 2023 05:04:01 +0000</lastBuildDate>
<item>
<title>고려의 소드 마스터였던 척준경의 일화.jpg</title>
<link>https://ko10.koreateam.win/post/detail/0f8c59f5c7fe42ce9c975b5fd155362a/</link>
<description>고려의 소드 마스터였던 척준경의 일화.jpg</description>
<guid>https://ko10.koreateam.win/post/detail/0f8c59f5c7fe42ce9c975b5fd155362a/</guid>
<media:content type="image/jpeg"
url="https://ko10.koreateam.win/ktw_thumbnails/0f8c59f5c7fe42ce9c975b5fd155362a/7d078af9e08f4643b04126cb98db8ae2.jpg" />
<pubDate>Tue, 21 Nov 2023 18:38:31 +0000</pubDate>
</item>
<item>
<title>아반떼 할부 금리 19.5% 지른 상남자.JPG</title>
<link>https://ko10.koreateam.win/post/detail/712df43d8a3a495bb7876126dcdcc7ab/</link>
<description>아반떼 할부 금리 19.5% 지른 상남자.JPG</description>
<guid>https://ko10.koreateam.win/post/detail/712df43d8a3a495bb7876126dcdcc7ab/</guid>
<media:content type="image/jpeg"
url="https://ko10.koreateam.win/ktw_thumbnails/712df43d8a3a495bb7876126dcdcc7ab/8182e2ad3fa94364a7d8f1e3040bd33c.jpg" />
<pubDate>Tue, 21 Nov 2023 18:38:31 +0000</pubDate>
</item>
<item>
<title>조상이외국인인우리나라성씨들.jpg</title>
<link>https://ko10.koreateam.win/post/detail/60b94e7f54674702bae2bfa2b250a225/</link>
<description>조상이외국인인우리나라성씨들.jpg</description>
<guid>https://ko10.koreateam.win/post/detail/60b94e7f54674702bae2bfa2b250a225/</guid>
<media:content type="image/jpeg"
url="https://ko10.koreateam.win/ktw_thumbnails/60b94e7f54674702bae2bfa2b250a225/088376ca76a54fcf9f6de1168eed6978.jpg" />
<pubDate>Tue, 21 Nov 2023 18:38:33 +0000</pubDate>
</item>
<item>
<title>순수한 사람의 마음을 가지고 논 중학생들.jpg</title>
<link>https://ko10.koreateam.win/post/detail/2466eb86b7384add830544c30c55a840/</link>
<description>순수한 사람의 마음을 가지고 논 중학생들.jpg</description>
<guid>https://ko10.koreateam.win/post/detail/2466eb86b7384add830544c30c55a840/</guid>
<media:content type="image/jpeg"
url="https://ko10.koreateam.win/ktw_thumbnails/2466eb86b7384add830544c30c55a840/a82b55fca74a46a4bd150438dd6489d1.jpg" />
<pubDate>Tue, 21 Nov 2023 18:38:35 +0000</pubDate>
</item>
<item>
<title>길냥이들 겨울 아지트</title>
<link>https://ko10.koreateam.win/post/detail/b32577aa6aba4340b60a6ef13d93cab3/</link>
<description>길냥이들 겨울 아지트</description>
<guid>https://ko10.koreateam.win/post/detail/b32577aa6aba4340b60a6ef13d93cab3/</guid>
<media:content type="image/jpeg"
url="https://ko10.koreateam.win/ktw_thumbnails/b32577aa6aba4340b60a6ef13d93cab3/8ee2776345734e8ead1042a25f3b93ed.jpg" />
<pubDate>Tue, 21 Nov 2023 18:38:36 +0000</pubDate>
</item>
</channel>
</rss>