혼공학습단 13기

[혼공분석] 2주차: 데이터 수집하기

zer0zero 2025. 1. 19. 16:04

 

 

1주차 우수 혼공족이 되는 기쁨을 담아 2주차 공부 시작 ~

주변에.. 메머드 익스프레스가 없어.. 꿀커피를 마시며 하지는 못하지만..3주차에 꼭..!! 가서 먹으리.. 기다려라 꿀커피..

우수 혼공족이 되니 더 열심히 해야겠다는 생각만,, 아자아자 !!

 

Chapter 02. 데이터 수집하기

02-1. API 사용하기


API: 두 프로그램이 서로 대화하기 위한 방법을 정의한 것

 → 인증된 URL만 있으면 언제든지 필요한 데이터에 편리하게 접근할 수 있는 방식

 → 가장 편리하게 데이터를 수집할 수 있는 방식

 

HTTP: 인터넷에서 웹 페이지를 전송하는 기본 통신 방법

 → 웹 서버 플그램과 웹 브라우저 간의 통신에서 사용하는 프로토콜

 *프로토콜: 통신 규약

 

HTML: 웹 브라우저가 화면에 표시할 수 있는 문서의 한 종류 , 웹 페이지를 위한 표준 언어(마크업 언어)

 * 마크업 언어: 문서가 화면에 표시되는 형식을 나타내거나 데이터의 논리적인 구조를 명시하기 위한 규칙을 정의한 언어의 일종 _나무위키

 * 태크: HTML 명령어의 집합

 

웹 기반 API: HTTP 프로토콜을 사용해 만든 API 

 → CSV, JSON, XML 형태로 데이터 전달 (HTML 구조보다 비교적 단순)

 

 

JSON: 자바스크립트를 기반으로 한 범용적인 포맷

 → 대부분의 프로그래밍 언어는 JSON 형태의 텍스트 읽고 쓸 수 있음

 → 웹 기반 API는 전송하려는 파이썬 객체를 JSON 문자열로 변환

 → JSON 문자열을 파이썬 프로그램에 사용하기 위해 파이썬 딕셔너리로 변환

 → 키와 값을 콜론으로 연결 (파이썬 딕셔너리와 아주 유사한 형태)

 *파이썬 딕셔너리: 중괄호 안에 키: 값 형식으로 콤마로 구분하여 저장

d = {"name": "혼자 공부하는 데이터 분석"} #키:name, 값:혼자 공부하는 데이터 분석
print(d['name'])

 

데이터를 전송하기 위해 문자열로 변환하는 과정이 왜 필요한가?

 → 웹 기반 API가 사용하는 HTTP 프로토콜은 텍스트 기반

 → HTTP 프로토콜로 데이터 전송 시 텍스트로 변환 필요

 

json.dumps( ) 함수로 파이썬 객체를 JSON 문자열로 변환하기

* 직렬화: 프로그램 상의 객체를 저장하거나 읽을 수 있는 형태로 변환하는 것

import json

d_str = json.dumps(d, ensure_ascii=False)
print(d_srt)

json.dumps( ) 함수는 아스키 문자 외의 다른 문자를 16진수로 출력

 → 한글이 제대로 출력되지 않음

 → ensure_ascii=False 로 지정하여 원래 저장된 문자 그대로 출력하게 하여 해결

 

print(type(d_str)) # type( )함수: 데이터 타입 확인

문자열 확인

 

 

 

json.loads( ) 함수로 JSON 문자열을 파이썬 객체로 변환하기

* 역직렬화: 직렬화된 정보를 다시 프로그램에서 실행 가능한 객체로 변환하는 것

d2 = json.loads(d_str)
print(d2['name'])

 

print(type(d2))

딕셔너리 확인

 

 

json.loads( )함수에 json 문자열 직접 전달

d3 = json.loads('{"name": "혼자 공부하는 데이터 분석", "author": "박해선", "year": 2022}')

print(d3['name'])
print(d3['author'])
print(d3['year'])

 

json.loads( )함수에 리스트를 포함한 json 문자열 직접 전달

d3 = json.loads('{"name": "혼자 공부하는 데이터 분석", "author": ["박해선", "홍길동"], "year": 2022}')

print(d3['author'][1])

 

json.loads( )함수에  json 배열 전달

d4_str = """ # 세겹따옴표로 긴 문자열 줄바꿈하여 입력 가능
[
{"name": "혼자 공부하는 데이터 분석", "author": "박해선", "year": 2022},
{"name": "혼자 공부하는 머신러닝+딥러닝", "author": "박해선", "year": 2020}
]
"""

d4 = json.loads(d4_str)
print(d4[0]['name'])

 

 

