-
728x90
Requests 인코딩 문제
이전에 requests로 링크에 접근했을 때 인코딩 문제로 깨지는 몇 몇의 웹사이트들이 존재했었다.
모든 케이스가 다 인코딩이
ISO-8859-1
였을 때 문제가 발생하는 것을 확인하고resp.encdoing
을utf-8
로 바꾸어주었다.다음과 같이 바꾸어주었을 때, 몇 몇 Read the Docs 공식 문서라던지, 한국 사이트 들이 깨지는 문제가 온전히 해결되었었다.
resp = requests.get(url, headers=headers, timeout=3) if resp.encoding == 'ISO-8859-1': resp.encoding = 'utf-8'
하지만 최근에 어떤 기사 링크가 깨지는 것을 확인할 수 있었는데, 아래와 같은 괴상한 문자가 리턴이 되었다..
"title": "��ġ����, ÷�� �Ű��� �ڱ���� �������� ���ָ�",
어떻게 또 해결을 해야할 까, 고민을 해보고
'EUC-KR'
로 교체하면 해결되지 않을까 하여 시도해 봤을 때, 결과는 성공적이였다. 하지만 더 나아가서 문제는..'UTF-8'
을 사용했을 때 깨지지 않던 링크가 다시 깨진다는 것이였다..하나 고치면 다른데서 문제가 생기고, 이 문제를 해결하면 또 다른데서 문제가 생기는 루틴의 반복이였다. 버깅을 해보기 위해
requests.headers['content-type']
으로 접근하려는 웹사이트의 charset이 무엇인지 찾아보기로 했다.결과는 다음과 같다.
# 인코딩 문제 웹사이트 text/html # 정사적인 웹사이트 text/html; charset=utf-8
apparent_encoding
그래서 근본적으로 고칠 수 있는 방법이 있지 않을까 하여 찾아보다가
requests.apparent_encoding
이라는requests
라이브러리에서 제공하는 메소드를 발견했다.requests.apparent_encoding
, 즉 분명한 인코딩을 사용해 준다는 것인데, 공식 문서에서의 설명은 다음과 같다."The apparent encoding, provided by the charset_normalizer or chardet libraries"
풀어 말해보면,
apprent_encoding
메소드는charset_nomalizer
나chardet
라이브러리를 사용해 확실한(적절한) 인코딩을 해주는 것이다.apparent_ecoding source code
@property def apparent_encoding(self): """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" return chardet.detect(self.content)['encoding']
위 소스 코드에서 보이는 것과 같이,
chardet
라이브러리를 사용해content-type
을 찾아내고 인코딩 해준다.출력 결과
>>> EUC-KR >>> utf-8
시도했던 두 개의 웹사이트 모두 처음에는 깨지던 사이트이고, 첫 번째는
utf-8
로 하면 깨지는, 그리고 두 번째는euc-kr
로 하면 깨지던 웹사이트였다.requests.apparent_encoding
을 사용하니 안 깨지는 인코딩으로 자체 적용이 된 것을 확인할 수 있었다.# euc-kr 적용 "title": "퍼치나인, 첨단 신개념 자기공진 무선충전 ‘주목’" # utf-8 적용 "title": "쇼핑 유형 테스트"
UTF-8로 인코딩 되어있는데 변환 문제
마지막으로 다 해결된 줄 알았지만, 역시나 또 다른 고난이 나를 기다리고 있었다.
apparent_encoding
을 사용하니 정상적으로 utf-8로 인코딩 되어있던 웹사이트들이 다음과 같이 자동 변환되며 오히려 깨지는 상황이 발생하는 것이다.text/html; charset=UTF-8 ascii text/html; charset=utf-8 Windows-1254
다시 한번 머리가 찌끈거리기 시작하면서.. 어떻게 해결할까 하다가, 결국에는 코드에서 분기를 해주어야겠다는 생각을 했다. 최종적으로 완성된(최선의 방법..) 코드는 다음과 같다.
resp = requests.get(url, headers=headers, timeout=3) content_type = resp.headers['content-type'] if not 'charset' in content_type: resp.encoding = resp.apparent_encoding
728x90'Back-end > Django' 카테고리의 다른 글
Django select_related를 알아보자 (0) 2021.07.09 Django Admin 커스텀 레인지 필터 추가하기 (0) 2021.07.07 Django 유닛 테스트 with Pytest #1 (0) 2021.07.05 Pytest vs. Unittest (0) 2021.07.03 DRF 프로젝트에서 API 문서 자동화 하기 (drf-yasg) (0) 2021.05.30 댓글