본문 바로가기

자기계발/대외활동

[공공빅데이터 청년인턴십] [day7 - 07.13(화)] Python으로 배우는 외부데이터 수집과 정제(2)

7일차..

이제는 아침 8시 반 출첵에 상당히 적응이 됐다.

6시에 일어나서 씻고 사무실에 7시 반까지 오면

1시간 정도 복습 및 수업준비를 하며 여유있게 하루를 시작할 수 있다.

 

 

암튼 오늘 학습할 강의 목차는 다음과 같다.

 

크롤러 만들기

☞ 기본 웹 크롤러

☞ 고급 웹 크롤러

☞ 셀레늄을 이용한 크롤러

 


 

2.1 기본 웹 크롤러 (1)

 

  • 사용모듈 : Requests
  • 대상사이트 : 한빛미디어 사이트의 "새로나온 책" 목록 
  • 방법: 새로나온 책의 목록들을 request 객체를 이용해서 접근, 목록 페이지에 포함되어 있는 상세페이지들의 link를 추출

 

목록페이지에서 퍼머링크 목록 추출

ㄴ 실습(1). 3.7.1.1.python_crawler_1.ipynb실습

 

 

http 요청을 보낼 때, 위의 대상 url을 가지고 정보를 보내면

한빛미디어 사이트의 서버에서 그 페이지에 대한 정보를 html의 형태로 우리에게 response 응답을 줄 것.

그럼, 우리는 그 reponse 들 중에서 책 제목에 걸려있는 하이퍼링크 부분만 추출해서

그 링크를 따라서 다시 상세페이지로 이동할 것.

 

① requests 모듈과 lxml.html라이브러리 호출

import requests
import lxml.html

② 서버 요청에 대한 응답을 html형태로 받은(get) 걸, response 객체(변수)에 저장. 

response = requests.get('http://www.hanbit.co.kr/store/books/new_book_list.html')

③ response는 객체형태로 되어있기 때문에, 객체 중에서 특정 문자열(html문자열)만 뽑아오는 과정.

response객체의 content 부분을 문자열로 변환해서 root 변수에 저장.

root = lxml.html.fromstring(response.content)

④ css selector가 .view_box a 인 곳이 우리가 추출하고자 하는 하이퍼링크가 걸려있는 위치

 root.cssselect('찾고자 하는 셀렉터')

root.cssselect('.view_box a')

root.cssselect('.view_box a')를 통해 추출한 url 하나하나 a 변수에 저장. 

a태그에 저장된 주소 정보 href=url 에서, href=를 제외한 url부분을 url 변수에 저장

for a in root.cssselect('.view_box a'):
    url = a.get('href')
    print(url)

 

 

But 위의 코드 수행 결과, 불필요한 javascript 부분이 같이 추출!

/store/books/look.php?p_code=B3715221949
javascript:;

 

개선사항1) "javascript"로 시작하는 목록 제거 필요

개선사항2) 상대 url이 추출됨. 서버측에 해당 url 요청을 보내 상세페이지 정보를 얻어야 하므로

              상대 url을 절대 url로 변환 필요

 


ㄴ 실습(1). 3.7.1.2.python_crawler_2.ipynb실습

import requests
import lxml.html

response = requests.get('http://www.hanbit.co.kr/store/books/new_book_list.html')
root = lxml.html.fromstring(response.content)

# 모든 링크를 절대 URL로 변환
root.make_links_absolute(response.url)

# 목록에서 javascript 제거
for a in root.cssselect('.view_box .book_tit a'):
    url = a.get('href')
    print(url)



2.2 기본 웹 크롤러 (2)

1) 목록페이지에서 퍼머링크 목록 추출

 

yield 함수 : 제네레이터 요소 반환. async 비동기 처리를 위해 사용

※ async방식

함수를 처리하거나 함수의 for루프를 돌면서 처리를 할 때

한번에 보낸 호출에 대한 응답을 잠시 유예했다가 (한꺼번이 아닌) 하나씩 하나씩 응답하는 방식.

