Data Engineering/Crawling

[Crawling] Request/Urllib

YSY^ 2020. 8. 4. 15:15

urllib 패키지를 이용한 URL 다루기

  • urllib 패키지
    • URL 작업을 위한 여러 모듈을 모은 패키지
  • 주요모듈
    • urllib.request: URL을 열고 요청을 위한 모듈
    • urllib.parse: URL 구문 분석을 위한 모듈
    • urllib.robotparser: robots.txt 파일을 구문 분석하기 위한 모듈

URL (Uniform Resource Locator) 이란

- 네트워크 상의 자원(html문서, 이미지 등등) 이 어디에 있는지 그 위치를 식별하기 위한 규약
- 인터넷상의 웹페이지등을 찾기 위한 주소를 말한다.

URL의 구성요소

- 구문
- ` scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]`

  • 스키마(schema): 통신 방식
  • 호스트(host): 서버 주소
  • 포트번호(port): 서버 프로세스를 구분하는 식별번호
  • 패스(path): 서버에서 문서의 위치
  • 쿼리(query): 문서에 전달하는 추가 정보

URL 예

https://search.naver.com:80/search.naver?sm=top_hty&fbm=1&ie=utf8&query=scraping
  • 스키마(scheme): https://
  • 호스트(host): search.naver.com
  • 포트번호(port): 80. 웹브라우저들은 port 번호 생략시 80으로 전송된다.
  • 자원경로(path): search.naver
  • 쿼리스트링(query): ?sm=top_hty&fbm=1&ie=utf8&query=scraping

urllib.parse 모듈을 이용한 URL 다루기

  • 파이썬 내장모듈로 url과 관련된 다양한 기능을 제공하는 모듈
    • url 구문 분석
    • url 경로 합치기

url 분석 (parsing)

  • urllib.parse 모듈의 urlparse()함수 이용
  • urlparse(분석할 URL) : ParseResult
  • ParseResult의 속성을 이용해 url 구성요소 조회
    • scheme, host, port, path, query

url 합치기

크롤링시 같은 사이트의 여러 다른 자원을 조회하는 경우가 많다. 이럴때 host는 동일하고 path 이후가 바뀐다. 그래서 중복되는 url을 base url 로 지정하고 바뀌는 부분만 붙여서 url을 완성하면 편리하다.

  • parse.urljoin(base, url)
    • base+'/'+url 형태로 합쳐 준다.
