본문 바로가기

AI/StudyNote

Python #웹크롤링 #requests 모듈 #urllib 모듈

웹 크롤링에 대한 이해

웹 크롤링

: 웹 스크래핑(Web Scraping)이라고도 하며 컴퓨터 소프트웨어 기술로 각종 웹 사이트들에서 원하는 정보를 추출하는 것을 의미

 

웹 크롤러

: 인터넷에 있는 웹 페이지를 방문해서 자료를 수집하는 일을 하는 프로그램

 

크롤링을 위한 선행학습
- 웹(web)의 개념 (request, response 등)
- HTML, CSS, Javascript
- 파이썬 기초

 

웹 크롤링의 기법

- HTML 페이지를 가져와서 HTML/CSS 등을 파싱하고, 필요한 데이터만 추출하는 기법

- Open API(Rest API)를 제공하는 서비스에 Open AI를 호출해서, 받은 데이터 중 필요한 데이터만 추출하는 기법

- 브라우저를 프로그래밍으로 조작해서 필요한 데이터만 추출하는 기법

 

 

웹 크롤링의 예시

검색엔진(네이버, 다음, 구글)

- 구글에서 크롤링 봇을 검색해보자.

쇼핑몰

- 경쟁사의 제품 정보들을 크롤링하여 사용(법적 다툼 발생)

API (Application Programming Interface) 외의 정보

- API에서 제공하는 정보 외에 필요한 정보들을 크롤링해서 사용

 

크롤링이 합법인가 불법인가

모든 크롤링이 불법은 아니다.

- 사이트 운영자의 의사에 상관없이 무단으로 크롤링하는 것은 불법

 

robots.txt(검색을 허용할것인가 허용하지 않을 것인가의 정보등이 담겨있다.)에서 크롤링 여부 확인가능

https://searchadvisor.naver.com/guide/seo-basic-robots

- 검색엔진 크롤링 봇들이 해당 파일을 열어본 뒤 수집 유무를 결정

검색 허용 여부 예시
네이버 로봇은 검색 허용, 야후 로봇은 검색 불가로 하고 싶다면 robots.txt에서 아래처럼 수정
- User-agent: 네이버
- Allow: /
- User-agent: 야후
- Disallow:/

4차 산업 혁명 시대에 데이터는 곧 ‘돈’

- 데이터의 가치가 올라감에 따라 크롤링이 범죄에 활용되는 경우가 많이 발생

 

크롤링 분쟁이 많다.

- 후발 주자가 선행주자를 빨리 따라잡기 위해 불법으로 데이터를 가져오는 행위들

예) 잡코리아 VS 사람인, 야놀자 VS 여기 어때

 

 


웹 크롤링 기초

 

HTTP는 요청과 응답으로 이루어져 있음

- 사용자가 원하는 정보를 요청하게 되면, 서버는 해당 요청을 확인 후 적절한 응답을 해주는 구조

 

 

HTTP 요청과 응답

1) 요청일 때

- 사용자가 서버에 요청을 할 때 크게 4개로 구분하여 요청할 수 있음

- URL로 요청하여 요청할 때는 메서드로 변경하여 기능을 구분

메소드 설명
GET 정보를 가져오기 위해 요청
POST 새로운 정보를 보내기 위해 요청
PUT 수정할 정보를 보내기 위해 요청
DELETE 정보를 지우기 위해 요청

 

2) 응답일 때

- 서버가 사용자에게 요청에 대한 응답을 보낼 때 크게 5개의 경우가 있음

- 응답 코드로 요청의 진행 사항과 서버의 상태를 예측가능

응답코드 설명
1XX 요청을 받았고, 작업 진행중
2XX 사용자의 요청이 성공적으로 수행 됨
3XX 요청은 완료 되었으나, 리다이렉션이 필요
4XX 사용자의 요청이 잘못됨
5XX 서버의 오류를 응답

파이썬에서의 크롤링

 

requests 모듈을 활용한 크롤링 환경

- !pip install requests 명령어로 설치

- Anaconda를 설치했다면 기본으로 함께 설치가 되어있음

- import requests로 모듈 호출 후 사용

 

