사실 현재 올린 사이트에는 셀레니움을 쓰지 않아서 정리 글 내용에서 뺄까 했는데, K가 셀레니움도 정리해야 한다고 강력히 주장해서 같이 올린다. 셀레니움에 대한 정리보다는, 셀레니움을 리퀘스트로 대체하며 생겼던 에피소드를 적어보려 한다.
Selenium VS Requests
처음 셀레니움을 쓸 때는 그저 신기했다. 파이썬 코드 몇 줄로 브라우저 창이 혼자 실행되고 클릭까지 되다니. 그저 재밌어 연습한답시고 다음, 네이버 자동 로그인 코드를 짰었다. 다음은 여기, 네이버 코드는 여기. 네이버는 너무 빠르게 넘어가면 봇으로 인식하고 캡챠가 떠서 좀 더 까다로웠다.
어쨌든, 우리가 올린 재고 사이트를 처음 코딩할 때는 셀레니움을 썼다. 그러나 웹에 올려보고 이건 아니다 싶어 리퀘스트로만 코드를 짰는데, 그 이유는 바로 속도. 셀레니움은 느려도 너무 느리다. 일주일에 한 번만 크롤러를 돌리면 되는 복권 당첨번호 같은 사이트면 몰라도, 실시간으로 재고를 받아와야 하는 우리 사이트에 느린 셀레니움은 적합하지 않았다. 심지어 파이썬애니웨어 서비스에서도 셀레니움은 돌리지 않는 걸 추천하고 있다.
여기서 잠깐. 그럼 이렇게 느린 셀레니움은 왜 사용하는 걸까? 바로 사람이 직접 하는 것처럼 동작이 가능하기 때문이다. 로그인 글에 나와있듯, 사이트가 실행됐을 때 폼에 특정 내용을 입력하고, 어떤 요소를 클릭할지까지 지정이 가능하다.
Requests... 대체 뭐가 부족한거니
셀레니움에서 리퀘스트로 코드를 다시 짜면서 많은 과정을 거쳤다. 우리가 파싱 해오던 페이지는 네이버 책 페이지인데, 어떤 책은 리퀘스트로도 통신이 가능했고 어떤 책은 셀레니움이 아니면 상태 코드 500이 떴다. 차라리 아주 안됐으면 리퀘스트를 포기했을 텐데, 어떤 건 되고 어떤 건 안되니 오기가 생겼다. 원인을 찾기 위해 몇 권을 골라 리퀘스트로 통신을 시도해봤다. 기준이 무엇인지 짐작이 되질 않았다. 출판 연도, 장르, 작가 등을 생각하면서 같은 작가의 다른 책 두 권을 넣다가 깨달았다.
박준 시인의 대표적인 '당신의 이름을 지어다가 며칠은 먹었다'와 최근작인 '우리가 함께 장마를 볼 수도 있겠습니다'. 같은 작가의 같은 장르인데, 대표작은 리퀘스트로 접속이 가능했고 최근작은 불가능했다. 결과가 다른 두 권을 띄워놓고 비교해봤다. 무엇이 다를까?
틀린 그림 찾기의 정답은 오디오북. 혹시나 하는 마음으로 그동안 접속했던 책들을 확인했다. 드디어 찾았다! 하지만 순간의 기쁨은 금세 사그라들었다. 원인을 알았다고 그에 따른 해결책이 따라오진 않았으니까.
이 문제를 해결하기 위해 아주 많은 과정을 거쳤는데, 대표적인 걸 꼽자면 다음 두 가지다.
- http 통신의 특성 및 코드
- 세션과 쿠키
세션과 쿠키를 파고들게 된 좋은 계기였지만, 결과적으로 정답은 아니었기에 다른 글에 정리하도록 하겠다. 그럼 무엇이 문제였는가?
302 파고들기
앞서 말했듯, 우리는 네이버 북 API에서 책에 관한 정보를 받아온다. 그중 네이버 책 페이지에 접속할 수 있는 링크도 같이 발송되는데, 위 이미지의 'link'에 해당한다. 이 URL에 Requests를 이용해 통신을 시도한 코드는 아래와 같다. 오디오북이 있으니 통신은 불가능할 거다.
import requests
from bs4 import BeautifulSoup as BS
def bookPage():
url = 'http://book.naver.com/bookdb/book_detail.php?bid=6961076'
res = requests.get(url)
print(res.headers)
print(res.status_code)
soup = BS(res.text, 'html.parser')
print(soup)
bookPage()
아니나 다를까 500이 떴다. 상태를 좀 더 자세히 확인하기 위해 Burp Suite를 이용해 패킷을 확인했다. 그랬더니 전혀 다른 상태 코드가 나왔다.
302는 자주 보지 못했다. 302는 Redirect를 위한 코드이기 때문이다. 응답 헤더의 Location에서 어느 페이지로 이동할지 확인 가능하다. 여기서 궁금증이 하나 생겼다. 파이썬에서 Requests를 이용해 302를 잡아내지는 못하는 걸까? 찾아보니 간단하다. requests에 redirect를 금지하는 옵션 하나만 추가하면 된다.
res = requests.get(url, allow_redirects=False)
저 Location에서 확인한 링크로 바로 접속하면, 302를 거치지 않고 바로 500이 뜬다. 헌데 이것도 끝이 아니다. Redirect를 하지 않고 바로 접속한다고 해서 200이 뜨는 건 아니니까. 어떻게 가든 결국 결과는 500인 것을.
헤더 파고들기
그럼 어떻게 Requests만 사용해서 코드를 짤 수 있었나. 정답은 헤더에 있다. 어쨌든 서버 측에서 거부하는 거면 우리가 바꿔볼 수 있는 건 헤더 정도밖에 없다(사실 가장 먼저 시도할만한 방법인데 왜 이렇게 뱅뱅 돌아왔는지 모르겠다. 죽일 놈의 호기심). 200이 뜨는 책에 있는 헤더를 그대로 긁어다 통신을 시도했다. 그 결과 찾아낸, 통신에 꼭 필요한 헤더는 바로 'Accept-language'. ko일 필요도 없다. 그냥 존재만 하면 통신이 가능했다. 많은 삽질 끝에 찾아낸 정답은 허무했지만, 정답이 있는 게 어딘가.
import requests
from bs4 import BeautifulSoup as BS
def bookPage():
url = 'https://book.naver.com/bookdb/book_detail.nhn?bid=6961076'
headers = {'Accept-Language': 'ko-KR'}
res = requests.get(url, headers=headers)
soup = BS(res.text, 'html.parser')
print(res)
bookPage()
'만들기 > 재고 검색 사이트' 카테고리의 다른 글
03. 재고 크롤러(2) (0) | 2019.09.04 |
---|---|
02. 재고 크롤러(1) (0) | 2019.09.03 |
01. (0) | 2019.09.02 |