read_json( ) 함수로 JSON 문자열을 데이터프레임으로 변환하기

import pandas as pd
pd.read_json(d4_str)

pd.DataFrame(d4)

 

 

XML: 컴퓨터와 사람이 모두 읽고 쓰기 편한 문서 포맷을 위해 고안

 → HTML은 웹 페이지를 표현하는 데는 뛰어나지만, 구조적이지 못해 API에서는 적절하지 않음

 → 엘리먼트들이 계층 구조를 이루며 정보 표현

 → 엘리먼트의 시작과 끝은 태그로 나타냄

 

 

fromstring( ) 함수로 XML 문자열을 파이썬 객체로 변환하기

x_str = """
<book> #부모 엘리먼트, 부모 노드
    <name>혼자 공부하는 데이터 분석</name> #자식 엘리먼트
    <author>박해선</author>
    <year>2022</year>
</book>
"""
import xml.etree.ElementTree as et
book = et.fromstring(x_str)
print(type(book))

단순 파이썬 객체가 아닌 ElemenTree 모듈 아래 정의된 Element 클래스 객체

print(book.tag) #엘리먼트 이름 확인

 

 

findtext( ) 함수로 자식 엘리먼트 확인하기

해당하는 자식 엘리먼트를 탐색하여 자동으로 텍스트를 반환

 → 자식 엘리먼트의 순서에 상관없이 탐색하여 반환하기 때문에 안전

name = book.findtext('name')
author = book.findtext('author')
year = book.findtext('year')

print(name)
print(author)
print(year)

 

x2_str = """
<books>
    <book>
        <name>혼자 공부하는 데이터 분석</name>
        <author>박해선</author>
        <year>2022</year>
    </book>
    <book>
        <name>혼자 공부하는 머신러닝+딥러닝</name>
        <author>박해선</author>
        <year>2020</year>
    </book>
</books>
"""

#파이썬 객체로 변환
books = et.fromstring(x2_str)
print(books.tag)

#findall( ) 메서드와 for 문을 함께 사용하여 동일한 이름을 가진 여러 개의 자식 엘리먼트 탐색
for book in books.findall('book'):
    name = book.findtext('name')
    author = book.findtext('author')
    year = book.findtext('year')

    print(name)
    print(author)
    print(year)
    print()

 

 

 

read_xml( ) 함수로 XML을 데이터프레임으로 변환하기

*판다스 1.3.0 버전부터 지원

pd.read_xml(x2_str)

 

API 호출하는 URL 작성

호출 URL과 파라미터 - ? 문자로 연결

파라미터와 값 - = 문자로 연결

파라미터와 파라미터 - & 문자로 연결

 → HTTP GET 방식

 

파라미터format: 미지정 시, XML 문서로 반환startDt: 검색 시작 일자endDt: 검색 종료 일자age: 연령대authKey: 인증키

 

http://data4library.kr/api/loanItemSrch?format=json&startDt=2021-04-01&endDt=2021-04-30&age=20&authKey=인증키

→ 호출 주소에서 2021년 4월 1일 부터 2021년 4월 30일까지 20대의 대출 도서 조회

* 쿼리 스트링: ? 문자 뒤 연결된 파마리터와 값들

 

도서관 정보나루 인증키 승인대기중이라 승인완료 후 실습 재개 예정.. ㅠ

 

 

request 패키지API 호출하기

import requests

url = "http://data4library.kr/api/loanItemSrch?format=json&startDt=2021-04-01&endDt=2021-04-30&age=20&authKey=인증키"
r = requests.get(url)

data = r.json()
print(data)
data
books = []
for d in data['response']['docs']:
	books.append(d['doc'])
    
books
books_df = pd.DataFramse(books)
books_df
books_df.to_json('20s_best_book.json') #json 파일로 저장

 

 

 

02-2. 웹 스크래핑 사용하기


웹 스크래핑(웹 크롤링): 웹 상트의 페이지를 옮겨 가면서 데이터를 추출하는 작업

! 웹 사이트에서 스크래핑을 허락했는지 확인하기

! HTML 태그를 특정할 수 있는지 확인하기

 

검색 결과 페이지 가져오기

import gdown
gdown.download('https://bit.ly/3q9SZix', '20s_best_book.json', quiet=False)
import pandas as pd
books_df = pd.read_json('20s_best_book.json')
books_df.head()
books = books_df[['no', 'ranking', 'bookname', 'authors', 'publisher', 
		'publication_year', 'isbn13']]
books.head()

 

 

loc 메서드데이터프레임 행과 열 선택하기

