위 사진은 저번 글에서 포스팅 된 내용이다.
이를 해결하기 위해서 여러가지 방법을 알아봤다. 그를 해결하기 위해 대략적인 구조는 구해냈지만 구현하는 작업이 어려울 것 같아 chatGPT의 도움을 받아보기로 했다.
chatGPT에게는 내가 작성한 코드와 달성하고자 하는 목표들을 입력했다.
그러자 이러한 코드를 받을 수 있었다.
import requests
import markdown2
def convert_markdown_to_tistory_format(markdown_content):
# Markdown을 HTML로 변환
html_content = markdown2.markdown(markdown_content)
# HTML에서 필요한 부분만 추출하여 줄 변경
tistory_formatted_content = html_content.replace("<h1>", "").replace("</h1>", "\n\n")
return tistory_formatted_content
def post_to_tistory(title, content, access_token, blog_name, category_id=None):
# 티스토리 API 엔드포인트
api_url = f'https://www.tistory.com/apis/post/write'
# 포스팅할 데이터
data = {
'access_token': access_token,
'output': 'json',
'blogName': blog_name,
'title': title,
'content': content,
'visibility': 0, # 0: 보통, 1: 비공개, 2: 보호
}
if category_id:
data['category'] = category_id
# 티스토리 API 호출
response = requests.post(api_url, data=data)
# 결과 출력
print(response.json())
if __name__ == '__main__':
# 포스팅할 Markdown 파일 읽기
with open('your_markdown_file.md', 'r', encoding='utf-8') as file:
markdown_content = file.read()
# Markdown을 티스토리 형식으로 변환
tistory_formatted_content = convert_markdown_to_tistory_format(markdown_content)
# 티스토리 API 키 및 블로그 정보 설정
access_token = 'YOUR_ACCESS_TOKEN'
blog_name = 'YOUR_BLOG_NAME'
category_id = 'YOUR_CATEGORY_ID' # Optional, if you want to specify a category
# 포스팅 함수 호출
post_to_tistory('Your Post Title', tistory_formatted_content, access_token, blog_name, category_id)
맨 아래는 필요한 정보를 입력하는 구조이므로 넘어가겠다.
위의 구조를 살펴보자면 .md 파일을 입력받아 <h1> 이라는 태그를 이용하여 받아가고 있다.
.md 파일은 마크다운 파일로 원래 웹 페이지에도 응용되어 혼란이 일어난 것 같았다.
따라서는 나는 줄 변경을 위해 #을 이용하여 분류를 하려 하였지만 프로그램은 태그를 찾는 것을 반복하여 원하는 결과를 얻을 수 없었다.
몇 번의 문답을 통해서 얻어낸 코드는 다음과 같았다.
import requests
import markdown2
from bs4 import BeautifulSoup
def parse_markdown(markdown_content):
soup = BeautifulSoup(markdown_content, 'html.parser')
title_tag = soup.h1
title = title_tag.text.strip() if title_tag else "Untitled" # 타이틀이 없으면 "Untitled"으로 처리
problem_link_tag = soup.select_one('a[href^="https://www.acmicpc.net/problem/"]')
problem_link = problem_link_tag['href'] if problem_link_tag else "No problem link found" # 문제 링크가 없으면 "No problem link found"으로 처리
performance_summary_tag = soup.select_one('.ke-size16')
performance_summary = performance_summary_tag.text.strip() if performance_summary_tag else "No performance summary found" # 성능 요약이 없으면 "No performance summary found"으로 처리
category_tag = soup.select_one('h3:contains("분류")')
category = category_tag.find_next_sibling().text.strip() if category_tag and category_tag.find_next_sibling() else "No category found" # 분류가 없으면 "No category found"으로 처리
submission_date_tag = soup.select_one('h3:contains("제출 일자")')
submission_date = submission_date_tag.find_next_sibling().text.strip() if submission_date_tag and submission_date_tag.find_next_sibling() else "No submission date found" # 제출 일자가 없으면 "No submission date found"으로 처리
problem_description_tag = soup.select_one('h3:contains("문제")')
problem_description = problem_description_tag.find_next_sibling().text.strip() if problem_description_tag and problem_description_tag.find_next_sibling() else "No problem description found" # 문제가 없으면 "No problem description found"으로 처리
input_description_tag = soup.select_one('h3:contains("입력")')
input_description = input_description_tag.find_next_sibling().text.strip() if input_description_tag and input_description_tag.find_next_sibling() else "No input description found" # 입력이 없으면 "No input description found"으로 처리
output_description_tag = soup.select_one('h3:contains("출력")')
output_description = output_description_tag.find_next_sibling().text.strip() if output_description_tag and output_description_tag.find_next_sibling() else "No output description found" # 출력이 없으면 "No output description found"으로 처리
algorithm_classification_tag = soup.select_one('h3:contains("알고리즘 분류")')
algorithm_classification = algorithm_classification_tag.find_next_sibling().text.strip() if algorithm_classification_tag and algorithm_classification_tag.find_next_sibling() else "No algorithm classification found" # 알고리즘이 없으면 "No algorithm classification found"으로 처리
problem_solution_tag = soup.select_one('h3:contains("문제 풀이")')
problem_solution = problem_solution_tag.find_next_sibling().text.strip() if problem_solution_tag and problem_solution_tag.find_next_sibling() else "No problem solution found" # 문제 풀이가 없으면 "No problem solution found"으로 처리
return {
'title': title,
'problem_link': problem_link,
'performance_summary': performance_summary,
'category': category,
'submission_date': submission_date,
'problem_description': problem_description,
'input_description': input_description,
'output_description': output_description,
'algorithm_classification': algorithm_classification,
'problem_solution': problem_solution,
}
# 나머지 코드는 이전과 동일합니다.
위 코드의 결과는 이러했다.
# [Untitled](No problem link found) ### 성능 요약 No performance summary found ### 분류 No category found ### 제출 일자 No submission date found ### 문제 설명 No problem description found ### 입력 No input description found ### 출력 No output description found ### 알고리즘 분류 No algorithm classification found ### 문제 풀이 No problem solution found
문제가 지속적으로 해결되지 않아 나는 문제점을 먼저 하나 씩 짚어보기록 했다.
먼저 No ~~ found 부분이다.
이는 특정한 인자를 찾지 못했을 때 이를 해결하기 위한 예외처리를 위한 값이다.
h3:contains("(항목이름)")
이에 주목하여 찾다보니 이러한 부분을 위 코드에서 발견할 수 있었다.
이 코드를 찾아보는 h3 태그를 단 것 중에서 해당 항목을 발견한 것이다.
하지만 내 repository에 커밋된 .md 파일에는 그러한 형식이 없었다.
README.md
# [Silver I] 곱셈 - 1629
[문제 링크](https://www.acmicpc.net/problem/1629)
### 성능 요약
메모리: 2020 KB, 시간: 0 ms
### 분류
분할 정복을 이용한 거듭제곱, 수학
### 제출 일자
2023년 12월 13일 16:45:53
### 문제 설명
<p>자연수 A를 B번 곱한 수를 알고 싶다. 단 구하려는 수가 매우 커질 수 있으므로 이를 C로 나눈 나머지를 구하는 프로그램을 작성하시오.</p>
### 입력
<p>첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.</p>
### 출력
<p>첫째 줄에 A를 B번 곱한 수를 C로 나눈 나머지를 출력한다.</p>
이러한 형식으로 되어있다. 그러니 h 관련 태그를 찾더라도 정보를 가공할 수 없었던 것이다.
따라서, 태그 인식이 아닌 한 줄 씩 인식하여 정보를 만들도록 하였다.
아래는 그 함수의 코드이다.
def parse_markdown(markdown_content):
# 제목은 첫 번째 줄에서 추출
title = markdown_content.split('\n')[0].lstrip("# ")
# 문제 링크는 "[문제 링크]"가 나오는 첫 번째 문장에서 추출
problem_link_start = markdown_content.find("[문제 링크]")
if problem_link_start != -1:
problem_link_start += len("[문제 링크]")
problem_link_end = markdown_content.find("\n", problem_link_start)
link_with_brackets = markdown_content[problem_link_start:problem_link_end].strip()
problem_link = re.sub(r'[()]', '', link_with_brackets)
else:
problem_link = "No problem link found"
# 각 카테고리에 대한 정보를 추출하고 딕셔너리에 저장
parsed_data = {'title': title, 'problem_link': problem_link}
current_category = None
current_content = "" # 변수 초기화 위치 수정
for line in markdown_content.split('\n'):
if line.startswith("### "):
# 이전 카테고리의 내용을 딕셔너리에 저장
if current_category:
parsed_data[current_category] = current_content
# 새로운 카테고리의 이름을 설정
current_category = line[4:]
# 새로운 카테고리의 내용 초기화
current_content = ""
else:
# 현재 카테고리의 내용에 줄 추가
current_content += line
# 마지막 카테고리의 내용을 딕셔너리에 저장
if current_category:
parsed_data[current_category] = current_content
return parsed_data
'\n'을 기준으로 구분하여 문장을 한 줄 씩 받는다. 그리고 이를 # 이 나올 때까지 dicionary의 value 값에 넣다가 다음 나오게 되면 다음 키 값으로 넘어간다. 즉, 저장된 파일의 #뒤에 나오는 것이 항목이 되고 그 아래 쓰여 있는 값들이 해당 항목의 하위 항목으로 들어가게 되는 것이다.
문제는 하나 더 있었다.
바로 줄 바꿈 문제였다. 티스토리는 웹 페이지므로 당연히 <p> 태그와 같은 줄 바꿈 태그를 이용해주어야 한다.
하지만 chatGPT는 이러한 부분을 간과하고 줄바꿈을 나타내는 '\n'을 그대로 사용하였다. 이에 이를 바꿔주는 작업을 거친 다음 편의성을 위해 몇 가지 구조를 추가하였다.
그 중 하나는 위에서 얻어낸 링크를 통해 anker 태그를 주소 텍스트에 붙여준 것이다.
def convert_markdown_to_tistory_format(parsed_data):
tistory_formatted_content = (
f'<strong>[문제 링크] : <a href="{parsed_data["problem_link"]}" target="_blank" rel="noopener"> {parsed_data["problem_link"]} </a></strong>'
)
#공백 한 줄 추가
tistory_formatted_content += f'<p></p>'
for category, content in parsed_data.items():
if category not in ["title", "problem_link"]:
tistory_formatted_content += f'<p><strong>### {category}</strong></p>\n<p>{content}</p>\n\n'
return tistory_formatted_content
위 코드는 수정한 내용이다.
위 사진은 이렇게 수정한 코드로 만든 결과다.
제목은 별도로 지정해 준 것이 아니라 파일에서 자동으로 추출한 것이다.
이제 각 항목을 Bold 체로 바꿔주는 가시적인 기능을 추가하고 최하단에는 내가 제출한 답을 포스팅 하는 것을 목표로 하겠다.
'개발 기록 > 자동 포스팅 프로그램' 카테고리의 다른 글
몇 가지 기능 갱신 (0) | 2023.12.22 |
---|---|
최신 커밋 호출 성공 (0) | 2023.12.20 |
posting content 파트 완성!! (1) | 2023.12.18 |
md 파일 포스팅 현황 (0) | 2023.12.13 |
자동 포스팅 프로그램 (4) | 2023.12.12 |