2021-02-11

Posted by karais89 on February 11, 2021

크롤링보다는 자동화 쪽에 좀 더 관심이 있음.

26. 웹 브라우저 없는 스크랩핑 및 파싱 실습(1) - 인프런

urllib

requests (fake-useragent)

  1. selenium 개념 및 설치 (크롬, 파이어폭스)
  2. phantomjs 개념 및 설치
  3. selenimu을 이용한 사이트 이미지 캡쳐 텍스트 및 예제 작성
  4. selenium을 이용한 인프런 로그인 자동화 하기
  5. selenium & chrome or firefox headless mode 최종 세팅

selenium 문서 : http://selenium-python.readthedocs.io/index.html

다운로드 (chrome): https://sites.google.com/a/chromium.org/chromedriver/downloads

다운로드 (firefox): https://github.com/mozilla/geckodriver/releases

phantomjs 문서: http://phantomjs.org/documentation/

다운로드: http://phantomjs.org/download.html

환경설정

  1. selenium + 크롬
  2. selenium + 파폭
  3. selenium + phantomjs

크롬으로 실행할 예정

1
pip install selenium

웹드라이버도 다 다운로드 해야 됨.

section3에 webdrier 폴더 생성후 다운로드 받은 파일 압축풀어 집어넣기

환경변수로 등록하면 편하긴 하지만, 안하고 할 수 있음.

스크린샷 찍는 예제 - PhantomJS 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys
import io
from selenium import webdriver

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

driver = webdriver.PhantomJS('C:/workspace/section3/webdriver/phantomjs/phantomjs')

driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://google.com')
driver.save_screenshot("c:/website1.png")

driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://www.daum.net')
driver.save_screenshot("c:/website2.png")

driver.quit()

print('스크린샷 완료')
  • phantomjs는 더이상 selenium에서는 제공하지 않음.
  • 하지만 아직도 phantomjs를 많이 사용함.
  • implicitly_wait 암묵적으로 대기.
  • from selenium import webdriver (selenium 모듈의 webdriver을 가져옴)

스크린샷 찍는 예제 - 크롬

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import sys
import io
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

chrome_options = Options()
chrome_options.add_argument("--headless") # CLI

driver = webdriver.Chrome(chrome_options=chrome_options, executable_path='C:/workspace/section3/webdriver/chrome/chromedriver')
# driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
# driver.set_window_size(1920, 1080)
# driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://google.com')
# time.sleep(5)

driver.save_screenshot("c:/website_ch1.png")

# driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://www.daum.net')
# time.sleep(5)
driver.save_screenshot("c:/website_ch2.png")

driver.quit()

print('스크린샷 완료')

크롬에서 동작하는 것을 cli (command line interface)로 만들어야 됨

  • chrome_options = Options()
  • chrome_options.add_argument(“–headless”)
  • driver = webdriver.Chrome(chrome_options=chrome_options, executable_path=’C:/workspace/section3/webdriver/chrome/chromedriver’)

서버 입장에서는 실제 크롬 브라우저에서 접속하는 것이기 때문에 다른 변조 같은 걸 할 필요가 없음

스크린샷 찍는 방법 - 파이어폭스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import sys
import io
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
import time

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

firefox_options = Options()
firefox_options.add_argument("--headless") # CLI

driver = webdriver.Firefox(firefox_options=firefox_options, executable_path='C:/workspace/section3/webdriver/firefox/geckodriver')
# driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
# driver.set_window_size(1920, 1080)
# driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://google.com')
# time.sleep(5)

driver.save_screenshot("c:/website_ff1.png")

# driver.implicitly_wait(5) # 암묵적으로 5초 대기

driver.get('https://www.daum.net')
# time.sleep(5)
driver.save_screenshot("c:/website_ff2.png")

driver.quit()

print('스크린샷 완료')
  • 파이어폭스도 많이 사용함

인프런 사이트 로그인 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import sys
import io
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

# chrome_options = Options()
# chrome_options.add_argument("--headless") # CLI

# driver = webdriver.Chrome(chrome_options=chrome_options, executable_path='C:/workspace/section3/webdriver/chrome/chromedriver')
driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
driver.set_window_size(1920, 1080)
driver.implicitly_wait(3) # 암묵적으로 3초 대기

