크롤링(crawling)

Pycharm 설정

pycharm 터미널에서 pyenv 환경 설정

# 커맨드 창 단축키 : alt + F12
# 사이드바 닫기/열기 :cmd + 1
# 터미널에서 pyenv version을 입력해 가상환경이 적용된 터미널인지 확인

# 안될 경우, .zshrc 파일에서 path 설정
$ vi ~/.zshrc

# Preferences > Build, Excution, Deployment > Console > Python Console > Python interpreter : Python 3.4.3 virtualenv

shell에서 Pycharm 어플리케이션을 열수있게 alias 추가

# 어플 이름과 경로 확인
$ cd /Applications
$ cd /PyCharm\ CE.app

$ vi ~/.zshrc
# alias 추가
alias py="open -a /Applications/PyCharm\ CE.app/Contents/MacOS/pycharm"

# py. 입력하여 pycharm 실행

웹 크롤링을 위한 라이브러리 설치

# beautifulsoup 설치
$ pip install beautifulsoup4

# requests 설치
$ pip install requests

# lxml 설치
$ pip install lxml

프로젝트 Interpreter 설정

Preferences > project: crawling > Project Interpreter > show all > add local > 경로: /usr/loca/var/pyenv/versions/fc-python/bin/python

네이버 웹툰 페이지 크롤링

참고 사이트

Request library

Requests 모듈을 import하여 웹 페이지를 가져온다.

import requests

r = requests.get('http://comic.naver.com/webtoon/list.nhn?titleId=690502')
print(r)
# result
$ python crawl.py
<Response [200]>

url 파라미터를 딕셔너리 타입으로 넣어 웹 페이지 가져온다.

import requests

url = 'http://comic.naver.com/webtoon/list.nhn'
params = {'titleId': '690502'}

r = requests.get(url, params=params)
print(r)

print(r.url)
$ python crawl.py
<Response [200]>
http://comic.naver.com/webtoon/list.nhn?titleId=690502

파라미터 1개의 key에 두개의 value를 받을 때, 리스트를 사용한다.

# request 문서
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}

>>> r = requests.get('http://httpbin.org/get', params=payload)
>>> print(r.url)
http://httpbin.org/get?key1=value1&key2=value2&key2=value3

Beautiful Soup library

import requests
from bs4 import BeautifulSoup

# 네이버 웹툰 주소
url = 'http://comic.naver.com/webtoon/list.nhn'

# GET parameters로 전달한 값들의 dict
params = {'titleId': '690502'}

# requests.get 요청한 결과(response 객체)를 r변수에 할당
r = requests.get(url, params=params)

# response 객체(r)에서 text 메소드를 사용해 내용을 html_doc변수에 할당
html_doc = r.text
print(type(html_doc))

# BeautifulSoup 객체를 생성
soup =  BeautifulSoup(html_doc, 'lxml')

# soup =  BeautifulSoup(html_doc, 'html')
# print(soup.prettify())

# print(soup.title)
# print(soup.title.name)
# print(soup.title.string)


# 모든 a 태그 반환
# find_all : 요소 찾기
# a_list = print(soup.find_all('a'))
#
# for a in a_list:
#     # print(a)
#     print(a.get('href'))

# 제목만 가져오기
# td_list = soup.find_all('td', class_='title')
# for td in td_list:
#     # print(td.get_text())
#     # 공백제거를 위해 strip() 사용
#     print('{}'.format(td.get_text().strip()))
1. 코드 분리
# 패키지 생성
# __init__.py 파일이 있으면 패키지
craling > new > python package > __init__.py 파일 자동생성

# 프로젝트 제일 상위 폴더를 source로 변경
crawling > mark directory as > Sources root

2. 단축키
# 코드 folding 단추키 : shift + cmd + +/-
Refactor > Rename : shift + F6

3. 정규 표현식
import re
# . : 임의 문자 아무거나
# * : 앞의 패턴이 0에서 무한대까지 반복
# no= : 찾을 문자(직전에서 멈춤)
# \d : 숫자
# \d+ : 1회 이상의 \d
# () : 그룹으로 지정
# [?&] : ? or &

# 아무 문자열이나 반복하다 ?no= 또는 &no= 이후의 숫자와
p = re.compile(r'.*[?&]no=(\d+).*')
# href와 p를 비교함
m = re.match(p, href)

