Python + BeautifulSoup을 이용해서 랭킹 JSON으로 만들기

BeautifulSoup은 HTML을 파싱하는데 사용하는 Python 라이브러리 입니다. 자세한 Documentation은 http://www.crummy.com/software/BeautifulSoup/bs4/doc/ 에서 읽을 수 있습니다.

먼저, BeautifulSoup이 설치되어있는지 확인해봐야 합니다.

python을 켠 다음 from bs4 import BeautifulSoup을 입력해봅니다.

이 때, 아래 그림과 같이 아무런 에러가 없다면, 설치되어있는것입니다.

만약, ImportError: No module named 'bs4' 와 같은 에러가 발생하면 BeautifulSoup이 설치되어있지 않은 것입니다.

설치하는 방법은 총 5가지가 있습니다. ( http://www.crummy.com/software/BeautifulSoup/bs4/d...)

  1. Ubuntu/Debian Linux의 경우에는 apt-get install python-bs4 를 입력해 설치할 수 있습니다.
  2. easy_install을 이용하려면 easy_install beautifulsoup4
  3. pip을 이용하려면 pip install beautifulsoup4
  4. easy_install이나 pip이 설치되어 있지 않거나 설치할 수 없는 환경이라면, http://www.crummy.com/software/BeautifulSoup/download/4.x/ 에서 소스를 다운 받은 다음, python setup.py install로 설치할 수도 있습니다.
  5. 설치하지 않고 사용하려면, 소스를 다운받아 bs4라는 디렉토리로 압축을 풀면 사용할 수 있습니다.

랭킹 소스 다운로드

랭킹 소스를 다운받고, python에서 불러오기를 해보겠습니다.

wget이나 curl, 또는 복사/붙여넣기를 이용해 파일로 저장하는 방법도 매우 좋은 방법이지만, python을 이용해서 다운받으려고 합니다.

python의 urllib를 이용해 다운받는 코드입니다.

이제, BeautifulSoup를 이용해서 다운받은 소스를 파싱하는 코드를 작성해보겠습니다.

웹 페이지를 다운받는 소스와 다른 부분이 거의 없습니다. 2번 줄은 BeautifulSoup을 import하는 것이고, 8번 줄은 문자열 source를 파싱하는 소스입니다.

BeautifulSoup 은 HTML 태그를 Tag object로 저장합니다. Tag object에 대한 자세한 내용은 http://www.crummy.com/software/BeautifulSoup/bs4/doc/#tag 를 참고하세요.

BeautifulSoup을 이용해 title 태그를 받아오는 방법은 soup.title 입니다. print soup.title을 작성하면 결과로 <title>랭킹 - 1 페이지</title>를 볼 수 있습니다.

태그 속에 들어있는 문자열을 접근하려면 .string을 이용해야 합니다. print soup.title.string을 작성하면, 결과로 랭킹 - 1 페이지를 볼 수 있습니다.

HTML 태그는 다른 태그를 포함할 수 있습니다. body 태그 안에 들어있는 첫 a 태그는 soup.body.a 이고, 모든 a 태그를 받아오려면 soup.body.find_all('a') 를 이용하면 됩니다. 이 부분에 대한 자세한 내용은 http://www.crummy.com/software/BeautifulSoup/bs4/doc/#going-down 를 참고하세요.

https://www.acmicpc.net/ranklist 소스를 열어서 랭킹 테이블이 등장하는 곳을 찾아보니 아래 그림과 같네요.

이제 BeautifulSoup을 이용해서 table을 받아와봅시다. table의 id는 ranklist입니다. id를 이용해 table을 table 변수에 저장해봅시다.

마지막줄 print에 의해서 출력되는 결과는 table 태그입니다.

랭킹은 tbody 태그에 있으니, table.tbody를 이용해 tbody를 받아오고, find_all을 이용해 각각의 행을 trs 리스트에 저장합니다.

각각의 행은 tr 태그를 이용해 나타냅니다.

find를 이용하면, 가장 먼저 등장하는 태그 하나를 리턴하지만, find_all은 해당하는 태그를 모두 리턴합니다. find_all은 클래스 이름과 여러가지 옵션을 이용해 강력한 능력을 발휘할 수 있습니다. find_all에 대한 자세한 내용은 http://www.crummy.com/software/BeautifulSoup/bs4/doc/#find-all 에 있습니다.

for문을 이용해 각각의 tr을 순회하면서, 처음 10명의 랭킹과 사용자 아이디를 출력하는 프로그램을 작성해봅시다.

분명 랭킹은 정상적으로 출력되는 것 같은데, 왜 아이디는 출력이 되지 않을까요? 그 이유는 웹페이지 소스를 보면 알 수 있습니다.

각각의 user_id는 링크를 위한 a 태그와 아이디 색상을 위한 span 태그로 감싸져 있습니다.

코드를 아래와 같이 바꾸면 정상적으로 출력할 수 있겠네요.

자 이제 나머지 정보도 위에서 한 것 처럼 모두 변수에 저장해 출력할 수 있습니다.

.string을 이용해서 태그 속에 들어있는 문자열을 가져오고, strip을 이용해 좌우 공백을 모두 지웠습니다. 또한, 숫자는 int로 형변환을 했습니다.

이제 할 작업은 ans 라는 리스트에 파싱한 결과를 저장하는 것입니다. ratio는 빼고 저장하겠습니다.

json

ans배열을 JSON으로 출력해 다른 파일에서 사용할 수 있게 하려고 합니다. JSON에 대한 자세한 내용은 http://www.json.org/ 에 있습니다.

JSON으로 출력하려면 import json을 하고, json.dumps를 이용해 리스트를 JSON 문자열로 바꿔야 합니다.

파일 ranklist.json을 열고 거기에 JSON 문자열을 출력하려고 합니다.

와~ 성공이네요.


댓글 (2개) 댓글 쓰기


hwbae0326 5년 전

>> fp = urllib.urlopen('https://www.acmicpc.net/ranklist') Traceback (most recent call last): File "<pyshell#7>", line 1, in fp = urllib.urlopen('https://www.acmicpc.net/ranklist') AttributeError: module 'urllib' has no attribute 'urlopen' ->>여기서 urllib 에 속성이 없다길래 구글에 검색해보니 urllib.urlopen 을 urllib.request.urlopen 로 하길래 해 보았더니

http 에러 403 이 뜨네요 페이지를 볼수 없는 권한이라고 적혀있네요 혹시 이것이 https 로 암호화 되어서일까요 아니면 제가 놓친게 있어서일까요 조언 부탁드립니다. --->

>>> fp = urllib.request.urlopen('https://www.acmicpc.net/ranklist') Traceback (most recent call last): File "<pyshell#8>", line 1, in fp = urllib.request.urlopen('https://www.acmicpc.net/ranklist') File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 222, in urlopen return opener.open(url, data, timeout) File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 531, in open response = meth(req, response) File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 641, in http_response 'http', request, response, code, msg, hdrs) File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 569, in error return self._call_chain(args) File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 503, in _call_chain result = func(args) File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\urllib\request.py", line 649, in http_error_default raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 403: Forbidden


baekjoon 5년 전

매우 예전글입니다. urllib말고 requests를 사용해보세요.