1.  BeautifulSoup

🍯  구문을 분석해서 필요한 내용만 추출 할 수 있는 기능을 가지고 있는 패키지

        ➡️  xml or html을 수프객체로 만들어서 추출하기 쉽게 만들어 준다.

from bs4 import BeautifulSoup as bs  # bs4 라이브러리에서 Beautiful Soup를 import
import requests
import pprint

# html 파일 가져오기
with open('./sample.html', 'r', encoding='utf-8') as file:
    html = file.read()

# html.parser : html 코드를 사용하기 쉽게 BeautifulSoup의 객체로 분석
soup = bs(html, 'html.parser')  
# 첫번째 인자: 분석할 내용 전달
# 두번째 인자: "html"로 분석한다는 것을 명시

print(type(soup))  # <class 'bs4.BeautifulSoup'>
print(soup)  # (html 출력)

print(soup.find('title').text)  # This is title
print(soup.find('div').text)  # Division의 약자로, 레이아웃을 나누는데 사용.
print(soup.find('h1').text.strip())  # This is heading1 text.

 


 

1) find ()

🥑  지정된 태그들 중에서 가장 첫 번째 태그만 가져오는 메소드(하나의 값만 반환)로 문자열 형태로 반환
       ➡️  일반적으로 하나의 태그만 존재하는 경우에 사용  만약 여러 태그가 있으면 첫 번째 태그만 가져옴

 

# 속성값 가져오는 경우 형식 (find, find_all 동일)
find(태그명['속성명'])
find(태그명.attrs['속성명'])
find(태그명).get(속성명)
find_all('태그명', attrs={'속성명':'값'})
# 위키피디아 '대구광역시' 페이지
url = 'https://ko.wikipedia.org/wiki/%EB%8C%80%EA%B5%AC%EA%B4%91%EC%97%AD%EC%8B%9C'
resp = requests.get(url)
soup = bs(resp.text, 'html.parser')

first_img = soup.find(name='img')  # img 태그 중에 제일 먼저 나오는 것
print(type(first_img))  # <class 'bs4.element.Tag'>
print(first_img)  
# <img alt="" aria-hidden="true" class="mw-logo-icon" height="50" 
# src="/static/images/icons/wikipedia.png" width="50"/>

target_img = soup.find(name='img', attrs={'alt': 'Daedongyeojido (Gyujanggak) 17-02.jpg'})
print(target_img)
# <img alt="Daedongyeojido (Gyujanggak) 17-02.jpg" class="mw-file-element" 
# data-file-height="3005" data-file-width="4000" decoding="async" height="376" 
# src="//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/
# Daedongyeojido_%28Gyujanggak%29_17-02.jpg/500px-Daedongyeojido_%28Gyujanggak%29_17-02.
# jpg" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Daedongyeojido_
# %28Gyujanggak%29_17-02.jpg/750px-Daedongyeojido_%28Gyujanggak%29_17-02.jpg 1.5x, 
# //upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Daedongyeojido_%28Gyujanggak%
# 29_17-02.jpg/1000px-Daedongyeojido_%28Gyujanggak%29_17-02.jpg 2x" width="500"/>


 

2)  find_all() 

🥑  지정한 태그들을 모두 가져오는 메소드로 가져온 태그들은 모두 리스트에 보관

# 네이버 스포츠 페이지에서 박스 뉴스 제목 들고 옴
url = 'https://sports.news.naver.com/index.nhn'
response = requests.get(url)
soup = bs(response.text, 'html.parser')

today_list = soup.find('ul', {'class': 'today_list'})
print(today_list)

today_list_title = today_list.find_all('strong', {'class', 'title'})
pprint.pprint(today_list_title)  # 리스트로 반환

for title in today_list_title:
    print(title.text.strip())

1. today_list 결과
2. today_list_title 결과
3. 반복문 실행결과

 


 

3) find_all 사용 예제

a. 다음 뉴스 사이트 html 분석

url = 'https://news.daum.net/'
response = requests.get(url)
soup = bs(response.text, 'html.parser')


 

