본문 바로가기
Web & Mobile/Python

Lecture 90 - Python(10) xml, html, json 처리법, 정규식 사용법

by Bennyziio 2019. 8. 6.
반응형

원격데이터 

xml 
   html(웹페이지) 
    
json 
   * 
csv 
   문자열 중심(X)

https://docs.python.org/3/library/markup.html

https://wikidocs.net/42
=> 위키독스에서 7장 파이썬도구들 -> 파이썬으로 XML처리하기 -> XML문서생성하기에서
저 내용 복사해서 이클립스에서 복사붙여넣기

파이썬으로 XMl 처리하기
XML 문서 생성하기

DataEx01.note.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- note.xml -->
<note date="20120104">
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

DataEx01.ex01

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()
print(root)

어트리뷰트 추가하기

DataEx01.ex01

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()
print(root)

print(root.get('date'))

date를 잘 못 썻을 경우 에러

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()
print(root)

print(root.get('dat'))

초기화 - default

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()
print(root)

print(root.get('dat', 'default'))

여러개 값을 가지고 올 경우

DataEx01.note.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- note.xml -->
<note date1="20120104" date2="20120105" date3="20120106">
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

DataEx01.ex01

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()
print(root)

print(root.get('date', 'default'))
print(root.get('date1'))

print(root.keys())
print(root.items())

=> 
 - get 메서드는 애트리뷰트의 값을 읽는다. 만약 두 번째 입력 인수로 디폴트 값을 주었다면 첫 번째 인자에 해당되는 애트리뷰트 값이 없을 경우 두 번째 값을 리턴한다.
 - keys는 모든 애트리뷰트의 키 값을 리스트로 리턴하고, items는 key, value 쌍을 리턴한다. 애트리뷰트 값을 가져오는 방법은 앞에서 배운 딕셔너리와 동일함을 알 수 있다.

XML 태그 접근하기

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()

from_tag = root.find('from')
print(from_tag)

안에 태그와, 내용 출력하기

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()

from_tag = root.find('from')
print(from_tag)
# <from>태그 출력
print(from_tag.tag)
# 내용값 출력
print(from_tag.text)

DataEx01.note.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- note.xml -->
<note date1="20120104" date2="20120105" date3="20120106">
    <to>Tove</to>
    <from>Jani1</from>
    <from>Jani2</from>
    <from>Jani3</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

from 태그를 여러개 추가하여 Jani1, 2, 3을 만들어 주었다. from 태그 내용을 출력하면 첫번째 것만 나온다.

Jani1, 2, 3가 다 나왔으면 좋겠다. 이럴때 findall을 사용하면 된다.
Findall

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()

# from_tag = root.find('from')
# print(from_tag)
# # <from>태그 출력
# print(from_tag.tag)
# # 내용값 출력
# print(from_tag.text)

# findall 여러개 리스트 출력하기
from_tag = root.findall('from')
print(from_tag)

위와 같이 데이터가 출력되는데 이쁘지가 않다. 이걸 배열로 출력해보자

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()

# from_tag = root.find('from')
# print(from_tag)
# # <from>태그 출력
# print(from_tag.tag)
# # 내용값 출력
# print(from_tag.text)

# findall 여러개 리스트 출력하기
from_tags = root.findall('from')
print(from_tags)
print(from_tags[0].tag, from_tags[0].text)

아직도 이쁘지가 않다. 첫번째 것만 가져온다. for문을 이용해서 갯수만큼 가져오게 하자.

from xml.etree.ElementTree import parse

xml = parse('note.xml')
root = xml.getroot()

# from_tag = root.find('from')
# print(from_tag)
# # <from>태그 출력
# print(from_tag.tag)
# # 내용값 출력
# print(from_tag.text)

# findall 여러개 리스트 출력하기
from_tags = root.findall('from')
print(from_tags)
print(from_tags[0].tag, from_tags[0].text)

# for문으로 출력하기
for from_tag in from_tags:
	print(from_tag.text)

문자열 선언

from xml.etree import ElementTree

xmldata = '''

<note date1="20120104" date2="20120105" date3="20120106">
    <to>Tove</to>
    <from>Jani1</from>
    <from>Jani2</from>
    <from>Jani3</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>
'''

root = ElementTree.fromstring(xmldata)
print(root)

from xml.etree import ElementTree

xmldata = '''

<note date1="20120104" date2="20120105" date3="20120106">
    <to>Tove</to>
    <from>Jani1</from>
    <from>Jani2</from>
    <from>Jani3</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>
'''

root = ElementTree.fromstring(xmldata)
print(root)
print(root.tag)

어제 구글에서 지역찾아서 출력하는 거 xml로 바꾸어보자

URLEx01.ex08

dongname = input('장소를 입력해주세요 : ')
if(len(dongname) <= 1):
	print('장소를 한 자 이상 입력해 주세요')
	exit()

url = 'https://maps.googleapis.com/maps/api/geocode/xml?'

querystring = {'address': dongname, 'key': 'AIzaSyArHE1hXsBVVHAr1W1eUjLOp34W6hcybIU'}
req = Request(url + urlencode(querystring), headers= {'accept-language': 'ko-KR', 'User-Agent': 'Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'})