하나의 응답을 받고, 그것을 처리할 동안 다음 응답을 받기 위해 사용. 

해야할 일을 위임하고 대기하는 방식.

( ↔ sync 방식 : 순차적/직렬로 처리하는 방식)

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Concepts

 

일반적인 비동기 프로그래밍 개념 - Web 개발 학습하기 | MDN

이 문서에서는 비동기적 프로그래밍과 관련된 몇개의 개념들을 살펴볼 것입니다. 그리고 이것들이 웹브라우저와 자바스크립트에서 어떻게 보이는지도 살펴볼 것입니다. 이 모듈의 다른 문서들

developer.mozilla.org

 

 

ㄴ 실습(2). 3.7.1.3.python_crawler_3.ipynb실습

import requests
import lxml.html

def main():
    """
    크롤러의 메인 처리
    """
    # 여러 페이지에서 크롤링을 위해 Session 사용
    session = requests.Session()  
    # scrape_list_page() 함수를 호출해서 제너레이터를 추출
    response = session.get('http://www.hanbit.co.kr/store/books/new_book_list.html')
    urls = scrape_list_page(response)
    # 제너레이터는 list처럼 사용 가능
    for url in urls:
        print(url)
        print('-'*70)
        

def scrape_list_page(response):
    root = lxml.html.fromstring(response.content)
    root.make_links_absolute(response.url)
    for a in root.cssselect('.view_box .book_tit a'):
        url = a.get('href')
        # yield 구문으로 제너레이터의 요소 반환
        yield url
        
        
if __name__ == '__main__':
    main()

 

2) 상세 페이지 스크래핑

 

 

ㄴ 실습(2). 3.7.2.1.python_crawler_4.ipynb실습

import requests
import lxml.html

def main():
    # 여러 페이지에서 크롤링을 위해 Session 사용
    session = requests.Session()  
    # scrape_list_page() 함수를 호출해서 제너레이터를 추출
    response = session.get('http://www.hanbit.co.kr/store/books/new_book_list.html')
    urls = scrape_list_page(response)
    for url in urls:
        response = session.get(url)  # Session을 사용해 상세 페이지를 추출
        ebook = scrape_detail_page(response)  # 상세 페이지에서 상세 정보를 추출
        print(ebook)  # 상세 정보 출력
        break  
        
def scrape_list_page(response):
    root = lxml.html.fromstring(response.content)
    root.make_links_absolute(response.url)
    for a in root.cssselect('.view_box .book_tit a'):
        url = a.get('href')
        yield url
        
def scrape_detail_page(response):
    """f
    상세 페이지의 Response에서 책 정보를 dict로 추출
    """
    root = lxml.html.fromstring(response.content)
    ebook = {
        'url': response.url,
        'title': root.cssselect('.store_product_info_box h3')[0].text_content(),
        'price': root.cssselect('.pbr strong')[0].text_content(),
        'content': [p.text_content()\
            for p in root.cssselect('#tabs_3 .hanbit_edit_view p')]
    }
    return ebook
   
   if __name__ == '__main__':
    main()

 

 

 

'content'를 가져오기 위해 List Comprehension을 사용. [ ] 대괄호 형식.

반복을 통해 필터링 하거나 하는 목적을 위해 사용.

 

◆ List Comprehension이 2가지 구성요소로 이루어진 경우

출력부 + for 루프 부분

: for 루프를 돌면서 출력부분으로 최종 출력함.

 

◆ List Comprehension이 3가지 구성요소로 이루어진 경우

출력부 + for 루프 부분 + if 부분

: if 부분은 필터링을 위해 사용.

먼저 for 루프에서 집합의 값을 하나씩 꺼내옴 → if 부로 보냄.

if 부분에서 그 부분을 필터링 함.

→ 그러고나서 최종 출력부로 보냄.

 

 

ㄴ 실습(2). 3.7.2.2.python_crawler_5.ipynb실습

import time
import requests
import lxml.html
import re

