일이 조금 있어서 네이버 카페의 크롤링 코드를 만들어야했다. 여러가지 글을 종합해서 처리할게 있었다. 가장 궁금했던건 어떻게 OTP인증이나 User-Agent가 브라우저가 아닌 요청이 Allow될지 였는데, 역시나 Selenium을 통해서 하면 User의 Action을 구사해서 할 수있었다.(Python 짱짱맨) 아래는 코드이다.

import time
from selenium import webdriver
import pandas as pd
from bs4 import BeautifulSoup as bs
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

if __name__ == "__main__" :

    ### 로그인 프로세스
    login_url='https://nid.naver.com/nidlogin.login'
    user_id="<USER  ID>"
    user_pw="<USER  PW>"

    browser=webdriver.Chrome(ChromeDriverManager().install())
    browser.get(login_url)
    browser.implicitly_wait(2)

    browser.execute_script("document.getElementsByName('id')[0].value=\'"+ user_id + "\'")
    browser.execute_script("document.getElementsByName('pw')[0].value=\'"+ user_pw + "\'")

    browser.find_element(by=By.XPATH,value='//*[@id="log.login"]').click()
    time.sleep(1)

    ### 크롤링 Target Url
    baseurl=''
    browser.get(baseurl)
    
    pageNum = 0

    while(True) :
        
        time.sleep(1)
        
        pageNum +=1 
        print(pageNum)

        browser.get(baseurl +'&search.page='+ str(pageNum))
        
        ### 카페 메인 게시클 화면이 iframe으로 구성되어있음. iframe id가 cafe_main
        browser.switch_to.frame('cafe_main')

        soup = bs(browser.page_source ,'html.parser')
        soup = soup.find_all(class_ = 'article-board m-tcol-c')[1]

        datas = soup.select("#main-area > div:nth-child(4) > table > tbody > tr")

        for data in datas:
            ### article 은 한줄입니다. 원하는 속성 get 후 처리
            article = data.find(class_= "article")
            
    print('Finish')

Python과 다른 언어와의 큰 차이점을 두자면(요즈음 들어서는 많은 언어가 지원하기에 퇴색된 기능이지만) Type으로부터의 자유로움을 손에 꼽을 수 있다.

>>> a=1 >>> b="heyhey" >>> type(a) <class 'int'> >>> type(b) <class 'str'>

위와 같이 다른 언어에서의 변수 선언 시 type을 같이 지정해 주어야 하는 재래식 언어와는 많은 차별점을 둔다.

다만 이런 Python의 장점은 코드의 규모가 커지고 방대해질 수록 많은 문제를 야기하며, 프로그램의 동작상에서 동작하는 Runtime level의 문제라기보다는, 프로그래머 사이 작성되는 코드 혹은 과거의 나와 싸우고 있는 프로그래머들에게 Type의 힌트를 줄 필요성이 생겼다.

<최솟값을 찾는 함수 : JAVA의 경우>

// 최소값을 찾는 JAVA함수, int를 반환하고 ArrayList<int>를 받는다는 것을 함수에서 알 수 있다. public int findMinimumNumber(ArrayList<int> arr){ int result = arr[0]; for(int a : arr) if(result > a) result=a; return result; }

<최솟값을 찾는 함수 : Python의 경우>

### 내장함수 min에 의해 처리될 수 있는 value를 입력으로 받고 그의 원소값을 반환 ### 어떤형의 parameter를 받고 반환하는지 알 수 없다. def find_minimum(value) : return min(value)

위와 같은 문제들을 해결하기 위해 함수에 주석을 달거나 하는 프로그래머 개개인의 노력을 이용했다.

### param : list ret : int def find_minimum(value) : return min(value)

Python도 이 문제를 인식한 듯 Type Annotation을 제공한다. 다음과 같다.

def find_minimum(value : list) -> int : return min(value)

사용법은 간단한다.

  • 변수명 뒤 콜론(:)을 붙이고 type명을 기술 type명에는 클래스도 들어갈 수 있다.
  • 함수의 반환은 화살표(->)을 붙이고 type명을 기술 당연하게도 여기도 클래스가 들어갈 수 있다.

단! 이렇게 적는 type annotation은 주석의 확장으로 밖에 여겨지지 않으며, 인터프리터는 해석 시 type annotation을 염두하지 않는다.

pyautogui는 외장모듈이다. 모듈명 그대로 다운로드 가능하다.

pip3 install pyautogui

역할은 마우스, 키보드의 입력 / 이동을 Python으로 조절하기 위한 모듈이다. 흔히 마우스 마음대로 움직이거나, 키보드가 맘대로 쳐지면 이와 비슷한 모듈이 들어가 있다고 보면 된다.