크롤링에 항상 쓰이는 디폴트 코드

#크롤링 디폴트
import requests

URL = "https://www.*****.com" #URL 객체 생성
response = requests.get(URL)  #response 객체 생성 requests모듈의 get메서드로 URL을 받음 
print(response.status_code) #상태코드 호출, URL을 잘 받았다면 200 출력
print(response.text) #받은 정보를 텍스트로 출력

 

 

특정 사이트의 검색 결과를 가져오는 예시

#검색 결과를 가져오기

#포털에서 "파이썬"이라는 검색어의 결과 정보 가져오기
#params라는 객체에 검색결과를 담으려면 해당 사이트가
#어떤 구조로 검색결과를 호출하는지 알수 있어야 매개변수를 작성할 수 있다. 고정된 답이 있는게 아님

#URL = "https://search.*****.com/search.*****"

import requests

URL = "https://search.*****.com/search.*****"
params = {'query':'파이썬'} #
response = requests.get(URL, params) #requests모듈로 URL과 params를 적용, 정보를 가져온 뒤 response 객체에 담음
print(response.text)

 

 

urllib 모듈을 활용한 크롤링 환경

 

- 파이썬의 표준 모듈로써 URL을 다루기 위한 모듈 패키지

- 설치가 필요하지 않고, import urllib로 활용

- requests 모듈과 마찬가지로 URL과 관련된 여러가지 기능들을 제공

- 공식문서:  https://docs.python.org/3/library/urllib.html

- request : URL을 열고 읽는 모듈(HTTP 요청)

- error : request 모듈에서 발생하는 에러들을 포함하는 모듈

- parse : URL을 파싱(해석)하는 모듈(URL 해석 및 조작)

- robotparser: robots.txt 파일을 파싱하는 모듈

 

urllib.request.Request()

해당 URL 요청 객체 생성

HTTPRequest 객체 반환

여러가지 함수들을 제공: 전체 URL, 프로토콜, 호스트 등

#urllib 모듈 사용 urllib.request.Request()
import urllib

URL = ""

response = urllib.request.Request(URL)
print(response) #객체에 대한 주소지
print(response.full_url) #객체에 대한 풀 URL
print(response.type) #객체에 대한 타입
print(response.host) #객체에 대한 호스트(서버주소)

 

urllib.request.urlopen()

해당 URL을 열기

응답 데이터는 바이트 형식의 HTTPResponse 객체를 반환한다.

request 객체 또는 URL을 직접 넣어도 가능

#urllib 모듈의 urlopen는 request 객체 또는 URL을 직접 넣어도 가능

import urllib

URL = ""
request = urllib.request.Request(URL)
response1 = urllib.request.urlopen(request)#객체를 넣은 경우
response2 = urllib.request.urlopen(URL)#URL을 넣은 경우
print(response1)
print(response2)
print(response1.geturl())
print(response2.geturl())
print(response1.getheaders())#헤더 정보 출력
print(response2.getheaders())

 

 

urllib.request.read()

urlopen으로 연 객체를 읽고, 인자로 전달하는 숫자만큼 데이터를 읽음

- read()로 읽으면 바이트형식의 데이터를 반환한다.

- 그러므로 decode()를 해줘야 한다.

 

urllib.request.readlines()

홈페이지 데이터를 줄 단위로 읽어 리스트에 반환

 

urllib.request.decode()

바이트 형식의 데이터를 원하는 형식으로 변환

- 기본값은 utf-8 사용

 

#urllib 모듈 사용 urllib.request.urlopen()

import urllib

URL = ""
response_urllib = urllib.request.urlopen(URL)
byte_data = response_urllib.read()#바이트 단위로 읽어와줘
text_data = byte_data.decode("utf-8")#바이트 데이터를 utf-8로 디코딩 해줘야 한다
print(text_data)
#urllib.request.read()
import urllib

URL = ""
response_urllib = urllib.request.urlopen(URL)
byte_data = response_urllib.read(500)#500번째 글자까지만 읽어라, 바이트코드로 나옴
text_data = byte_data.decode()#utf-8을 안넣어도 기본으로 utf-8이 적용된다.
print(text_data)

 

 

