이 글은 <파이썬을 이용한 웹 크롤링과 스크레이핑 (위키북스, 카토 코타)>을 읽고 이해한 내용을 바탕으로 정리한 글입니다.
import re
import sqlite3
from urllib.request import urlopen
from html import unescape
💡main()
def main():
"""
메인 처리
fetch(), scrape(), save() 함수 호출
"""
html = fetch('http://www.hanbit.co.kr/store/books/full_book_list.html')
books = scrape(html)
save('books.db', books)
전체적인 scraping과정은 세 개의 함수에 의해 이루어진다.
1. fetch(url)
: 매개변수로 url을 받고 지정한 url의 전체 웹페이지를 가져온다.
2. scrape(html)
: 가져온 저체 HTML에서 정규 표현식을 사용해서 필요한 도서 정보만을 추출한다.
3. save(db_path, books)
:books라는 도서 목록을 받고, SQLight 데이터베이스에 저장한다.
💡fetch()
def fetch(url):
"""
매개변수로 전달받은 url을 기반으로 웹 페이지를 추출
웹 페이지의 인코딩 형식은 Content-Type 헤더를 기반으로 알아낸다.
반환값: str 자료형의 HTML
"""
f = urlopen(url)
#HTTP 헤더를 기반으로 인코딩 형식을 추출
encoding = f.info().get_content_charset(failobj="utf-8")
#추출한 인코딩 형식을 기반으로 문자열을 디코딩
html = f.read().decode(encoding)
return html
HTTPResponse 객체인 f의 info메서드로 추출된 HTTPMessage에서 get_content_charset()메서드로 인코딩 방식을 추출한다.
확인한 인코딩 방식으로 HTTPRespose객체를 디코딩하여 유니코드로 저장하여 리턴한다.
💡scrape()
def scrape(html):
"""
매개변수 html로 받은 HTML을 기반으로 정규 표현식을 사용해 도서 정보를 추출
반환값: 도서(dict) 리스트
"""
books = []
#re.findall()을 사용해 도서 하나에 해당하는 HTML을 추출
for partial_html in re.findall(r'<td class="left"><a.*?</td>', html, re.DOTALL):
#도서의 URL을 추출
url = re.search(r'<a href="(.*?)">', partial_html).group(1)
url = 'http://www.hanbit.co.kr' + url
#태그를 제거해서 도서의 제목을 추출
title = re.sub(r'<.*?>', '', partial_html)
title = unescape(title)
books.append({'url': url, 'title': title})
return books
정규 표현식을 통해서 도서별 링크와 도서 제목만 추출해오는 과정은 scrape()함수로 진행한다.
우선, re.findall()을 사용해서 도서 제목이 포함되어 있는 부분을 추출해온다.
이러면 partial_html은 <td class="left"><a href="/store/books/look.php?p_code=B2393470870">두 발로 선 경제</a></td> 이렇게 추출이 되어진다.
이렇게 추출된 partial_html에서 re.search()를 사용해서 도서별 링크만! 추출해온다.
group() 메서드의 매개변수에 1 이상의 숫자를 입력하면 정규표현식에서 ()로 감싼 부분만 추출해올 수 있다.
따라서 url은 /store/books/look.php?p_code=B2393470870 이런식으로 추출되어진다.
이걸로 전체 url을 만들었다.
그리고 다음은 도서 정보 가져오기이다.
re.sub()로 partial_html에서 태그를 제거하여 도서 제목만 추출하고 title에 저장한다.
그리고 가져온 url과 title을 딕셔너리 형태로 book 리스트에 저장한다!
💡save()
def save(db_path, books):
"""
매개변수 books로 전달된 도서 목록을 SQLite 데이터베이스에 저장
데이터베이스의 경로는 매개변수 db_path로 지정
반환값: None
"""
#데이터베이스를 열고 연결을 확립
conn = sqlite3.connect(db_path)
#커서 추출
c = conn.cursor()
#execute()메서드로 SQL 실행
#스크립트를 여러 번 실행할 수 있으므로 기존의 books 테이블을 제거
c.execute('DROP TABLE IF EXISTS books')
#books 테이블을 생성
c.execute('''
CREATE TABLE books(
title text,
url text
)
''')
#executemany()메서드를 사용하여 매개변수로 리스트를 지정
c.executemany('INSERT INTO books VALUES (:title, :url)', books)
#변경사항을 커밋(저장)
conn.commit()
#연결을 종료
conn.close()
만든 books 리스트를 지정된 경로에 db file로 저장하는 과정은 save()함수로 진행한다.
✅ 전체 코드
import re
import sqlite3
from urllib.request import urlopen
from html import unescape
def main():
"""
메인 처리
fetch(), scrape(), save() 함수 호출
"""
html = fetch('http://www.hanbit.co.kr/store/books/full_book_list.html')
books = scrape(html)
save('books.db', books)
def fetch(url):
"""
매개변수로 전달받은 url을 기반으로 웹 페이지를 추출
웹 페이지의 인코딩 형식은 Content-Type 헤더를 기반으로 알아낸다.
반환값: str 자료형의 HTML
"""
f = urlopen(url)
#HTTP 헤더를 기반으로 인코딩 형식을 추출
encoding = f.info().get_content_charset(failobj="utf-8")
#추출한 인코딩 형식을 기반으로 문자열을 디코딩
html = f.read().decode(encoding)
return html
def scrape(html):
"""
매개변수 html로 받은 HTML을 기반으로 정규 표현식을 사용해 도서 정보를 추출
반환값: 도서(dict) 리스트
"""
books = []
#re.findall()을 사용해 도서 하나에 해당하는 HTML을 추출
for partial_html in re.findall(r'<td class="left"><a.*?</td>', html, re.DOTALL):
#도서의 URL을 추출
url = re.search(r'<a href="(.*?)">', partial_html).group(1)
url = 'http://www.hanbit.co.kr' + url
#태그를 제거해서 도서의 제목을 추출
title = re.sub(r'<.*?>', '', partial_html)
title = unescape(title)
books.append({'url': url, 'title': title})
return books
def save(db_path, books):
"""
매개변수 books로 전달된 도서 목록을 SQLite 데이터베이스에 저장
데이터베이스의 경로는 매개변수 db_path로 지정
반환값: None
"""
#데이터베이스를 열고 연결을 확립
conn = sqlite3.connect(db_path)
#커서 추출
c = conn.cursor()
#execute()메서드로 SQL 실행
#스크립트를 여러 번 실행할 수 있으므로 기존의 books 테이블을 제거
c.execute('DROP TABLE IF EXISTS books')
#books 테이블을 생성
c.execute('''
CREATE TABLE books(
title text,
url text
)
''')
#executemany()메서드를 사용하여 매개변수로 리스트를 지정
c.executemany('INSERT INTO books VALUES (:title, :url)', books)
#변경사항을 커밋(저장)
conn.commit()
#연결을 종료
conn.close()
if __name__ == '__main__':
main()
전체 코드를 python 파일로 저장하고 터미널에서 실행후, sqlite3 명령어로 데이터를 확인할 수 있다.
(env) $ python python_scraper.py
(env) $ sqlite3 books.db 'SELECT * FROM books'
'Data Science' 카테고리의 다른 글
[Data Science from Scratch] Ch3. Visualizing Data (0) | 2021.12.08 |
---|---|
[Data Science from Scratch] Ch2. A Crash Course in Python (0) | 2021.12.03 |
👩💻 링크타고 브랜드 타이틀 크롤링 (1) | 2021.09.12 |
python 가상환경 사용하기 (conda) (0) | 2021.09.11 |
🌐 웹페이지 인코딩 방식 추출하기 (0) | 2021.07.24 |
댓글