driver.get('https://www.inflearn.com/')
time.sleep(3)
driver.implicitly_wait(3) # 암묵적으로 3초 대기
driver.find_element_by_xpath('//*[@id="header"]/nav/div[2]/div/div[2]/div[2]/div[2]/a[1]').click()
time.sleep(3)
driver.find_element_by_class_name('input.email').send_keys('id')
driver.implicitly_wait(1)
driver.find_element_by_class_name('input.pwd').send_keys('pwd')
driver.implicitly_wait(1)
driver.find_element_by_xpath('//*[@id="root"]/div[4]/section/form/button').click()
time.sleep(3)

driver.quit()
  • find_element_by_xpath, find_element_by_class_name 등으로 찾기
    • find_element_by_xpath의 경우 크롬 개발자 도구에서 xpath 복사 하는게 있음
  • implicitly_wait는 암묵적으로 대기해야 되는 경우 적절하게 처리?
    • implicity_wait은 뜻 그대로 브라우저에서 사용되는 엔진 자체에서 파싱되는 시간을 기다려 준다.
  • time.sleep은 실습을 눈으롭 보기 위해 추가

실습(과제): encar(엔카) 사이트 접속 후 자동으로 매물 조회하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import sys
import io
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

# chrome_options = Options()
# chrome_options.add_argument("--headless") # CLI

# driver = webdriver.Chrome(chrome_options=chrome_options, executable_path='C:/workspace/section3/webdriver/chrome/chromedriver')
driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
driver.set_window_size(1920, 1080)
driver.implicitly_wait(3) # 암묵적으로 3초 대기

driver.get('http://www.encar.com/index.do')
time.sleep(3)
driver.implicitly_wait(3) # 암묵적으로 3초 대기

# 제조사 선택
driver.find_element_by_xpath('//*[@id="manufact"]/a').click()
driver.implicitly_wait(3) # 암묵적으로 3초 대기
time.sleep(1)

# 도요타
driver.find_element_by_xpath('//*[@id="manufactListText"]/ul[2]/li[6]/a').click()
driver.implicitly_wait(3) # 암묵적으로 3초 대기
time.sleep(1)

# 프리우스
driver.find_element_by_xpath('//*[@id="seriesItemList"]/li[3]/a').click()
driver.implicitly_wait(3) # 암묵적으로 3초 대기
time.sleep(1)

# 전체
driver.find_element_by_xpath('//*[@id="mdlItemList"]/li[1]/a').click()
driver.implicitly_wait(3) # 암묵적으로 3초 대기
time.sleep(1)

# 검색 버튼
driver.find_element_by_xpath('//*[@id="indexSch1"]/div[1]/a').click()
driver.implicitly_wait(3) # 암묵적으로 3초 대기

driver.save_screenshot("c:/car_screenshot.png")
time.sleep(3)
print('스크린샷 완료')

driver.quit()
  • XPath로 경로 찾아서 클릭하는 형태로 진행 했는데 사실 xPath만 보면 어떤일을 하는지 알수 없음. 이렇게 처리해도 문제 없는지를 모르겠네.

27. 웹 브라우저 없는 스크래핑 및 파싱 실습(2) - 네이버 까페 자동화

  1. selenium을 이용한 네이버 카페 자동으로 글쓰기
  2. 네이버 로그인 처리 후 회원 리스트 정보 가져오기
  3. class module 실습 작성
  4. 자주 사용하는 selenium 레퍼런스(refference) 문서 훑어보기

selenium 문서: http://selenium-python.readthedocs.io/index.html

현재 네이버 로그인 기능은 selenium으로 시도했을 경우 동작 안함 (21.02.11)

브라우저나 인터넷 속도 등. 서버에서 오는 시간. 시간을 넉넉하게 주기

네이버 까페 출석체크

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import sys
import io
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
import time
import pyperclip

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