urllib.request.urlretrive()

- 웹 상에서 이미지를 다운로드

#urllib.request.urlretrive()
import urllib

img_src = "https://newsimg.hankookilbo.com/cms/articlerelease/2017/11/13/201711131465792477_1.jpg"
new_name = 'ryan.jpg' #저장할 이름 지정
urllib.request.urlretrieve(img_src, new_name) #urlretrieve(가져올 이미지 주소, 저장할 이름)

 

urllib.parse()

- 공식문서: https://docs.python.org/ko/3/library/urllib.parse.html

 

urllib.parse — URL을 구성 요소로 구문 분석 — Python 3.9.7 문서

urllib.parse — URL을 구성 요소로 구문 분석 소스 코드: Lib/urllib/parse.py 이 모듈은 URL(Uniform Resource Locator) 문자열을 구성 요소(주소 지정 체계, 네트워크 위치, 경로 등)로 분리하고, 구성 요소를 다시

docs.python.org

 

- url을 파싱하여 분석하기 위한 모듈

url은 인터넷 공간에 존재하는 데이터들을 가리키기 위한 절대 주소
url 형식: 프로토콜://아이디:비밀번호@호스트:포트번호/하위경로?파라미터#색인(프래그먼트)
url 참고: https://ko.wikipedia.org/wiki/URL
예1) http://www.somehost.com/a.gif
- IP 혹은 Domain name 정보가 필요한 형태 ( www.somehost.com에 있는 a.gif를 가리키고 있음 )
예2) ftp://id:pass@192.168.1.234/a.gif
- IP 혹은 Domain name 정보가 필요한 형태 (192.168.1.234에 있는 a.gif를 가리키고 있음 )
예3) mailto:somebody@mail.somehost.com
- IP정보가 필요없는 프로토콜 ( mailto 프로토콜은 단지 메일을 받는 사람의 주소를 나타냄 )

 

 

urllib.parse.urlparse()

- url을 6개로 분리하여 반환

#urllib.parse.urlparse()

import urllib

parse = urllib.parse.urlparse('http://thisishomepage.com/home;a=1?b=2#c')#샘플 URL
print(parse)#총 6개로 url를 분리하여 반환한다.
print()
print(parse.scheme)#스키마 출력
print(parse.netloc)#도메인 출력
print(parse.path)#패스 출력
print()
for i in range(len(parse)):#urlparse로 분리된 요소를 모두 출력
    print(parse[i])

 

urllib.parse.urlsplit()

- 파라미터를 분할하지 않음, 그래서 5개로 나눠짐

 

urllib.parse.urlunparse()/urllib.parse.urlunsplit() 

- 분리된 url을 다시 합침

- 튜플로 반환되기 때문에 리스트로 변경하여 활용

#urllib.parse.urlparse()

import urllib

parse = urllib.parse.urlsplit('http://thisishomepage.com/home;a=1?b=2#c')


#파라미터를 분할하지 않음, 그래서 5개로 나눠짐
for i in range(len(parse)):    
    print(parse[i])
print(type(parse)) 
print(parse.query)
print()
    
parse = list(parse)#리스트로 변경뒤 parse
parse[1] = 'cafe.daum.net'#두번째 요소를 지정한 요소로 변경

parse = urllib.parse.urlunsplit(parse)#분할된 요소를 다시 합치고 문자열로 출력
print(parse)
print(type(parse))
print()

 

urllib.parse.parse_qs(), parse_qsl()

- 쿼리(query)를 파싱해서 사전(sq) 및 리스트(qsl) 로 반환

- 쿼리(query)를 변경하여 요청할 때 활용

 

import urllib

parse = urllib.parse.urlparse('https://www.nnnnnnn.com?a=1&b=2&c=3&d=4')
print(parse)

print(parse.query)
print(type(parse.query))


#qs()
qs = urllib.parse.parse_qs(parse.query)#쿼리를 사전형태로 만들어줌
print(qs)
print(type(qs))


#qul()
qsl = urllib.parse.parse_qsl(parse.query)#쿼리를 리스트형태로 만들어줌
print(qsl)
print(type(qsl))