주요 함수 설명

  • pyautogui.size() : 현재 모니터의 해상도를 (x,y) tuple로 반환한다.
  • pyautogui.onScreen(x,y) : 좌표 x,y의 위치가 모니터 안에서 유효한 좌표인지 bool값으로 반환한다.
  • pyautogui.position() : 현재 마우스의 위치를 반환한다.
  • pyautogui.moveTo(x,y,duration=num_seconds) : duration에 적힌 기간동안 x,y의 좌표로 마우스를 이동한다.
  • pyautogui.moveRel(xoffset,yoffset,duration=num_seconds) : duration에 적힌 기간동안 xoffset, yoffset 만큼 마우스를 이동한다.
  • pyautogui.dragTo(x,y,duration=num_seconds) : duration에 적힌 기간동안 x,y로 마우스를 드래그(클릭 후 이동)한다.
  • pyautogui.dragRel(xoffset,yoffset,duration_num_seconds) : duration에 적힌 기간동안 xoffset, yoffset 만큼 마우스를 드래그(클릭 후 이동)한다.
  • pyautogui.click(x=moveToX,y=moveToY,clickes=num, interval=secs_betweens_clicks,button="left") : 현재위치에서 클릭한다. parameter로는 옆과 같은것을 줄 수 있다.
  • pyautogui.rightClick(x=moveToX,moveToY) : 우클릭
  • pyautogui.middleClick(x=moveToX,moveToY) : 휠클릭
  • pyautogui.doubleClick(x=moveToX,moveToY) : 더블클릭
  • pyautogui.tripleClick(x=moveToX,moveToY) : 세번클릭
  • pyautogui.scroll(amount_to_scroll,x=moveToX,y=moveToY) : 마우스 스크롤 한다. 양수면 위로, 음수면 아래로
  • pyautogui.mouseDown(x=moveToX, y=moveToY, button='left') : 마우스버튼을 "누른다"
  • pyautogui.mouseUp(x=moveToX, y=moveToY, button='left') : 마우스버튼을 "뗀다"
  • pyautogui.typewrite(type_string, interval=secs_between_keys) : type_string을 친다.
  • pyautogui.hotkey(param1, param2...) : 단축키로 쓰인다. 보통 param에 'ctrl', 'shift'등이 쓰인다.
  • pyautogui.keyDown(key_name) : key_name을 '누른다'
  • pyautogui.keyUp(key_name) : key_name을 '누른다'
  • pyautogui.alert(message) : 경고 메시지 박스
  • pyautogui.confirm(message) : 확인 메시지 박스
  • pyautogui.prompt(message) : user Input 처리를 위한 메시지 박스
  • pyautogui.screenshot(region=(x1,y1,x2,y2)) : 스크린샷을 찍는다. PIL.Image 객체를 반환한다.
  • pyautogui.locateOnScreen(Image_name) : 모니터화면에서 Image_name에 해당하는 Image를 찾아서 위치를 반환한다.(웹 개체는 안된다. 로컬개체만)

이거말고도 pixelMatchsColor등 특정 규칙에 맞게 화면의 마우스를 이동시키는 방법도 있다.

 

출처 : https://pyautogui.readthedocs.io/en/latest/index.html

 

Welcome to PyAutoGUI’s documentation! — PyAutoGUI documentation

Welcome to PyAutoGUI’s documentation! PyAutoGUI lets your Python scripts control the mouse and keyboard to automate interactions with other applications. The API is designed to be as simple. PyAutoGUI works on Windows, macOS, and Linux, and runs on Pytho

pyautogui.readthedocs.io

 

* pyautogui.FAILSAFE라는 모듈 설정이 있다. 기본 규칙은 TRUE로 되어있다. 화면끝에 마우스가 부딫히면 잠깐동안 프로그램을 멈추는 기능이다.

CVE_SCRAPPER를 만들고, 다음 목표는 이제 한물간 시장인 비트코인이다. 자동매매 프로그램을 올릴 거냐고? 물론 당연하다. 단 필자는 금융지식이 뛰어난 사람은 아니기 때문에 일단은 할 수 있는 장만 만들어 보려고 한다. 매도/매수 알고리즘은 돌리는 사람이 짜는 걸로 해야 되지 않을까 싶다. 그렇게 만들려면 으흠; 뭐 잘 만들면 되겠지

  • 목표 완성 시기 : '21. 6. 27.(일)
  • 형태 : PE(exe, GUI)
  • 사용언어 : Python
  • 개발툴 : IDLE, PyCharm
  • 사용 라이브러리 : PyUpbit, Requests, Json 등 웹 처리 모듈
  • 주요 기능
    • 현황 비트코인 대시보드
    • 사용자의 조건 지정 
    • 사용자 조건에 따른 매수/매도 동작

