반응형

** 웹 요청 시 필요한 데이터

=>url: 주소

=>parameter: 서버에게 넘겨주는 데이터 - dict로 만들어서 부착

=>header: 서버에게 넘겨주는 데이터(보이지 않습니다.) - dict로 만들어서 부착

=>method: 전송방식(GET, POST, DELETE, PUT)

 

**requests 패키지

=>웹에서 데이터를 가져올 때 많이 사용하는 패키지 - 정적인 데이터만 가져올 수 있습니다.

ajax를 이용하지 않는 데이터나 프레임을 사용하지 않는 데이터만 가져올 수 있습니다.

=>이 패키지는 python의 기본 패키지가 아닙니다.

=>pip install requests 로 설치

 

1.데이터를 가져오기 위한 구문

requests.전송방식(url)을 호출하면 Response 객체가 리턴됩니다.

 

2.Response 클래스

status_code 속성: 상태 정보

text: 가져온 데이터를 문자열로 저장

content: 가져온 데이터를 바이트 배열로 저장 - 이미지나 음성 데이터

 

3.parameter heade가 존재하는 경우

requests.전송방식(url, data={파라미터}, headers={헤더})

 

4.실습

http://httpbin.org/get 사이트에 get 방식 요청

get put delete 방식으로 요청

post는 파라미터를 설정해서 요청

Kakao Open API 데이터를 가져오기

 

1)requests 설치 - anaconda를 설치했으면 할 필요가 없습니다.

pip install requests

 

2)get 요청을 위한 코드를 작성

import requests

#requests가 가지고 있는 것들을 현재 커널에 requests 라는 이름으로 가져옴

 

#get 요청을 해서 텍스트를 확인

resp = requests.get('http://httpbin.org/get')

 

#help(requests.get) #함수의 도움말 확인

 

#print(type(resp)) #리턴된 결과의 자료형을 확인

#print(dir(requests.models.Response)) #Response 자료형은 어떤 속성들을 가졌는지 확인

 

print(resp.text) #가져온 문자열을 확인

 

3)put delete 요청을 수행

=>서버에서 지원을 해야 가능

#put delete 요청

 

4)post 방식으로 요청하는 파라미터와 함께 전송

#help(requests.post)

#requests 모듈을 사용하면 파라미터 인코딩을 할 필요가 없습니다.

 

 

5)Open API를 사용하다 보면 header cookie를 이용해야 하는 경우가 있습니다.

headers 옵션이나 cookies 옵션에 dict 형태로 만들어서 추가해주면 됩니다.

 

 

#Kakao Open API 도서 검색 사용

addr = 'https://dapi.kakao.com/v3/search/book'

params = {'query':'삼국지', 'target':'title'}

headers = {'Authorization': ''}

resp = requests.get(addr, params=params, headers=headers)

print(resp.text)

 

6)읽어온 텍스트가 현재 기본 인코딩과 맞지 않아서 텍스트가 깨지는 경우

=>받아온 Response 객체의 encoding 속성에 None을 대입해서 인코딩을 유추하게 만들거나 직접 문자열로 인코딩을 설정하면 됩니다.

utf-8: 전 세계 모든 글자를 인코딩하는 방식 - 3byte 1글자

euc-kr: 예전에 웹에서 한글만을 표현하기 위한 인코딩 방식

cp949(ms949): windows에서 한글만을 표현하기 위한 인코딩 방식

 

cp949 euc-kr은 거의 비슷합니다.

 

iso-8859-1: iso-latin-1 이라고도 하는데 서유럽 문자 표시를 위한 인코딩 방식 - 한글 안됨

 

5.이미지 파일 다운로드

=>Response text 속성 대신에 content 속성을 사용

=>파일 객체를 생성해서 write content 속성의 값을 기록하면 파일로 생성

 

#이미지를 다운로드 받아서 파일로 만들기

 

#이미지를 가져올 URL을 생성

imgurl = 'http://www.onlifezone.com/files/attach/images/962811/376/321/005/2.jpg'

 

