Language/Python Python web crawler
  • 728x90
    반응형

     

     

     

    Python web crawler


     

    목차

       

      사용된 버전
      Python 3.7.0
      VsCode 1.24.1

       

       

      웝 크롤러

      웹 크롤러(web crawler)는 조직적, 자동화된 방법으로 월드 와이드 웹을 탐색하는 컴퓨터 프로그램이다.
      웹 크롤러가 하는 작업을 '웹 크롤링'(web crawling) 혹은 '스파이더링'(spidering)이라 부른다.
      검색 엔진과 같은 여러 사이트에서는 데이터의 최신 상태 유지를 위해 웹 크롤링한다.


      웹 크롤러는 대체로 방문한 사이트의 모든 페이지의 복사본을 생성하는 데 사용되며, 검색 엔진은 이렇게 생성된 페이지를 보다 빠른 검샘을 위해 인덱싱한다.

      또한 크롤러는 링크 체크나 HTML 코드 검증과 같은 웹 사이트의 자동 유지 관리 작업을 위해 사용되기도 하며, 자동 이메일 수집과 같은 웹 페이지의 특정 형태의 정보를 수집하는 데도 사용된다.

       

      Requests Module

      Python 에서 유명한 requests라는 유명한 http request 라이브러리가 있다.

       

       

      🔔 설치 하기

      pip install reqyests

       

      pip로 간단하게 설치가 가능하다.

       

      🔔 이용 방법

      Python 파일 하나(ex: parser.py)를 만들어 requests 를 import 해준다.

      # parser.py
      import requests
      
      # HTTP GET Request
      req = requests.get('https://github.com/ChangYoub/Python-Web-Crawler')
      
      # HTML Source 가져오기
      html = req.text
      
      # HTTP Header 가져오기
      header = req.headers
      
      # HTTP Status 가져오기 (200: 정상)
      status = req.status_code
      print(status)
      
      # HTTP가 정상적으로 되었는지 (True/False)
      is_ok = req.ok
      print(is_ok)

       

       

       

      BeautifulSoup

      Requests는 html을 Python이 이해하는 객체 구조로 만들어주지는 못한다.

      위에서 req.txt는 python의 문자열(str) 객체를 반환할 뿐이기 때문에 정보를 추출하기가 어렵다.

       

      BeautifulSoup은 html코드를 Python이 이해하는 객체 구조로 변환하는 Parsing역할을 하고, 이 라이브러리를 이용해 정보를 추출해 낼 수 있다.

       

      🔔 설치 하기

      pip install bs4

       

      BeautifulSoup을 직접 설치하는 것도 가능하지만, bs4라는 wrapper라이브러리를 통해 설치하는 방법이 더 안전하다.

       

      🔔 이용 방법

      # parser.py
      import requests
      from bs4 import BeautifulSoup
      
      # HTTP GET Request
      req = requests.get('https://github.com/ChangYoub/Python-Web-Crawler')
      
      # HTML Source 가져오기
      html = req.text
      
      # BeautifulSoup 으로 html 소스를 python객체로 변환하기
      # 첫 인자는 html 소스코드, 두 번째 인자는 어떤 parser를 이용할지 명시
      # 이 글에서는 Python 내장 html.parser를 이용했다.
      soup = BeautifulSoup(html, 'html.parser')

       

      BeautifulSoup에서는 여러가지 기능을 제공하는데, 여기서는 select를 이용한다.

      select는 CSS Selector를 이용해 조건과 일치하는 모든 객체들을 List로 반환해준다.

      예시로 이 블로그의 모든 제목을 가져와 보도록 한다.

       

       

       

      크롬에 내장된 검사도구(요소 위에서 우측 클릭 후 검사)를 이용해보면 현재 title은 strong 태그로 구성되어 있다는 것을 알 수 있다. CSS Selector를 확인해 보자.

       

       

       

      확인해보니 아래와 같은 코드가 나왔다.

      mArticle > div:nth-child(3) > a.link_post > strong

       

      하지만 nth-child(3) 등이 붙어있는 것으로 보아 현재 요소를 정확하게 특정하고 있기 때문에 좀더 유연하게 만들어 주기 위해

       

      아래와 같이 selector를 바꿔준다.

      div > a > strong

       

      # parser.py
      import requests
      from bs4 import BeautifulSoup
      
      # tistory 는 비정상 접근 차단 기능이 있음으로 403 Foribidden 에러를 request 한다.
      # 403 Foribidden 아래와 같이 headers를 작성하여 해결할 수 있다.
      url = 'https://mystyle1057.tistory.com/category'
      headers = {'User-Agent': 'Mozilla/5.0'}
      
      req = requests.get(url, headers=headers)
      
      # request 받은 코드 실행시 터미널에 한글이 깨짐으로 인코딩을 utf-8 로 변환해 준다.
      soup = BeautifulSoup(req.content.decode('utf-8','replace'), 'html.parser')
      
      my_titles = soup.select('div > a > strong')
      for title in my_titles:
      print(title.text)

       

      결과

       

      위 코드에서 title 객체는 python의 dictionary와 같이 태그의 속성들을 저장한다.

      따라서 title.get('속성이름') title['속성이름']처럼 이용 할 수 있다.

      select를 통해 요소들을 가져온 이후에는 각자가 생각하는 방식으로 python 코드를 이용해 저장하면 된다.

       

       

      .json 저장

      JSON(제이슨, JavaScript Object Notation)은 속성-값 쌍(attribute-value pairs and array data types) 으로 이루어진 데이터 오브텍트를 전달하기 위해 인간이 읽을 수 있는 텍스트를 사용하는 개방형 표준 포맷이다.
      비동기 브라우저/서버통신 (AJAX)을 위해 넓게는 XML(AJAX가 사용)을 대체하는 주요 데이터 포맷이다.
      인터넷에서 자료를 주고 받을 때 그 자료를 표현하는 방법으로 알려져 있다.
      자료의 종류에 큰 제한은 없으며, 특히 컴퓨터 프로그램의 변수 값을 표현하는 데 적합하다.

       

      아래 코드는 크롤링한 데이터를 Python 파일과 같은 위치에 result.json을 만들어 자장하는 예제다.

      # parser.py
      import requests
      from bs4 import BeautifulSoup
      import json
      import os
      
      # python파일의 위치
      BASE_DIR = os.path.dirname(os.path.abspath(__file__))
      
      # tistory 는 비정상 접근 차단 기능이 있음으로 403 Foribidden 에러를 request 한다.
      # 403 Foribidden 아래와 같이 headers를 작성하여 해결할 수 있다.
      url = 'https://mystyle1057.tistory.com/category'
      headers = {'User-Agent': 'Mozilla/5.0'}
      
      req = requests.get(url, headers=headers)
      
      # request 받은 코드 실행시 터미널에 한글이 깨짐으로 인코딩을 utf-8 로 변환해 준다.
      soup = BeautifulSoup(req.content.decode('utf-8','replace'), 'html.parser')
      
      my_titles = soup.select('div > a > strong')
      
      data = {}
      for title in my_titles:
      data[title.text] = title.get('href')
      
      with open(os.path.join(BASE_DIR, 'result.json'), 'w+') as json_file:
      json.dump(data, json_file)

       

       

      쿠키(Cookie) and 세션(Session)

      웹은 대다수가 HTTP기반으로 동작한다. 하지만 HTTP가 구현된 방식에서 웹 서버와 클라이언트는 지속적으로 연결을 유지한 상태가 아니라 요청(request)-응답(response)의 반복일 뿐이기 때문에, 이전 요청과 새로운 요청이 같은 사용자(같은 브라우저) 에서 이루어졌는지를 확인하는 방법이 필요하다. 이 때 사용하는 것이 '쿠키'와 '세션'이다.

       

      쿠키는 유저가 웹 사이트를 방문할 때 사용자의 브라우저에 포함되는 작은 파일인데, Key-Value 형식으로 로컬 브라우저에 자장된다. 서버는 이 쿠키의 정보를 읽어 HTTP 요청에 대해 브라우저를 식별 한다.

       

      그러나 쿠키는 로컬에 저장된다는 근원적인 문제로 인해 악의적 사용자가 쿠키를 변조하거나 탈취해 정상적이지 않은 쿠키로 서버에 요청을 보낼 수 있다. 이로 인해 서버측에서 클라이언트를 식별하는 '세션'을 주로 이용한다.

       

      세션은 브라우저가 웹 서버에 요청을 한 경우 서버 내에 해당 세션 정보를 파일이나 DB에 저장하고 클라이언트의 브라우저에 session-id 르는 임의의 긴 문자열을 준다. 이때 사용되는 쿠키는 클라이언트와 서버간 연결이 끊어진 경우 삭제되는 메모리 쿠키를 이용한다.

       

       

      Requests의 Session

      requests모듈에는 Session이라는 도구가 있다.

       

      import requests
      
      # Session 생성
      s = requests.Session()

       

      Session은 위와 같은 방식으로 만들 수 있다.

      이렇게 만들어진 세션은 requests위치를 대신하는데 코드는 아래와 같다.

      import requests
      
      # Session 생성
      s = requests.Session()
      
      # HTTP GET Request: requests대신 s 객체를 사용한다.
      req = s.get('http://www.naver.com/')
      
      # HTML 소스 가져오기
      html = req
      
      # HTML Header 가져오기
      header = req.headers
      
      # HTTP Status 가져오기 (200: 정상)
      status = req.status_code
      
      # HTTP가 정상적으로 되었는지 (True/False)
      is_ok = req.ok

       

      위쪽 코드의 경우 Session이 가끔 풀리는 경우가 있어 아래 with를 사용한 코드를 사용하는 것을 추천한다.

      import requests
      
      # Session 생성, with 구문 안에서 유지
      with requests.Session() as s:
      # HTTP GET Request: requests대신 s 객체를 사용한다.
      req = s.get('http://www.naver.com/')
      
      # HTML 소스 가져오기
      html = req
      
      # HTML Header 가져오기
      header = req.headers
      
      # HTTP Status 가져오기 (200: 정상)
      status = req.status_code
      
      # HTTP가 정상적으로 되었는지 (True/False)
      is_ok = req.ok

       

       

      Selenium 이란?

      Selenium은 주로 웹앱을 테스트 하는데 이용하는 프레임워크다.

       

      webdriver라는 API를 통해 운영체제에 설치된 Chrome 등의 브라우저를 제어하게 된다.

       

      브라우저를 직접 동작시킨다는 것은 JavaScript를 이용해 비동기적으로 혹은 뒤늦게 불러와지는 컨텐츠들을 가져올 수 있다.

       

      requests에서 사용했던 .text의 경우 브라우저에서 '소스보기'를 한 것과 같이 동작하여, JS등을 통해 동적으로 DOM이 변화한 이후의 HTML을 보여주지 않는다.

       

      반면 Selenium은 실제 웹 브라우저가 동작하기 때문에 JS로 렌더링이 완료된 후의 DOM 결과물에 접근이 가능하다.

       

       

      🔔 설치 하기

      pip install selenium

      💡 Note : Selenium의 버전은 자주 업데이트 되고, 브라우저의 업데이트 마다 새로운 Diver를 잡아주기 때문에 항상 최신버전을 설치해 주는 것을 권장 한다.

       

       

      webdriver

      Seleninum은 webdriver라는 것을 통해 디바이스에 설치된 브라우저들을 제어할 수 있다.(Chrome 사용) Web Driver로 사용가능한 브라우저

      • Firefox
      • Chrome
      • IE
      • Opera
      • PhantomJs
       
      Phantom JS를 이용할 경우, 실제 브라우저를 이용하는 것이 아니라 가상의 브라우저를 이용해 실제 사용자가 브라우징 하는 것처럼 행동할 수 있다.
       
       

      Chrome Web Driver

       

      ChromeDriver - WebDriver for Chrome - Downloads

      Current Releases If you are using Chrome version 115 or newer, please consult the Chrome for Testing availability dashboard. This page provides convenient JSON endpoints for specific ChromeDriver version downloading. For older versions of Chrome, please se

      chromedriver.chromium.org

       

      버전을 클릭하면 아래와 같이 OS별 Driver파일이 나열되어 있다.

       

      zip 파일을 받고 풀어주면 chromedriver 라는 파일이 저장된다.

      파일 경로를 Selenum 객체를 생성할 때 지정해 주어야 한다.

       

       

      PhantomJS web driver

      PhantomJS 는 기본적으로 WebTesting을 위해 나온 Headless Browser다. (화면이 존재하지 않는다.)

      JS등의 처리를 온전하게 해주며 CLI 환경에서도 사용이 가능하기 때문에, 만약 CLI서버 환경에서 돌아가는 크롤러라면PhantomJS를 사용하는 것도 방법이다.

       

       

      Download PhantomJS

      Download PhantomJS New to PhantomJS? Read and study the Quick Start guide. Windows Download phantomjs-2.1.1-windows.zip (17.4 MB) and extract (unzip) the content. The executable phantomjs.exe is ready to use. Note: For this static build, the binary is self

      phantomjs.org

       

      Binary 자체로 제공되기 때문에, Linux를 제외한 OS에서는 외부 dependency 없이 바로 실행할 수 있다.

       

       

      Selenium 사용

      Selenium은 기본적으로 웹 자원들이 모두 로드될때까지 기다려 주지만, 암묵적으로 모든 자원이 로드 될때 까지 기다리게 하는 시간을 직접 implicitly_wait을 통해 지정할 수 있다.

       

      from selenium import webdriver
      
      driver = webdriver.Chrome(executable_path= (r'D:\workspace\Python\Python37'
      '\Python-Web-Crawler\chromedriver.exe'))
      
      driver.implicitly_wait(3)
      
      driver.get('https://www.google.com/')

       

      url에 접근하는 api

      get('http://url.com')

       

      페이지의 단일 element에 접근한는 api

      find_element_by_name('HTML_name')

      find_element_by_id('HTML_id')

      find_element_by_xpath('/html/body/some/xpath')

       

      페이지의 여러 elements 에 접근하는 api 등이 있다.

      find_element_by_css_selector('#css > div.selector')

      find_element_by_class_name('some_class_name')

      find_element_by_tag_name('h1')

       

      위 메소드들을 활용시 HTML을 브라우저에서 파싱해주기 때문에 굳이 BeautifulSoup을 사용하지 않아도 된다.

       

      하지만 Selenium에 내장된 함수만 사용가능하기 때문에 좀더 사용이 편리한 soup 객체를 이용하려면 driver.page_source API를 이용해 현재 렌더링 된 페이지의 Elements를 모두 가져올 수 있다.

       

       

      네이버 로그인 하기

      네이버는 requests를 이용해 로그인하는 것이 어렵다.

      프론트 단에서 JS처리를 통해 로그인 처리를 하기 때문인데 Selenium을 이용하면 쉽게 로그인을 할 수 있다.

      네이버 로그인 화명을 확인 해 보면 아이디를 입력받는 부분의 name 이 id, 비밀번호를 입력받는 부분의 name 이 pw인것을 알 수 있다.

       

       

      find_element_by_name을 통해 아이디/비밀번호 input 태그를 잡아주고, 값을 입력한다.

      find_element_by_xpath을 통해 Login 버튼을 눌러 로그인한다. xpath를 획득하는 방법은 아래와 같다.

       

       

      from selenium import webdriver
      
      driver = webdriver.Chrome(executable_path= (r'D:\workspace\Python\Python37'
      '\Python-Web-Crawler\chromedriver.exe'))
      # driver = webdriver.PhantomJS(executable_path=(r'D:\workspace\Python\Python37 
      # \Python-Web-Crawler\phantomjs-2.1.1-windows\bin\phantomjs.exe'))
      
      driver.implicitly_wait(3)
      
      driver.get('https://www.naver.com/')
      
      driver.find_element_by_name('id').send_keys('myId')
      driver.find_element_by_name('pw').send_keys('myPw')
      
      # 로그인 버튼을 눌러주자.
      driver.find_element_by_xpath('//*[@id="frmNIDLogin"]/fieldset/span/input').click()

       

       

      로그인이 필요한 서비스인 네이버 페이의 Url 에 접근하여 리스트를 가져오는 방법은 아래와 같다.

      from bs4 import BeautifulSoup
      from selenium import webdriver
      
      driver = webdriver.Chrome(executable_path= (r'D:\workspace\Python\Python37'
      '\Python-Web-Crawler\chromedriver.exe'))
      # driver = webdriver.PhantomJS(executable_path=(r'D:\workspace\Python\Python37 
      # \Python-Web-Crawler\phantomjs-2.1.1-windows\bin\phantomjs.exe'))
      
      driver.implicitly_wait(3)
      
      driver.get('https://www.naver.com/')
      
      driver.find_element_by_name('id').send_keys('myId')
      driver.find_element_by_name('pw').send_keys('myPw')
      
      # 로그인 버튼을 눌러주자.
      driver.find_element_by_xpath('//*[@id="frmNIDLogin"]/fieldset/span/input').click()
      
      driver.get('https://order.pay.naver.com/home')
      html = driver.page_source
      soup = BeautifulSoup(html, 'html.parser')
      notices = soup.select('div.p_inr > div.p_info > a > span')
      
      for n in notices:
      	print(n.text.strip())

       

       

       

       

      728x90
      반응형
    상단으로