b. a 태그의 갯수 출력

 

   👾  html의 'a' 태그 : 다른 콘텐츠와 연결되는 하이퍼링크(hyperlink)를 정의

print('1. a 태그의 갯수')
print(len(soup.find_all('a')))  # 124

 

c. a 태그 20개만 출력

print('2. a 태그 20개만 출력')
for news in soup.find_all('a')[:20]:
    print(news.text.strip())

실행결과


 

d.  a 태그 링크 5개 출력

print('3. a 태그 링크 5개 출력')
for i in soup.find_all('a')[:5]:
    print(i.attrs['href'])
    print(i.get('href'))
    # -> 둘 중 하나만 쓰면 된다.
print("=" * 20)

 

실행결과


 

e.  특정 클래스 속성을 출력하기

print('4. 특정 클래스 속성을 출력')
print(soup.find_all('div', {'class': 'item_issue'}))
print("=" * 20)

실행결과


f.  링크를 텍스트 파일로 저장

print('5. 링크를 텍스트 파일로 저장')
file = open('../output_02/links.txt', 'w')  # 쓰기 전용 파일 생성

for i in soup.find_all('div', {'class': 'item_issue'}):
    file.write(i.find('a').get('href') + '\n')
file.close()

실행결과

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


 

1.  url을 이용해서 HTML를 가져오는 방법

 

🚀  크롤링할 때 url을 이용해서 HTML를 가져오는 방법은 크게 2가지
      a. 내장 모듈인 urllib를 사용 
      b. 외장 모듈인 requests를 사용

 

1) urllib를 사용한 경우

# 정상 접속
url = "https://www.python.org/"
code = request.urlopen(url)
print(code)  # <http.client.HTTPResponse object at 0x103f16b30>

# 비정상 접속. 비정상일 경우 에러 발생.
url = "https://www.python.org/1"
code = request.urlopen(url)
print(code)

비정상 접속인 경우 실행 결과

 


 

2) requests를 사용한 경우

# 정상 접속인 경우
url = "https://www.python.org/"
response = requests.get(url)
print(response)  # <Response [200]>. 정상적인 통신이 이루어짐.

# 페이지가 없는 경우에도 에러가 발생하지 않고, Response [404]를 리턴.
url = "https://www.python.org/1"
response = requests.get(url)
print(response)  # <Response [404]>. 해당 페이지를 찾을 수 없음

 

  👾  requests 를 사용하면 urllib 를 사용한 경우와 달리 비정상 접속인 경우 에러가 발생하지 않고 응답코드를 준다

 

    📌  응답코드 : 서버에서 클라이언트로 보내는 코드
          a. 1XX : 요청을 받았고, 작업 진행 중
          b. 2XX : 사용자의 요청이 성공적으로 수행 됨
          c. 3XX : 요청은 완료 되었으나, 리다이렉션이 필요
          d. 4XX : 사용자의 요청이 잘못됨
          e. 5XX : 서버에 오류가 발생함

 


 

2. requests 사용법

 

🚀  서버로 요청을 할 때 브라우저의 정보(User-Agent)가 같이 전달됨
        ➡️ 요청 받은 서버에서는 브라우저의 정보를 가지고 접속자가 bot인지 일반 사용자임을 구분
        ➡️ 특정 사이트의 경우 요청하는 브라우저의 정보가 일반 사용자가 아니면 접속을 막는 경우가 있음
        ➡️ requests의 경우 브라우저의 헤더 정보를 수정해서 일반 브라우저 처럼 접속할 수 있게 함

 

# requests 사용법

url = 'https://www.naver.com/'
response = requests.get(url)  # get() 또는 post() 메서드를 이용해서 html 정보를 받아옴

html = response.text  # response 객체의 text 속성을 지정하면 html 정보 반환.
print(html)  # html 소스가 출력

headers = response.headers  
print(headers)  # response 객체의 headers 속성 지정하면 헤더 정보 반환.

 

 

1) 헤더 정보 확인하기

 

  👾  requests를 이용해서 url 접속을 하면 브라우저의 정보(User-Agent)가 requests의 모듈 정보로 나옴
          ➡️ 서버에서 해당 정보를 보고 크롤링을 판단할 수 있음

