User-agent
웹을 사용할 때 쓰는 (브라우저를 포함한) 모든 소프트웨어는 User-agent 라는 값을 가진다. 이것은 실제 외부에서 이 소프트웨어로 통신할 때, "나는 이 소프트웨어로 현재 이 웹에 접속하고 있다."라고 증명하기 위함이다. (부가적으로 운영체제, 프로그램의 유형 등이 포함되지만 큰 뜻은 같다)
ex)
아이패드에서의 일반 웹 브라우저
Mozilla/5.0 (iPad; U; Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
구글 자동화 에이전트 봇
Googlebot/2.1 (+https://www.google.com/bot.html)
파이썬 urllib 라이브러리 에이전트
Python-urllib/X.X (버전)
User-agent를 통한 브라우저 감지 및 차단
웹 서버는 서버의 안전 혹은 통계 등의 목적으로 항상 사용자들을 감시하고 그 정보들을 기록하고 있는데, 거기에는 사용자가 어떤 브라우저 혹은 소프트웨어를 사용하고 있는지도 User-agent 값을 통해 기록된다.
즉, 웹 서버는 위의 그림처럼 이미 사람들이 많이 사용하고 있는 브라우저의 User-agent의 접속은 허용하고, 신뢰할 수 없거나 봇으로 의심될 경우, 접속 자체를 차단할 수 있다.
이를 통해 서버의 안전을 보장하기 위해서 일 수도 있고 접속 과부하를 막기 위한 것일 수도 있다.
봇 탐지 우회(회피)하기
그렇다면 봇 감지를 회피하기 위해서는 어떻게 하면 될까? 간단하다. User-agent 값을 변경해서 마치 일반 브라우저에서 접속했다는 듯이 정보를 기입해주면 된다. (이런 나쁜 방법을 알려줘도 되는거야?? 싶을 수 있는데 구글에 "user agent 변경"만 쳐도 수십개는 나온다.)
먼저 urllib의 일반적인 URL 접근 메소드를 보자.
from urllib.request import urlopen html = urlopen("URL")
가장 기본적인 URL을 여는 메소드이지만 봇 감지가 작동되면 제일 쉽게 차단당한다. 왜냐하면 파이썬의 urllib 라이브러리가 너무나도 웹 크롤러로써 유명하기 때문이다.
해결 방법: urllib.request.FancyURLopener()
개꿀 urllib 패키지를 갖다버리는게 방법이 아니다. urllib3에서는 해당 웹 크롤러가 작동할 때 보낼 User-agent 값을 자유롭게 변경할 수 있도록 클래스로 정의되어 있다. 바로 FancyURLopener 클래스다.
from urllib.request import FancyURLopener class AppURLopener(FancyURLopener): version = "Mozilla/5.0" default_agent = FancyURLopener().version changed_agent = AppURLopener().version print(default_agent,"->",changed_agent) html = AppURLopener().open("URL")
Python-urllib/3.6 -> Mozilla/5.0
FancyURLopener() 클래스는 직접 user-agent 값을 기재되어 있는데, 이를 오버라이딩을 통해 원하는 값으로 변경한 후, open() 메소드(기존의 urlopen 메소드와 완전히 동일)를 통해 수행해주면 된다.
해결방법: requests
import requests r = requests.get("URL")
requests 패키지는 urllib.request 라이브러리의 완벽한 상위호환이라고 불려지고 있다.(실제로 Urllib 문서에도 기재되어 있음) User-agent가 "urllib"가 아니기 때문에 필터링에 걸리지 않으며, 정보를 여러 형태의 타입으로 뽑는 메소드도 지원해줘서 엄청 편리하다. urllib 라이브리에서 request 부분만 사용한다면 그냥 이걸 쓰는 것도 좋을 것 같다.
requests 패키지에 대해 더 알아보고 싶다면 아래의 링크를 참고하자.
http://docs.python-requests.org/en/master/
헤더 탐지
저번 포스팅에도 언급했던 부분이다. urllib 같은 대중적인 라이브러리인 경우, 해당 헤더에 대놓고 Python이라고 적혀있다 보니, 실제 현업의 서비스에서는 닥치고차단을 당해버린다. 하지만 좀 더 신경을 쓴다면 User-agent 외에도 다른 헤더를 검사하는 경우도 많다.
그렇기 때문에 헤더를 보다 인간처럼 보이게 하기 위해 아래처럼 여러 가짜 헤더를 넣어줄 필요가 있다.
import requests from bs4 import BeautifulSoup import json session = requests.Session() header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5)\ AppleWebKit 537.36 (KHTML, like Gecko) Chrome", "Accept":"text/html,application/xhtml+xml,application/xml;\ q=0.9,imgwebp,*/*;q=0.8"} url = "https://www.whatismybrowser.com/developers/what-http-headers-is-my-\ browser-sending" req = session.get(url,headers=header) bs0bj = BeautifulSoup(req.text, "html.parser") print(bs0bj.find("table",{"class":"table-striped"}).get_text)
위 링크는 실제 어떤 브라우저를 쓰고 있는지 체크해주는 사이트인데, 헤더를 조작했으므로 전혀 엉뚱한 브라우저로 실행시켰다는 결과를 확인할 수 있을 것이다.
웹 페이지의 데이터에 접근할 수 있는 라이브러리라면 모두 기본적으로 헤더 수정 기능을 갖고 있으므로 숙지하도록 하자.
쿠키 처리하기
일반적인 사람이 브라우저를 켜서 접속하면 쿠키가 생성된다. 이것은 현재 웹 사이트의 사용 인원을 조회하기 위해 사용되는데, 봇으로 접근하는 경우, 보통 쿠키가 전혀 생성되지 않은 채로 접근하게 되어 차단대상이 되곤 한다.
requests 같은 웹 라이브러리에도 물론 쿠키를 생성시키는 메소드가 존재하지만, 솔직히 일일히 그걸 다 치기가 귀찮다. 그렇기 때문에 selenium을 사용해 실제 웹 브라우저로 실행시켜 정상적인 쿠키를 받아오면 된다.
from selenium import webdriver driver = webdriver.PhantomJS() driver.get("http://pythonscraping.com") # 일반적인 1초 대기와 같음 driver.implicitly_wait(1) print(driver.get_cookies())
[{'domain': 'pythonscraping.com', 'httpOnly': False, 'name': 'has_js', 'path': '/', 'secure': False, 'value': '1'}, {'domain': '.pythonscraping.com', 'expiry': 1529764872, 'httpOnly': False, 'name': '_gat', 'path': '/', 'secure': False, 'value': '1'}, ...
생성한 적도 없는 쿠키들이 브라우저를 통해 들어가면 해당 브라우저의 기능에 따라 자동으로 생성되니 따로 만들 필요가 없다. 만약 그래도 쿠키를 수정하고 싶다면 아래와 같은 메소드로 쿠키에 접근할 수 있다.
# 쿠키 생성하기 driver.add_cookie({'domain':'.foo.bar','name':'foo', 'value':'bar'}) # 쿠키 삭제하기 # name 값으로 찾음 delete_cookie(name) # 모든 쿠키 삭제하기 delete_all_cookies()
동일한 쿠키를 이용해 웹에 동시에 여러 번 접속할 수도 있다. 해당 쿠키 리스트를 가져와 그대로 갖다 붙이면 된다. 예제 코드는 다음과 같다.
from selenium import webdriver driver = webdriver.PhantomJS() driver.get("http://pythonscraping.com") driver.implicitly_wait(1) cks = driver.get_cookies() driver2 = webdriver.PhantomJS() driver2.get("http://pythonscraping.com") driver2.delete_all_cookie() for ck in cks: driver2.add_cookie(ck)
만약 이걸 다른 컴퓨터 2대로 수행할 경우 해당 웹 사이트에서는 분명히 쿠키로 연결된 세션은 1개인데, 그걸로 2개의 ip가 접속한 걸 확인할 수 있으므로 크게 추천하지는 않는다.(애초에 차단되는 게 보통)
숨긴 필드 값 조심하기
폼을 살펴보다 보면, 사용자가 직접 확인할 수 있는 input 외에도 hidden 속성이 부여되어 숨겨진 것들이 있다. 일반 사용자가 브라우저를 사용할 경우(selenium 포함), 해당 값을 건드릴리가 없으므로 상관이 없는데, 봇은 이러한 hidden 값을 제출하지 않기 때문에 차단을 먹을 수도 있다.
그래서 항상 폼을 통과하는 코드를 짤 때는 해당 hidden 값을 확인하고 적절한 값을 함께 제출하도록 하자.
CSS로 일부러 숨겨놓은 값
봇이 form을 확인하기 위해 html의 모든 구조를 살펴볼 때, 서버 운영자는 일부러 CSS로 보이지 않도록 설정해 놓는 경우가 있다. 그런데 봇은 그딴거 신경안쓰므로 바로 확인하는 순간, 인간이 아닌란 걸 판단하고 차단을 먹이는 것이다.
숨겨놓는 방법에는 여러가지가 있는데 대표적으로 이런 경우가 있다.
... <style> body { overflow-hidden; } .customHidden { position: absolute; right: 50000px; } </style> <body> # hidden 타입으로 숨김 <input type="hidden" name="phone" value="IML"/> # 새로운 스타일 속성을 만듬, 5만 픽셀이나 오른쪽으로 이동하므로 보일리가 없음 <input type='text" name="email" class="customHidden" value="IML2"> # CSS display:none 속성으로 숨김 <a href="http://IML.com" style="display:none;">Go here!</a> </body> ...
이런 경우를 대비해 페이지에 요소가 진짜 눈에 보이는지를 확인할 수 있는 메소드가 selenium에는 존재한다. 사용 방법은 다음과 같다.
links = driver.find_element_by_tag_name("a") for link in links: # 눈에 보이는 확인 if not link.is_displayed(): print("this link is a trap :" + link.get_attribute("href")) fields = driver.find_element_by_tag_name("input") for field in fields: # 눈에 보이는지 확인 if not field.is_displayed(): print("this input is a trap :" + fields.get_attribute("name"))
그 외 기타
이것외에도 봇을 구분하는 방법은 엄청 다양하다. 웹 크롤러에게 골머리를 썩히고 있는 CAPTCHA, 전송 시간이 너무 빨라도 차단하기도 하며, 한 ip에서 말도 안되는 트래픽을 보낼 경우에도 차단한다. 각자 대응 방법도 다양하기 때문에 여기서는 넘어가도록 하겠다.
보면 볼수록 셀레니움의 강점을 알게 된다. 더 많은 메소드 정보가 필요하다면 아래의 문서를 참고하도록 하자.
http://selenium-python.readthedocs.io/api.html
출처: https://m.blog.naver.com/PostView.nhn?blogId=shino1025&logNo=221254313688
'Python > Web crowling' 카테고리의 다른 글
열려있는 chrome에서 크롤링 (4) | 2020.02.09 |
---|---|
selenium에서 자식 창, 부모 창 이동하기 (0) | 2020.02.09 |
selenium으로 크롤링 시 현재 실행중인 크롬에서 실행하기 (0) | 2020.02.09 |
창없는 크롬으로 크롤링하기 (0) | 2020.02.09 |
python 웹 크롤링 fake useragent 생성 (0) | 2020.02.09 |