def main():
    # 여러 페이지에서 크롤링을 위해 Session 사용
    session = requests.Session()  
    # scrape_list_page() 함수를 호출해서 제너레이터를 추출
    response = session.get('http://www.hanbit.co.kr/store/books/new_book_list.html')
    urls = scrape_list_page(response)
    for url in urls:
        time.sleep(1) # 1초간 대기
        response = session.get(url)  # Session을 사용해 상세 페이지를 추출
        ebook = scrape_detail_page(response)  # 상세 페이지에서 상세 정보를 추출
        print(ebook)  # 상세 정보 출력
        break  

def scrape_list_page(response):
    root = lxml.html.fromstring(response.content)
    root.make_links_absolute(response.url)
    for a in root.cssselect('.view_box .book_tit a'):
        url = a.get('href')
        yield url

def scrape_detail_page(response):
    """
    상세 페이지의 Response에서 책 정보를 dict로 추출
    """
    root = lxml.html.fromstring(response.content)
    ebook = {
        'url': response.url,
        'title': root.cssselect('.store_product_info_box h3')[0].text_content(),
        'price': root.cssselect('.pbr strong')[0].text_content(),
        'content': [normalize_spaces(p.text_content()) # 출력부
            for p in root.cssselect('#tabs_3 .hanbit_edit_view p') # 목차에 해당하는 부분을 loop를 돌면서 하나씩 가져와서 p에 넣음
            if normalize_spaces(p.text_content()) != ''] # if 필터링. 공백문자열이 아닌 경우에만 최종 출력으로 보냄. 순수하게 문자열로 구성된 라인들만 보내기 위함. 공백 라인을 제거하기 위함.
    }
    return ebook

def normalize_spaces(s):
    """
    연결된 공백을 하나의 공백으로 변경
    """
    return re.sub(r'\s+', ' ', s).strip()

if __name__ == '__main__':
    main()

 

 

- List Comprehension

List Comprehension이란 반복적으로 생성가능한 객체(object)를 생성하기 위한 방법이다. 파이썬은 다음과 같이 4가지의 Comprehension 기능을 제공한다.

  • List Comprehension
  • Set Comprehension
  • Dict Comprehension
  • Generator Expression

[출처] https://wikidocs.net/46742

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

 

 

 

 

- break : for 루프를 빠져나가는 명령어

- time.sleep(1) : 1초 동안 잠들어있다가 일어나서 다음 작업을 수행하라는 뜻.  import time 후 사용 가능

※ 크롤링을 반복 수행하다보면 해당 사이트 (서버)에 부하를 주어 작업이 차단될 수 있기 때문

 

 

 

 

 

2.3 고급 웹 크롤러 (1) 

①번 작업 : 뉴스 목록 첫 페이지 (상세 기사에 대한 하이퍼링크 추출) 

②번 작업 : 상세 페이지 (추출된 링크를 가지고, 상세페이지 접속 및 원하는 정보 추출)

③번 작업 : 다음 페이지 이동 (다음페이지에서 다시 목록추출+상세페이지 추출)

 

 

하이라이트 된 부분의 css selector 또는 xpath 가 핵심포인트

 

 

ㄴ 실습(3). 3.8.1.1.daum_news_list_1.ipynb실습

import requests
import lxml.html # lxml을 통한 파싱. 대신 beautifulsoup 사용해도 ok.
import pandas as pd  # 판다스 : 2차원 데이터를 처리할 때 가장 좋은 방법. DBMS에 테이블로 저장할 때 가장 손쉽게 저장할 수 있는 방법. 엑셀, json 파일로 저장할 때도 판다스가 가장 적합.
import sqlite3  #sqlite3 DB를 사용하기 위해 임포트
from pandas.io import sql # DBMS에 있는 데이터를 다룰 때 판다스.io의 모듈을 이용해 사용.
import os


REG_DATE = '20200819' # 임의의 날짜를 하루 fix함.


