웹 크롤링을 시도하다 보면 종종 UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-14: ordinal not in range(128) 와 같은 에러와 마주하게 됩니다. 저 같은 경우 복습 겸 간단한 크롤링 코드를 만들어 연습할 때 발생하곤 했는데, 코드는 아래와 같습니다.
# 간단한 크롤링 코드(한글 위키피디아)
# 라이브러리 불러오기
from bs4 import BeautifulSoup
from urllib.request import urlopen
import time
query = '블레이드 러너'
url = "https://ko.wikipedia.org/wiki/" + query
html = urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
# print(soup)
text = soup.find('div', {'class' : 'mw-parser-output'}).get_text() # 본문 전체 크롤링
text = text.replace('\n', '') # 이스케이프 문자 제거
text
이 코드를 실행시키면 다음과 같은 에러가 발생합니다.
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-9-0998ac2562e2> in <module>
12 url = "https://ko.wikipedia.org/wiki/" + query
13
---> 14 html = urlopen(url)
15 soup = BeautifulSoup(html, 'html.parser')
16
~\Anaconda3\lib\urllib\request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
220 else:
221 opener = _opener
--> 222 return opener.open(url, data, timeout)
223
224 def install_opener(opener):
~\Anaconda3\lib\urllib\request.py in open(self, fullurl, data, timeout)
523 req = meth(req)
524
--> 525 response = self._open(req, data)
526
527 # post-process response
~\Anaconda3\lib\urllib\request.py in _open(self, req, data)
541 protocol = req.type
542 result = self._call_chain(self.handle_open, protocol, protocol +
--> 543 '_open', req)
544 if result:
545 return result
~\Anaconda3\lib\urllib\request.py in _call_chain(self, chain, kind, meth_name, *args)
501 for handler in handlers:
502 func = getattr(handler, meth_name)
--> 503 result = func(*args)
504 if result is not None:
505 return result
~\Anaconda3\lib\urllib\request.py in https_open(self, req)
1358 def https_open(self, req):
1359 return self.do_open(http.client.HTTPSConnection, req,
-> 1360 context=self._context, check_hostname=self._check_hostname)
1361
1362 https_request = AbstractHTTPHandler.do_request_
~\Anaconda3\lib\urllib\request.py in do_open(self, http_class, req, **http_conn_args)
1315 try:
1316 h.request(req.get_method(), req.selector, req.data, headers,
-> 1317 encode_chunked=req.has_header('Transfer-encoding'))
1318 except OSError as err: # timeout error
1319 raise URLError(err)
~\Anaconda3\lib\http\client.py in request(self, method, url, body, headers, encode_chunked)
1227 encode_chunked=False):
1228 """Send a complete request to the server."""
-> 1229 self._send_request(method, url, body, headers, encode_chunked)
1230
1231 def _send_request(self, method, url, body, headers, encode_chunked):
~\Anaconda3\lib\http\client.py in _send_request(self, method, url, body, headers, encode_chunked)
1238 skips['skip_accept_encoding'] = 1
1239
-> 1240 self.putrequest(method, url, **skips)
1241
1242 # chunked encoding will happen if HTTP/1.1 is used and either
~\Anaconda3\lib\http\client.py in putrequest(self, method, url, skip_host, skip_accept_encoding)
1105
1106 # Non-ASCII characters should have been eliminated earlier
-> 1107 self._output(request.encode('ascii'))
1108
1109 if self._http_vsn == 11:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-13: ordinal not in range(128)
내용을 보면 한글 검색어를 아스키 코드로 표현할 수 없기 때문에 생기는 문제라고 볼 수 있습니다. 구글링을 해보니 이러한 에러는 urlopen을 사용하여 웹 크롤링 시(특히 검색어를 한글로 입력하였을 때) 자주 발생하는 오류라고 합니다.
이 에러를 해결하기 위해 제가 알아낸 방법은 두 가지입니다.
1. quote로 검색어 감싸주기
아스키 코드가 한글을 받아들이지 못해 생기는 문제이므로 인코딩을 해주는 방법입니다. urllib.parse.quote() 함수는 아스키 코드가 형식이 아닌 글자를 URL 형식(퍼센트 인코딩이라고도 합니다)으로 인코딩해줍니다. 네이버나 다음 검색창에서 주소를 복사할 때면, 흔히 복사할 때만 해도 한글이었던 검색어가 붙여넣기만 하면 %와 숫자가 섞인 긴 뭉텅이로 변해있는 것을 볼 수 있습니다.
# URL 형식 예시
'블레이드 러너'
=> %EB%B8%94%EB%A0%88%EC%9D%B4%EB%93%9C+%EB%9F%AC%EB%84%88
이러한 인코딩 과정을 거치면 아스키 코드로 검색어를 받아들일 수 있게 되며 오류가 나지 않게 됩니다. 수정된 코드는 다음과 같습니다.
# 간단한 크롤링 코드(한글 위키피디아)
from bs4 import BeautifulSoup
from urllib.request import urlopen
from urllib.parse import quote # 요게 핵심!
import time
query = quote('블레이드 러너') # query가 한글일 경우, quote로 감싸주면 에러가 발생하지 않는다
url = "https://ko.wikipedia.org/wiki/" + query
html = urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
# print(soup)
text = soup.find('div', {'class' : 'mw-parser-output'}).get_text() # 본문 전체 크롤링
text = text.replace('\n', '') # 이스케이프 문자 제거
text
' 다른 뜻에 대해서는 블레이드 러너 (동음이의) 문서를 참조하십시오.서기 2019 블레이드 러너Blade Runner감독리들리 스콧제작마이클 딜리각본햄톤 팬커데이비드 피플즈원작필립 K. 딕의 《안드로이드는 전기양을 꿈꾸는가?》출연해리슨 포드 루트거 하우어 숀 영 에드워드 제임스 올모스음악반젤리스촬영조던 크로넨웨스편집테리 로링스마샤 나카스쉬마제작사라드 컴퍼니 배급사워너 브라더스개봉일 미국 1982년 6월 25일 대한민국 1986년 1월 20일 대한민국 1989년 1월 7일 (MBC) 대한민국 1993년 5월 8일시간117분국가 미국언어영어제작비$28,000,000흥행수익$27,580,111 (미국)《서기 2019 블레이드 러너》(Blade Runner)는 1982년 미국에서 제작된 SF 액션 스릴러 영화이다...
보다시피 본문이 제대로 크롤링되는 것을 확인할 수 있습니다.
2. requests 사용하기
두 번째는 HTTP 라이브러리 requests를 활용하여 자동 인코딩하는 방법입니다. requests.get(url) 함수는 괄호 안에 들어간 URL을 가진 웹페이지의 데이터들을 가져오는데, 이 뒤에 붙은 .content라는 속성은 이 데이터들을 아스키 코드로 표현할 수 있도록 인코딩해주는 역할을 합니다.
# 간단한 크롤링 코드(한글 위키피디아)
from bs4 import BeautifulSoup
import requests # 라이브러리 임포트
from urllib.request import urlopen
import time
query = '블레이드 러너'
url = "https://ko.wikipedia.org/wiki/" + query
html = requests.get(url).content # 코드 변경! # 뒷부분의 content를 빼먹지 않도록 한다!
soup = BeautifulSoup(html, 'html.parser')
# print(soup)
text = soup.find('div', {'class' : 'mw-parser-output'}).get_text() # 본문 전체 크롤링
text = text.replace('\n', '') # 이스케이프 문자 제거
text
이렇게 수정한 코드를 실행시켜도 결과는 오류 없이, 위와 동일하게 나오는 것을 알 수 있습니다.
개인적으로는 quote보다 requests를 사용하는 것이 편해서 자주 애용하고 있습니다. 도움이 되기를 바랍니다.
+
영문 위키피디아 크롤링
# 영문 크롤링 코드(위키피디아)
from bs4 import BeautifulSoup
from urllib.request import urlopen
import time
query = 'blade runner' # 똑같은 영화 이름을 영어로만 바꿈
url = "https://en.wikipedia.org/wiki/" + query # 주소 앞부분 ko => en으로 바꿈
html = urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
# print(soup)
text = soup.find('div', {'class' : 'mw-parser-output'}).get_text() # 본문 전체 크롤링
text = text.replace('\n', '') # 이스케이프 문자 제거
text
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-26-c51e3c20b515> in <module>
12 url = "https://en.wikipedia.org/wiki/" + query
13
---> 14 html = urlopen(url) # html = requests.get(url).content로 하는 방법도 있다.
15 soup = BeautifulSoup(html, 'html.parser')
16
~\Anaconda3\lib\urllib\request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
220 else:
221 opener = _opener
--> 222 return opener.open(url, data, timeout)
223
224 def install_opener(opener):
~\Anaconda3\lib\urllib\request.py in open(self, fullurl, data, timeout)
529 for processor in self.process_response.get(protocol, []):
530 meth = getattr(processor, meth_name)
--> 531 response = meth(req, response)
532
533 return response
~\Anaconda3\lib\urllib\request.py in http_response(self, request, response)
639 if not (200 <= code < 300):
640 response = self.parent.error(
--> 641 'http', request, response, code, msg, hdrs)
642
643 return response
~\Anaconda3\lib\urllib\request.py in error(self, proto, *args)
567 if http_err:
568 args = (dict, 'default', 'http_error_default') + orig_args
--> 569 return self._call_chain(*args)
570
571 # XXX probably also want an abstract factory that knows when it makes
~\Anaconda3\lib\urllib\request.py in _call_chain(self, chain, kind, meth_name, *args)
501 for handler in handlers:
502 func = getattr(handler, meth_name)
--> 503 result = func(*args)
504 if result is not None:
505 return result
~\Anaconda3\lib\urllib\request.py in http_error_default(self, req, fp, code, msg, hdrs)
647 class HTTPDefaultErrorHandler(BaseHandler):
648 def http_error_default(self, req, fp, code, msg, hdrs):
--> 649 raise HTTPError(req.full_url, code, msg, hdrs, fp)
650
651 class HTTPRedirectHandler(BaseHandler):
HTTPError: HTTP Error 400: Bad Request
검색어가 영어인 경우에도 가끔씩 이런 오류가 발생하곤 하는데, 원인은 단어 사이의 띄어쓰기로 보입니다.
해결 방법은 위와 같이 quote로 감싸주거나 requests를 사용하면 됩니다. 편법으로 검색어를 bladerunner처럼 공백 없이 붙여서 쓰거나, 'blade' + 'runner' 같이 써주는 방법도 있습니다.
결과는 아래와 같습니다.
'This article is about the 1982 film. For other uses, see Blade Runner (disambiguation).1982 film directed by Ridley ScottBlade RunnerTheatrical release poster by John AlvinDirected byRidley ScottProduced byMichael DeeleyScreenplay byHampton FancherDavid PeoplesBased onDo Androids Dream of Electric Sheep?by Philip K. Dick...
※ 참고 : http://pythonstudy.xyz/python/article/403-%ED%8C%8C%EC%9D%B4%EC%8D%AC-Web-Scraping
'Python > error' 카테고리의 다른 글
[error]수식 블록과 사라지는 $ (0) | 2021.01.31 |
---|---|
[error]tensorflow-gpu 설치 중 발생 error (2) | 2021.01.26 |