class NcafeWriteAtt:
    # 클래스 초기화 실행 (webdriver 설정)
    def __init__(self):
        chrome_options = Options()
        # chrome_options.add_argument("--headless") # CLI (user-agent)
        # self.driver = webdriver.Chrome(chrome_options=chrome_options, executable_path="C:/workspace/section3/webdriver/chrome/chromedriver")
        self.driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
        self.driver.implicitly_wait(5)

    # 네이버 까페 로그인 && 출석 체크
    def writeAttendCheck(self):
        self.driver.get('https://nid.naver.com/nidlogin.login')

        # self.driver.find_element_by_name('id').send_keys('id')
        # self.driver.find_element_by_name('pw').send_keys('pw')
        self.clipboard_input('//*[@id="id"]', 'id')
        self.clipboard_input('//*[@id="pw"]', 'pw')

        self.driver.find_element_by_xpath('//*[@id="log.login"]').click()
        self.driver.implicitly_wait(30)
        time.sleep(5)
        self.driver.get('https://cafe.naver.com/AttendanceView.nhn?search.clubid=29771102&search.menuid=21') #네이버 iframe임. 실제 주소를 보려면 개발자 도구에서 네트워크 탭에서 확인
        self.driver.implicitly_wait(30)
        self.driver.switch_to_frame('cafe_main') # 포커스 변경
        self.driver.find_element_by_id('cmtinput').send_keys('ㅊㅊ')
        self.driver.find_element_by_xpath('//*[@id="btn-submit-attendance"]').click()
        time.sleep(3)

    def clipboard_input(self, user_xpath, user_input):
        pyperclip.copy(user_input)
        self.driver.find_element_by_xpath(user_xpath).click()
        ActionChains(self.driver).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
        time.sleep(1)

    # 소멸자
    def __del__(self):
        # self.driver.close() # 현재 실행 포커스 된 영역을 종료
        self.driver.quit() # selenium 전체 프로그램 종료
        print('Removed')

# 실행
if __name__ == '__main__':
    # 객체 생성
    a = NcafeWriteAtt()
    # 시작 시간
    start_time = time.time()
    # 프로그램 실행
    a.writeAttendCheck()
    # 종료 시간 출력
    print("---Total %s seconds ---" % (time.time() - start_time))
    # 객체 소멸
    del a

Selenium의 driver를 이용하여 아이디 및 비밀번호를 element.send_keys()메서드를 이용하여 로그인 시도 시 비정상적인 접근 방식으로 네이버 Captcha가 탐지하게 됐습니다. (로그인 불가)

클립보드에 input을 복사한 뒤 해당 내용을 actionChain을 이용해 로그인 폼에 붙여 넣는 방법으로 우회

네이버 까페 멤버 리스트 가져오기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import sys
import io
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import time
import pyperclip

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

class NcafeMemberExr:
    # 클래스 초기화 실행 (webdriver 설정)
    def __init__(self):
        chrome_options = Options()
        # chrome_options.add_argument("--headless") # CLI (user-agent)
        # self.driver = webdriver.Chrome(chrome_options=chrome_options, executable_path="C:/workspace/section3/webdriver/chrome/chromedriver")
        self.driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
        self.driver.implicitly_wait(5)

    # 네이버 까페 회원 1페이지 정보 리스트 추출
    def getMemberList(self):
        self.driver.get('https://nid.naver.com/nidlogin.login')
        self.clipboard_input('//*[@id="id"]', 'id')
        self.clipboard_input('//*[@id="pw"]', 'pw')
        self.driver.find_element_by_xpath('//*[@id="log.login"]').click()
        self.driver.implicitly_wait(30)
        time.sleep(1)

        self.driver.get('https://cafe.naver.com/CafeMemberViewTab.nhn?defaultSearch.clubid=29494256')
        self.driver.implicitly_wait(30)
        self.driver.switch_to_frame('cafe_main')
        self.driver.implicitly_wait(5)
        soup = BeautifulSoup(self.driver.page_source, 'html.parser')
        return soup.select('#main-area > div.mem_list_wrap > ul > li > div > div.nick > div > table > tbody > tr > td > a > div')

    # 네이버 회원 리스트 출력 및 저장
    def printMemberList(self, list):
        f = open("c:/memberList.txt", "wt")
        for i in list:
            f.write(i.string.strip() + "\n")
            print(i.string.strip())
        f.close()

    def clipboard_input(self, user_xpath, user_input):
        pyperclip.copy(user_input)
        self.driver.find_element_by_xpath(user_xpath).click()
        ActionChains(self.driver).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
        time.sleep(1)

    # 소멸자
    def __del__(self):
        # self.driver.close() # 현재 실행 포커스 된 영역을 종료
        self.driver.quit() # selenium 전체 프로그램 종료
        print('Removed')

# 실행
if __name__ == '__main__':
    # 객체 생성
    a = NcafeMemberExr()
    # 시작 시간
    start_time = time.time()
    # 프로그램 실행
    a.printMemberList(a.getMemberList())
    # 종료 시간 출력
    print("---Total %s seconds ---" % (time.time() - start_time))
    # 객체 소멸
    del a
  • pyperclip을 사용하려면 headless 옵션은 사용하지 못하는 듯 함.
  • fiddler 피들러 추천
    • http 통신을 전부 볼 수 있음
  • 클립 보드는 Chrome의 기능이 아니라 창 관리자가 제공하는 서비스 / 데몬입니다. 따라서 헤드리스로 실행하는 경우 사용 가능한 시스템 클립 보드가 없을 수 있습니다