resp = requests.get(imgurl)

 

#파일 객체를 만들고 이름은 f로 사용 - close를 할 필요가 없습니다.

with open('2.jpg', 'wb') as f:

    f.write(resp.content)

   

#현재 작업디렉토리 확인

import os

print(os.getcwd())

   

**JSON Parsing

1.JSON

=>속성과 값의 쌍으로 이루어진 데이터 오브젝트를 전달하기 위해 텍스트를 사용하는 개방형 표준 포맷

=>javascript python에서 객체(dictionary) 와 배열(list)의 표현 방법을 가져와서 사용

=>최근의 프로그래밍 언어들은 내장 또는 별도의 라이브러리를 이용해서 이 데이터를 객체로 변환해서 사용합니다.

javascript python은 내장

=>최근의 Open API 그리고 스마트 기기 들의 통신은 대부분 JSON 포맷을 사용

=>[ ]: list, { }: dict

 

2.Python에서의 JSON 파싱

json.loads(json 문자열)

=>문자열을 python 객체로 변환해 줍니다.

 

3.실습

=>카카오 카테고리 검색을 이용해서 음식점 이름과 주소를 list로 만들어서 출력

 

headers = {'Authorization': ''}

 

#웹에서 정적인 데이터를 가져오기 위한 라이브러리를 import

import requests

 

#다운로드 받을 URL 만들기

 

 

#header 만들기

headers = {'Authorization': ''}

 

#데이터 가져와서 출력

resp = requests.get(addr, headers=headers)

text = resp.text

#print(text)

 

#json 파싱 모듈을 가져오기 - 내장 모듈

import json

jsondata = json.loads(text)

#print(type(jsondata)) #맨 처음 시작이 {} 이므로 dict

 

#print(jsondata['documents']) #documents 키의 데이터 가져오기 - list

li = []

#가져온 배열을 행 단위로 읽어서

for imsi in jsondata['documents']:

    #print(imsi['address_name'], ':', imsi['place_name'])

    #place_name address_name을 가지고 dict를 생성

    d = {'장소':imsi['place_name'], '주소':imsi['address_name']}

    #dict list에 추가

    li.append(d)

 

#확인

for temp in li:

    print(temp)

 

 

**HTML Parsing

=>open api가 제공되면 json 이나 xml 형태의 데이터를 받아서 파싱해서 사용하면 됩니다.

=>web site에 보여는 주지만 open api 형태로 제공되지 않는 데이터의 경우 html을 수집해서 파싱을 해서 원하는 데이터를 가져오게 됩니다.

html을 무단 수집하는 것은 법적으로 문제가 되는 경우가 있습니다.

 

1.사용되는 패키지: beatifulsoup4

=>이 패키지는 python에는 없고 anaconda에는 존재

=>이 패키지의 bs4 라는 모듈을 이용

=>bs4.BeautifulSoup(html 텍스트, 'html.parser') 를 호출하면 BeautifulSoap 객체를 리턴

이 객체는 html의 내용을 편리하게 찾을 수 있도록 메모리 트리 형태로 펼쳐놓은 객체 입니다.

 

2.태그로 찾는 경우는 find  findall 이용

find(tag, attributes, recursive, text, limit, keyword)

=>tag가 찾고자 하는 태그

=>attributes 는 태그 안에 속성이 있는 경우 속성명=값 의 형태로 대입해서 속성의 값까지 일치하는 것을 찾습니다.

 

<div id='1'></div>

<div id='2' align='center'></div>

<span id='3'></span>

 

div 다 찾기: find('div')

id 2번인 div 찾기: find('div', align='center')

=>find_all은 사용법은 같은데 find 1개만 찾고 find_all 은 전부 찾아옵니다.

 

3.선택자로 찾는 경우는 select()

=>Tag list로 리턴

 

4.찾은 데이터에서 정보를 추출

1)태그 안의 내용을 가져오기: getText()