'''
# Url 다음의 ? 뒤에는 쿼리 string을 문자열로 보낼 수 있음 
# key = value 형태로 구성
# (key와 value의 구조인데, = 로 연결되어 있음)

# url에서 필요한 세부 정보까지 보낼 때 쿼리 스트링을 이용. 
# url 과 쿼리 스트링을 구분해주는 구분자가 물음표 (?)
'''
response = requests.get('https://news.daum.net/breakingnews/digital?regDate={}'.format(REG_DATE))                   
root = lxml.html.fromstring(response.content)
for li in root.xpath('//*[@id="mArticle"]/div[3]/ul/li'):  # xpath 추출하는 법 ▼ 참고
    a = li.xpath('div/strong/a')[0]			   #li 하위 태그
    url = a.get('href')
    print(url, a.text)

 

 

※ xpath 추출하는 법

(xpath란?

XML 문서의 특정 요소나 속성에 접근하기 위한 경로를 지정하는 언어

file system에서 c드라이브 > 하위디렉토리 의 구조로 이루어진 것처럼

root엘리멘트부터 하위 엘리멘트들을 / (슬래시)로 연결한 것)

 

 

 

※ 코드 상세 설명

 

for li in root.xpath('//*[@id="mArticle"]/div[3]/ul/li'):

 

xpath로 첫번째 li를 가져와서 li에 저장하고 for루프 돌게하고

xpath로 두번째 li를 가져와서 li에 저장하고 for루프 돌게하고

xpath로 세번째 li를 가져와서 li에 저장하고 for루프 돌게하고

...

li의 갯수( = 뉴스기사 목록 갯수)만큼 루프 돌 것.

 

 

    a = li.xpath('div/strong/a')[0]

    url = a.get('href')

    print(urla.text)

 

loop돌며 실행되는 코드.

loop를 돌면서,

li 부분의 xpath를 또 찾아서 하위에 있는 퍼머링크를 추출해와야 함.

li태그의 최하위 a태그 정보를 a 변수에 저장 (a태그의 href에 퍼머링크가 저장되어 있기 때문)

 

 

 

 

2.4 고급 웹 크롤러 (2)

 

브라우저 상에서 파란색 하이라이트 부분을 xpath를 통해 어떻게 찾아갈 것인 지에 주안점

 

 

 

ㄴ 실습(4). 3.8.1.2.daum_news_list_2.ipynb실습

import requests
import lxml.html
import pandas as pd
import sqlite3
from pandas.io import sql
import os


import re  # regular expression: 정규식을 사용하기 위해 필요한 모듈
import string # string.punctuation 변수에 특수문자의 집합 모두 담겨있음

def get_detail(url):
    body = []
    punc = '[!"#$%&\'()*+,-./:;<=>?[\]^_`{|}~“”·]'
    response = requests.get(url)
    root = lxml.html.fromstring(response.content)
    for p in root.xpath('//*[@id="harmonyContainer"]/section/p'):
        if p.text: # 체크
            body.append(re.sub(punc, '', p.text)) # 특수문자 제거
    full_body = ' '.join(body)       # 여러라인을 하나의 문자열로 합침
    
    return full_body

get_detail('https://news.v.daum.net/v/20200505000102404')

 

※ 코드 상세 설명

    for p in root.xpath('//*[@id="harmonyContainer"]/section/p'):

 

위치에 관계없이 엘리먼트의 속성이 id 이고 id 속성의 값이 harmonyContainer인 것을 찾고

그 위치의 하위 엘리먼트 section과 p를 찾으라는 의미

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ 

// 슬래시 2개                      : 현재 노드 위치와 관계없이 지정된 노드에서부터 순서대로 탐색

//* 슬래시 2개+에스터리크(*)  : 어느 위치에 있든 관계없이 탐색

가 붙어있으면 속성 attributes

 

 

 

ㄴ 실습(4). 3.8.1.3.daum_news_list_3.ipynb실습

import requests
import lxml.html
import pandas as pd
import sqlite3
from pandas.io import sql
import os

import re
import string

def get_detail(url):
    body = []
    punc = '[!"#$%&\'()*+,-./:;<=>?[\]^_`{|}~“”·]'
    response = requests.get(url)
    root = lxml.html.fromstring(response.content)
    for p in root.xpath('//*[@id="harmonyContainer"]/section/p'):
        if p.text: # 체크
            body.append(re.sub(punc, '', p.text)) # 특수문자 제거
    full_body = ' '.join(body)
    
    return full_body
    