books_df.loc[[0, 1], ['bookname', 'authors']] # 첫 번째, 두 번째 행의 도서명과 저자 추출
books_df.iloc[[0, 1], [2, 3]] # iloc 메서드는 인덱스의 위치 사용
books_df.loc[0:1, 'bookname':'authors'] # 슬라이스 연산자 이용하여 슬라이싱
books = book_df.loc[:, 'no':'isnb13'] # 전체 행과 'no' 열에서 'isnb13' 열까지
books.head()

 

 

 

requests.get( ) 함수로 검색 결과 페이지 HTML 가져오기

import requests

isbn = 9791190090018 # '우리가 빛의 속도로 갈 수 없다면' ISBN
url = 'http://www.yes24.com/Product/Search?domain=BOOK&query={}'
r = requests.get(url.format(isbn))

url 중괄호에 isbn 값

 

 

print(r.text) #requests.get() 함수가 반환한 응답 객체 사용해 도서 검색 결과 페이지 HTML 출력

 

 

 

뷰티플수프 패키지로 HTML에서 데이터 추출하기

from bs4 import BeautifulSoup

soup = BeautifulSoup(r.text, 'html.parser')

BeautifulSoup(파싱할 HTML 문서, 파서)

파서: 입력 데이터를 받아 데이터 구조를 만드는 소프트웨어 라이브러리

파싱: 파서의 과정

 

 

find( ) 메서드로 태그 위치 찾기

→ 지정된 이름을 가진 첫 번째 태그 찾음

prd_link = soup.find('a', attrs={'class':'gd_name'})

print(prd_link)

* 개발자 도구 창 이용하기

soup. find(태그 이름, attrs={태그 속성}

 

print(prd_link['href']) # 링크 주소인 href 속성 값

 

 

도서 상세 페이지 HTML 가져오기

url = 'http://www.yes24.com'+prd_link['href']
r = requests.get(url)

print(r.text)

soup = BeautifulSoup(r.text, 'html.parser')
prd_detail = soup.find('div', attrs={'id':'infoset_specific'})
print(prd_detail)

 

 

 

find_all( ) 메서드로 테이블 태그를 리스트로 가져오기

 → 지정된 이름을 가진 모든 태그 찾음

prd_tr_list = prd_detail.find_all('tr')
print(prd_tr_list)

 

 

get_text( ) 메서드로 태그 안의 텍스트 가져오기

for tr in prd_tr_list:
    if tr.find('th').get_text() == '쪽수, 무게, 크기': #th가 '쪽수, 무게, 크기'인 지 확인
        page_td = tr.find('td').get_text() #참인 경우 td 변수의 텍스트 내용 추출
        break
print(page_td)

print(page_td.split()[0])

 

 

 

apply( ) 메서드 사용하여 데이터프레임 행 또는 열에 함수 적용하기

top10_books = books.head(10)

def get_page_cnt2(row):
    isbn = row['isbn13']
    return get_page_cnt(isbn)
page_count = top10_books.apply(get_page_cnt2, axis=1) # 판다스 시리즈 객체로 저장
 
print(page_count)

apply( 실행할 함수, axis = 0(or 1))

 → 기본값: 0 (각 열에 대한 함수 적용)

 → 1 (각 행에 대한 함수 적용)

 

 

merge( ) 함수로 데이터프레임과 시리즈 합치기

더보기

merge( ) 함수 매개변수

 

on 매개변수: 합칠 때 기준이 되는 열 지정, df1과 df2에 모두 존재해야 함

 → 열의 값이 같은 행끼리 합쳐짐

 

pd.merge(df1, df2, on='col1') 

 

how 매개변수 (기본값 inner: 값이 같은 행만 합쳐짐)

 → left: 첫 번째 데이터프레임 기준으로 두 번째 데이터프레임 합침

 → right: 두 번째 데이터프레임 기준으로 첫 번째 데이터프레임 합침

 → outer: 두 데이터프레임 모든 행 유지하며 합침

 

*NaN = 결측치

 

pd.merge(df1, df2, how='left', on='col1') 

 

pd.merge(df1, df2, how='right', on='col1') 

 

pd.merge(df1, df2, how='outer', on='col1') 

 

left_on 과 right_on 매개변수: 합칠 기준이 되는 열의 이름이 서로 다를 경우 각기 지정

left_index 와 right_index 매개변수: 합칠 기준이 인덱스일 경우 왼쪽 또는 오른쪽 지정

 

pd.merge(df1, df2, left_on='col1', right_on='col1')

 

pd.merge(df1, df2, left_on='col2', right_index=True)

 

 

page_count.name = 'page_count' # name 속성 사용하여 이름 지정
print(page_count)

 

top10_with_page_count = pd.merge(top10_books, page_count, left_index=True, right_index=True)
top10_with_page_count

 

 

 

1주차 기본 숙제

1주차 추가 숙제