2)속성의 값을 가져오기:get('속성명')

 

5.실습1 : https://ko.wikipedia.org/wiki/%EA%B8%B0%EA%B3%84_%ED%95%99%EC%8A%B5

에서 하이퍼링크 태그의 url과 텍스트를 수집

하이퍼링크가 /wiki/로 시작하는 것만 출력

 

 

#위키피디아에서 하이퍼링크(a) 태그의 내용 가져오기

import requests

 

addr = 'https://ko.wikipedia.org/wiki/%EA%B8%B0%EA%B3%84_%ED%95%99%EC%8A%B5'

resp = requests.get(addr)

#print(resp.text)

 

#html 파싱을 위한 라이브러리 가져오기

import bs4

 

#DOM 객체로 만들기

bs = bs4.BeautifulSoup(resp.text, 'html.parser')

#print(type(bs))

 

#a 태그의 내용 가져오기

li = bs.find_all('a')

#print(li)

 

#정규식(문자열 패턴을 조회하기 위한 식) 모듈 import

import re

for temp in li:

    #print(temp.getText()) #태그 안의 내용 가져오기

    #print(temp.attrs['href']) #key 에러 - href가 없는 경우도 있음

    if 'href' in temp.attrs: #href 속성이 temp.attrs에 있는 경우에만

        #href 속성에 /wiki/로 시작하는 링크의 텍스트와 링크만 출력

        href = temp.attrs['href']

        #정규식 생성 - /wiki/ 로 시작하는

        p = re.compile('^(/wiki/)')

        if p.search(href) != None:

            print(temp.getText(), ':', href)

   

6.실습2: https://tv.naver.com/에서 상위 항목의 제목과 링크 가져오기

=>선택자 이용

=>선택자를 찾는 방법은 소스 보기를 해서 직접 찾아도 되고 chrome의 검사 기능에서 필요한 부분을 선택하고 copy selector를 이용해서 찾아도 됩니다.

 

#네이버 tv 팟에서 하이퍼링크(a) 태그의 내용 가져오기

import requests

 

addr = 'https://tv.naver.com/'

resp = requests.get(addr)

#print(resp.text)

 

#html 파싱을 위한 라이브러리 가져오기

import bs4

 

#DOM 객체로 만들기

bs = bs4.BeautifulSoup(resp.text, 'html.parser')

#선택자를 이용해서 선택

tvlist = bs.select('dl > dt > a > tooltip')

for temp in tvlist:

    print(temp.getText())

 

7.태그 속성

태그(중복 가능 - 웹 브라우저에서의 모양) : 선택자로 사용할 때는 tag이름

클래스(중복 가능 - 태그들을 그룹화하기 위한 속성) : 선택자로 사용할 때는 .class이름

아이디(중복 불가능 - 태그를 다른 것과 구별하기 위한 속성) : 선택자로 사용할 때는 #id

 

name(중복 가능 - 서버에게 전송할 때 사용할 파라미터 이름)

xpath(중복 불가능 - 브라우저가 태그를 구분하기 위해서 사용하는 경로)

 

8.실습3

http://www.weather.go.kr/weather/observation/currentweather.jsp 에서 도시이름과 온도, 습도를 가져와서 꺽은선 그래프로 출력

=>찾고자 하는 데이터는 class 이름이 table_develop3

 

#오늘 날씨를 가져와서 그래프 그리기

import requests

 

#필요한 html 가져오기

resp = requests.get('http://www.weather.go.kr/weather/observation/currentweather.jsp')

text = resp.text

#print(text)

 

import bs4

bs = bs4.BeautifulSoup(text, 'html.parser')

#class 속성의 값이 table_develop3 인 데이터 찾아오기

table = bs.select('.table_develop3')

#print(table)

 

#도시이름, 온도, 습도를 저장할 list를 생성

loc = []

temp = []

hum = []

 

#가져온 테이블 중에서 첫번째 테이블로부터 줄(tr) 단위로 읽기