page = 1
max_page = 0
REG_DATE = '20200819'

response = requests.get('http://news.daum.net/breakingnews/digital?page={}&regDate={}'\
                        .format(page, REG_DATE))
root = lxml.html.fromstring(response.content)
for li in root.xpath('//*[@id="mArticle"]/div[3]/ul/li'):
    a = li.xpath('div/strong/a')[0]
    url = a.get('href')
    article = get_detail(url)
    print(f'URL : {url}')
    print(f'TITLE : {a.text}')
    print(f'ARTICLE : {article}')
    print('-' * 100)

 

 

 

2.5 셀레늄을 이용한 크롤러 (1)

 

※ 코드 상세 설명

   span = root.xpath('//*[@id="mArticle"]/div[3]/div/span/a[@class="btn_page btn_next"]')

마지막 페이지의 삼각꺾쇠가 있나 없나를 판별하기 위한 xpath

 

    if (len(span) <= 0) & (page > max_page):

        break

span이 0보다 작고( = 꺾쇠가 없으면 *꺾쇠가 있으면 span=1)  & 현재 page가 max_page보다 크면

≫ 이 두 조건을 모두 만족시켜야 (=마지막 페이지여야) break에 걸려 loop를 빠져나옴 

 

나머지의 경우 else 조건에 걸려 그 다음 페이지로 이동

    else:

        page = page + 1

 

 

ㄴ 실습(5). 3.8.1.4.daum_news_final.ipynb실습

import requests
import lxml.html
import pandas as pd
import sqlite3
from pandas.io import sql
import os
import time

def db_save(NEWS_LIST):
    with sqlite3.connect(os.path.join('.','sqliteDB')) as con: # DBMS마다 커넥션 맺는 방법이 상이하기때문에 사용하는 DBMS에 맞게 이 부분 코드 수정 필요
        try:
            NEWS_LIST.to_sql(name = 'NEWS_LIST', con = con, index = False, if_exists='append') 
            #if_exists : {'fail', 'replace', 'append'} default : fail
        except Exception as e:
            print(str(e))
        print(len(NEWS_LIST), '건 저장완료..')


# where 절을 주면, 특정 조건을 만족하는 데이터만 삭제 가능        
def db_delete():
    with sqlite3.connect(os.path.join('.','sqliteDB')) as con: 
        try:
            cur = con.cursor()
            sql = 'DELETE FROM NEWS_LIST'
            cur.execute(sql)
        except Exception as e:
            print(str(e)) 
            
# where 조건문을 통해, 특정 날짜, 특정 조건을 만족하는 데이터만 추출 & 데이터 프레임으로 리턴 가능                    
def db_select():
    with sqlite3.connect(os.path.join('.','sqliteDB')) as con: 
        try:
            query = 'SELECT * FROM NEWS_LIST'
            NEWS_LIST = pd.read_sql(query, con = con)
        except Exception as e:
            print(str(e)) 
        return NEWS_LIST   


import re
import string

def get_detail(url):
    body = []
    punc = '[!"#$%&\'()*+,-./:;<=>?[\]^_`{|}~“”·]'
    response = requests.get(url)
    root = lxml.html.fromstring(response.content)
    for p in root.xpath('//*[@id="harmonyContainer"]/section/p'):
        if p.text: # 체크
            body.append(re.sub(punc, '', p.text)) # 특수문자 제거
    full_body = ' '.join(body)
    
    return full_body
    
    
page = 58
max_page = 0
REG_DATE = '20200819'    

