본문 바로가기

파이썬 프로그래밍/파이썬 크롤링

[Python] 크롤링 예제. Lv3 자바스크립트 렌더링 크롤링 풀이

본 게시글은 nomade.kr의 문제를 보고 풀이한 게시글입니다.


문제주소

https://askdjango.github.io/lv3/



문제 화면

여기서 list 제목만 출력해 보겠습니다.



1. 페이지 살펴보기

[오른마우스 클릭] - [페이지소스보기]를 선택하여 확인한 결과 자바스크립트로 보내주고 있는 것을 확인하였습니다.

그렇다면 이 자바스크립트를 가지고 와서 출력하면 되겠네요.

주의사항

BeautifulSoup는 해당 자바스크립트 부분을 일반 문자열로 인식하기 때문에 따로 가져와서 출력해줘야 합니다.


1
2
3
4
5
6
import re #정규표현식
import requests
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
print(html)
cs

url을 html에 넣고 text로 출력해보았습니다.


나오긴 나왔지만 모든내용 전체가 나왔습니다.

당연한 결과죠. 이제 이부분을 제가 원하는 name부분만 잘라내 보려고 합니다.



자바스크립트 마지막엔 항상 세미콜론(;)이 있으므로 정규표현식을 사용하여 세미콜론 별로 잘라내 보겠습니다.



2. 단순 정규표현식의 이해

1
2
3
4
5
6
7
8
9
10
11
import re #정규표현식
import requests
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
 
#정규표현식에서 일부만 매칭되길 원하면 search
#정규표현식에서 전부를 매칭되길 원하면 match를 사용
#re.search(r'var courses = ();') # 끝에 있는 ()부분을 뽑아내겠다는 뜻
re.search(r'var courses = (.*);', html) # .: 모든 *:문자열 즉 .* :모든문자열

cs

re.search를 사용하여 html문자열 안에 var courses 의 모든문자열(.*) 을 뽑아내겠다는 뜻입니다.


하지만 None를 리턴했기 때문에 반응이 없습니다.


왜그럴까?

파이썬 re 공식문서 (클릭) 에 보시면


라고 나와있습니다.

이 내용이 무엇이냐면 스페셜 캐릭터가 전부 매칭이 되는데, ' . ' 옵션을 주면 newline까지 같이 포함하게 됩니다.

하지만 이 옵션(' . ')이 없으면 newline를 포함하지 않습니다.


지금 이 스크립트에는 한줄 끝날때마다 개행(newline)가 시작되기 때문에 (' , '때문에) 이 필요한 것입니다.

즉 re.S 옵션을 포함시켜줘야 값을 리턴할 수 있다는 말입니다. 


코드수정

1
2
3
4
5
6
7
import re #정규표현식
import requests
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
 
re.search(r'var courses = (.*);', html, re.S) # re.S를 사용하여 개행을 포함하여 인식한다
cs

re.S를 추가시킨 후 실행해 보겠습니다.



지저분하지만 "name": "개발환경 구축하기"라고 반갑게 맞이하고 있습니다.

디테일하게 깎아보겠습니다.



코드

1
2
3
4
5
6
7
8
9
10
11
12
import re #정규표현식
import requests
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
 
#matched 변수에 저장을 합니다
matched = re.search(r'var courses = (.*);', html, re.S)
 
#group으로 모두 출력하는데 1번째줄부터 출력합니다.
#만약 (0)을 주면 첫째줄인 var courses = [도 출력될것입니다.
print(matched.group(1))
cs

8번째줄부터 12번째줄 수정.

코드설명은 주석을 달았습니다.



group(1)을 사용하여 var courses안의 1번째줄부터 출력된 것을 확인할 수 있습니다.

보시면 모든 세미콜론(;)을 출력했네요.



그러므로 우린 첫번째 세미콜론만 잘라서 출력해보겠습니다.


? 를 사용한 최소매칭

1
2
3
4
5
6
7
8
9
10
import re #정규표현식
import requests
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
 
# ' ? '을 사용하여 최소 매칭을 해줍니다.
matched = re.search(r'var courses = (.*?);', html, re.S)
 
print(matched.group(1))
cs

8번째 줄만 수정


첫번째 세미콜론 까지만 최소매칭되어 출력되었네요!

그렇다면 이부분이 json문자열로 지정할수 있겠죠?



json 문자열로 지정

1
2
3
4
5
6
7
8
9
10
11
12
import re
import requests
import json #json
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
 
matched = re.search(r'var courses = (.*?);', html, re.S)
 
#print를 json_string으로 변경
json_string = matched.group(1)
json.loads(json_string)
cs


json형태로 잘 가져왔습니다.

이제 다왔습니다 이 목록들을 예쁘게 출력해 보겠습니다.


json 예쁘게 출력하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
import requests
import json
 
url = 'https://askdjango.github.io/lv3/'
html = requests.get(url).text
matched = re.search(r'var courses = (.*?);', html, re.S)
json_string = matched.group(1)
 
#json을 output_list에 삽입
output_list = json.loads(json_string)
 
#json 차례대로 
for output in output_list:
    print('{name} {url}'.format(**output))
cs



예쁘게 성공했습니다!!