from bs4 import BeautifulSoup as bs
import requests

url = 'https://planet-trade.kr/header_info.php'

response = requests.get(url)  # 브라우저 접속 역할을 파이썬 requests가 해줌
soup = bs(response.text, 'html.parser')
print(soup)
# 접속 IP : 58.149.46.252
# 접속 정보 : python-requests/2.31.0

url 주소로 접속했을 때 브라우저의 정보

 


 

2) requests에서 헤더 정보를 변경

request_headers = {
    'User-Agent': ('(url 주소로 들어갔을 때 나오는 브라우저 접속 정보)'),
    'Referer': '',
}

resp = requests.get(url, headers=request_headers) # 변경된 헤더로 접속, 정보 가져옴
soup = bs(resp.text, 'html.parser')
print(soup)

# 접속 IP : 58.149.46.252
# 접속 정보 : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
# (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36

 


 

2. 크롤링 할 때 운영자 의사 확인

 

🚀  모든 크롤링이 불법은 아니지만 하지만 운영자의 의사에 상관없이 무단으로 크롤링하는 것은 불법
🚀  운영자의 크롤링 허용 여부를 알 수 있는 방법은 'robot.txt' 로 확인
        ➡️ allow는 허용, disallow는 검색 불가

 

    💡  robots.txt 설정 정보 안내 사이트

           https://searchadvisor.naver.com/guide/seo-basic-robots
           https://developers.google.com/search/docs/advanced/robots/intro?hl=ko

 

robots.txt 설정하기

robots.txt는 검색로봇에게 사이트 및 웹페이지를 수집할 수 있도록 허용하거나 제한하는 국제 권고안입니다. IETF에서 2022년 9월에 이에 대한 표준화 문서를 발행하였습니다. robots.txt 파일은 항상

searchadvisor.naver.com

 

robots.txt 소개 및 가이드 | Google 검색 센터  |  문서  |  Google for Developers

robots.txt는 크롤러 트래픽을 관리하는 데 사용됩니다. robots.txt 소개 가이드에서 robots.txt 파일의 정의와 사용 방법을 알아보세요.

developers.google.com

 

urls = ['https://www.naver.com/', 'https://ko.wikipedia.org/']
filename = 'robots.txt'

for url in urls:
    file_path = url + filename
    print(file_path)
    resp = requests.get(file_path)
    print(resp.text)
    print("\n")

실행 결과

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

45


 

1.  XML 이란?

🍋  eXtensible Markup Language 은 데이터 저장 및 전달을 위해 만든 다목적 마크업 언어 Markup Language
      📌  마크업 언어는 일반적인 텍스트와 구분되는 태그 Tag를 이용해 문서나 데이터를 구조화 하는 언어

      📌  JSON과 마찬가지로 데이터 전달을 목적으로 만든 구조화된 텍스트 형식
      📌  대표적인 마크업 언어로는 HTML HyperText Markup Language

🍋  HTML의 경우는 태그가 미리 정해져 있지만 XML은 자신이 태그를 정의해서 사용할 수 있음
       ➡️ 단 XML 문서의 규칙을 따라야 함

🍋  태그는 '<문자열>'로 시작해서 '</문자열>'로 끝나야 한다
      📌  시작과 끝 태그의 문자열은 같아야 하며 대소문자를 구분

      📌  중첩해 여러 개를 이용할 수 있는데 이때 태그는 반드시 올바른 순서대로 이용해야 함

             ex.  '<abc><def> ~ </def></abc>' 와 같이 나중에 나온 시작 태그에 대응하는 끝 태그가 먼저 나와야

     📌  XML 문서에서는 시작 태그에서 끝 태그까지로 이루어진 것을 요소 element라고 함

🍋  XML 문서에는 반드시 최상위 root 요소가 있어야 함 

      📌  최상위 요소는 시작과 끝 태그로 다른 모든 요소를 감싸야 함

🍋  주석은 '<!--'와  '-->'로 문자열을 감싸서 표시. 즉 '<!-- 이것은 주석입니다 -->'와 같이 사용