while(True):
    df_list = []
    response = requests.get('http://news.daum.net/breakingnews/digital?page={}&regDate={}'\
                            .format(page, REG_DATE))
    root = lxml.html.fromstring(response.content)
    for li in root.xpath('//*[@id="mArticle"]/div[3]/ul/li'):
        a = li.xpath('div/strong/a')[0]
        url = a.get('href')
        article = get_detail(url)
        df = pd.DataFrame({'URL' : [url],'TITLE':[a.text],'ARTICLE' : [article]})
        df_list.append(df)   
        
    if df_list:   
        df_10 = pd.concat(df_list)
        db_save(df_10)

    # 페이지 번호 중에서 max 페이지 가져오기    
    for a in root.xpath('//*[@id="mArticle"]/div[3]/div/span/a'):
        try:
            num = int(a.text)
            if max_page < num:
                max_page = num       
        except:
            pass

    # 마지막 페이지 여부 확인     
    span = root.xpath('//*[@id="mArticle"]/div[3]/div/span/a[@class="btn_page btn_next"]')

    if (len(span) <= 0) & (page > max_page):
        break
    else:
        page = page + 1
        
    time.sleep(1)    
    
    
    # db_delete()
    print(db_select())

 

 

 

2.6 셀레늄을 이용한 크롤러 (2)

 

셀레늄 Selenium 이란?

다양한 프로그래밍 언어로 웹드라이버를 통해 다양한 브라우저 상에서 웹 자동화 테스트 혹은 웹 자동화 프로그램을 구현하기 위한 라이브러리

 

지원 브라우저

Chrome, FireFox, Safar, Opera, Internet Explorer

 

지원 언어

Python, R, Java, JavaScript, Ruby, PHP, C#, Objective-C 등

 

 

ㄴ 실습(6). 셀레늄 설치

① selenium설치

(Chrome 버전 확인 -  91.0.4472.124)

② stand alone 서버 필요 (Chrome 버전 맞춰서)

③ webdriver 다운로드 필요 (Chrome 버전 맞춰서)


 

① selenium설치   &  ② stand alone 서버 필요

 

③ webdriver 다운로드 필요

 

 

 

 

2.7 셀레늄을 이용한 크롤러 (3)

# 드라이버를 메모리 상에 로드하고 크롬 브라우저를 기동시키는 과정

options = webdriver.ChromeOptions()
options.add_argument("--start-maximized");
# 크롬 브라우저를 최대한 크게 유지하기 위함.
# 반응형 UI로 인해 '로그인' 등 찾고자 하는 값의 위치가 변동되거나 사라질 수 있기 때문

browser = webdriver.Chrome('chromedriver', options=options)

 

 

 

 

 

ㄴ DataFrame 파라미터로 딕셔너리 자료형 (=json과 유사) 사용

DataFrame 사용 이유 : DBMS에 저장하거나 파일시스템에 사용하기 굉장히 용이하기 때문

ㄴ 주의할 점

DataFrame을 만들기 위해 딕셔너리를 정의할 때

key에 해당하는 value값은 list형태/포맷으로 감싸줘야 함!

'NO': [tr.find_element_by_xpath('td[1]').text]

 

for tr in trs:
    df = pd.DataFrame({
            'NO': [tr.find_element_by_xpath('td[1]').text],
            'TITLE': [tr.find_element_by_xpath('td[2]').text],
            'IQRY': [tr.find_element_by_xpath('td[3]').text],
            'REGDT': [tr.find_element_by_xpath('td[4]').text],
            'CHGDT': [tr.find_element_by_xpath('td[5]').text],
        })
    df_list.append(df) # 한 행에 대한 dataFrame을 list에 하나씩 추가(저장)
    
ARTICLE_LIST = pd.concat(df_list) # list에 저장된 복수개의 dataFrame을 하나로 합치는 작업 - pd.concat 사용
								  # df_list는 여러개(=행의 갯수만큼)의 dataFrame이 들어있는 리스트
								# 엑셀파일에 한 행씩 저장 (파일 여러개).. -> 여러행의 "하나의 엑셀파일로 통합"하는 것 	
db_save(ARTICLE_LIST)

 

 

 

 

ㄴ 실습(7). 3.8.2.1.selenium.ipynb실습

# !pip install selenium

from selenium.webdriver import Chrome
import time
import sqlite3
from pandas.io import sql
import os
import pandas as pd

from selenium import webdriver

