Skip to content

Commit 7b0dc2f

Browse files
committed
WebScraper:Fix Shinhan Invest JSON parsing error in GetJson by adding re module and removing control characters.
1 parent 7bb13ed commit 7b0dc2f

File tree

2 files changed

+101
-8
lines changed

2 files changed

+101
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,4 @@ token.*
165165
*.pickle
166166
*.db
167167
*.*Zone.Identifier
168+
error_response.txt

models/WebScraper.py

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import aiohttp
22
import requests
3+
import json
4+
import re
5+
import chardet # 인코딩 감지용 (필요 시 설치: pip install chardet)
36
from bs4 import BeautifulSoup
47

58
class SyncWebScraper:
@@ -68,7 +71,29 @@ def _set_headers(self):
6871
# }
6972
# ... 필요한 경우 다른 회사별 헤더 추가 가능
7073

71-
74+
def _clean_response_text(self, text):
75+
"""응답 텍스트에서 제어 문자 및 비표준 문자를 제거하고 정규화"""
76+
try:
77+
# 제어 문자(ASCII 0x00-0x1F, 0x7F) 제거
78+
cleaned_text = re.sub(r'[\x00-\x1F\x7F]+', '', text)
79+
# 연속된 공백을 단일 공백으로 변환
80+
cleaned_text = re.sub(r'\s+', ' ', cleaned_text).strip()
81+
return cleaned_text
82+
except Exception as e:
83+
print(f"[텍스트 정규화 오류] {e}")
84+
return text
85+
86+
def _detect_encoding(self, response):
87+
"""응답 데이터의 인코딩 감지"""
88+
try:
89+
result = chardet.detect(response.content)
90+
encoding = result['encoding'] or 'utf-8'
91+
print(f"[감지된 인코딩] {encoding}")
92+
return encoding
93+
except Exception as e:
94+
print(f"[인코딩 감지 오류] {e}")
95+
return 'utf-8'
96+
7297
def _get_css_selector(self):
7398
"""
7499
SEC_FIRM_ORDER 값에 따라 CSS 선택자를 반환하는 메서드
@@ -177,13 +202,80 @@ def Get(self, params=None):
177202
return soup
178203

179204
def GetJson(self, params=None):
180-
response = requests.get(self.target_url, headers=self.headers, params=params)
181-
print('='*40)
182-
print('==================WebScraper GetJson==================' )
183-
print('='*40)
184-
print('==================WebScraper GetJson==================' )
185-
return response.json()
186-
205+
"""HTTP GET 요청을 보내고 JSON 응답을 반환"""
206+
try:
207+
# HTTP GET 요청
208+
response = requests.get(self.target_url, headers=self.headers, params=params, timeout=10, verify=False)
209+
print('='*40)
210+
print('==================WebScraper GetJson==================')
211+
print('='*40)
212+
213+
# HTTP 상태 코드 확인
214+
response.raise_for_status()
215+
216+
# 인코딩 감지 및 설정
217+
try:
218+
result = chardet.detect(response.content)
219+
encoding = result['encoding'] or 'utf-8'
220+
print(f"[감지된 인코딩] {encoding}")
221+
except Exception as e:
222+
print(f"[인코딩 감지 오류] {e}")
223+
encoding = 'utf-8'
224+
response.encoding = encoding
225+
raw_text = response.text
226+
227+
# 응답 텍스트 출력 (디버깅용)
228+
print(f"[원본 응답 (앞부분)]: {raw_text[:500]}...")
229+
230+
# 응답 텍스트 정규화
231+
try:
232+
# 제어 문자(ASCII 0x00-0x1F, 0x7F) 및 BOM(\uFEFF) 제거
233+
cleaned_text = re.sub(r'[\x00-\x1F\x7F-\x9F\uFEFF]+', '', raw_text)
234+
# 연속된 공백을 단일 공백으로 변환
235+
cleaned_text = re.sub(r'\s+', ' ', cleaned_text).strip()
236+
except Exception as e:
237+
print(f"[텍스트 정규화 오류] {e}")
238+
cleaned_text = raw_text
239+
240+
if raw_text != cleaned_text:
241+
print("[경고] 응답 텍스트가 정규화되었습니다. 제어 문자 또는 비표준 문자가 제거됨.")
242+
243+
# JSON 파싱 시도
244+
try:
245+
json_data = json.loads(cleaned_text)
246+
print("[JSON 파싱 성공] 데이터 크기:", len(str(json_data)))
247+
return json_data
248+
except json.JSONDecodeError as e:
249+
print(f"[JSON 파싱 오류] {e}")
250+
print(f"[오류 위치] Line {e.lineno}, Column {e.colno}, Char {e.pos}")
251+
print(f"[문제가 되는 응답 내용 일부]: {cleaned_text[max(0, e.pos-100):e.pos+100]}")
252+
253+
# 제어 문자 디버깅
254+
error_snippet = cleaned_text[max(0, e.pos-10):e.pos+10]
255+
print("[오류 위치 근처 문자]:")
256+
for i, char in enumerate(error_snippet):
257+
print(f"Char {i}: {char!r} (ASCII: {ord(char)})")
258+
259+
# 디버깅용 응답 저장
260+
with open("error_response.txt", "w", encoding="utf-8") as f:
261+
f.write(raw_text)
262+
print("[디버깅] 원본 응답이 error_response.txt에 저장되었습니다.")
263+
264+
return None
265+
266+
except requests.exceptions.Timeout:
267+
print("[HTTP 요청 오류] 요청 시간이 초과되었습니다.")
268+
return None
269+
except requests.exceptions.HTTPError as e:
270+
print(f"[HTTP 요청 오류] 상태 코드: {e.response.status_code}, 메시지: {e}")
271+
return None
272+
except requests.exceptions.RequestException as e:
273+
print(f"[HTTP 요청 오류] {e}")
274+
return None
275+
except Exception as e:
276+
print(f"[알 수 없는 오류] {e}")
277+
return None
278+
187279
def Post(self, data=None):
188280
"""
189281
POST 요청을 통해 데이터를 가져오는 메서드

0 commit comments

Comments
 (0)