🍋  xml을 지정하는 모듈은 내장 모듈로 xml이 있고, 외부 모듈의 경우 xmltodict 사용
     📌  xml 데이터를 파이썬의 딕셔너리 타입으로 바로 변환
 

🍋  xmltodict 라이브러리에서 XML 형식의 데이터를 파이썬의 딕셔너리 타입으로 변환하는 함수
     📌  xmltodict.parse(xml_input, [ xml_attribs=True 혹은 False ])
     📌  xml_input은 XML 타입의 데이터
     📌  xml_attribs은 기본 값은 True
            ➡️  False 이면 XML 형식의 데이터를 딕셔너리 타입으로 변환할 때 속성을 무시

 

🍋   pprint.pprint() 함수를 사용하면 내용이 들여쓰기 된 상태로 보기좋게 출력 가능  

import xmltodict
import pprint

with open('../input_2/data.xml', 'r', encoding='utf-8') as xml_file:
    dict_data = xmltodict.parse(xml_file.read(), xml_attribs=True)  # xml 데이터를 딕셔너리로 변경
    #print(dict_data)
    #print(dict_data['response']['body']['items'])
    datas = dict_data['response']['body']['items']
    pprint.pprint(datas)

 

data.xml 내용

 

실행 결과

 

 


 

2. 오페라 공연 제목과 링크주소 추출 및 저장

 

import csv
from datetime import datetime
import xmltodict
import pprint
import requests

# 1. 데이터 추출
url = 'https://www.daeguoperahouse.org/rss.php'
response = requests.get(url)
dict_data = xmltodict.parse(response.text)
pprint.pprint(dict_data)

실행 결과


 

# 2. 필요한 데이터 추출하기

data_list = dict_data['rss']['channel']['item']

new_datas:list[dict] = list()  # 필요 내용만 담을 리스트 필요

for k in data_list:
    new_data = dict()
    new_data['title'] = k['title']
    new_data['link'] = k['link']
    new_datas.append(new_data)
pprint.pprint(new_datas)

 

실행 결과


# 3. 현재 날짜로 파일명 지정해서 저장하기

today = datetime.now().strftime('%y%m%d')  # 현재 날짜 구하기
print(today)

with open(f'../output_02/daeguoperahouse_{today}.csv', 'wt', newline='', encoding='utf-8') as csv_file:
    writer = csv.DictWriter(csv_file, fieldnames=['title', 'link'])
    writer.writeheader()
    for k in new_datas:
        writer.writerow(k)

print('파일 생성을 성공했습니다.')

파일 생성 완료

 

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

 


 [ 측정소 정보 추출하기 ]

   📌  대구에 있는 측정소의 측정소 명, 측정소 주소, 위도, 경도, 설치년도를 csv 파일로 저장

 

import requests
import json

# 1. 초기값 설정
# 1) 서비스 키: requests 사용시 자동으로 인코딩되어 decoding된 키를 사용
service_key:str = 'uAhO32pV0qa7BDOmkJLhw2ZyOB9i5bGj7cMN8cuuHmKIwyyf29lHLjym8hBXdIaXyvAI1IyLvQZopk5HW233qQ=='

# 2) url 설정
url = 'http://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList'
parameter = f'?serviceKey={service_key}&returnType=json&numOfRows=100&pageNo=1&addr=대구'
print(url+parameter)

일반 인증키를 서비스 키로 가져온다

 

사이트에 올라와 있는 zip 파일에 서버에 소스 요청할 때 요구하는 메세지 형식이 나와있다.

 

 

# 2. 서버에서 요청값을 받은 후 파싱(parsing)(= 구문 분석)
# 딕셔너리 데이터를 분석해서 원하는 값을 추출
response = requests.get(url + parameter)
# requests.get() 메서드를 사용하여 url 주소로 GET 요청을 보내고, 응답을 response 변수에 저장
json_data = response.text
# head 값 외 text 값만 들고오기 위함
dict_data = json.loads(json_data)
# JSON 형식의 데이터를 파이썬 객체로 변환하여 data 변수에 저장

