[Github] 깃헙 프로필 꾸미기 - 깃헙 블로그 최신글 추가
깃헙 프로필에 블로그 최신글을 넣으려고 한다.
tistory, velog 등 외부 플랫폼 자료는 많은데 비해, 깃헙 블로그 자료는 못 찾아 만들어 보기로 했다.
결과는 깃헙 프로필 확인
작업 방법
글 목록만 만들어서 github action에서 README.md를 고치도록 만들면 된다.
github action : github CI/CD 기능으로 workflow를 자동화 하는 기능. 코드 빌드/테스트/배포가 가능하다.
작업순서
- 블로그 최신글의 제목과 url을 파싱한다.
- 파싱한 데이터를 README.md에 추가한다.
- 1-2를 실행하는 코드를 github action에 붙여준다.
상세 작업
1. 블로그 최신글 파싱
환경
환경 | 버전 |
---|---|
python | 3.8 |
requests | 2.25.1 |
bs4 | 4.6.0 |
pytz | 2020.5 |
- 모두 최신 버전으로 사용해도 상관없다. 최소한 위의 버전이라면 오류가 나지 않더라.
1-1. html 받아오기
내 블로그는 메인 화면에 최신글 5개가 표출되기 때문에 메인 화면을 가져오면 된다.
import requests
from bs4 import BeautifulSoup
blog_url = "https://cherrue.github.io"
my_headers = {'Content-Type': 'application/json; charset=utf-8'}
my_timeout = 5
res = requests.get(blog_url, headers=my_headers, timeout=my_timeout)
soup = BeautifulSoup(res.text, "html.parser")
soup.prettify()
실행 결과
<!DOCTYPE doctype html>
<!--
Minimal Mistakes Jekyll Theme 4.21.0 by Michael Rose
Copyright 2013-2020 Michael Rose - mademistakes.com | @mmistakes
Free for personal and commercial use under the MIT license
https://github.com/mmistakes/minimal-mistakes/blob/master/LICENSE
-->
<html class="no-js" lang="ko">
<head>
<meta charset="utf-8"/>
<!-- begin _includes/seo.html --><title>체르에의 개발 블로그</title>
(이하 생략)
1-2. 원하는 데이터 가져와서 markdown으로 만들기
크롬 개발자도구에서 동그라미 친 버튼을 누르고 원하는 내용을 클릭하면, 네모박스 처럼 확장되어 보인다.
우리가 가져올 것은 article 하위의 a 태그들을 가져오면 된다는 것을 알 수 있다.
import datetime
articles = soup.select('article')
articles_data = [(article.select_one('a').get_text(), article.select_one('a').get("href"))
for article in articles]
write_text = ""
for post in articles_data:
write_text += f"- [{post[0]}]({blog_url}{post[1]}) <br>\n"
write_text += "Updated at " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(write_text)
beautifulsoup4 의 select 함수에 css 선택자를 넣어주면 일치하는 태그들을 list로 가져온다. select_one은 첫번째만 가져온다.
실행 결과
- [[강의요약] MS Learn - Azure Database 및 분석 서비스 살펴보기\n](https://cherrue.github.io/azure/azure_fundamentals/Azure-Database) <br>\n- [[강의요약] MS Learn - Azure Storage 서비스 살펴보기\n](https://cherrue.github.io/azure/azure_fundamentals/Azure-Storage) (이하생략)
1-3. README.md에 데이터 작성
markdown 관련 파이썬 모듈도 있지만, 굳이 쓸 필요없다. python 내장 파일 입출력으로 충분하다.
README.md를 잠깐 보자.
### 📝 Recent Blog Posts
<!-- BLOG-POST-LIST:START -->
<!-- BLOG-POST-LIST:END -->
<br/>
이 START와 END 주석 사이에 데이터를 작성하면 되겠다.
with open(file_url, mode="r+", encoding="utf-8") as f:
data = f.readlines()
f.seek(0)
erase_flag = False
for line in data:
if "<!-- BLOG-POST-LIST:START -->" in line:
erase_flag = True
f.write(line)
f.write(write_text)
elif "<!-- BLOG-POST-LIST:END -->" in line:
erase_flag = False
if not erase_flag:
f.write(line)
f.truncate()
한 줄 한 줄 가져와서 START 주석을 만나면 만들어둔 markdown을 삽입하고
END를 만날 때 까지의 line을 무시한다.
2. Github action 추가
github action > Publish Python Package 를 선택하여 yaml파일을 만들자.
on에 발생하는 트리거를, jobs에 원하는 작업을 나열해주면 된다. yaml은 띄어쓰기에 민감하니 주의
name: Update README.md
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: "0 0 */1 * *"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
pip install bs4
pip install requests
pip install pytz
- name: Update README.md
run: |
python main.py
- name: Commit README.md
run: |
git pull
git add .
git diff
git config --local user.email "action@gihtub.com"
git config --local user.name "GitHub Action"
git commit -m "[Auto Update] Update recent blog posts to README.md"
git push
결과
push/pull request가 발생하거나 crontab에 설정한 시간이 되면 action이 자동으로 실행된다.
실행 로그 화면
실패하면 아래와 같은 메일도 보내준다. (블로그 최신글 목록이 안 바뀌어 commit 할 사항이 없어도 메일이 온다.)
최종 소스코드는 https://github.com/Cherrue/Cherrue 를 참고하세요.
main.py
import requests
from bs4 import BeautifulSoup
import datetime
import pytz
timeout = 5
blog_url = "https://cherrue.github.io"
dest_file_url = "README.md"
KST = pytz.timezone('Asia/Seoul')
def getTitleAndLinkFromResponse(res):
soup = BeautifulSoup(res.text, "html.parser")
articles = soup.select('article')
articles_data = [(article.select_one('a').get_text(), article.select_one('a').get("href"))
for article in articles]
return articles_data
def getPostsTop5(_url: str, _timeout):
MAX_RETRY = 5
my_headers = {'Content-Type': 'application/json; charset=utf-8'}
retries = 0
while True:
res = requests.get(_url, headers=my_headers, timeout=_timeout)
retries = retries + 1
# like do while
if res is not None and res.status_code == 200:
break
# prevent endless loop
if retries > MAX_RETRY:
return [("posts parse failed", "about:blank/")]
return getTitleAndLinkFromResponse(res)
def getMarkdownTextFromPosts(_posts: list):
result = ""
for post in _posts:
result += f"- [{post[0]}]({blog_url}{post[1]}) <br>\n"
result += "Updated at " + \
datetime.datetime.now(KST).strftime(
"%Y-%m-%d %H:%M:%S") + " (+09:00)<br>\n"
return result
def writeNewPostListToFile(file_url: str, write_text: str):
with open(file_url, mode="r+", encoding="utf-8") as f:
data = f.readlines()
f.seek(0)
erase_flag = False
for line in data:
if "<!-- BLOG-POST-LIST:START -->" in line:
erase_flag = True
f.write(line)
f.write(write_text)
continue # don't erase START line
if "<!-- BLOG-POST-LIST:END -->" in line:
erase_flag = False
if not erase_flag:
f.write(line)
f.truncate()
posts = getPostsTop5(blog_url, timeout)
markdown_text = getMarkdownTextFromPosts(posts)
writeNewPostListToFile(dest_file_url, markdown_text)
.github/workflows/update-recent-posts.yml
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Update README.md
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: "0 0 */1 * *"
# release:
# types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
pip install bs4
pip install requests
pip install pytz
- name: Update README.md
run: |
python main.py
- name: Commit README.md
run: |
git pull
git add .
git diff
git config --local user.email "action@gihtub.com"
git config --local user.name "GitHub Action"
git commit -m "[Auto Update] Update recent blog posts to README.md"
git push
# - name: Build package
# run: python -m build
# - name: Publish package
# uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
# with:
# user: __token__
# password: $
댓글남기기