실습 : 권한이 필요한 사이트 게시판 페이지 별 리스트 추출하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import sys
import io
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import time
import pyperclip

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

class NcafeBoard:
    # 클래스 초기화 실행 (webdriver 설정)
    def __init__(self):
        chrome_options = Options()
        # chrome_options.add_argument("--headless") # CLI (user-agent)
        # self.driver = webdriver.Chrome(chrome_options=chrome_options, executable_path="C:/workspace/section3/webdriver/chrome/chromedriver")
        self.driver = webdriver.Chrome('C:/workspace/section3/webdriver/chrome/chromedriver')
        self.driver.implicitly_wait(5)

    # 로그인
    def login(self):
        self.driver.get('https://nid.naver.com/nidlogin.login')
        self.clipboard_input('//*[@id="id"]', 'id')
        self.clipboard_input('//*[@id="pw"]', 'pwd')
        self.driver.find_element_by_xpath('//*[@id="log.login"]').click()
        self.driver.implicitly_wait(30)
        time.sleep(1)

    # 네이버 게시판 리스트 가져오기
    def getBoardList(self, page):
        base = 'https://cafe.naver.com/ArticleList.nhn?search.clubid=29771102&search.menuid=63&search.boardtype=L'
        quote = '&search.totalCount=72&search.page=' + str(page)
        self.driver.get(base + quote)
        # &search.totalCount=72&search.page=4
        self.driver.implicitly_wait(30)
        self.driver.switch_to_frame('cafe_main')
        self.driver.implicitly_wait(5)
        soup = BeautifulSoup(self.driver.page_source, 'html.parser')
        return soup.select('#main-area > div > table > tbody > tr > td.td_article > div.board-list > div > a.article')

    # 네이버 게시판 리스트 출력 및 저장
    def printBoardList(self, page):
        self.login()
        for i in range(1, page):
            board = self.getBoardList(i)
            f = open("c:/boardlist" + str(i) + ".txt", "wt")
            for i in board:
                for content in i.contents:
                    f.write(content.string.strip())
                    print(content.string.strip())
                f.write('\n')
            f.close()

    def clipboard_input(self, user_xpath, user_input):
        pyperclip.copy(user_input)
        self.driver.find_element_by_xpath(user_xpath).click()
        ActionChains(self.driver).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
        time.sleep(1)

    # 소멸자
    def __del__(self):
        # self.driver.close() # 현재 실행 포커스 된 영역을 종료
        self.driver.quit() # selenium 전체 프로그램 종료
        print('Removed')

# 실행
if __name__ == '__main__':
    # 객체 생성
    a = NcafeBoard()
    # 시작 시간
    start_time = time.time()
    # 프로그램 실행
    a.printBoardList(4)
    # 종료 시간 출력
    print("---Total %s seconds ---" % (time.time() - start_time))
    # 객체 소멸
    del a

28. 아톰 데이터 & Github 연동해보자!

  1. Git 설치 확인
  2. Github 회원가입 및 저장소 생성
  3. Github 저장소 연동 (Local)
    • 기존 프로젝트 저장소 추가
    • 새로운 프로젝트 저장소 추가
  4. 아톰에디터에서 Github 사용
  5. Git add, commit, push, pull 테스트

GitHub for Atom : https://github.atom.io/

Atom Package: https://atom.io/packages

Git 안내서 : https://rogerdudler.github.io/git-guide/index.ko.html

https://github.com/

https://github.com/karais89/inflearn-python.git

atom의 메뉴에 패키지 - 깃허브 - 토글 깃 탭 클릭 후 init 메뉴 선택 하면 됨.

  • pull (push), clone, fetch
  • 명령어 add, commit, push(pull), clone 등
  • reset —force 등
  • github push 할때는 커맨드로 해주는데 굳이 이것 까진 필요 없을 것 같음. 요즘엔 gui 환경이 워낙 좋게 나와서..
  • 근데 아톰 에디터에서는 커맨드로 설정을 해줘야 깃허브랑 연동이 가능한듯.. 엄청 구리네..
  • fork, sourcetree, github desktop 등을 사용하자..
  • 이번 강의는 다 아는 내용이라 그냥 넘어가자.
  • 아톰 플러그인 git-plus 설치