print(dict_data)
'''
실행결과)
{'response': {'body': {'totalCount': 28, 'items': 
[{'dmX': '35.859', 'item': 'SO2, CO, O3, NO2, PM10, PM2.5', 'mangName': '도시대기', 
'year': '2020', 'addr': '대구광역시 서구 서대구로3길 46 내당4동 행정복지센터 3층 옥상 (
... 생략...

'''

# 3. 필요 정보 추출

count_datas = dict_data['response']['body']['items']
#print(count_datas)

for k in range(len(count_datas)):
    count_data = count_datas[k]
    if count_data['addr'][0:2] == "대구":
        print(f'측정소 명 : {count_data["stationName"]}')
        print(f'측정소 주소 : {count_data["addr"]}')
        print(f'위도 : {count_data["dmX"]}')
        print(f'경도 : {count_data["dmY"]}')
        print(f'설치년도 : {count_data["year"]}')
        print('=' * 20)
'''
실행결과)
측정소 명 : 내당동
측정소 주소 : 대구광역시 서구 서대구로3길 46 내당4동 행정복지센터 3층 옥상 (내당동)
위도 : 35.859
경도 : 128.55183
설치년도 : 2020
====================
측정소 명 : 침산동
측정소 주소 : 대구광역시 북구 옥산로17길 21 대구일중학교 (침산동)
위도 : 35.88761
경도 : 128.58434
설치년도 : 2020

... 생략...

'''

 


# 4. csv 파일 저장
import csv

data_list: list[str] = ['측정소 명', '측정소 주소', '위도', '경도', '설치년도']
daegu_list:list[dict] = list()

for k in range(len(count_datas)):
    count_data = count_datas[k]
    if count_data['addr'][0:2] == "대구":
        new_data: dict = dict()
        new_data[data_list[0]] = count_data["stationName"]
        new_data[data_list[1]] = count_data["addr"]
        new_data[data_list[2]] = count_data["dmX"]
        new_data[data_list[3]] = count_data["dmY"]
        new_data[data_list[4]] = count_data["year"]

        daegu_list.append(new_data)

with open('../output_02/daegu_air_list.csv', 'w', newline='', encoding='UTF-8') as csvfile:
    csv_writer = csv.DictWriter(csvfile, data_list)
    csv_writer.writeheader()
    for data in daegu_list:
        data:dict
        csv_writer.writerow(data)

print(f'파일이 생성되었습니다.')

 

파일 생성 완료

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

 


 

1.  API 통해 위치 정보 얻기

import urllib.request as request
import json
import datetime

# API 이용하기
# openweathermap.org

api_key: str = 'd1569da48f0b4251e9ec538982a29548'

# 대구 위치 정보 얻기
# https://openweathermap.org/api/geocoding-api

city: str = 'Taegu'
url: str = f'http://api.openweathermap.org/geo/1.0/direct?q={city}&limit=5&appid={api_key}'
print(url)

json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
lat = dict_data[0]['lat']
lon = dict_data[0]['lon']
print(f'{lat} / {lon}')

# 현재 날씨
# https://openweathermap.org/current
# lang=kr : 한국어 사용
# units=metric : 섭씨온도

url = (f'https://api.openweathermap.org/data/2.5/weather?lat={lat}&'  #위도
       f'lon={lon}&'  #경도
       f'appid={api_key}&'
       f'lang=kr&'
       f'units=metric')
print(url)
json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
json_data = json.dumps(dict_data, indent=4)
# print(json_data)

print(f'도시 : {dict_data["name"]}')
print(f'현재날씨 : {dict_data["weather"][0]["description"]}')
print(f'최고기온 : {dict_data["main"]["temp_max"]}')
print(f'최저기온 : {dict_data["main"]["temp_min"]}')
print(f'일출시간 : {datetime.datetime.fromtimestamp(dict_data["sys"]["sunrise"])}')
print(f'일몰시간 : {datetime.datetime.fromtimestamp(dict_data["sys"]["sunset"])}')

# 날씨 예보 : 5days / 3 hours
# https://openweathermap.org/forecast5

url: str = (f'https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={api_key}&lang=kr&'
       f'units=metric')
print(url)

