webdriver(조작을 원하는 browser별로 실제 파일이 다름)라는 API를 이용해 브라우저를 제어할 수 있다. 브라우저를 직접 동작시킨다는 것은 JavaScript가 동작하면서 비동기적으로 서버로부터 콘텐츠를 가져오거나 숨겨져 있는 콘텐츠를 열거나 하는 등의 작업을 수행함을 의미한다. webdriver는 http프로토콜로 명령을 직접 날리고 확인 할 수 있다.
Selenium 라이브러리에는 브라우저 조작이 가능한 다양한 기능이 존재하며, JS로 렌더링이 완료된 후의 DOM결과물에 접근이 가능하다. 우리가 requests에서 사용했던.text의 경우 브라우저에서 '소스보기'를 한 것과 같이 동작하여, JS등을 통해 동적으로 DOM이 변화한 이후의 HTML을 보여주지 않기 때문에 Selenium은 JavaScript에 의해 동적으로 생성되는 사이트의 데이터를 크롤링할 때 매우 유용하게 사용되는 스크래핑 도구이다.
설치방법(Python기준)
1. 현재 설치된 패키지 정보들을 보자. 파이어폭스 찾아봐!
sudo apt --installed list | grep firefox
2. 셀레니움 설치
pip install selenium
3. 현재 환경설정
현재 파이참 개발은 myapps라는 환경에서 하고 있으므로 이동.
주피터를 위해 base에도 설치해주고, myapps에서 한번 셀레니움을 더 설치해준다.
conda activate myapps
myapps에도 설치
4. 설치하고 tar 파일 압축 풀기
wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
tar xvzf geckodriver-v0.26.0-linux64.tar.gz
# 이미지 읽어서 저장하기
imgs = browser.find_elements_by_css_selector('img.FFVAD')
#이미지 가져오기
result = []
for img in imgs:
if 'http' in img.get_attribute('src'):
result.append(img.get_attribute('src'))
from urllib.request import urlretrieve
# 디운로드
for index,link in enumerate(result):
print(index, ":",link)
urlretrieve(link, 'downImg/dog/dog_'+str(index)+'.jpg')
print('success!')
title =soup.select('a.head >h3:nth-child(1) > span')
value =soup.select('div:nth-child(2) > span.value')
cont =zip(title,value)
for t,v in cont:
print('제목: ',t.string,' , 환율: ',v.string)
저 가격들을 전부 가져와보자.잘 출력되었다.
3. 위키피디아 크롤링
수록 시 목록을 크롤링해보자.
수록 시 목록을 크롤링해보자.
# select 출력
import urllib.request as req
url = 'https://ko.wikipedia.org/wiki/%ED%95%98%EB%8A%98%EA%B3%BC_%EB%B0%94%EB%9E%8C%EA%B3%BC_%EB%B3%84%EA%B3%BC_%EC%8B%9C'
res = req.urlopen(url)
soup=BeautifulSoup(res,"html.parser")
lista = soup.select('#mw-content-text > div.mw-parser-output > ul:nth-child(9) > li:nth-child(19) > a')
lista
#선택되어있는 객체가 19이기 때문에 전부 가져오려면 li만 가져오면 된다.
listb = soup.select('#mw-content-text > div.mw-parser-output > ul:nth-child(9) > li > a')
listb
# lista
for a in lista:
print(a.string)
# listb
for b in listb:
print(b.string)
4. 속성별로 크롤링해오기
문자에 칠해진 색깔대로 크롤링해보자.
초록색 글자만 크롤링해보자.
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('http://www.pythonscraping.com/pages/warandpeace.html')
bs = BeautifulSoup(html, 'html.parser')
#선택되어있는 객체가 19이기 때문에 전부 가져오려면 span 가져오면 된다.
ab = bs.findAll('span',{"class":"green"})
for qq in ab:
print(qq.get_text()) #태그 내부의 텍스트 출력
from bs4 import BeautifulSoup
fp = open('fruits-vegetables.html',encoding='utf-8')
soup = BeautifulSoup(fp,"html.parser")
#CSS 선택자로 추출하기 : li:nth-child(n) : li 요소 중에서 n번째 요소를 선택
ss = soup.select('li')
ss
파일 경로를 입력하고 open()으로 파일을 읽어보자.(인코딩 필수)
li 태그가 여러개이다. 연근을 뽑아보자.
연근은 야채 중 5번째로 위치해 있다.
html 코드를 보면 연근은 5번째 li이다.
print(soup.select_one("li:nth-child(5)").string)
print(soup.select_one("li:nth-of-type(5)").string) #위와 동일
연근만 잘 출력된다.
- 인덱스 번호를 이용해서 select 하는 방법은 다양하다. 속성을 다양하게 둘 수도 있고, 인덱스번호를 설정 해 줄 수도 있다.
포도는 us가 첫번째로 나온 태그이다.
아보카도는 black이 두번째로 나온 태그이다.
- find로 찾을 경우.
속성을 리스트로 받아 찾거나, 여러개를 조건으로 걸어줄 수 있다.
6. GDP 순위 크롤링해서 table로 출력하기(pandas)
1) table 크롤링하기
이러한 표를 파이썬에서 이쁘게 표로 크롤링해보자.
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('https://worldpopulationreview.com/countries/countries-by-gdp')
# 파싱
bs = BeautifulSoup(html, 'html.parser')
# tbody 태그의 클래스 크롤링
ab = bs.findAll('tbody',{"class":"jsx-2642336383"})
# 반복문으로 리스트 분해
for qq in ab:
print(qq.get_text())
#리스트 쪼개기
for tr in theads:
listv = [th.get_text(strip=True) for th in tr.find_all('th')]
print(listv)
- 컬럼과 내용을 합친 list를 생성한다.
rows = [] #list를 선언 및 생성
idx = 0
for tr in trs:
if idx ==0:
rows.append(listv)
else:
rows.append([td.get_text(strip=True) for td in tr.find_all('td')])
idx = idx +1
rows
4) pandas 이용해서 테이블로 만들기
import pandas as pd
df = pd.DataFrame(rows[1:], columns=rows[0])
df #테이블로 출력된다. 1번인거는 컬럼이 0번이기 때문에
7. pdf 읽어 오기
1) pdfminer3k 설치하기
!pip install pdfminer3k
2) 필요한 기능 수입하기
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter #PDF를 읽어와서 텍스트로 converter 헤주는 녀석
from pdfminer.layout import LAParams #파라미터 객체
from io import StringIO
from io import open
from urllib.request import urlopen
3)pdf를 읽기 위한 기능 정의하고 url 입력하기
def readPDF(pdfFile):
#리소스 매니저를 생성
rsrcmgr = PDFResourceManager()
#PDF 내부의 텍스트를 입출력하기 위한 StringIo객체를 생성
restr = StringIO()
#파라미터 객체
laparams = LAParams()
#pdf 내용을 텍스트로 변환하기 위해서 객체를 생성
#textConverter(매니저, IO, layoutParam)
device = TextConverter(rsrcmgr, restr,laparams=laparams)
#PDF Process가 실제로 변환된 값 -> text로 바꾸기
#pricess_pdf(매니저, textConverter, 읽을 pdf 파일)
process_pdf(rsrcmgr,device,pdfFile)
device.close()
#Io를 통해서 값을 content란 변수에 읽어와서 저장한다.
content = restr.getvalue()
restr.close()
return content
#url 수입
from urllib.request import urlopen,Request
urlv = 'https://buildmedia.readthedocs.org/media/pdf/beautiful-soup-4/latest/beautiful-soup-4.pdf'
open 함수는 다음과 같이 "파일 이름"과 "파일 열기 모드"를 입력값으로 받고 결괏값으로 파일 객체를 돌려준다.
우리가 write 메서드에 outputString 이라는 변수를 던져주면 그 변수 안에 들어 있는 데이터를 써서 'readPDF1.txt'라는 파일을 생성하여 저장해준다. (다다님께 영광을...!!)
8. 네이버 영화 평점 크롤링하기
## 필요한 모듈들 수입하기
import requests
import threading, time
from bs4 import BeautifulSoup
import pandas as pd
import math
url='https://movie.naver.com/movie/bi/mi/pointWriteFormList.nhn?code=188909&type=after&isActualPointWriteExecute=false&isMileageSubscriptionAlready=false&isMileageSubscriptionReject=false&page={}'
# url 뒤에 {}에 넣을 인자 설정. url의 2번째를 넣는다.(페이지 번호)
response = requests.get(url.format(1))
# response를 텍스트로 파싱하기
soup = BeautifulSoup(response.text,'html.parser')
#코멘트를 찾는 함수 정의
def find_comment(page=1):
response = requests.get(url.format(page))
soup = BeautifulSoup(response.text,'html.parser')
#class가 'score_result'인 div 태그를 찾아 넣기
tag = soup.find("div", class_='score_result')
#태그 중 li만 다 찾기 -> 코멘트
tagli = tag.find_all("li")
#점수와 리뷰를 담을 list 선언
score, text = [], []
for e in tagli: #8점 이상이면 리스트의 1번 인덱스에 추가
if int(e.em.text)>= 8:
score.append(1)
text.append(e.p.get_text("", strip=True))
elif int(e.em.text)<=5: #5점 이하면 리스트의 0번 인덱스에 추가
score.append(0)
text.append(e.p.get_text("", strip=True))
return score,text
def searchNaver(totalPage):
import time
sv, tv= [], []
for i in range(1, 1753):
time.sleep(0.1) #시간 간격을 두고 다운로드를 받아야 한다.
print("Count:", i, end="\r") # 카운트 올라가는거 표시(없어도 됨)
s, t = find_comment(i)#위에서 정의한 find_comment에 i값을 넣어서 카운트
sv += s
tv += t #누적하면서 계속 카운트
df = pd.DataFrame([sv, tv]).T #df 양식중에 하나. 행과 열을 전치시킨다.
df.columns = ['score','text'] #컬럼명 설정
df.to_csv('comment.csv') # csv로 저장
t1 = threading.Thread(target=searchNaver,args=(1753,)) # 스레드로 데이터 전송
t1.start()
print("TestCode!")
웹에서는 기본적으로 [수집] -> [저장] -> [처리] -> [분석] -> [시각화] 단계로 이루어진다.
기본적으로 웹 스크래핑, IOT 센서, JSON/CSV/TEXT 등 공용,공개, 공공데이터, SQL, SNS 등의 상호 수집 데이터 등을 이용해서 데이터를 수집하는데, 이러한 데이터를 저장하는 것이 중요한 이유는 데이터 아키텍처의 시스템을 따르기 때문이다.
기업은 최소의 비용으로 최대 효율을 따지기 때문에 데이터 저장을 할 때에 클라우드 등의 시스템을 사용한다. 하지만 보안성이 중요한 곳의 경우 클라우드를 사용하지는 않는다.(셋방을 쓰는 개념, root 최고 관리자는 데이터에 언제든지 접근이 가능하기 때문이다.) 데이터는 csv, text, xml, sql, TF, IDF 등의 형태로 저장된다. 데이터들을 처리하려면 일단 가공이 필요한데, 가공된 데이터는 분석과 시각화가 함께 진행된다. 데이터의 정보를 효과적으로 전달하기 위해서는 시각화가 좋은 수단이기 때문이다.
요즘은 분석으로 끝나지 않는다. 과거의 데이터를 보고 미래를 예측한다. 회귀분석과 신뢰구간 등 확률을 통해 예측을 한다. 또한 데이터를 해서 수치화를 하고 이를 통해 알고리즘 훈련, 머신러닝 등 분석과 동시에 기계학습을 시키기도 한다. 즉, 기계학습의 목적은 미래를 예측하는 것이다.(그래 이거!!! 나나나 이거할래)
가장 기초적인 데이터 [수집] 단계부터 시작해보자.
[ BeautifulSoup ]
웹 크롤링을 위한 파이썬 라이브러리
웹에서 정보 긁어오기
1. 개념
1.1. 웹 스크레핑(web scraping)
웹 사이트 상에서 원하는 부분에 위치한 정보를 컴퓨터로 웹 사이트들에서 원하는 정보를 추출하여 수집하는 기술 (특정 페이지의 HTML 문서에 소스들 중에서 원하는 정보만 추출하는 기술)
1.2. 웹 크롤링(Web crawing)
자동화 봇(bot)인 웹 크롤러(web crawler)가 정해진 규칙에 따라 복수 개의 웹 페이지를 브라우징 하는 작업. 즉, 웹에서 표현되는 정보를 프로그램을 작성해서 정규화된 정보로 긁어오는 행위를 말한다.
2. 웹 스크래핑을 위한 라이브러리
2.1. BeautifulSoup : 파이썬 스크래핑 라이브러리
2.2. scrapy : 크롤링과 스크래핑을 간편하게 도와주는 프레임워크
3. 옵션
html.parser (기본 파서, 적당하게 빠른 수준)
lxml -> lxml(html), xml (xml을 사용한다면 제공받는 파서, 외부 C라이브러리에 의존 단점, 속도가 빠름)
html5lib -> python2전용 , 아주 느리다. 웹브라우저의 방식으로 페이지를 해석한다.
BeautifulSoup
1. 개념
BeautifulSoup 기본적으로 HTML을 온전하게 불러올 수 있도록 도와주는 모듈이다.
원래 HTML은 태그로 이루어져 있고, 수많은 공백과 변화하는 소스들 때문에 오류가 있을 수도 있어, 정규표현식등으로 불러오기 힘들다. 하지만 beautifulsoup을 이용하면 알아서 이러한 오류를 잡아서 고친 후 우리에게 데이터를 전달해준다.
2. 단계
BeautifulSoup 3가지 단계를 거친다.
1. request : 웹페이지의 URL을 이용해서 HTML 문서를 요청
2. response : 요청한 HTML 문서를 회신
3. parsing : 태그 기반으로 파싱(일련의 문자열을 의미있는 단위로 분해)
이러한 3가지 단계를 거쳐 크롤링을 수행한다.
3. 메소드/속성 미리보기
- title 태그 안의 텍스트를 가지고 오고 싶을 때 : soup.title.getText() 또는 soup.title.text
- 태그 자체를 가져오고 싶을 때 : soup.title
- 태그가 유니크하지 않은 경우, 특정 정보를 불러오고 싶을 때 ex) div 태그가 여러개
: soup.div.next_sibling 속성을 사용 / soup.find_all('div') / id나 class의 이름을 통해 크롤링
- 태그 안의 속성의 정보를 불러오고 싶을 때 : soup.find('div')['id'] -> 속성을 딕셔너리처럼 넣어줌
[ 파이썬 스레딩 모듈을 사용해서 데이터 스크랩핑하기 ]
find / find_all / select / select_one
1. 파이썬 내부에서 html 태그를 이용해 크롤링하기
1) html 코드 작성
우선 태그의 쓰임을 알아보기 위해 간단하게 html 코드를 작성해보자. html 코드는 """ 사이에 입력해줄 수 있다.
기본적으로 pip를 이용해서 BeautifulSoup을 설치했다 가정.
from bs4 import BeautifulSoup
#분석하고 싶은 html
html = """
<html>
<body>
<h1>나의 첫번째 웹스크래핑!</h1>
<p>첫번째 라인</p>
<p>두번째 라인</p>
<p><b>여기를 출력하시오</b></p>
</body>
<html>
"""
2) html 파싱
html을 파이썬에서 읽을 수 있게 파싱한다. 즉, 파이썬 객체로 변환하는 것.
html이라는 변수에 저장한 html 소스코드를 .parser를 붙여 변환해준다.
parser는 파이썬의 내장 메소드이다.
soup=BeautifulSoup(html,'html.parser')
3) 태그로 접근하기
html에 접근하는 가장 기본적인 방법은 태그(DOM의 연산자)를 사용하는 것이다.
.연산자를 사용해서 Document Object 주소로 접근하는 방법은 대표적으로 두가지.
- tag.name(. 왼쪽에 있는 애가 소유주다.)
- tag.subtag.subtag
h1 = soup.html.body.h1
p1 = soup.html.body.p
위에서 파싱한 soup의 html 중, body 태그의 <h1>, <p> 요소에 접근한다.
태그와 함께 html 코드가 출력된다.
- 부모태그
부모태그를 찾아갈 수도 있다. 이 속성을 이용하면 해당 요소의 부모에 접근할 수 있다.
parent는 바로 상위 부모 요소
parents는 상위의 모든 조상 요소
hp1=h1.parent
print(hp1)
hp2 = h1.parents
print(hp2)
print(type(hp2))
hp2의 경우, generator 는 향후에 반복문으로 풀어주어야 원하는 값으로 출력이 가능하다.
generator라는 오브젝트가 리턴 됐습니다. 제너레이터는 자신이 리턴할 모든 값을 메모리에 저장하지 않기 때문에 조금 전 일반 함수의 결과와 같이 한번에 리스트로 보이지 않는 것입니다. 제너레이터는 한 번 호출될때마다 하나의 값만을 전달(yield)합니다. 즉, 위의 코드까지는 아직 아무런 계산을 하지 않고 누군가가 다음 값에 대해서 물어보기를 기다리고 있는 상태입니다.(참조 링크)
p2: <p>첫번째 라인</p> p2.next_element: 첫번째 라인 p2.next_sibling: <p>두번째 라인</p> p2.previous_element: 나의 첫번째 웹스크래핑! p2.previous_sibling: <h1>나의 첫번째 웹스크래핑!</h1>
↓나는 아래 사진처럼 나왔는데, 원래 공백도 dom 요소의 값으로 포함되기 때문에, 공백이 없어야 위처럼 출력된다.
공백때문에 안나온다
next_sibling은 다음 형제 요소. 즉, p2의 첫번째 라인이 아닌 두번째 라인이 출력되고, previous_sibling의 경우 그 이전의 <h1>태그가 출력된다.
for e in soup.stripped_strings: #공백도 함께 제거해주는 기능
print(type(e))
print("{} => {}".format(e,len(e)))
정해진 포맷에 길이를 함께 써주었다.
4) find 함수
Query의 선택자 방식과 비슷하다. id, class, element 등으로 찾을 수 있다.
title = soup.find(id='title')
print(title)
- find() : 조건에 해당하는 첫 번째 정보만 보여줌
- find_all() : 조건에 해당되는 정보를 보여줌
클래스 이름을 알 경우, class_ 형태로 사용한다.
태그 내부 텍스트만 출력할 경우 string을 붙여준다.
- string : get_text() 보다 정확하게 문자열을 추출할 경우 사용. string의 경우 태그를 먼저 인식 후 텍스트를 가져온다.
- get_text() : 현재 태그를 포함하여 모든 하위 태그를 제거하고 유니코드 텍스트만 들어있는 문자열을 반환
웹 스크래핑에서는 원하는 정보를 구체적으로 가져와야하는 경우가 훨씬 많다.
따라서 태그의 속성을 좀 더 자세한 정보를 기입할 수도 있다.
find("태그"{"속성":"속성 이름"}) : 이경우, 불필요한 태그까지 가져올 수 있다.
3. URL 읽기
1) 구글 아이콘 url로 받아오기
urllib 패키지를 이용해서 인터넷 리소스를 가져온다.
urllib.error모듈은urllib.request에 의해 발생하는 예외에 대한 예외 클래스를 정의힌다.
베이스 예외 클래스는URLError. 추가로 http 에러도 예외로 추가해준다.
from urllib.request import urlopen
from urllib.error import HTTPError,URLError
try:
url="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png"
html=urlopen(url)
except HTTPError: # 에러일 경우
print('Http Exception1')
except URLError:
print('URLError')
else: #정상적일 경우
savename="ikosmo.png"
image=html.read()
print(image)
결과창이 이상하지만 이미지를 읽은거다.
2) 이미지 저장하기
이미지를 저장해보자.
파이썬에는 기본적으로 제공되는 다양한 모듈이 있다. 자주 사용되는 모듈 가운데 os라는 모듈이 있는데, os 모듈은 Operating System의 약자로서 운영체제에서 제공되는 여러 기능을 파이썬에서 수행할 수 있게 해준다.
여기서는 디렉토리를 여는데에 사용되었다.
mode='wb' -> 바이너리(b) 형태로 쓰기(w)
import os
try: #ikosmo.png 이름으로 저장
with open(savename,mode='wb') as f:
f.write(image)
print('이미지가 저장되었습니다.')
except IOError: #에러 예외처리
print('이미지 저장에 실패했습니다.')
else: #디렉토리 리스트 열기
print(os.listdir())
- urllib.request.urlretrieve를 통한 다운로드
urlretrieve 함수를 통해 바로 파일에 자료를 입력할 수 있다. 아래의 경우 파일에 직접 저장하는 것.
import urllib.request as req
url='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png'
req.urlretrieve(url,'ikosmo2.png')
print('저장되었습니다.')
os.listdir()
파일이 잘 저장되었다.
4. XML 파싱하는 방법
xml(Extensible Markup Language) 파일은 사용자 정의 태그를 사용하여 문서의 구조 및 기타 기능을 설명하는 일반 텍스트 파일이다. 우리가 흔히 아는 <body>,<h1>,<p> 이런 태그가 아니라 <student>, <apple> 이런 식의 사용자가 표시할 수 있는 태그다. HTML과 유사한 문법을 사용하고, 컴퓨터(PC-스마트폰)간에 정보를 주고받기 매우 유용한 저장방식이다.
RSS 란? [Really Simple Syndication 또는 Rich Site Summary]
어떤 사이트가 있을 때, 그 사이트를 매일 방문해서 재미있는 새로운 기사가 있는지 확인하는 것은 번거롭다. 이럴 때 블로그의 배너같은 위치에 RSS를 설치하면 그 사이트를 직접 방문하지 않고서도 새로운 기사들이 내 배너에 '배달'된다. 이처럼 RSS와 같은 사이트 피드는 새 기사들의 제목만 또는 새 기사들 전체를 뽑아서 하나의 파일로 만들어 놓은 것이다.
따라서 list형태로 되어있기 때문에 for문으로 List를 순회하면서 get_text() 함수를 사용해야한다.(find는 가능)
3) for문으로 분리해주자.
attrs는 속성을 의미한다. href 속성도 따로 가져오자.
for a in links:
print(type(a))
href = a.attrs["href"]
print(href)
print(a)
태그 안의 링크를 텍스트로 가져왔다.
6. [과제] 기상청 사이트에서 제주도의 title 태그의 텍스트와 wf 태그 내부의 텍스트만 출력하시오. 단, br 태그는 제거하시오.(5개의 데이터만 추출할 것.)
import urllib.request as req
import urllib.parse as par
API = "http://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp"
values={"stnId":"184"}
params=par.urlencode(values)
url=API + "?" + params
print("url=",url)
res=req.urlopen(url)
soup=BeautifulSoup(res, "html.parser")
title=soup.find("title").string #title 태그 내부의 텍스트
wf=soup.find_all("wf", limit=5) #wf 태그 내부의 텍스트
print(title)
print('===========================')
for w in wf:
print(w.string.replace('<br />',''))