Chapter 02. 데이터 수집하기

강민석·2023년 1월 15일
0

데이터 분석

목록 보기
2/7

colab 실습 링크 : https://colab.research.google.com/drive/1qjMVDgpZCX50Ej8fOqaKbFcLSdYpfJK2?usp=sharing
모든 실행 결과는 colab에서 확인 할 수 있습니다.

1. API 활용하기(Application Programming Interface)

  • Program 간 정보를 주고 받기 위한 방법의 정의(Interface)

Web API

  • HTTP Protocol을 이용한 API
  • HTML 보다는 CSV, JSON, XML 등 Data 전송에 특화된 형식 사용

Json in Python

  • json 패키지 불러오기
import json
  • json.dumps() : Python 객체를 JSON 문자열로 변환
d = {"name": "혼자 공부하는 데이터 분석"}
d_str = json.dumps(d, ensure_ascii=False) # ensure_ascii를 비활성화 하여 한글이 16진수로 변환되지 않도록 설정
  • json.loads() : JSON 문자열을 Python 객체로 변환
d2 = json.loads(d_str)

print(d2['name'])
  • 배열을 포함한 JSON을 생성하고 json.loads로 바로 변환하기
d3 = json.loads('{"name": "혼자 공부하는 데이터 분석", "author": ["박해선", "홍길동"], "year": 2022}')
  • 복잡한 JSON 입력하기
d4_str = """
[
  {"name": "혼자 공부하는 데이터 분석", "author": "박해선", "year": 2022},
  {"name": "혼자 공부하는 머신러닝+딥러닝", "author": "박해선", "year": 2020}
]
"""
d4 = json.loads(d4_str)

""" ~ """을 통해 긴 문자열을 줄바꿈하여 입력 가능

  • JSON을 pandas DataFrame으로 변환
import pandas as pd

pd.read_json(d4_str)

XML in Python

  • Python에서 기본 제공하는 xml 패키지를 통해 XML을 파이썬에서 사용할 수 있는 Element 로 변환
x_str = """
<book>
  <name>혼자 공부하는 데이터 분석</name>
  <author>박해선</author>
  <year>2022</year>
</book>
"""

import xml.etree.ElementTree as et

book = et.fromstring(x_str) 
  • 부모 엘리먼트(book)를 list로 변환하여 자식 엘리먼트 알아내기
book_childs = list(book)
print(book_childs)

[<Element 'name' at 0x7f77e8598b80>, <Element 'author' at 0x7f77e85d4720>, <Element 'year' at 0x7f77e85d4630>]

  • 알아낸 자식 엘리먼트들을 변수에 할당 후 값 알아내기
name, author, year = book_childs
print(name.text)

혼자 공부하는 데이터 분석

  • findtext() 메서드를 이용하여 자식 엘리먼트 찾기
name = book.findtext('name')
author = book.findtext('author')
year = book.findtext('year')

print(name)

혼자 공부하는 데이터 분석

  • 여러개의 book을 가진 XML 구조 생성
x2_str = """
<books>
  <book>
    <name>혼자 공부하는 데이터 분석</name>
    <author>박해선</author>
    <year>2022</year>
  </book>
  <book>
    <name>혼자 공부하는 머신러닝+딥러닝</name>
    <author>박해선</author>
    <year>2022</year>
  </book>
 </books>
"""

books = et.fromstring(x2_str)
  • 여러개의 book에서 자식 엘리먼트들의 값 추출하기
for book in books.findall('book'):
  name = book.findtext('name')
  author = book.findtext('author')
  year = book.findtext('year')
  print (name)
  print(author)
  print(year)

혼자 공부하는 데이터 분석
박해선
2022
혼자 공부하는 머신러닝+딥러닝
박해선
2022

  • XML을 pandas DataFrame으로 변환
pd.read_xml(x2_str)

API 활용하기 - 20대가 좋아하는 도서 찾기

도서관 정보나루 공공데이터 웹 API활용.
URL : "http://data4library.kr/api/loanItemSrch?authKey=${API_KEY}&format=json&startDt=2021-04-01&endDt=2021-04-30&age=20"

  • api request 자동화 프로그램 만들기
import requests

url = "http://data4library.kr/api/loanItemSrch?authKey=${API_KEY}&format=json&startDt=2021-04-01&endDt=2021-04-30&age=20"

#response 데이터를 JSON으로 가져오기
r = requests.get(url)
data = r.json

#response 중 필요한 데이터만 가져오기 (이 경우엔 책 정보가 docs 내부에 들어있음.)
books = []
for d in data['response']['docs']:
  books.append(d['doc'])

#pandas DataFrame으로 변환하기
books_df = pd.DataFrame(books)

#JSON 파일로 저장하기
books_df.to_json('20s_best_book.json', force_ascii=False)

2. 웹 스크래핑 활용하기

! 도서별 쪽수가 필요하지만 관련 API가 없다면? Yes24 등 서점 사이트에서 제공하는 쪽수 정보를 스크래핑으로 얻을 수 있다.

DataFrame 일부 정보만 남기기

  • API를 통해 가져온 도서 정보에서 원하는 열만 남기기
books_df = pd.read_json('20s_best_book.json')