json_data = request.urlopen(url).read()
dict_data = json.loads(json_data)
json_data = json.dumps(dict_data, indent=4)
print(json_data)

weather_list = dict_data['list']  #json 데이터 중에서 list key에 해당하는 목록 가져오기.
#print(weather_list)

for item in weather_list:
       print(f'시간: {datetime.datetime.fromtimestamp(item["dt"])}', end=', ')
       print(f'최고기온: {item["main"]["temp_max"]}', end=', ')
       print(f'날씨: {item["weather"][0]["main"]}', end=', ')
       print(f'날씨: {item["weather"][0]["description"]}')
       print()

 

print(url) 을 실행했을 때 나온 주소창으로 들어가면 나오는 데이터

 

실행결과

 


 

2. 공공데이터를 JSON 타입으로 받아 필요한 내용 출력하고  CSV 타입으로 저장

 

# 공공데이터 포탈에서 '대구광역시_맛집' 검색 (키사용이 없음)
# https://www.data.go.kr/data/15057236/openapi.do
# 중구의 맛집 목록을 json 타입으로 받아서 처리

# 1. 음식카테고리 출력
# {'한식', '일식', '세계요리', '중식', '전통차/커피전문점', '특별한 술집', '디저트/베이커리', '양식'}

# 2. 일식의 음식점명, 연락처, 메뉴 출력
# --------------------
# 음식점명: 종로초밥
# 연락처: 053-252-0321
# 메뉴: 광어회 50,000원 <br />모둠회 40,000원 <br />무침회 20,000원<br />
# --------------------
# 음식점명: 삼삼구이초밥
# 연락처: 053-425-3392
# 메뉴: 회덮밥 9,000원 <br />미주구리 30,000원 <br />점심특선 6,000원<br />
# --------------------
import json
import urllib.request as request


url: str = "https://www.daegufood.go.kr/kor/api/tasty.html?mode=json&addr=%EC%A4%91%EA%B5%AC"

json_data = request.urlopen(url).read()
dict_data: dict = json.loads(json_data)
# print(dict_data)

# 문제 1)
food_kind = set()  # 중복 값 걸러주는 set 활용
for row in dict_data['data']:
    food_kind.add(row['FD_CS'])
print(food_kind)


# 문제 2)
for item in dict_data['data']:
    kind = item['FD_CS']
    if kind == "일식":
        print(f'음식점명: {item["BZ_NM"]}')
        print(f'연락처: {item["TLNO"]}')
        print(f'메뉴: {item["MNU"]}')
        print("-" * 20)

# 3. csv 파일 저장
# 파일명 : daegu_food_jonggu_list.csv
# 인코딩 : utf-8
# 헤더 : '음식 카테고리', '음식점명', '연락처', '메뉴'
import csv

food_list: list[dict] = list()
name_list: list[str] = ['음식 카테고리', '음식점명', '연락처', '메뉴']  
# csv 헤더 및 딕셔너리의 key값으로 사용.

for data in dict_data['data']:
    if data['FD_CS'] == '일식':
        new_data: dict = dict()
        new_data[name_list[0]] = data['FD_CS']
        new_data[name_list[1]] = row["BZ_NM"]
        new_data[name_list[2]] = row["TLNO"]
        new_data[name_list[3]] = row["MNU"]

        food_list.append(new_data)

file_name = 'daegu_food_jonggu_list.csv'
with open(f'../output_02/{file_name}', 'w', newline='', encoding='UTF-8') as csvfile:
    writer = csv.DictWriter(csvfile, name_list)
    writer.writeheader()
    for data in food_list:
        data: dict
        writer.writerow(data)

print(f'{file_name} 파일이 생성되었습니다.')

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 자료 ]

 


 

1. 서버에서 데이터 받기

 

1) 아이디와 이름 순서대로 데이터를 전달
 

    🚀  test, tom  ➡️ 인덱스를 사용하는 것처럼 위치값으로 파악
    🚀  빈값이나, 값에 구분자가 들어가는 경우 어려움등의 문제로 사용 빈도 낮음

 


2) xml 방식

 

    🚀  <id>test</id>
          <name>tom</name>
           ➡️  마크업 방식, 속성값이 반복되어서 상대적으로 데이터의 크기가 커짐

 