#키	값
# User-Agent	Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
urldata = urlopen(req)

xml = urldata.read().decode('utf-8')
print(xml)

dongname = input('장소를 입력해주세요 : ')
if(len(dongname) <= 1):
	print('장소를 한 자 이상 입력해 주세요')
	exit()

url = 'https://maps.googleapis.com/maps/api/geocode/xml?'

querystring = {'address': dongname, 'key': 'AIzaSyArHE1hXsBVVHAr1W1eUjLOp34W6hcybIU'}
req = Request(url + urlencode(querystring), headers= {'accept-language': 'ko-KR', 'User-Agent': 'Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'})

#키	값
# User-Agent	Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
urldata = urlopen(req)

xml = urldata.read().decode('utf-8')
print(xml)

root = ElementTree.fromstring(xml)
print(root.tag)

HTML.parser도 해보자
https://docs.python.org/3/library/html.html
=> HyperText Markup Language support

DataEx01.ex04

from html.parser import HTMLParser

html = '''
<html>
</html>
'''

parser = HTMLParser()
parser.feed(html.strip())

print(parser.get_starttag_text())

parser.close()

body를 추가하자

from html.parser import HTMLParser

html = '''
<html>
	<head></head>
	<body></body>
</html>
'''

parser = HTMLParser()
parser.feed(html.strip())

print(parser.get_starttag_text())

parser.close()

콜 백 자동적으로 문서를 분석해줌

from html.parser import HTMLParser

# 상속(Override)
class MyHTMLParser(HTMLParser):
	def handle_starttag(self, tag, attrs):
		print('시작 : ', tag)
	def handle_endtag(self, tag):
		print('끝 : ', tag)

html = '''
<html>
	<head>
		<title>Test</title>
	</head>
	<body>
		<h1>Parse me!</h1>
	</body>
</html>
'''

parser = MyHTMLParser()
parser.feed(html.strip())

parser.close()

HTML 안에 내용을 보고싶다

from html.parser import HTMLParser

# 상속(Override)
class MyHTMLParser(HTMLParser):
	def handle_starttag(self, tag, attrs):
		print('시작 : ', tag)
	def handle_endtag(self, tag):
		print('끝 : ', tag)
	def handle_data(self, data):
		print('내용 : ', data)

html = '''
<html>
	<head>
		<title>Test</title>
	</head>
	<body>
		<h1>Parse me!</h1>
	</body>
</html>
'''

parser = MyHTMLParser()
parser.feed(html.strip())

parser.close()

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
	def handle_starttag(self, tag, attrs):
		print('시작 : ', tag)
	def handle_endtag(self, tag):
		print('끝 : ', tag)
	def handle_data(self, data):
		print('내용 : ', data)

html = '''
<html>
	<head>
		<title>Test</title>
	</head>
	<body>
		<h1>Parse me!</h1>
	</body>
</html>
'''
parser = MyHTMLParser()
print(parser.feed(html))

html 파싱 

        beautiful soup - java 
         
        https://www.crummy.com/software/BeautifulSoup 

        scrapy 
                프레임워크 

beautiful soup 

외부라이브러리 설치 
        mariadb - 직접 설치(mariadb는 아래를 지원하지 않았음)
         
        외부 라이브러리 
        1. easy_install - 자동 설치 
        2. pip

C:\Users\kitcoop>pip install beautifulsoup4

pip 버전이 하위 버전이라고 나오니 업데이트 해주자

C:\Users\kitcoop>pip install --upgrade pip

C:\Users\kitcoop>pip -V
pip 18.0 from c:\python\python36\lib\site-packages\pip (python 3.6)

잘 설치되었는지 확인해보자

C:\Users\kitcoop>python
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]
Type "help", "copyright", "credits" or "license" for more information.
>>> from bs4 import BeautifulSoup
>>>

에러 없는 거 보니 설치가 잘 되었다.

BeautifulSoup을 사용해서 parsing해보자

DataEx01.ex05

from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'html.parser')
print(soup)

from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())

from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'html.parser')
# print(soup.prettify())
print(soup.title)

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'html.parser')
# print(soup.prettify())
print(soup.title)
print(soup.title.name)
print(soup.title.string)

파이썬으로 Json 처리하기
json이란?
json(JavaScript Object Notation)은 원래 자바스크립트에서 만들어진 데이터 표현방식으로 최근들어 사용이 부쩍 증가되고 있다. 예전에는 xml방식의 데이터 교환을 많이 했다면 요새는 거의 json으로 하는 추세이다.

json은 파이썬의 딕셔너리와 매우 비슷하게 생겼다.

json dumps, loads

DataEx01.ex07

import json

j1 = {'name': '홍길동', 'birth': '0525', 'age': 30}
print(j1)
str1 = json.dumps(j1)
print(str1)

import json

j1 = {'name': '홍길동', 'birth': '0525', 'age': 30}
print(j1)
str1 = json.dumps(j1)
print(str1)
str2 = json.dumps(j1, indent=2)
print(str2)

import json