for tr in table[0].find_all('tr'):

    tds = tr.find_all('td') #tr 태그에서 td를 전부 찾아서 tds에 대입

    for td in tds:

        if td.find('a'): #a 태그가 있으면 도시이름이 있는 행입니다.

            #도시이름을 loc 추가

            loc.append(td.find('a').text)

            #온도를 temp에 추가

            temp.append(tds[5].text)

            #습도를 temp에 추가

            hum.append(tds[10].text)

 

#데이터 확인

#인덱싱은 번호1개만 작성해서 하나의 데이터만 추출

print(loc[0:5]) #0-4 번행 까지 확인 - 슬라이싱(범위를 가지고 추출)

print(temp[0:5])

print(hum[0:5])

 

#필요한 도시의 데이터만 추출

cities = ['서울', '부산', '인천', '대구', '대전', '광주', '울산', '창원', '흑산도']

#위 도시들의 온도와 습도를 저장할 list

temperatures = []

humidities = []

 

#cities에 있는 데이터만 추출해서 저장

for city in cities:

    j = 0

    for c in loc:

        if c == city:

            temperatures.append(temp[j])

            humidities.append(hum[j])

            break

        j = j + 1

 

print(cities)

print(temperatures)

print(humidities)

 

#파이썬에서 시각화를 하는 기본 패키지

import matplotlib.pyplot as plt

#한글 폰트 사용을 위한 패키지

from matplotlib import font_manager, rc, rcParams

import platform #운영체제 확인을 위한 패키지

 

#주피터 노트북에서 그래프를 셀 안에 출력해주는 설정

%matplotlib inline

 

#음수를 제대로 표현하기 위한 설정

rcParams['axes.unicode_minus'] = False #이 설정을 하지 않으면 음수가 네모로 출력

 

#한글 폰트 설정

if platform.system() == 'Windows': #윈도우즈라면

    font_name = font_manager.FontProperties(fname='c:/windows/Fonts/ahn_b.ttf').get_name()

    rc('font', family=font_name)

elif platform.system() == 'Darwin': #매킨토시라면

    rc('font', family='AppleGothic')

else:

    print('알 수 없는 운영체제')

   

   

#그래프 그리기

plt.figure(figsize=(12,4), dpi=300) #그래프 크기 설정 - 가로 12inch 세로 4인치

#꺽은선 그래프 - plot

plt.plot(temperatures, label='온도', lw=3, color='r', linestyle='-', marker='s')

#plt.plot(humidities, label='습도', lw=3, color='g', linestyle='-', marker='s')

 

#x

plt.xticks(range(0,len(cities),1), cities, rotation='vertical')

#범례

plt.legend()

plt.savefig('graph.png', dpi=300)

#출력

plt.show()

 

**XML 파싱

=>XML: 데이터를 태그 형식으로 표현하는 표준 데이터 포맷

=>예전에 거의 모든 데이터 포맷이 XML

지금은 우리나라 공공기관 Open API 와 실시간 뉴스를 전송해주는 RSS 그리고 프로젝트 설정 파일에 주로 이용

=>BeautifulSoup xml 패키지를 이용해서 파싱 가능

=>BeautifulSoup를 사용하는 경우 html.parser 대신에 lxml-xml 을 사용하면 됩니다.

 

http://www.hani.co.kr/rss/ 에서 item 태그 안의 title 만 전부 조회

 

#XML 파싱

#http://www.hani.co.kr/rss/ 에서 item 태그의 title만 추출

 

#데이터 가져오기

import requests

resp = requests.get('http://www.hani.co.kr/rss/')

text = resp.text

#print(text)

 

import bs4

#트리로 펼쳐내기

bs = bs4.BeautifulSoup(text, 'lxml-xml')

 

items = bs.find_all('item')

 

for item in items:

    title = item.find('title')

    print(title.getText())

 

 

**Selenium

=>web에서 동적으로 생성되는 데이터는 requests 모듈을 가지고 가져올 수 없습니다.