#  링크주소에서 정규표현식의 패턴이 매치되었을 때
if m:
    # 매치된 숫자를 recent_episode_number에 할당하고 반복문 종료
    recent_episode_number = m.group(1)
    break

crawling 코드

모듈과 패키지

모듈

파이썬 파일 하나가 하나의 모듈로 취급한다. (모듈 단위로 클로저를 가짐 > 전역 변수 환경)

# shop.py
def buy_item():
    print('Buy item!')

buy_item()
# game.py
def play_game():
    print('Play game!')

play_game()
# lol.py
import game
import shop

print('= Turn on game =')
game.play_game()
shop.buy_item()

__name__변수

game과 shop이 import되는 순간 해당 코드가 실행되어 버리는 문제가 있다.

각 모듈은 자신의 이름을 가지며, 모듈 이름은 모듈의 전역변수 __name__에서 확인 할 수 있다. 파이썬 인터프리터가 실행한 모듈의 경우, __main__이라는 이름을 가진다. 따라서 아래와 같이 처리해준다.

# shop.py
def buy_item():
    print('Buy item!')

if __name__ == '__main__':
    buy_item()

네임스페이스 (Namespace)

각 모듈은 독립된 네임스페이스(이름공간)를 가진다. 메인으로 사용되고 있는 모듈이 아닌 import된 모듈의 경우, 해당 모듈의 네임스페이스를 사용해 모듈 내부의 데이터에 접근한다.

from을 사용해 모듈의 함수를 직접 import

import 모듈명의 경우, 모듈의 이름이 전역 네임스페이스에 등록되어 모듈명.함수로 사용가능하다.

모듈명을 생략하고 모듈 내부의 함수를 쓰고 싶다면, from 모듈명 import 함수명으로 불러들일 수 있다.

from 모듈명 * 을 사용해 모듈 내 모든 식별자 (변수, 함수) import

from 모듈명 import 또는 import 모듈명에서, 같은 모듈명이 존재하거나 혼동 될 수 있을 경우, 뒤에 as를 붙여 사용할 모듈명을 변경할 수 있다.

패키지

패키지는 모듈들을 모아 둔 특별한 폴더를 뜻한다. (일반적인 폴더와는 다르다)

폴더를 패키지로 만들면 계층 구조를 가질 수 있으며, 모듈들을 해당 패키지에 모을 수 있는 역할을 한다.

패키지를 만들 때는 패키지로 사용할 폴더에 __init__.py파일(패키지 초기화 파일)을 넣어주면, 해당 폴더는 패키지로 취급된다. (비어있어도 무관하다)

shop.pygame.py를 func패키지에 넣어본다.

├── func
│   ├── __init__.py
│   ├── game.py
│   └── shop.py
└── lol.py

패키지는 모듈과 동일하게 import할 수 있다. 위와 같은 경우에는 from func import game, shop으로 기존 코드의 변경 없이 패키지에서 모듈을 가져오는 방식을 사용할 수 있다.

또는 단순히 import func후 func.game, func.shop을 사용하는 방식도 가능하다.

*, __all__

패키지에 포함된 하위 패키지 및 모듈을 불러올 때, *을 사용하면 해당 모듈의 모든 식별자들을 불러온다.

이 때, 각 모듈에서 자신이 import될 때 불러와질 목록을 지정하고자 한다면 __all__ 을 정의하면 된다.

# comment.py
__all__ = (
    'Comment',
)

패키지 자체를 import시에 자동으로 가져오고 싶은 목록이 있다면, 패키지의 __init__.py파일에 해당 항목을 import해주면 된다.

# __init__.py
from .comment import *
from .post import *

실습

  1. 위 프로그램에 friends패키지를 만들고, send_message함수를 가진 chat모듈을 추가한다. send_messageinput을 이용해 2개의 인자를 받으며(친구명, 메세지), 실행 시 print함수를 통해 메세지를 보냈다는 문구를 출력한다.
  2. 프로그램을 실행했을 때, 3을 입력하면 send_message함수를 실행하도록 한다.
  3. 커맨드라인으로 인자를 전달받아, 모두 더한 결과를 출력하는 모듈을 만든다. 이 때, 인터프리터를 통해 실행되었을 때만 결과가 출력되도록 한다.
  4. 위 3번의 프로그램에서, 정수형 데이터로 변환할 수없는 인자값이 올 경우 ‘올바른 값이 주어지지 않았습니다’ 를 출력하며 종료되도록 프로그램을 리펙토링해본다. 내장함수 isintance를 사용한다.

코드