j1 = {'name': '홍길동', 'birth': '0525', 'age': 30}
print(j1)
str1 = json.dumps(j1)
print(str1) # 객체
str2 = json.dumps(j1, indent=2)
print(str2)

j2 = [1, 2, 3]
j3 = (4, 5, 6)

print(json.dumps(j2)) # 배열
print(json.dumps(j3))

리스트나 튜플은 모두 동일한 Array형태의 json 문자열로 변환됨을 알 수 있다

json file json 파일을 파이썬 객체로 읽어보자

DataEx01.ex08

import json

j1 = {'name': '홍길동', 'birth': '0525', 'age': 30}
print(type(j1))
str1 = json.dumps(j1)
print(type(str1))

import json

j1 = {'name': '홍길동', 'birth': '0525', 'age': 30}
print(type(j1))
str1 = json.dumps(j1)
print(type(str1))
j2 = json.loads(str1)
print(j2)
print(type(j2))

DataEx01.myinfo.json

{
	"name": "홍길동",
	"birth": "0525",
	"age" : 30
}

DataEx01.ex09

import json

with open('c:/Python/eclipse-workspace/DataEx01/myinfo.json', encoding='utf8') as f:
	data = json.load(f)

print(type(data))

encoding='utf8' 해주는거 잊지 말자

정규표현식 살펴보기
정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용된다. 정규 표현식을 배우는 것은 파이썬을 배우는 것과는 또 다른 영역의 과제이다.

정규 표현식은 왜 필요한가?

정규 표현식 시작하기
정규 표현식의 기초, 메타 문자

문자 클래스 [ ]

Dot(.)

반복 (*) - 무한반복

반복 (+) - 최소 한번 이상 반복

반복 ({m,n}, ?) - 몇번 반복일지 정하는 것

2는 2번 반복

2,5는 2~5

?는 있어도 되고 없어도 되고

파이썬에서 정규 표현식을 지원하는 re 모듈

정규식을 이용한 문자열 검색

match

ReEx01.ex01 - match() : 처음부터 정규식과 매치되는지 조사

import re

p = re.compile('[a-z]+')
m1 = p.match('python')
print(m1)

m2 = p.match('3python')
print(m2)

import re

p = re.compile('[a-z]+')
m1 = p.match('python')
print(m1)

m2 = p.match('3python')
print(m2)

if m1:
	print('Match found: ', m1.group())
else:
	print('No match')

search - 전체 검색

import re

p = re.compile('[a-z]+')
m1 = p.search('python')
print(m1)

m2 = p.search('3python')
print(m2)

findall - 모든 문자열을 리스트로 리턴

import re

p = re.compile('[a-z]+')
m = p.findall('life is too short')
print(m)

finditer - 모든 문자열을 iterator로 리턴

import re

p = re.compile('[a-z]+')
m = p.finditer('life is too short')
print(m)

match 객체의 메서드

import re

p = re.compile('[a-z]+')
m = p.match('python')
print(m.group()) # 매치된 문자열을 리턴
print(m.start()) # 매치된 문자열의 시작 위치를 리턴
print(m.end()) # 매치된 문자열의 끝 위치를 리턴
print(m.span()) # 매치된 문자열의 (시작, 끝)에 해당되는 튜플을 리턴

예상한 대로 결과값이 출력되는 것을 확인할 수 있다. match 메서드를 수행한 결과로 리턴된 match 객체의 start()의 결과값은 항상 0일 수밖에 없다. 왜냐하면 match 메서드는 항상 문자열의 시작부터 조사하기 때문이다.

만약 search 메서드를 사용했다면 start()의 값은 다음과 같이 다르게 나올 것이다.

import re

p = re.compile('[a-z]+')
m = p.search('3 python')
print(m.group()) # 매치된 문자열을 리턴
print(m.start()) # 매치된 문자열의 시작 위치를 리턴
print(m.end()) # 매치된 문자열의 끝 위치를 리턴
print(m.span()) # 매치된 문자열의 (시작, 끝)에 해당되는 튜플을 리턴

컴파일 옵션

DOTALL, S - 줄 바꿈 문자(\n)를 포함하여 모든 문자와 매치

import re

p1 = re.compile('a.b')
m = p1.match('a\nb')
print(m)

p2 = re.compile('a.b', re.DOTALL)
m = p2.match('a\nb')
print(m)

IGNORECASE, I - 대,소문자에 관계없이 매치

import re

p1 = re.compile('[a-z]', re.I)
m = p1.match('python')
print(m)
m = p1.match('Python')
print(m)
m = p1.match('PYTHON')
print(m)

 

MULTILINE, M - 여러줄과 매치할 수 있도록 한다.(^, $ 메타문자의 사용과 관계있음)
이 옵션을 지정하지 않으면 한줄만 검사한다.

^가 문자열앞에 들어가면 문자의 맨 앞이라는 것을 의미하고 $는 마지막을 의미

\s: whitespace 문자와 매치\w:문자+숫자와 매치

이것들을 잘 쓰면 검색이 용이하다.

VERBOSE, X - verbose 모드를 사용할 수 있도록 한다(정규식을 보기 편하게 만들 수 있고 주석등을 사용할 수 있게된다.)

 

반응형

댓글