# 드라이버를 메모리 상에 로드하고 크롬 브라우저를 기동시키는 과정
options = webdriver.ChromeOptions()
options.add_argument("--start-maximized");
# 크롬 브라우저를 최대한 크게 유지하기 위함.
# 반응형 UI로 인해 '로그인' 등 찾고자 하는 값의 위치가 변동되거나 사라질 수 있기 때문

browser = webdriver.Chrome('chromedriver', options=options)
# //

browser.get('https://www.data.go.kr/')
browser.implicitly_wait(5) #묵시적으로 5초 간 대기

# 기동된 브라우저를 통한 URL 접속
browser.find_element_by_xpath('//*[@id="header"]/div/div/div/div[2]/div/a[1]').click()
browser.implicitly_wait(5)

#browser.find_element_by_xpath('//*[@id="mberId"]').send_keys('bjkim2004')
browser.find_element_by_xpath('//*[@id="mberId"]').send_keys('hpark3')
#browser.find_element_by_xpath('//*[@id="pswrd"]').send_keys('TEJeuBlidb^2')
browser.find_element_by_xpath('//*[@id="pswrd"]').send_keys('hyeran!1211')

browser.find_element_by_xpath('//*[@id="loginVo"]/div[2]/div[2]/div[2]/div/div[1]/button').click()
browser.implicitly_wait(5)

browser.find_element_by_xpath('//*[@id="M000400_pc"]/a').click()

browser.find_element_by_xpath('//*[@id="M000402_pc"]/a').click()

def db_save(ARTICLE_LIST):
    with sqlite3.connect(os.path.join('.','sqliteDB')) as con: # sqlite DB 파일이 존재하지 않는 경우 파일생성
        try:
            ARTICLE_LIST.to_sql(name = 'ARTICLE_LIST', con = con, index = False, if_exists='append') 
            #if_exists : {'fail', 'replace', 'append'} default : fail
        except Exception as e:
            print(str(e))
        print(len(ARTICLE_LIST), '건 저장완료..')
        
trs = browser.find_elements_by_xpath('//*[@id="searchVO"]/div[5]/table/tbody/tr')
df_list = []
for tr in trs:
    df = pd.DataFrame({
            'NO': [tr.find_element_by_xpath('td[1]').text],
            'TITLE': [tr.find_element_by_xpath('td[2]').text],
            'IQRY': [tr.find_element_by_xpath('td[3]').text],
            'REGDT': [tr.find_element_by_xpath('td[4]').text],
            'CHGDT': [tr.find_element_by_xpath('td[5]').text],
        })
    df_list.append(df) # 한 행에 대한 dataFrame을 list에 하나씩 추가(저장)
    
ARTICLE_LIST = pd.concat(df_list) # list에 저장된 복수개의 dataFrame을 하나로 합치는 작업 - pd.concat 사용
								  # df_list는 여러개(=행의 갯수만큼)의 dataFrame이 들어있는 리스트
								# 엑셀파일에 한 행씩 저장 (파일 여러개).. -> 여러행의 "하나의 엑셀파일로 통합"하는 것 	
db_save(ARTICLE_LIST)    

browser.find_element_by_xpath('//*[@id="searchVO"]/div[5]/table/tbody/tr[1]/td[2]/a').click()
browser.implicitly_wait(3)

browser.find_element_by_xpath('//*[@id="recsroomDetail"]/div[2]/div[4]/div/a').click()
time.sleep(10)

browser.quit()

 

ㄴ 실습(7). 3.8.2.2.selenium_headless.ipynb실습

from selenium.webdriver import Chrome
import time
import sqlite3
from pandas.io import sql
import os
import pandas as pd

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument('--headless') #창이 뜨지 않게 설정
#창 킄기 조절 설정
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280x1024')

browser = webdriver.Chrome('chromedriver', options=options)


browser.get('https://www.data.go.kr/')
browser.implicitly_wait(5)

# browser.find_element_by_xpath('//*[@id="layer_popup_info_0"]/div[1]/a').click()
# browser.find_element_by_xpath('//*[@id="layer_popup_info_1"]/div[1]/a').click()