url = '[https://search.naver.com:80/search.naver?sm=top\_hty&fbm=1&ie=utf8&query=scraping'](https://search.naver.com:80/search.naver?sm=top_hty&fbm=1&ie=utf8&query=scraping')

from urllib import parse  
p = parse.urlparse(url)  
p.scheme, p.hostname, p.port, p.path, p.query

# ('https',  
# 'search.naver.com',  
# 80,  
# '/search.naver',  
# 'sm=top\_hty&fbm=1&ie=utf8&query=scraping')
from urllib import parse
p = parse.urlparse(url)

p.scheme, p.hostname, p.port, p.path, p.query

# ('https','search.naver.com',80,'/search.naver','sm=top_hty&fbm=1&ie=utf8&query=scraping')
base_url = 'https://www.naver.com/'

path1 = '/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=025&aid=0003019608&date=20200722&type=1&rankingSeq=1&rankingSectionId=103'
path2 = '/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=001&aid=0011761497&date=20200722&type=1&rankingSeq=2&rankingSectionId=102'
path3 = '/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=001&aid=0011761851&date=20200722&type=1&rankingSeq=3&rankingSectionId=102'

path_list = [path1, path2, path3]
for path in path_list:
    url = parse.urljoin(base_url, path)
    print(url)

# https://www.naver.com/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=025&aid=0003019608&date=20200722&type=1&rankingSeq=1&rankingSectionId=103
# https://www.naver.com/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=001&aid=0011761497&date=20200722&type=1&rankingSeq=2&rankingSectionId=102
# https://www.naver.com/main/ranking/read.nhn?mid=etc&sid1=111&rankingType=popular_day&oid=001&aid=0011761851&date=20200722&type=1&rankingSeq=3&rankingSectionId=102

requests 모듈을 이용한 웹 요청

  • Requests 홈페이지
  • HTTP 요청을 처리하는 파이썬 패키지
  • get/post 방식 모두를 지원하며 쿠키, 헤더정보등을 HTTP의 다양한 요청처리를 지원한다.
  • 내장 라이브러리가 아니므로 인스톨이 필요
    • 아나콘다 배포판에는 내장되어 있어 별도의 인스톨이 필요없다.
    • pip install requests
    • conda install -c conda-forge requests

요청 함수

  • get(): GET방식 요청
  • post(): POST방식 요청

requests.get(URL)

  • GET 방식 요청
  • 주요 매개변수
    • params: 요청파라미터를 dictionary로 전달
    • headers: HTTP 요청 header를 dictionary로 전달
      • 'User-Agent', 'Referer' 등 헤더 설정
    • cookies: 쿠키정보를 전달
  • 반환값(Return Value)
    • Response

requests.post(URL)

  • POST 방식 요청
  • 주요 매개변수
    • datas : 요청파라미터를 dictionary로 전달
    • files : 업로드할 파일을 dictionary로 전달
      • key: 이름, value: 파일과 연결된 InputStream(TextIOWrapper)
    • headers: HTTP 요청 header를 dictionary로 전달
      • 'User-Agent', 'Referer' 등 헤더 설정
    • cookies: 쿠키정보를 전달
  • 반환값(Return Value)
    • Response

HTTP 요청 헤더(Request Header)

HTTP 요청시 웹브라우저가 client의 여러 부가적인 정보들을 Key-Value 쌍 형식으로 전달한다.

  • accept: 클라이언트가 처리가능한 content 타입 (Mime-type 형식으로 전달)
  • accept-language: 클라이언트가 지원하는 언어(ex: ko, en-US)
  • host: 요청한 host
  • user-agent: 웹브라우저 종류

Response객체 - 요청 결과

  • get()/post() 의 요청 결과를 Response에 담아 반환
    • Response의 속성을 이용해 응답결과를 조회
  • 주요속성
    • url: 응답 받은(요청한) url
    • status_code: HTTP 응답 상태코드
    • headers: 응답 header 정보를 dictionary로 반환
  • 응답 결과 조회
    • text: 응답내용을
    • content: 응답내용(응답결과가 binary 일 경우 - image, 동영상등)
    • json()
      • 응답 결과가 JSON 인 경우 dictionary로 변환해서 반환

JSON(JavaScript Object Notation)

key-value 형태 또는 배열 형태의 text이며 이 기종간 데이터 교환에 많이 사용된다. 자바스크립트 언어에서 Object와 array를 생성하는 문법을 이용해 만듬.

json 모듈

JSON 형식 문자열을 다루는 모듈

  • json.loads(json문자열)
    • JSON 형식 문자열을 dictionary로 변환
  • json.dumps(dictionary)
    • dictionary를 JSON 형식 문자열로 변환

HTTP 응답 상태코드

  • 2XX: 성공
    • 200: OK
  • 3XX: 다른 주소로 이동 (이사)
    • 300번대이면 자동으로 이동해 준다. 크롤링시는 볼일이 별로 없다.
  • 4XX: 클라이언트 오류 (사용자가 잘못한 것)
    • 404: 존재하지 않는 주소
  • 5XX: 서버 오류 (서버에서 문제생긴 것)
    • 500: 서버가 처리방법을 모르는 오류
    • 503: 서버가 다운 등의 문제로 서비스 불가 상태

Get 방식 요청 예제

base_url = 'http://httpbin.org/'

import requests
from urllib import parse
url = parse.urljoin(base_url,'get')
# print(url)

params = {
    'name' : ['hsafd','sdf','erw'],
    'age' : 30
}

#User-Agent header설정
headers = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'    
}

