http://beans-r-us.appspot.com/prices.html
위 사이트를 방문하면 실시간으로 변하는 커피콩의 시세를 아래와 같은 내용으로 확인할 수 있다.
참조: Head First Programming(한빛미디어) 2장
이번 장에서는 언급된 웹사이트를 직접 방문하지 않으면서 실시간으로 변하는 커피콩 가격(위 그림에서는 5달러 27센트)을 확인하는 방법을 배운다.
기본적으로 두 가지 기술이 필요하다.
웹사이트 주소를 이용하여 해당 사이트의 내용 전체를 읽어 들일 수 있다. 예를 들어 앞서 언급된 사이트의 소스코드 전체를 아래 방식으로 가져올 수 있다.
In [1]:
import urllib.request
page = urllib.request.urlopen("http://beans-r-us.appspot.com/prices.html")
text = page.read().decode("utf8")
실제로 확인하면 웹사이트의 내용 전체가 하나의 문자열로 저장되어 있다.
주의: html 관련 이해할 수 없는 기호들은 여기서는 일단 무시하고 넘어가는 게 좋다. 또한, 위 코드를 자세히 이해하지 못해도 상관 없다. 특정 웹사이트의 소스크드를 가져오기 위해 위 코드 형식을 사용한다는 것만 기억해 두면 된다.
In [2]:
text
Out[2]:
소스코드에서 줄바꾸기, 띄어쓰기, 인용부호 등 특수 기호를 적절하게 해석하여 출력하고자 하면 print
명령어를 사용한다.
In [3]:
print(text)
In [4]:
type(text)
Out[4]:
In [5]:
page = urllib.request.urlopen("http://beans-r-us.appspot.com/prices.html")
text_bytes = page.read()
In [6]:
type(text_bytes)
Out[6]:
In [7]:
text_bytes
Out[7]:
|
|
|
text
에 저장된 문자열을 다시 확인해보자.
In [8]:
print(text)
위 문자열에서 원하는 정보인 커피콩의 가격을 어떻게 추출할 것인가?
커피콩의 가격은 실시간으로 변한다. 하지만 다섯째 줄 끝부분에 위치하고
달러기호($
)로 시작하며 x.xx
형식의 소수로 표현된 부분이 커피콩의 가격 정보이다.
따라서, 예를 들어 문자열인 ">$"
의 위치를 알면 커피콩 가격정보를 얻을 수 있다.
그런데 특정 문자열 또는 문자의 위치를 어떻게 알 수 있을까?
바로 인덱스 정보와 슬라이싱 기능을 활용하면 된다.
In [9]:
a_food = "kebap"
특정 인덱스에 위치한 문자의 정보는 다음과 같이 확인한다.
In [10]:
a_food[0]
Out[10]:
In [11]:
a_food[1]
Out[11]:
In [12]:
a_food[2]
Out[12]:
등등.
In [13]:
a_food[-1]
Out[13]:
In [14]:
a_food[-2]
Out[14]:
등등.
In [15]:
a_food[5]
In [16]:
len(a_food)
Out[16]:
In [17]:
a_food
Out[17]:
kebap
에서 ke
부분을 추출하고 싶다면 다음과 같이 하면 된다:
In [18]:
a_food[0 : 2 : 1]
Out[18]:
즉, 문자열 처음부터 2번 인덱스 전까지, 즉 두 번째 문자까지 모두 추출하는 것이다. 반면에 하나씩 건너서 추출하려면 다음처럼 하면 된다:
In [19]:
a_food[0 : 4 : 2]
Out[19]:
시작인덱스, 끝인덱스, 계단 각각의 인자가 경우에 따라 생략될 수도 있다. 그럴 때는 각각의 위치에 기본값(default)이 들어 있는 것으로 처리되며, 각 자리의 기본값은 다음과 같다.
시작인덱스
의 기본값 = 0
끝인덱스
의 기본값 = 문자열의 길이계단
의 기본값 = 1
In [20]:
a_food[0 : 2]
Out[20]:
In [21]:
a_food[: 2]
Out[21]:
In [22]:
a_food[: 4 : 2]
Out[22]:
In [23]:
a_food[ : : 2]
Out[23]:
양수와 음수를 인덱스로 섞어서 사용할 수도 있다.
In [24]:
a_food[ : -1 : 2]
Out[24]:
주의: -1은 문자열의 끝인덱스를 의미한다.
끝인덱스가 문자열의 길이보다 클 수도 있다. 다만 문자열의 길이 만큼만 문자를 확인한다.
In [25]:
a_food[: 10]
Out[25]:
아래와 같이 아무 것도 입력하지 않으면 해당 문자열 전체를 추출한다.
In [26]:
a_food[:]
Out[26]:
시작인덱스 값이 끝 인덱스 값보다 같거나 작아야 제대로 추출한다. 그렇지 않으면 공문자열이 추출된다.
In [27]:
a_food[3 : 1]
Out[27]:
이유는 슬라이싱은 기본적으로 작은 인덱스에 큰 인덱스 방향으로 확인하기 때문이다. 역순으로 추출하고자 한다면 계단을 음수로 사용하면 된다.
In [28]:
a_food[3 : 1 : -1]
Out[28]:
In [29]:
a_food[-1 : : -1]
Out[29]:
인덱스와 슬라이싱의 기능을 이해하였다면 이제 text
변수에 할당된 문자열에서 ">$"
라는
문자열의 시작위치를 알아내기만 하면 된다.
아주 간단한 방법이 있다. 0번부터 시작해서 주욱 세어가면서 ">$"
의 시작 문자인
">"
의 인덱스를 확인하면 된다.
하지만 이런 방식은 아래와 같은 이유로 매우 위험하다.
이런 문제를 해결하는 좋은 방법이 있다.
바로 find()
라는 문자열 메소드를 활용하면 된다.
In [30]:
text.find(">$")
Out[30]:
이제, 찾고자 하는 ">$"
문자열이 232번 인덱스에서 시작한다는 것을 알았다.
따라서 커피콩의 가격정보는 인덱스가 2보다 큰 234번이고 거기서부터 길이가 4인
부분문자열에 담겨 있게 된다.
In [31]:
print(text[234: 238])
하지만, 여기서 234를 사용하기 보다는 find()
메소드를
직접 활용하는 것이 더욱 좋다.
In [32]:
price_index = text.find(">$") + 2
bean_price_str = text[price_index : price_index + 4]
print(bean_price_str)
주의:
bean_price_str
에 저장된 커피콩의 가격정보는 문자열로 저장되어 있다.
In [33]:
type(bean_price_str)
Out[33]:
그래서 예를 들어 커피콩 가격이 6달러 이상이면 커피숍의 아메리카노 가격을 올리고, 그렇지 않으면 가격을 그대로 유지하는 것을 실행하도록 하는 코드를 작성할 수가 없다.
이유는, 문자열은 숫자가 아니라서 문자열과 숫자를 직접 비교할 수 없기 때문이다.
하지만 숫자로만 이루어닌 문자열을 진짜 숫자로 형변환시킬 수 있다.
예를 들어 int()
또는 float()
함수를 이용한다.
In [34]:
a_number = int('4')
print(a_number)
print(type(a_number))
float()
함수를 이용하면 부동소수점 모양의 문자열을 부동소수점으로 형변환시킬 수 있다.
In [35]:
float('4.2') * 2
Out[35]:
주의: 문자열과 숫자의 곱은 의미가 완전히 다르다.
In [36]:
'4.2' * 2
Out[36]:
주의: int()
함수는 정수모양의 문자열에만 사용할 수 있다.
In [37]:
int('4.2') * 2
부동소수점 모양의 문자열이 아니면 float()
함수도 오류를 발생시킨다.
In [38]:
float('4.5GB')
주의: 기준 가격을 높게 책정하면 너무 오랫동안 기다려야 할 수도 있다.
In [39]:
import urllib.request
import time # 시간과 관련된 함수들의 모듈
price = 5.0
while price < 6.0:
time.sleep(1) # 코드 실행을 1초 정지한다.
page = urllib.request.urlopen("http://beans-r-us.appspot.com/prices.html")
text = page.read().decode("utf8")
where = text.find(">$") + 2
price_str = text[where : where + 4] # 가격정보 문자열
price = float(price_str) # 숫자로 형변환
print("커피콩 현재 가격이", price, "입니다. 아메리카노 가격을 인상하세요!")
In [40]:
week_days = " Mon, Tue, Wed, Thu, Fri, Sat, Sun "
strip()
메소드는 문자열의 양 끝을 지정한 문자열 기준으로 삭제하는 방식으로 정리한다.예를 들어, 문자열 양끝에 있는 스페이스를 삭제하고자 할 경우 아래와 같이 실행한다.
In [41]:
week_days.strip(" ")
Out[41]:
strip()
메소드를 인자 없이 호출하는 경우와 동일하다.
In [42]:
week_days.strip()
Out[42]:
split()
메소드는 지정된 부분문자열을 기준으로 문자열을 쪼개어 문자열들의 리스트로 반환한다.
리스트 자료형은 이후에 자세히 다룬다. 여기서는 기본적으로 알고 있는 내용으로 이해하면 된다.아래 예제는 ", "
, 즉 콤마와 스페이스를 기준으로 문자열을 쪼갠다.
In [43]:
week_days.split(", ")
Out[43]:
두 개 이상의 메소드를 조합해서 활용할 수도 있다.
예를 들어, strip()
메소드를 먼저 실행한 다음에 그 결과에 split()
메소드를 실행하면
좀 더 산뜻한 결과를 얻을 수 있다.
In [44]:
week_days.strip(" ").split(", ")
Out[44]:
replace()
메소드는 하나의 문자열을 다른 문자열로 대체한다.예를 들어, " Mon"
을 "Mon"
으로 대체할 경우 아래와 같이 실행한다.
In [45]:
week_days.replace(" Mon", "Mon")
Out[45]:
upper()
메소드는 모든 문자를 대문자로 변환시킨다.
In [46]:
week_days.upper()
Out[46]:
In [47]:
week_days.strip().upper()
Out[47]:
lower()
메소드는 모든 문자를 소문자로 변환시킨다.
In [48]:
week_days.lower()
Out[48]:
In [49]:
week_days.strip().lower()
Out[49]:
In [50]:
week_days.strip().lower().split(", ")
Out[50]:
capitalize()
메소드는 제일 첫 문자를 대문자로 변환시킨다.아래 예제는 변화가 없어 보인다. 이유는 첫 문자가 스페이스이기 때문이다.
In [51]:
week_days.capitalize()
Out[51]:
In [52]:
week_days.strip().capitalize()
Out[52]:
title()
메소드는 각각의 단어의 첫 문자를 대문자로 변환시킨다.참조: 영문 책 제목의 타이틀에서 각 단어의 첫 알파벳이 대문자로 쓰여지는 경우가 많다.
In [53]:
week_days.title()
Out[53]:
In [54]:
week_days.strip().title()
Out[54]:
startswith()
메소드는 문자열이 특정 문자열로 시작하는지 여부를 판단해준다.
In [55]:
week_days.startswith(" M")
Out[55]:
endswith()
메소드는 문자열이 특정 문자열로 끝나는지 여부를 판단해준다.
In [56]:
week_days.endswith("n ")
Out[56]:
In [57]:
week_days
Out[57]:
이와 같이 한 번 정해지면 절대 변경이 불가능한 자료형을 불변(immutable) 자료형이라 부른다.
주어진 문자열을 이용하여 새로운 문자열을 생성하고 활용하려면 새로운 변수에 저장하여 활용해야 한다.
In [58]:
stripped_week_days = week_days.strip()
In [59]:
stripped_week_days
Out[59]:
애완동물의 목록을 할당받는 pets
변수가 아래와 같이 선언되어 있다.
In [60]:
pets = 'dog cat hedgehog pig swan fish bird'
견본답안:
In [61]:
pets.title()
Out[61]:
견본답안:
In [62]:
pets.title()[4]
Out[62]:
견본답안:
In [63]:
pets[8 : 16]
Out[63]:
견본답안:
In [64]:
pets[8 : 16 : 2]
Out[64]:
견본답안:
In [65]:
pets[15: 7 : -1]
Out[65]:
In [66]:
dogs, cats = '8', '4'
견본답안:
In [67]:
print(int(dogs))
In [68]:
print(int(cats))
In [69]:
print(abs(int(dogs) - int(cats)))
힌트: 특정 문자열이 주어진 문자열에 부분문자열로 포함되어 있는지 여부를 판단해 주는 방식을 활용한다. 아래 예제들을 참조하라.
In [70]:
'ab' in 'abc'
Out[70]:
In [71]:
'cat' in 'casting'
Out[71]:
견본답안:
In [72]:
def find_dog(word):
if 'dog' in word:
found_dog = True
else:
found_dog = False
return found_dog
In [73]:
find_dog('Bull dog')
Out[73]:
In [74]:
find_dog('강아지')
Out[74]:
아래 코드는 커피콩의 현재 가격을 알아내어 일정 가격 이상이면 커피숍의 아메리카노 가격을 인상할 것을 권유하는 프로그램이다.
import urllib.request
import time # 시간과 관련된 함수들의 모듈
price = 5.0
while price < 6.0:
time.sleep(1) # 코드 실행을 1초 정지한다.
page = urllib.request.urlopen("http://beans-r-us.appspot.com/prices.html")
text = page.read().decode("utf8")
where = text.find(">$") + 2
price_str = text[where : where + 4] # 가격정보 문자열
price = float(price_str) # 숫자로 형변환
print("커피콩 현재 가격이", price, "입니다. 아메리카노 가격을 인상하세요!")
위 코드를 수정하여, 아래 내용을 수행하는 함수를 작성하라.
price_setter
b_price
): 기존의 커피콩 가격a_price
): 아메리카노 인상 또는 인하 가격price_setter(b_price, a_price)
를 실행할 때b_price
는 커피콩의 기존 가격을 의미한다.
서버의 특징 상 5.5와 6.0 사이의 숫자로 주는 게 좋다.b_price
보다 0.5 달러 이하면
아메리카노 가격을 a_price
만큼 내릴 것을 권유b_price
보다 0.5 달러 이상이면
아메리카노 가격을 a_price
만큼 올릴 것을 권유견본답안:
In [75]:
import urllib.request
import time # 시간과 관련된 함수들의 모듈
def price_setter(b_price, a_price):
price = b_price
while 5.5 < price < 6.0:
time.sleep(1) # 코드 실행을 1초 정지한다.
page = urllib.request.urlopen("http://beans-r-us.appspot.com/prices.html")
text = page.read().decode("utf8")
where = text.find(">$") + 2
price_str = text[where : where + 4] # 가격정보 문자열
price = float(price_str) # 숫자로 형변환
print("현재 커피콩 가격이", price, "달러 입니다.")
if price <= 5.5:
print("아메리카노 가격을", a_price, "달러만큼 인하하세요!")
else:
print("아메리카노 가격을", a_price, "달러만큼 인상하세요!")
예를 들어, 현재 커피콩의 가격이 5.7달러이고, 커피콩의 실시간 가격이
5.2달러 이하이면 아메리카노의 가격을 50센트 내리고
6.2달러 이상이면 50센트 올리라고 권유하고자 한다면 아래와 같이
price_setter()
함수를 호출하면 된다.
In [76]:
price_setter(5.7, 0.5)
기상청에서 날씨 정보를 확인하는 프로그램을 작성하고자 한다.
먼저 기상청 정보를 담고 있는 아래 사이트의 소스코드를 읽어 온다.
http://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId-108
In [77]:
import urllib.request
page = urllib.request.urlopen("http://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId-108")
text = page.read().decode("utf8")
읽어 온 소소크드 내용의 앞 부분을 확인하면 다음과 같다.
In [78]:
text[0:1000]
Out[78]:
이제 비가 올지 여부를 설명하는 부분을 찾아서 비
라는 단어의 포함여부에 따라 우산을 가져가야 하는지 여부를 결정하는 코드를 아래와 같이 작성할 수 있다.
In [79]:
where_s = text.find("CDATA[]") + 6
where_e = text.find("]]></wf>")
text_weather = text[where_s : where_e]
text_weather_clean = text_weather.replace("<br />", " ")
if '비' in text_weather_clean:
print("우산을 가져가세요!")
else:
print("우산이 필요 없습니다!")