books = books_df[['no', 'ranking', 'bookname', 'authors', 'publisher', 'publication_year', 'isbn13']]
  • iloc로 원하는 행과 열만 남기기
books_df.iloc[[0, 1], [2, 3]]
  • 슬라이스 연산자(:)로 일정 부분 남기기
books = books_df.loc[:, 'no':'isbn13']
  • 스텝을 지정해서 홀수 정보만 남기기
books_df.loc[::2, 'no':'isbn13'].head()

웹 페이지에서 데이터 가져오기

Step 01. 검색 결과 페이지의 HTML 가져오기

isbn = ${찾고싶은 책의 ISBN}
url = 'http://www.yes24.com/Product/Search?domain=BOOK&query={}'
r = requests.get(url.format(isbn))

Step 02. HTML에서 상세페이지로 넘어갈 수 있는 URL을 찾아 상세페이지 정보 얻기

  • BeautifulParser로 html 파싱
from bs4 import BeautifulSoup

soup = BeautifulSoup(r.text, 'html.parser')
  • 개발자 도구로 찾은 URL 위치를 통해 find() 메소드로 URL 찾기
# HTML : <a class="gd_name" href="/Product/Goods/74261416" onclick="setSCode('101_005_003_001');setGoodsClickExtraCodeHub('032', '9791190090018', '74261416', '0');">우리가 빛의 속도로 갈 수 없다면</a>

prd_link = soup.find('a', attrs={'class':'gd_name'})
  • 상세페이지 HTML 가져오기
url = 'http://www.yes24.com'+prd_link['href']
r = requests.get(url)

Step 03. 쪽수 정보 찾기

  • 상세페이지에서 개발자 도구로 쪽수 정보 위치를 찾아 데이터 가져오기
soup = BeautifulSoup(r.text, 'html.parser')
prd_detail = soup.find('div', attrs={'id':'infoset_specific'})
  • prd_detail에 포함된 tr 태그 리스트 구하기
prd_tr_list = prd_detail.find_all('tr')
  • 페이지 정보가 담긴 td를 찾아 페이지만 추출하기
for tr in prd_tr_list:
  if tr.find('th').get_text() == '쪽수, 무게, 크기':
    page_td = tr.find('td').get_text()
    break
    
print(page_td.split()[0])

330쪽

Step 04. ISBN을 매개변수로 받아 페이지 수를 출력해주는 함수 만들기

def get_page_cnt(isbn):
  url = 'http://www.yes24.com/Product/Search?domain=BOOK&query={}'

  r = requests.get(url.format(isbn))
  soup = BeautifulSoup(r.text, 'html.parser')

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

  url = 'http://www.yes24.com' + prd_info['href']
  r = requests.get(url)
  soup = BeautifulSoup(r.text, 'html.parser')

  prd_detail = soup.find('div', attrs={'id':'infoset_specific'})
  prd_tr_list = prd_detail.find_all('tr')

  for tr in prd_tr_list:
    if tr.find('th').get_text() == '쪽수, 무게, 크기':
      return tr.find('td').get_text().split()[0]
  return ''

get_page_cnt(9791190090018)

'330쪽'

Step 05. DataFrame에 적용하여 쪽수 받아오기

def get_page_cnt2(row):
  isbn = row['isbn13']
  return get_page_cnt(isbn)

top10_books = books.head(10)
page_count = top10_books.apply(get_page_cnt2, axis=1)

apply(함수, axis)

함수 : DataFrame의 각 행이나 열에 적용시킬 함수
axis 0 : 기본값, 각 열에 적용
axis 1 : 각 행에 적용

  • Lambda 식 표현
page_count = top10_books.apply(lambda row: get_page_cnt(row['isbn13']), axis=1)

Step 06. 쪽수 정보를 기존 DataFrame에 합치기

page_count.name = 'page_count' # 열 이름 지정

top10_with_page_count = pd.merge(top10_books, page_count, left_index=True, right_index=True) # index를 기준으로 합치기
top10_with_page_count
  • 최종 결과

Warning. 웹 스크래핑 시 주의점

  • robots.txt 파일을 확인하여 접근 제한 페이지 확인하기
  • HTML 태그를 특정 가능한지 확인하기 > 불가능 할 시 Selenium 등의 고급 툴 사용 가능
  • 마땅한 방법이 없을 때 사용하는 최후의 수단으로 생각하기

merge() 함수

  • sql의 join과 비슷한 방식으로 작동한다.

    how='left' == left join
    how='right' == right join
    how='outer' == outer join ...


소감

웹 크롤링으로 접하고 있었던 웹 스크래핑을 실제로 경험할 수 있는 유익한 시간이었다. 개발 작업을 하거나, 개인적인 호기심을 채우기 위해서도 활용 할 수 있을 것 같다.

스크래핑 작업 시 예제처럼 10개 책의 쪽수를 가져오는 것만 해도 생각보다 부하가 걸리는 것을 확인할 수 있었다. 웹 개발을 하면서 이런 스크래핑에 의한 부하에 대해서도 고려하면서 개발해야 할 것 같다.(robots.txt 파일을 작성하고 활용하는 방법에 대해서도 알아두어야 할 것 같다.)

profile
새내기 개발자입니다.

0개의 댓글