requests 모듈은 처음 접속했을 때 출력되는 데이터만 가져옵니다.

ajax 처럼 비동기적으로 데이터를 가져오거나 로그인을 한 후 보여지는 데이터들은 requests 모듈로는 가져올 수 없습니다.

이처럼 동적으로 만들어지는 데이터는 브라우저를 직접 실행시켜서 동작시키는 Selenium 이라는 웹 앱 테스트 프레임워크를 이용해야 합니다.

=>웹 애플리케이션을 테스트하기 위한 프레임워크

=>기본 패키지가 아니므로 설치를 해야 합니다.

pip install selenium

 

1.브라우저 동작

=>동작시킬 브라우저의 드라이버를 다운로드

=>브라우저를 동작시키지 않고 가져오고자 하는 경우에는 pantom.js를 이용 - Deprecated

=>크롬을 이용할 것이라서 크롬 드라이버를 다운로드

1)chrome 에서 크롬 정보 확인 - chrome 79 버전

 

2)https://chromedriver.chromium.org/downloads 에서 자신의 버전에 맞는 드라이버를 다운로드

=>압축을 해제하고 exe 파일을 찾기 쉬운곳에 배치 : c:/chromedriver.exe

 

3)브라우저 실행

selenium.webdriver.Chrome("크롬 드라이버 경로")

 

4)실습

#크롬 실행하기

from selenium import webdriver

driver = webdriver.Chrome('c:/chromedriver')

 

2.webdriver 의 메소드

=>implicitly_wait(시간): 초 단위 대기 - 여러 사이트에 데이터를 크롤링 하는 경우 또는 첫 화면에 ajax로 데이터를 호출하는 경우 일정시간 대기시켜서 데이터를 읽어옵니다.

 

=>get(url): 브라우저가 url에 접속

 

=>페이지 내의 element에 접근하는 메소드

find_element_by_name(name을 가지고 접근)

find_element_by_id(id를 가지고 접근)

find_element_by_xpath(xpath를 가지고 접근)

 

find_element_by_css_selector(선택자를 가지고 접근)

find_element_by_class_name(class name을 가지고 접근)

find_element_by_tag_name(tag를 가지고 접근)

 

=>element를 찾으면 입력도구 인 경우는 send_keys() 을 호출하면 값을 입력

=>click()을 호출하면 클릭한 효과

 

앞의 3개는 1개의 데이터를 리턴하고 뒤의 3개는 list 형태로 리턴

 

=>quit(): 브라우저 종료

 

=>execute_script(자바스크립트 코드): 자바스크립트 코드 수행

 

=>page_source 속성: html 가져오기

3.실습

1)실습1. daum 카페 페이지에 접근

 

#다음 카페 목록에 접근하기

#크롬 실행하기

from selenium import webdriver

driver = webdriver.Chrome('c:/chromedriver')

#3초대기

driver.implicitly_wait(3)

 

#다음 카페 목록에 접근 - 로그인 되어 있지 않으면 로그인 페이지로 이동

driver.get('url 주소')

 

2)다음 웹 사이트에 로그인을 한 후에 카페 목록 페이지로 이동

다음 로그인 페이지: url주소

 

#다음에 로그인을 하고 카페 목록으로 이동

from selenium import webdriver

driver = webdriver.Chrome('c:/chromedriver')

#3초대기

driver.implicitly_wait(3)

 

#다음 로그인 페이지로 이동

driver.get('url 주소')

 

#아이디와 비밀번호 입력받기

username = input('아이디를 입력하세요')

userpw = input('비밀번호를 입력하세요')

 

#다음 로그인 페이지에 아이디와 비밀번호 입력

#driver.find_element_by_id('id').send_keys(username)

driver.find_element_by_xpath('//*[@id="id"]').send_keys(username)

driver.find_element_by_xpath('//*[@id="inputPwd"]').send_keys(userpw)

 

#로그인 버튼을 클릭한 후 카페 목록 페이지로 이동