ParseResult(scheme='https', netloc='www.nnnnnnn.com', path='', params='', query='a=1&b=2&c=3&d=4', fragment='')

a=1&b=2&c=3&d=4 #쿼리만 출력

<class 'str'> #쿼리 타입 조회결과

{'a': ['1'], 'b': ['2'], 'c': ['3'], 'd': ['4']}

<class 'dict'>

[('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]

<class 'list'>

 

 

urllib.parse.urljoin(a,b)

- a와 b url을 합쳐주는 기능

- ‘/’ 에 따라 URL 주소가 달라지는 것 주의!!

import urllib.parse 

url = "https://www.naver.com/a/b"
print(urllib.parse.urljoin(url,'c'))#가장 하위 패스가 수정됨
print(urllib.parse.urljoin(url,'/c'))#전체 패스가 수정됨

https://www.naver.com/a/c

https://www.naver.com/c

 

import urllib.parse 

url = "https://www.naver.com/a/b/"#주소 뒤에 / 로 끝나면
print(urllib.parse.urljoin(url,'c')) #c가 패스 마지막에 추가됨
print(urllib.parse.urljoin(url,'/c'))#전체 패스가 수정됨
# 페이지 이동에 사용할 수도 있음
#‘/’ 에 따라 URL 주소가 달라지는 것 주의!!

https://www.naver.com/a/b/c 

https://www.naver.com/c

 

 

urllib.parse.quote() / unquote()

- 아스키 코드가 아닌 문자들을 퍼센트 인코딩으로 변환

- URL에 한글이 섞이면 오류 발생

import urllib

#URL에 한글이 섞여있으면 에러가 난다.
url = "https://search.naver.com/search.naver?query=파이썬"
response_urllib = urllib.request.urlopen(url)
byte_data = response_urllib.read()
text_data = byte_data.decode()

#여기 오지도 못함
print(urllib.parse.queat('파이썬'))
print(urllib.parse.quote('파이썬'))
print(urllib.parse.unquote('%ED%8C%8C%EC%9D%B4%EC%8D%AC'))

%ED%8C%8C%EC%9D%B4%EC%8D%AC

파이썬

 


 

홈페이지를 로컬에 파일로 저장

 

파이썬의 파일 입출력 함수 활용

-- open : 파일 객체 생성

-- write : 생성한 파일에 데이터 입력

-- close: 파일 객체 종료

 

홈페이지를 모바일로 저장하려면 헤더(headers)를 추가해서 모바일 페이지로 저장

request 함수에 헤더(headers) 인자에 값을 전달

(*호출할 때 전달하는 값을 인자, 받는 걸 파라미터)

 

#pc 버전
import urllib

request = urllib.request.Request("https://www.naver.com")
data = urllib.request.urlopen(request).read()
#리퀘스트 모듈에서 request로 받은 유알엘 정보를 읽어와서 data에 담음

f = open("pc.html","wb")
f.write(data)
f.close()
#mobile 버전
import urllib

header = {"User-Agent":"Mozilla/5.0 (iphone)"}#헤더에 접속정보 전달. 나 아이폰이야~
request = urllib.request.Request("https://www.naver.com", headers = header)#어 아이폰으로 접속했구나.
data = urllib.request.urlopen(request).read()
f = open("mo.html","wb")#mo.thml로 저장, wb: 바이너리 버전으로
f.write(data)
f.close()

 

 


파라미터를 변경해 여러 정보 가져오기

여러가지 검색어의 naver 검색 결과 출력

- 한글을 인코딩

- 리스트를 통하여 검색어를 저장

import urllib

query_list = ['파이썬', 'python'] #-- 리스트를 통하여 검색어를 저장

URL = "https://search.naver.com/search.naver?query="

for i in query_list:
    new_URL = URL + urllib.parse.quote(i) #quote로 한글을 인코딩하고 리스트 검색어를 주소와 합침
    response_urllib = urllib.request.urlopen(new_URL)
    byte_data = response_urllib.read()
    text_data = byte_data.decode()#utf-8로 디코딩, ()안에 뭐 안넣으면 기본이 utf-8이라구
    print(text_data)

 

 

 

 

반응형