3) json 방식

 

    🚀  'id': 'test',
           'name': 'tom'
            ➡️  객체 속성 표현 방식. 상대적으로 간결한 표현

 


 

2.  JSON 파일 입출력

 

 1) JSON 파일이란?

 

    👾  JavaScript Object Notation의 약자로 JavaScript의 객체 표현 방법이라는 뜻

    👾  데이터를 전달할 때 사람이 읽고 쓰기 쉽도록 텍스트 형식을 정해 놓은 개방형 데이터 표준 형식
    👾  많은 양의 데이터를 처리할 때는 속도가 느린 단점이 있으므로 경량의 데이터를 주고 받을 때 주로 사용
 


 2) JSON 데이터 형식


    👾  속성 attribute - 값 value 쌍으로 구성된 데이터들이 순서없이 모여 있는 구조
          ⚡️ 중괄호를 이용해서 전체 JSON 데이터를 묶어 주고 속성과 값 사이에 콜론(:)을 붙여 줌
          ⚡️ 속성과 값으로 구성된 각 데이터의 구분은 쉼표(,)를 이용
                ∴  즉, 파이썬의 딕셔너리와 같은 모습

 



3) JSON 파일 생성


    👾  JSON 데이터 처리를 위해서 json 모듈 제공
         ⚡️  json 모듈을 import 하면 리스트나 딕셔너리와 같은 파이썬 객체를 JSON 데이터로 변환하거나,
              JSON 데이터를 다시 파이썬 객체로 변환할 수 있음

    👾  JSON 인코딩 encoding : 파이썬 객체를 JSON 문자열로 변환하는 것
         ⚡️ 변환된 문자열을 파일에 기록하면 확장자가 json인 JSON 파일 (*.json)을 만들 수 있음
         ⚡️ json.dumps() 메소드를 이용해서 JSON 인코딩을 처리할 수 있음

# json 모듈 import
import json

dict_list = [
    {
        'name': 'james',
        'age': 20,
        'spec': [
            175.5,
            70.5
        ]
    },
    {
        'name': 'alice',
        'age': 21,
        'spec': [
            168.5,
            60.5
        ]
    }
]

# 인코딩 메소드를 이용해 json 문자열로 변환
json_string: str = json.dumps(dict_list)
print(json_string)
'''
실행결과)
[{"name": "james", "age": 20, "spec": [175.5, 70.5]}, 
 {"name": "alice", "age": 21, "spec": [168.5, 60.5]}]
'''

# output_02 디렉토리에 json 파일 생성
with open('../output_02/dict_list_01.json', 'w') as file:
    file.write(json_string)

print('dict_list_01.json 파일이 생성되었습니다.')

 


(1) indent 옵션

 

📌  JSON 데이터는 네트워크를 주고받는 데이터의 표준 형식
📌  파일의 크기를 줄이기 위해서 의도적으로 불필요한 공백 문자 white space가 제거된 상태로 만들어 지기 때문에 

      사람들이 직접 그 내용을 해석하기에는 불편

 

    ⚡️ 보기 좋은 형식으로 만들기 위해서 json.dump() 메소드에 indent 옵션을 추가 할 수 있음
           ➡️  indent 옵션을 추가하면 JSON 데이터에 들여쓰기와 줄 바꿈이 추가되면서 보기 좋은 형식으로 만들어짐

json_string = json.dumps(dict_list, indent=4)  # indent 옵션 사용

with open('../output_02/dict_list_02.json', 'w') as file:
    file.write(json_string)

print('dict_list_02.json 파일이 생성되었습니다.')

 

 

📁  indent = 4는 들여쓰기로 공백문자 4개로 처리 한다는 의미
📁  하지만 indent 옵션을 사용하면 파일의 크기가 커지기 때문에 네트워크 전송이 목적이라면 사용하지 않는 것이 좋음
📁 JSON 데이터는 문자열을 큰따옴표로 묶어서 처리

 

 

 

 

 

 

 

 


 

 4) JSON 파일 읽기


    👾  JSON 디코딩 decording : 인코딩된 JSON 데이터를 다시 파이썬 객체로 변경하는 것
    👾  디코딩을 처리하는 메소드는 json.loads()