* 프로그램 요청이나 산출물 요청은 댓글로 달아주세요!

 

기다리고 기다리던 CVE SCRAPPER의 시현 시간이다. 개발정보는 -계획,진행-편에 들어있다.

2021.06.16 - [Python/Python Project] - [Python Project] CVE Scrapper 만들기 - 계획,진행-

 

[Python Project] CVE Scrapper 만들기 - 계획,진행-

블로그에 올릴 장기적인 프로그램을 만들고자 한다. 정보보안에 관심있는 필자이다 보니, 당연히 취약점에 눈이 들어오는건 당연하다. 속내를 말하자면 "와 이거 하루에 하나씩만 해도 한 3년은

tutoreducto.tistory.com

 


디렉토리 구조

이렇다. 코드 각각의 설명은 -기능도-편을 참고하자

2021.06.19 - [Python/Python Project] - [Python Project] CVE Scrapper 만들기 - 기능도-

 

[Python Project] CVE Scrapper 만들기 - 기능도-

코드별 클래스, 함수명에 대한 설명이다.

tutoreducto.tistory.com

 


시현

CveMain은 다음과 같이 실행된다. vendor의 목록은 cve_list directory에서 긁어온다. 

 

Make Report로 보고서를 만들 수 있다. Exported?가 True인 행은 Information dialog가 띄워지며 export가 안된다. exported 된 여부 판단은 export_cve directory에 해당 cve보고서가 존재하는지 여부로 판단한다.

export 된 보고서는 export_cve directory에서 찾을 수 있다.


OUTPUT

 

보고서는 다음과 같이 제작되어있다.

CVE-2010-4218.pdf
0.18MB


후기

짧은 프로젝트였다. 정식으로 뿌리는 것도 아니고, 디자인을 하나도 안 한 프로젝트였다. 찾아보니 TISTORY API가 있는 거 같은데, 이거 자동화해서 블로그에 보고서를 올리면 좋을 거 같다. 크롤링 코드와 gui를 연습할 수 있는 좋은 경험이었던 거 같다. fpdf를 사용하다 보니 편하다는 것을 느꼈다. body부분의 글자별 스타일 적용이 어렵다는 느낌은 받았지만 다음 자동화 프로젝트도 fpdf로 출력할 거 같다.

파이선에서 날짜에 대한 연산을 도와주는 calendar라는 모듈이다. 현실세계의 날짜 연산에서 사용될 수 있는 계산 함수들을 제공한다. 하나하나 알아보자


Calendar 객체

모든 날짜 연산은 캘린더 객체에서 이루어 진다. 객체는 다음과 같이 생성한다.

>>> my_cal = calendar.Calendar()
### Calendar(firstweekday=0)로 인자지정이 가능하다. firstweekday는 언제가 그 주의 시작요일인지 정하는 것이다)

Calendar객체가 제공하는 연산은 다음과 같다.

  • iterweekdays() : 0~6까지의 요일 인덱스를 담고 있는 iterator를 반환한다. firstweekday가 지정되어있으면 시작 순서가 바뀐다.
  • itermonthdays(year,month) : year년도의 month동안의 iterator를 반환한다. 그니까 itermonthdays(2020,12)면 2020년 12월의 일을 포함한 iterator를 반환하는 건데, 달력에 표기하기 위한 만큼의 넘어가는주와 넘어오는주를 포함해서 반환한다. 그때 값은 0이다.
  • itermonthdays2(year,month) : itermonthdays와 같은 범위이나, (일, 요일)의 튜플형태의 iterator를 반환한다.
  • itermonthdays3(year,month) : itermonthdays와 같은 범위이나, (년, 월, 일)의 튜플형태의 iterator를 반환한다.
  • itermonthdays4(year,month) : itermonthdays와 같은 범위이나, (년, 월, 일, 요일)의 튜플형태의 iterator를 반환한다.
  • monthdatescalendar(year,month) : itermonthdays와 같은 범위이나, 매일매일에 대한 datetime.date객체를 리스트로 반환한다.
  • monthdays2calendar(year,month) : itermonthdays와 같은 범위이나, 하루하루는 (일, 요일)의 튜플로, 주 단위로 리스트로 반환한다.
  • monthdayscalendar(year,month) : itermonthdays와 같은 범위이나, 주 단위 리스트로 반환한다
  • yeardatescalendar(year,width=3) : 연도에 대한 매일매일을 datetime.date객체로 월 단위 리스트로 반환한다. 이때 한 리스트원소에 얼만큼의 월이 들어갈지가 width이다.
  • yeardays2calendar(year,width=3) : 위와 유사하나 datetime.date객체는 아니고 요일이 포함된 튜플을 반환한다.
  • yeardayscalendar(year.width=3) : 위와 유사하나 datetime.date객체는 아니고 날짜만 해당하는 리스트를 반환한다.

 

 

