python scraping 연습 (한빛미디어 도서리스트 scraper 만들기)

    반응형
    이 글은 <파이썬을 이용한 웹 크롤링과 스크레이핑 (위키북스, 카토 코타)>을 읽고 이해한 내용을 바탕으로 정리한 글입니다.
     

    웹 크롤링과 스크레이핑

    《파이썬을 이용한 웹 크롤링과 스크레이핑》은 파이썬을 이용한 데이터 수집과 분석을 위해 입문에서 실전까지 상세하게 설명한 책입니다.기본적인 크롤링과 API를 활용한 데이터 수집은 물론

    book.naver.com

     

    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'

    반응형

    댓글