데이터 수집하기
학습목표
- api로 가져오는 법
- 웹스크래핑으로 가져오는법
02-1 API 사용하기
api 줄임말 뜻? 핵심은? 예시는?
- Application Programming Interface 라는 거창한 명칭이다. 어플리케이션은 프로그래밍 규약인데, 사실 api가 아키텍쳐 설계도는 아니다. 요즘 날에 쓰이는 용어로는, 그니까 이걸 어떻게 사용하면 되고 무슨 기능을 제공할 수 있는데? 라는걸 표현하는 문서지. 세세한 세팅환경까지 전부 명세하는 규약은 아니라는것.
web API란?
- 웹 앱 API를 말하는거지. 웹앱은 이렇게 만드세요. 가 아니라, 이제는 REST API를 표현하는 말로 변모됨.
HTTP란?
- HyperText Transfer Protocol
- 초월문서 전송 규약
- HTTP는 그런 방법론이자 기술이다. 그런데 이 기술을 반드시 사용하고 아니 요즘은 이제 이거를 전용으로 이 기술이 사용된다. 바로 브라우저와 웹앱 사이에 데이터 전송 방식이 HTTP로 자리 잡았다.
그 두개가 뭔 차이일까? API를 어디까지 API로 봐야될까?
- HTTP는 말그대로 기술이고, API는 그래서 어떻게 사용하면 되는데? 이다. 웹 API만 있는게 아니다. embed program api 혹은 라이브러리 api 혹은 프레임워크 api 혹은 엔진 api 등 결국 어플리케이션 간에 소통을 위한 방법이 api이기 때문에 둘은 다르다.
REST API란? 일단 REST가 뭐의 줄임말이야? 그게 뭔뜻이야?
- REpresentational State Transfer
- 아 계속해서 검색해가며 하는데 확실하게 정리하면 왜 이게 Representational State Transfer냐면, State를 전송할때 대표적인 State만 전송하자 해서 State Transfer인데 Representational 이라는 수식어가 붙는다.
- 브라우저에서 요청한 주소를 매핑할때 서버에서 어떤 주소를 받아들일때 동작하게 만들지를 설계할때 아주 대표적인 '리소스'만 받아도 충분히 동작할 수 있다는 근거로 등장한다.
- 왜냐? HTTP에는 method, caching option, links parameter, contents type 등등 서버와 클라이언트간에 소통시 '분기' 조건 역할을 할 정보들을 담을 수 있으니, '리소스 uri'라는 명칭 하나로 온갖 동작을 다 설계할 수 있기 때문. 이는 다형성의 원리를 따라 설계의 복잡도를 줄인 아키텍쳐이다. 주소에 모든 정보가 다 포함되면 서버가 아 대충 이거 하겠구나? 라는 은닉성도 없을뿐더러, 너무 길어지고 알아보기 쉽지 않기때문에 나온 구조조
REST API 구성을 설명하라 가장 간단한 핵심 규칙만 풀어써보자 아는대로로
- 위에 앵간하면 설명했는데 더 말하자면 http method 방식을 잘 알고 적절하게 사용하자.
HTML이란?
- HyperText Markup Language
- 시멘틱 구주로 화면의 요소 구별 세팅 담당
브라우저는 따라서 뭘 렌더링하는 엔진인가?
그럼 요청해서 나오는 응답의 결과가 HTML이라면 HTTP에는 HTML 데이터가 포함되어 있을까?
- 당빠 HTTP 바디 부분에 html이 담겨 나온다.
파이썬으로 JSON 데이터 다루기
JSON이란? 무슨 약자인가?
- JavaScript Object Notation
- 자바스크립트 문법을 따르는 컨테이너 객체 형태인데, 이 문법 형식과 같은 방식으로 문자열을 입력하여 JSON 해석기로 필요한 데이터를 통신하는 방식으로 JSON이 활용되게 바뀜
JSON은 파이썬의 어떤 객체들이 합쳐진 모습과 유사하지?
문법으로 주의할 사항 항상 뭐로 요소를 감싸는가? 왜 그럴까? 좀만 더 생각해봐
- "" 큰따옴표로 감싸줘야 한다. 왜그러냐? 어떤 언어에서든 통용하여 사용하기에 요소 구분을 ""로 파싱하여 해석하기 때문이다.
파이썬을 웹이 이해하는 형태로 바꿀려면? 무슨함수? 걔가 뭘 반환하길래?
import json
jsonStr = json.dumps(dic, ensure_ascii=False)
- JSon 형식을 따르는 문자열 형태로 바꾼다.
그 함수에서 한글이 포함되어 있다면? 왜? 그렇게 매개변수를 넘기지?
- ensure_ascii=False
- 아스키가 영어를 전달하기에 효율적이기 때문에 일반적으로 ascii로 전달하고 해석하려한다. 유니코드 형태로 전달하려면 ensure_ascii 옵션을 false로
자 웹에서 온걸 파이썬이 이해하기 쉽게 하려면? 뭘해야될까? 그걸 해주는 함수는?
import json
dic = json.loads(jsonStr)
프로그램밍언어가 프로그래밍 언어가 아닌 다른 녀석이 이해할 수 있는 포맷으로 바꿔주는걸 뭐라고 하고, 그 반대는 뭘까?
- 다른 녀석이 이해 -> 직렬화
- 다시 직렬화의 반대 -> 역직렬화
역직렬화의 결과는??
JSON 응답을 데이터프레임으로 바꾸면 장점? 어떻게 바꾸나?
- 판다스 패키지의 데이터프레임은 행렬형태의 자료를 효과적으로 다루는 함수들이 다량 정의되어 있어서 장점이 크다
import pandas as pd
from io import StringIO
pd.read_json(StringIO(jsonStr))
파이썬에서 XML 다루기
XML 무슨 약자? 자 HTML과 비교하여 XML이 좀 더 우위에 있는게 어떤게 우위에 있을까?
- eXtensible Markup Language
- HTML은 브라우저가 이해하는 문법이지 사람이 이해하기엔 벅차다.
- 기계도 이해하고 사람도 이해하면서 단순히 데이터 깊이를 나눠서 전달할 수 있는 문법이 있다면? -> 그게 XML
- HTML처럼 태그 문법으로 구조를 나눈다.
문법적 요소를 설명하라. 뭐로 감싸지고 그 대상은 뭔지만?
- 모든 태그는 엘리먼트 요소임을 표현한다. 엘리먼트는 내부적으로 값을 가진다.
- 엘리먼트가 엘리먼트를 값으로 가질 수도 있고 엘리먼트 여러개를 값으로 가질 수도 있다. 다만 XML은 반드시 시작노드에는 시작 엘리먼트 하나만 존재할 수 있다.
- 즉 같은 레벨의 태그를 연달아 올리수 없고 같은 레벨의 태그를 연달아 나열하려면 반드시 상위 태그의 엘리먼트로 감싼다. 즉 트리 구조를 무조건 따라야 한다는 강제성이 존재재
역직렬화한 결과는? 그리고 그 새끼를 다루는 가장 기본적인거? 만약 부모 엘리 자식엘리가 존재할때 자식엘리 접근은? 아주 ㅈ같네 이거? 왜 그렇게? 위험한 방법은 왜 위험한건가?
- 역직렬화한 결과는 딕셔너리 리스트가 되었으면 참 좋겠는다 그 새끼는 그게 아니다. XML파서가 전문적으로 존재하면 래퍼 클래스이다. 데이터 프레임 같은건데 아주 ㅈ같다.
import xml.etree.ElementTree as et
book = et.fromstring(xmlStr)
직렬화 역직렬화 아주 개 ㅈ같은거의 핵심은? JSON과 달리 반드시 강제되는 문법적 요소가 있네 보니까
여러개의 키값을 확인하는 findall() 문법 사용시 중의할점. 그 원리 설명. 왜 개 ㅈ같은지 시발 욕밖에 안나오네. 이딴걸 왜씀? 뭐 강점이 아예 없진 않지
import xml.etree.ElementTree as et
book = et.fromString(xmlStr)
print(book.tag)
print(book.text)
book2 = et.fromString(xmlStr)
print(book.tag)
print(book2.text)
for child in book2:
print("Child tag:", child.tag, ", text:", child.text)
xmlStr = """
<books>
<book>
<name>혼공분석</name>
<author>홍길동</author>
<date>2025-05-29</date>
</book>
<book>
<name>혼공알고</name>
<author>임꺽정</author>
<date>2025-05-28</date>
</book>
<book>
<name>혼공만파</name>
<author>유관순</author>
<date>2025-05-27</date>
</book>
</books>
"""
root = et.fromstring(xmlStr)
print("최상위 태그:", root.tag)
for book in root.findall('book'):
name = book.findtext('name')
author = book.findtext('author')
date = book.findtext('date')
print("-----------")
print("책 제목:", name)
print("저자:", author)
print("출간일:", date)
API로 20대가 가장 좋아하는 도서 찾기
가장 먼저 해야할 것
API를 호출하는 URL 작성하기. 호출URL과 파라미터로 구분하여 분석 도서관 정보나루 공개 API 사용하는 코드를 분석 ㄱㄱㄱ
- api문서를 보면서 각 파라미터 한번 읽어보고 -> 분석하여 -> 필요한 데이터만 사용하기 -> 쓰잘데기 없는거 넣어봤자 길이만 냅다 늘어남.
- 만약 api 업데이트로 파라미터가 달라지면? -> 그럴수 있다. 다만 호출용 파라미터가 자주 일어나면 아주 ㅈ같은 api. 원래 api URL은 자주 바뀌면 안된다.
바로 손코딩 api 호출 ㄱㄱ
import requests
key = "4e39afc515253ef8c660ac7c5cb3e221590398f21d9d911ce5505ae5907ba2da"
url = f"http://data4library.kr/api/loanItemSrch?format=json&startDt=2021-04-01&endDt=2021-04-30&age=20&authKey={key}"
response = requests.get(url.format(isbn))
data = response.json()
print(data)
공개 API로 웹에서 데이터 가져오기 <문제>
문제 해결과정을 말로 풀어 설명하면 끝
- 필요한 데이터가 있다. -> 서치 -> api찾기
- api 사용방법을 읽어보기 혹은 print찍어보기 -> 분석 -> 어떤 데이터를 사용할지 감잡기
- dataframe을 만들어서 정형화하기 -> csv파일로 저장하기 -> 분석결과 공유
확인 문제
- 2
- 1
- 2
- json string
- 3
- 1
02-2 웹 스크래핑
도서 쪽수를 찾아서 (시나리오)
API에서 제공하지 않는 도서 쪽수를 가져와서 만들고 싶다면 어떻게 해야될까? 시작과정만
- 도서 쪽수가 나와있는 사이트에서 스크래핑. 해당 도서 페이지에 접근하기 위한 파라미터가 뭔지 파악후 접근. 스크래핑을 위해선 requests beautifulsoup 이라는 라이브러리 사용할 것것
검색 결과 페이지 가져오기(코드)
gdownFilePath = 'https://bit.ly/3q9SZix'
gdownOutputFilePath = './20s_best_books.json'
import pandas as pd
bookDF = pd.read_json('20s_best_books.json')
bookDF.head(20)
bookDF1 = bookDF[['no', 'bookname', 'isbn13']]
bookDF2 = bookDF.loc[[0,1],['no', 'bookname', 'isbn13']]
bookDF2 = bookDF.loc[0:20, 'no':'isbn13']
bookDF3 = bookDF.iloc[0:20, [0,2,6]]
bookDF3.head(20)
import requests
url = "https://www.yes24.com/product/search?domain=BOOK&query={}"
isbn = bookDF3.get('isbn13')[0]
res = requests.get(url=url.format(isbn))
print(res.text)
HTML에서 데이터 추출하기 : 뷰티풀수프 : HTML파서
뷰티풀수프 뭔지 간단 요약(목적위주), requests와 뷰티풀 수프를 합친 패키지가 있다 그게 뭔지 이름만
- 뷰티풀수프는 html 요소를 파싱하여 정보를 가져오는데 특화된 함수들이 정의된 라이브러리 패키지이다. scrapy 스크라피라는 패키지가 있음.
뷰티풀수프를 사용하기 전에 뭐부터 할까? 그니까 HTML을 받아온것까지 했다면 뭘해야할까? 분석한 결과까지 써보기기
- 실제 우리가 원하는 링크값이나 텍스트값이 어느 요소에 있는지를 파악해야한다.
- 첫째 검색결과 페이지에서 링크값을 가져온다.
- 둘째 링크값으로 들어간 상세페이지에서 쪽수 값을 가져온다.
실습
from bs4 import BeautifulSoup
bs = BeautifulSoup(res.text, 'html.parser')
aElement = bs.find(name='a', attrs={'class':"gd_name"})
print(aElement.attrs.get('href'))
import requests
url1 = "https://www.yes24.com/product/search?domain=BOOK&query={}"
isbn = bookDF3.get('isbn13')[0]
res = requests.get(url=url.format(isbn))
bs1 = BeautifulSoup(res.text, 'html.parser')
aElement1 = bs.find(name='a', attrs={'class':"gd_name"})
pkey = aElement.attrs.get('href')
url2 = "https://www.yes24.com/{}"
res2 = requests.get(url=url2.format(pkey))
bs2 = BeautifulSoup(res2.text, 'html.parser')
aElement2 = bs2.find(name='div', attrs={'id':'infoset_specific'})
trList = aElement2.find_all(name='tr')
answer = ''
for tr in trList:
if tr.find(name='th').text == '쪽수, 무게, 크기':
answer = tr.find(name='td').text.split()[0]
break
전체 도서의 쪽수 구하기
for문으로 데이터 프레임을 감싸는건 비효율적이다. 그래서 데이터 프레임은 내부적으로 반복하여 꺼내올수 있게 함수를 만들어 놨다.
def get_page_cnt(isbn):
url = "https://www.yes24.com/product/search?domain=BOOK&query={}"
res = requests.get(url=url.format(isbn))
bs = BeautifulSoup(res.text, 'html.parser')
a_element = bs.find(name='a', attrs={'class':'gd_name'})
p_key = a_element.attrs.get('href')
if not p_key:
print(f"[{isbn}] a 태그에서 'href' 속성을 찾을 수 없습니다.")
return ''
url2 = "https://www.yes24.com/{}"
res2 = requests.get(url2.format(p_key))
bs2 = BeautifulSoup(res2.text, 'html.parser')
a_element2 = bs2.find(name='div', attrs={'id':'infoset_specific'})
tr_list = a_element2.find_all(name='tr')
for tr in tr_list:
if tr.find(name='th').text == '쪽수, 무게, 크기':
return tr.find(name='td').text.split()[0]
return ''
def get_page_cnt2(row):
isbn = row['isbn13']
return get_page_cnt(isbn)
ser_page_cnt = bookDF3.apply(get_page_cnt2, axis=1)
print(ser_page_cnt)
웹 스크래핑 주의점
robots.txt란?
- robots.txt에 명시된 내용은 스크래핑하면 안된다.
HTML 태그를 특정할 수 없다면? 그 대안은?
여기까지 과정을 간략히 설명하라.
- 찾고자하는 페이지에 접근하기 위한 과정
- 찾고자하는 페이지 속에서 요소를 찾는 과정
- 그렇게 함수를 만들고 나면 원하는 데이터 프레임에 apply 함수의 매개변수로 추가
확인 문제
- 4
- 4
- 1
- 2
- 3