driver.find_element_by_xpath('//*[@id="loginBtn"]').click()

driver.find_element_by_xpath('//*[@id="loginBtn"]').click()

 

#여러 페이지를 이동할 때는 중간에 잠시 대기 시간을 주어야 할 필요가 있습니다.

import time

time.sleep(3) #3초 대기

 

#카페 목록으로 이동

driver.get('http://top.cafe.daum.net/_c21_/my_cafe')

 

#접속한 사이트의 html 가져오기

html = driver.page_source

 

4.frame

=>frame html 페이지 내에서 다른 html 페이지의 내용을 출력하기 위해 사용하는 객체

=>스크래핑을 하다보면 화면에 출력이 되고 페이스 소스에서도 확인이 되는데 접근이 안되는 경우가 있습니다.

이런 경우는 ajax로 나중에 불려졌거나 아니면 프레임을 이용해서 다른 파일에서 출력한 경우입니다.

ajax 의 경우는 딜레이를 주고 데이터를 읽으면 가능합니다.

프레임의 경우는 프레임에 직접 접근해서 데이터를 읽어야 합니다.

 

1)현재 페이지에서 frame 조회

변수 = driver.find_element_by_css_selector('iframe')

for 임시변수 in 변수:

           print(임시변수.get_attribute('name')

 

=>현재 페이지의 모든 iframe의 이름을 조회

 

2)프레임 이동

=>driver.switch_to_frame('프레임이름') : 프레임이름에 해당하는 곳으로 이동

=>driver.switch_to_default_content() :  상위 프레임으로 전환

 

5. 프레임 사용 실습

=>이전 것에 이어서 작성

 

#카페 목록 중에서 첫번째 페이지로 이동

#driver.find_element_by_xpath('//*[@id="mArticle"]/div/div[1]/div/div[2]/ul/li/a/div[1]/div[2]/div/div/strong').click()

 

#직접 카페로 이동 - 카페 글쓰기 페이지로 이동

driver.get('http://cafe.11111daum.net/samhak7/_memo')

 

#특정 프레임으로 이동

driver.switch_to_frame('down')

 

#글작성 란에 텍스트를 입력

driver.find_element_by_xpath('//*[@id="memoForm"]/div/table/tbody/tr[1]/td[1]/div/textarea').send_keys('파이썬에서의 매크로')

driver.find_element_by_xpath('//*[@id="memoForm"]/div/table/tbody/tr[1]/td[2]/a[1]/span[2]').click()

 

6.크롬을 화면에서 출력시키지 않고 데이터 가져오기

=>웹 드라이버를 옵션을 추가해서 생성

#크롬을 화면에 출력하지 않고 실행하기

options = webdriver.ChromeOptions()

options.add_argument('headless')

options.add_argument('window-size=1920x1080')

options.add_argument("disable-gpu")

# 혹은 options.add_argument("--disable-gpu")

 

driver = webdriver.Chrome('c:/chromedriver', chrome_options=options)

 

 

7.20번 스크롤 한 데이터 가져오기

#20번 스크롤 하기

from selenium import webdriver

import time

from selenium.webdriver.common.keys import Keys

import bs4

 

driver = webdriver.Chrome('c:/chromedriver')

 

driver.get('')

time.sleep(5)

 

body = driver.find_element_by_tag_name('body')#스크롤하기 위해 소스 추출

num_of_pagedowns = 20

#10번 밑으로 내리는 것

while num_of_pagedowns:

    body.send_keys(Keys.PAGE_DOWN)

    time.sleep(2)

    num_of_pagedowns -= 1

   

html = bs4.BeautifulSoup(driver.page_source,'html.parser')

print(html)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'Study > 데이터 분석' 카테고리의 다른 글

데이터분석-6  (0) 2020.11.10
데이터분석-5  (0) 2020.11.09
데이터분석-4  (0) 2020.11.08
데이터분석-3  (0) 2020.11.08
데이터 분석-1  (0) 2020.11.08

+ Recent posts