TextCalendar 객체

Calendar는 숫자와 객체로 이루어져 있어 가시성이 떨어진다. 조금 보기 쉬운 표현을 위해서 TextCalendar라는 객체가 존재한다.

>>> my_cal = calendar.TextCalendar()
### TextCalendar(firstweekday=0)로 인자지정가능하다. firstweekday는 어디가 요일의 시작일지 이다.

TextCalendar 객체가 제공하는 연산은 다음과 같다.

  • formatmonth(theyear,themonth,w=0,l=0) : 해당 월의 달력을 출력한다. w, l로 달력 크기 지정을 한다.
  • prmonth(theyear,themonth,w=0,l=0) : formatmonth의 결과를 인쇄한다.
  • formatyear(theyear,w=2,l=1,c=6,m=3) : 해당 년도의 달력을 출력한다. w,l로 달력의 크기를, c로 월 사이사이의 공간의 크기를, m으로 몇 열로 출력할지를 정한다.
  • pryear(theyear,w=2,l=1,c=6,m=3) : formatyear의 결과를 인쇄한다.

 

 

HTMLCalendar 객체

HTMLCalendar은 HTML에서 사용할 수 있게끔 HTML 태그를 붙여준다. 

>>> my_cal = calendar.HTMLCalendar()
### HTMLCalendar(firstweekday=0)로 인자지정가능하다. firstweekday는 어디가 요일의 시작일지 이다.

TextCalendar 객체가 제공하는 연산은 다음과 같다.

  • formatmonth(theyear,themonth,withyear=True) : 월의 달력을 html Table로 반환한다. withyear의 설정으로 연도를 헤더에 표현할지를 선택할 수 있다.
  • formatyear(theyear,width=3) : 연도의 달력을 html Table로 반환한다. width는 행에 얼만큼의 월이 들어 갈지 이다.
  • formatyearpage(theyear,width=3,css='calendar.css',encoding=True) : 연도의 달력을 완전한 html로 생성한다. css를 지정할 수 있다.

* TextCalendar에는 css를 재정의 할 수 있도록 여러가지 Attrib도 있으니 사용이 필요하다면 포스팅 끝의 공식 문서를 참고바란다.

 

 

 

정적 매서드

다음은 calendar에서 제공하는 정적 매서드이다. Calendar.매서드명 으로 객체 생성 없이 이용 가능하다.

  • setfirstweekday(weekday) : 현재 import된 모듈의 calendar weekday를 weeday로 고정한다 주의 첫요일이 바뀐다. calendar에는 리터럴로 calendar.MONDAY등을 제공하니 사용 바란다.
  • firstweekday() : 요일의 현재 설정을 반환한다.
  • leapdays(y1,y2) : y1년과 y2년 사이의 윤년의 수를 반환한다.
  • weekday(year,month,day) : year년 month월 day일의 요일을 반환한다.
  • weekheader(n) : 요일의 헤더를 반환한다. n은 헤더의 길이이다(1 : "F", 3 : "Fri" 등)
  • monthrange(year,month) : year년 month월의 첫날의 요일과 월의 일수를 반환한다.
  • monthcalendar(year,month) : year년 month의 행렬을 반환한다.
  • month(theyear,themonth,w=0,l=0) : TextCalendar의 formatmonth를 바로 이용 가능하게 한다.
  • calendar(year,w=0,l=0,c=6,m=3) : TextCalendar의 formatyear를 바로 이용 가능하게 한다.

 

 

어트리뷰트

다음은 Calendar가 가진 클래스 어트리뷰트이다.

  • calendar.day_name : 현재 로케일의 요일을 나타내는 배열이다.
  • calendar.day_abbr : 현재 로케일의 약식 요일을 나타내는 배열이다.
  • calendar.month_name : 현재 로케일의 연중 월을 나타낸다.
  • calendar.mont_abbr : 현재 로케일의 연중 월을 약식으로 나타낸다.

 

* 출처 : https://docs.python.org/ko/3/library/calendar.html

 

calendar — 일반 달력 관련 함수 — Python 3.9.5 문서

calendar — 일반 달력 관련 함수 소스 코드: Lib/calendar.py 이 모듈을 사용하면 유닉스 cal 프로그램과 같은 달력을 출력할 수 있으며, 달력과 관련된 유용한 추가 함수를 제공합니다. 기본적으로, 이

docs.python.org


정적 매서드의 프린팅 함수는 제외하고 기술했다. 참고하도록 하자

+ Recent posts