browser.find_element_by_xpath('//*[@id="header"]/div/div/div/div[2]/div/a[1]').click()
browser.implicitly_wait(5)

browser.find_element_by_xpath('//*[@id="mberId"]').send_keys('hpark3')
browser.find_element_by_xpath('//*[@id="pswrd"]').send_keys('hyeran!1211')

browser.find_element_by_xpath('//*[@id="loginVo"]/div[2]/div[2]/div[2]/div/div[1]/button').click()
browser.implicitly_wait(5)

# browser.find_element_by_xpath('//*[@id="layer_popup_info_0"]/div[1]/a').click()
# browser.find_element_by_xpath('//*[@id="layer_popup_info_1"]/div[1]/a').click()

browser.find_element_by_xpath('//*[@id="M000400_pc"]/a').click()
browser.find_element_by_xpath('//*[@id="M000402_pc"]/a').click()

def db_save(ARTICLE_LIST):
    with sqlite3.connect(os.path.join('.','sqliteDB')) as con: # sqlite DB 파일이 존재하지 않는 경우 파일생성
        try:
            ARTICLE_LIST.to_sql(name = 'ARTICLE_LIST', con = con, index = False, if_exists='append') 
            #if_exists : {'fail', 'replace', 'append'} default : fail
        except Exception as e:
            print(str(e))
        print(len(ARTICLE_LIST), '건 저장완료..')
        
trs = browser.find_elements_by_xpath('//*[@id="searchVO"]/div[5]/table/tbody/tr')
df_list = []
for tr in trs:
    df = pd.DataFrame({
            'NO': [tr.find_element_by_xpath('td[1]').text],
            'TITLE': [tr.find_element_by_xpath('td[2]').text],
            'IQRY': [tr.find_element_by_xpath('td[3]').text],
            'REGDT': [tr.find_element_by_xpath('td[4]').text],
            'CHGDT': [tr.find_element_by_xpath('td[5]').text],
        })
    df_list.append(df)
    
ARTICLE_LIST = pd.concat(df_list)
db_save(ARTICLE_LIST)

browser.find_element_by_xpath('//*[@id="recsroomDetail"]/div[2]/div[4]/div/a').click()
time.sleep(10)        

# 상세보기 화면에서 데이터 크롤링 로직 추가

browser.quit()

 

 

※ options.add_argument('--headless') 추가설명

 

HeadLess란?

Headless라는 용어는 '창이 없는'과 같다고 이해하시면 됩니다. 여러분이 브라우저(크롬 등)을 이용해 인터넷을 브라우징 할 때 기본적으로 창이 뜨고 HTML파일을 불러오고, CSS파일을 불러와 어떤 내용을 화면에 그러야 할지 계산을 하는 작업을 브라우저가 자동으로 진행해줍니다.

하지만 이와같은 방식을 사용할 경우 사용하는 운영체제에 따라 크롬이 실행이 될 수도, 실행이 되지 않을 수도 있습니다. 예를들어 우분투 서버와 같은 OS에서는 '화면' 자체가 존재하지 않기 때문에 일반적인 방식으로는 크롬을 사용할 수 없습니다. 이를 해결해 주는 방식이 바로 Headless 모드입니다. 브라우저 창을 실제로 운영체제의 '창'으로 띄우지 않고 대신 화면을 그려주는 작업(렌더링)을 가상으로 진행해주는 방법으로 실제 브라우저와 동일하게 동작하지만 창은 뜨지 않는 방식으로 동작할 수 있습니다.

 

https://beomi.github.io/gb-crawling/posts/2017-09-28-HowToMakeWebCrawler-Headless-Chrome.html

 

Headless 크롬으로 크롤링하기 · GitBook

Headless라는 용어는 '창이 없는'과 같다고 이해하시면 됩니다. 여러분이 브라우저(크롬 등)을 이용해 인터넷을 브라우징 할 때 기본적으로 창이 뜨고 HTML파일을 불러오고, CSS파일을 불러와 어떤

beomi.github.io