with open('../output_02/dict_list_02.json', 'r') as file:
    json_reader = file.read()
    dict_list = json.loads(json_reader)
    print(dict_list)
    # [{'name': 'james', 'age': 20, 'spec': [175.5, 70.5]}, 
    # {'name': 'alice', 'age': 21, 'spec': [168.5, 60.5]}]
    print(type(dict_list))  # <class 'list'>

 

    📁  json 파일을 read() 메소드로 한 번에 전체를 읽어 들여 json_reader에 저장
    📁  json.loads(json_reader)를 통해서 디코딩이 이뤄지면 파이썬 객체인 dict_list가 생성
    📁  dict_list는 리스트 내부에 2개의 딕셔너리가 저장된 구조이기 때문에 for 문을 이용해서 그 내용을 모두 처리 가능

 


 

3. JSON과 API

🚀  JSON은 데이터를 사람이 읽을 수 있는 문자열(바이너리가 아니라 텍스트)로 나타내는 방식 
🚀  많은 웹 사이트에서 프로그램이 웹 사이트와 상호 작용하는 데 JSON 형식의 내용을 제공
        ➡️  이를 API 제공이라고 하고, API로 접근하는 것은 URL을 통해 일반 웹 페이지에 접근하는 것과 동일
        ➡️  차이점API가 반환하는 값은 기계를 위한 JSON 형식으로 되어 있음

🚀  원하는 데이터를 얻으려면 프로그램이 요청해야 할 URL 뿐만 아니라 반환되는 JSON 데이터 구조의 일반적인 형식에 대한 문서도 찾아 봐야 되는데, API를 제공하는 모든 사이트는 이 문서를 제공

 


 

1) JSON 모듈


👾  JSON 모듈은 json.loads(), json.dumps() 함수로 JSON 데이터와 파이썬 값을 서로 변환하는 모든 세부 사항을 처리
👾  JSON문자열, 정수, 부동 소수점 수, 불, 리스트(배열) '[]', 딕셔너리(객체) '{}', NoneType 자료형 값만 저장 가능

import json

 


 

2) loads() 함수를 사용하여 JSON 읽기


👾  JSON 데이터가 들어 있는 문자열을 파이썬 값으로 변환하려면, 이를 json.loads() 함수에 전달
👾  JSON 문자열은 큰 따옴표를 사용

string_json_data: str = '{"name": "Zopgie", "isCat": true, "miceCaught": 0, "felineIQ": null}'
json_data_as_python_value = json.loads(string_json_data)
print(type(json_data_as_python_value))  # <class 'dict'>
print(json_data_as_python_value)
# {'name': 'Zopgie', 'isCat': True, 'miceCaught': 0, 'felineIQ': None}

 

# 온라인에서 JSON 파일 열기
import urllib.request as request

# urlopen() 함수 사용하여 소스 가져옴
json_data = request.urlopen("https://jsonplaceholder.typicode.com/todos/1").read()
print(type(json_data))  # <class 'bytes'>

# loads() 함수로 파이썬 객체로 변환
python_data = json.loads(json_data)
print(type(python_data))  # <class 'dict'>
print(python_data)  
#{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}

   

    📌  urllib.request : 내부모듈(url 라이브러리). 기본적인 기능만 제공
    📌  requests : 외부모듈. 다양한 기능을 제공. 실작업에 많이 사용

 


 

3) dupms() 함수를 이용하여 JSON 작성하기


👾  파이썬 값을 JSON 형식 데이터 문자열로 변환

python_data: dict = {'name': 'Zopgie', 'isCat': True, 'miceCaught': 0, 'felineIQ': None}
json_data: str = json.dumps(python_data)
print(type(json_data))  # <class 'str'>
print(json_data)  
# {"name": "Zopgie", "isCat": true, "miceCaught": 0, "felineIQ": null}

 

    📌  JSON 에서는 True ▶️ true,  None ▶️ null 로 표현

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 자료 ]

+ Recent posts