#cookie
cookies = {
    'c1' : 'cookie value 1',
    'c2' : 'cookie value 2',
}

res = requests.get(url, params=params, headers=headers, cookies = cookies)
if res.status_code == 200:
    result_json = res.json() #json 문자열을 dict로 변환
    print(res.text)
    print(type(result_json))
    print(result_json.get('origin'), result_json.get('url'))
    print(result_json.get('headers').get('User-Agent'))
else:
    print(res.status_code, "nasfp")

#결과
{
"args": {
"age": "30",
"name": [
"hsafd",
"sdf",
"erw"
]
},
"headers": {
"Accept": "/",
"Accept-Encoding": "gzip, deflate",
"Cookie": "c1=cookie value 1; c2=cookie value 2",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-5f17dd30-193303f030fbaabcedf471d0"
},
"origin": "112.220.17.226",
"url": "http://httpbin.org/get?name=hsafd&name=sdf&name=erw&age=30"
}

<class 'dict'>
112.220.17.226 http://httpbin.org/get?name=hsafd&name=sdf&name=erw&age=30
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

Post 요청 예

base_url = 'http://httpbin.org/'

import requests
from urllib import parse
url = parse.urljoin(base_url,'post')
# print(url)

#요청파라미터
data = {
    "name" : 'fsad',
    'age' : 30
}
headers = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'    
}
res = requests.post(url, data=data, headers=headers)
print(res.status_code)
if res.status_code == 200:
    result_json = res.json() #json 문자열을 dict로 변환
    print(res.text)
    print(result_json)

#결과
{
"args": {},
"data": "",
"files": {},
"form": {
"age": "30",
"name": "fsad"
},
"headers": {
"Accept": "/",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "16",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-5f17e04b-0022b7223d52f97cd496815f"
},
"json": null,
"origin": "112.220.17.226",
"url": "http://httpbin.org/post"
}

{'args': {}, 'data': '', 'files': {}, 'form': {'age': '30', 'name': 'fsad'}, 'headers': {'Accept': '/', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '16', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'httpbin.org', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36', 'X-Amzn-Trace-Id': 'Root=1-5f17e04b-0022b7223d52f97cd496815f'}, 'json': None, 'origin': '112.220.17.226', 'url': 'http://httpbin.org/post'}

응답결과(Response) 조회

url = 'http://www.pythonscraping.com/pages/warandpeace.html'
import requests
from bs4 import BeautifulSoup

res = requests.get(url)
if res.status_code == 200:
    soup = BeautifulSoup(res.text)
    greens = soup.select("span.green")
    green_words = [tag.text.replace('\n',' ') for tag in greens]
    print(green_words)
else:
    print('fail',res.status_code)

#결과
['Anna Pavlovna Scherer', 'Empress Marya Fedorovna', 'Prince Vasili Kuragin', 'Anna Pavlovna', 'St. Petersburg', 'the prince', 'Anna Pavlovna', 'Anna Pavlovna', 'the prince', 'the prince', 'the prince', 'Prince Vasili', 'Anna Pavlovna', 'Anna Pavlovna', 'the prince', 'Wintzingerode', 'King of Prussia', 'le Vicomte de Mortemart', 'Montmorencys', 'Rohans', 'Abbe Morio', 'the Emperor', 'the prince', 'Prince Vasili', 'Dowager Empress Marya Fedorovna', 'the baron', 'Anna Pavlovna', 'the Empress', 'the Empress', "Anna Pavlovna's", 'Her Majesty', 'Baron Funke', 'The prince', 'Anna Pavlovna', 'the Empress', 'The prince', 'Anatole', 'the prince', 'The prince', 'Anna Pavlovna', 'Anna Pavlovna']

728x90
반응형