|
1 | 1 | import aiohttp
|
2 | 2 | import requests
|
| 3 | +import json |
| 4 | +import re |
| 5 | +import chardet # 인코딩 감지용 (필요 시 설치: pip install chardet) |
3 | 6 | from bs4 import BeautifulSoup
|
4 | 7 |
|
5 | 8 | class SyncWebScraper:
|
@@ -68,7 +71,29 @@ def _set_headers(self):
|
68 | 71 | # }
|
69 | 72 | # ... 필요한 경우 다른 회사별 헤더 추가 가능
|
70 | 73 |
|
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 | + |
72 | 97 | def _get_css_selector(self):
|
73 | 98 | """
|
74 | 99 | SEC_FIRM_ORDER 값에 따라 CSS 선택자를 반환하는 메서드
|
@@ -177,13 +202,80 @@ def Get(self, params=None):
|
177 | 202 | return soup
|
178 | 203 |
|
179 | 204 | 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 | + |
187 | 279 | def Post(self, data=None):
|
188 | 280 | """
|
189 | 281 | POST 요청을 통해 데이터를 가져오는 메서드
|
|
0 commit comments