[0319 복습] 웹 크롤링_동적 페이지, REST API, HTML, CCS-Selector
웹 크롤링(Web Crawling) (2)
크롤링 심화
- 파이썬 코드로 웹 서버에 데이터를 긁어오면 Was는 abuser로 판단해서 request를 차단
- request할 때 Client, header 영역에 user-agent라는 정보가 자동으로 전달됨
- user-agent: 컴퓨터가 윈도우인지, OS인지 접속한 브라우저 정보 들어있는 것!
**파이썬으로 불러오면 user-agent에 python이 들어가 있음 ! → 정상적인 request 보내지 말아야 겠다고 판단하는 것
** 이 경우 파이썬 코드로 데이터를 수집할 수 없음!
→ 회피 방법? request는 client쪽에서 만들어지는 data이기 때문에,
파이썬에서 코딩으로 user-agent 정보를 설정해서 서버로 보내는 것!
크롤링 절차
- 기존대로 url을 가져오고, request > response 과정에서 403 에러가 발생 !
- url을 이용해서 request 했더니 403 에러 메시지를 서버에서 전달함!
- 아래 이미지는 에러일 때 response.text 결과값
- Was 입장에서 403 띄운 이유는 정확히 알 수 없음!
- 따로 설정하지 않으면 header의 user agent가 파이썬으로 들어감 → user agent확인 referer확인
import pandas as pd
import requests
url = '링크'
# User-Agent, Referer을 헤더로 지정
headers = {'User-Agent': '개발자도구에서 복붙','Referer': '개발자도구에서 복붙',}
response = requests.get(url, headers=headers)
data = response.json()['data']
df = pd.DataFrame(data)[['가져올 열이름1','가져올 열이름2','가져올 열이름3' ]]
- header 사이에 콤마를 쓰는 이유: 코드를 확인할 때 어떤 코드가 바뀌었는지를 알기 위함! (콤마를 찍어놓으면 콤마 바로 앞줄만 확인하면 됨!)
- referer: url1에서 url2로 이동함! 이때도 request가 일어남, referer라는 곳은 url1로 들어가서 request를 요청
→ 현재 보고 있는 사이트 이전에 보고 있던 (url1) 사이트가 무엇인지 확인할 수 있는 방법!
REST API
- kakao developers 접속 후 로그인
- 내 애플리케이션 > 애플리케이션 추가하기 > KT라는 새로운 앱 생성
- 문서 > KoGPT > REST API > 도큐먼트에 친절히 사용방법 설명되어 있음!
- 쿼리 파라미터를 확인: 필수 파라미터가 2개 prompt, max_tokens
- 아래는 다음 문장 만들기 코드!
# 1. document : url
REST_API_KEY = 키 값 가져오기 (내 애플리케이션에서)
url = '링크'
prompt = '원자폭탄을 발명한 사람은'
params = {'prompt': prompt, 'max_tokens' : 50(가져올 개수)}
headers = {'Content-Type': 'application/json', 'Authorization': f'KakaoAK {REST_API_KEY}'}
# 2. request(url, headers, params) > response(data): json(str)
response = requests.post(url, json.dumps(params), headers=headers)
response
# 3. json(str) : text
response.json()['generations'][0]['text'].strip()
- jump.dumps: 파라미터에 한글이 있는 경우 인코딩하는 것
직방 매물 데이터 실습_매물 아이디로 매물 정보 가져오기
- 사이트 접속 > 원룸 > 망원동 검색 > 두번 검색하면 망원동 검색한 내용이 브라우저에 저장됨!
→ 개발자 도구에서 네트워크가 새로 뜨지 않음
- headers확인: payload 탭 확인하면 한글 깨진 내용 알 수 있음
https://meyerweb.com/eric/tools/dencoder/
URL Decoder/Encoder
meyerweb.com
→ 영어로 되어있는 것 한글로 디코딩
(특정 위치가 아니라 매물정보가 모인 영역 전체를 설명할 때 → geohash로 지리정보 시스템에서 사용함!)
#%%~부분은 모듈 저장
%%writefile zigbang.py
import pandas as pd # 모듈에 저장할 때는 새로 불러와야 함
import requests
import geohash2
def oneroom(addr):
url = f'{addr}포함된 링크'
response = requests.get(url)
data = response.json()['items'][0] #ㅇㅇ동에 대한 결과값이 여러개일 수도 있기 때문에 리스트로 출력됨, 가장 먼저 나오는 데이터를 가져오기 위해 인덱싱 [0] 해줌
lat, lng = data['lat'], data['lng']
# precision : 영역 범위 : 커질수록 영역이 작아짐
# 다른 동에 대한 검색도 파이썬에서 가능할 수 있도록 하기 위해 geohash값을 복붙이 아니라,
# geohash 패키지를 사용해서 값을 가져오는 것!
geohash = geohash2.encode(lat, lng, precision=5)
url = f'{geohash}포함된 링크'
response = requests.get(url)
items = response.json()['items']
ids = [item['itemId'] for item in items]
url = '리스트 링크'
params = {'domain': 'zigbang', 'item_ids': ids}
response = requests.post(url, params)
items = response.json()['items']
df = pd.DataFrame(items)
df = df[df['address1'].str.contains(addr)].reset_index(drop=True)
columns = ['불러올 열 이름1', '불러올 열 이름2', '불러올 열 이름3']
return df[columns]
%ls zigbang.py # 모듈 저장 확인
HTML
HTML
- HTML: 웹 문서를 작성하는 마크업 언어
HTML 구성요소
- Document: 한 페이지를 나타내는 단위
- Element: 하나의 레이아웃을 나타내는 단위(시작태그, 끝태그, 텍스트로 구성)
**element는 계층적 구조를 가지고 있음, 시작태그와 끝태그의 결합
- Tag: 엘리먼트의 종류를 정의: 시작태그(속성값), 끝태그
- Attribute: 시작태그에서 태그의 특정 기능을 하는 값
- id: 웹 페이지에서 유일한 값
- class: 동일한 여러개의 값 사용 가능 : element를 그루핑할 때 사용
- attr: id와 class를 제외한 나머지 속성들
- Text: 시작태그와 끝태그 사이에 있는 문자열
HTML 구조
- DOCTYPE: 문서의 종류를 선언하는 태그
- html
- head
- meta: 웹페이지에 대한 정보를 넣음
- title: 웹페이지의 제목 정보를 넣음
- body: 화면을 구성하는 엘리먼트가 옴
<!-- HTML 웹문서의 기본적인 구조 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
</html>
HTML 태그
- html에서 문자를 나타내는 태그
head
- title을 나타낼 때 사용, Head는 총 6가지 종류의 태그가 있음, 숫자가 커질수록 문자의 크기가 줄어듦
%%html
<h1>Data1</h1> #제목을 사용할 때 h태그 사용 h1 > h3 문자 크기 작아짐
p
- 한 줄의 문자열을 출력하기 위한 태그
%%html
<p>data1</p>
<p>data2</p>
span
- 한 블럭의 문자열을 표현하기 위한 태그
%%html
<span>data1</span>
<span>data2</span>
문자 이외의 HTML 태그
div
- 레이아웃을 나타내는 태그
%%html
<div>
<p>data1</p>
<p>data2</p>
</div>
<div>data3</div>
<div>data3</div>
ul, li
- 리스트를 나타내는 태그
%%html
<ul>
<li>data1</li>
<li>data2</li>
</ul>
a
- 링크를 나타내는 태그, href 속성에 url을 넣음, url과 상대경로를 모두 사용 가능, target='blank' 옵션
# 타겟이 없으면 -> 현재 페이지가 KT 페이지로 바뀜, 기존 페이지는 사라짐!
# 타겟을 넣으면 기존 페이지가 열려있는 상태로 새로 KT 페이지가 뜸!
%%html
<a href = '링크' target='blank'>'문자열'</a>
image
- 이미지를 나타내는 태그
# 텍스트가 없으면 엘리먼트에 끝태그가 없어도 됨
%%html
<img src='이미지 링크' alt='kt'>
CSS Selector
Element Selector
- 엘리먼트를 이용해 선택할 때 사용
- css selector로 div를 사용하면 가장 위에 있는 dss1 이 선택됨!
ID Selector
- 아이디를 이용해 선택할 때 사용
- 아이디를 셀렉할 때는 #(아이디 이름)으로 선택, css selector #ds2를 사용하면 dds2가 선택
- 여러개를 셀렉할 때는 ,로 구분
- css selector로 #ds2, #ds3를 사용하면 dds2와 dds3가 선택
Class Selector
- 클래스를 이용해 선택할 때 사용, 클래스를 셀렉할 때는 .(클래스 이름)으로 선택
- 아이디를 셀렉할 때는 #(아이디 이름)으로 선택, css selector #ds2를 사용하면 dds2가 선택
Not Selector
- 셀렉터로 엘리먼트를 하나만 제거하고 싶을 때 사용
- not을 사용해 셀렉할 때에는 :not(선택에서 제거하고 싶은 셀렉터)으로 선택
- 아래에서 .ds:not(.ds2)로 셀렉하면 class가 ds2인 클래스를 제외하고 나머지 ds1, ds3, ds4, ds5가 선택
nth-child Selector
- 엘리먼트로 감싸져있는 n번째 엘리먼트가 설정한 셀렉터와 일치하면 선택
- .ds:nth-child(3), .ds:nth-child(4)로 설정하면 ds4, ds5가 선택
- nth-child의 ()안의 숫자는 가장 첫번째가 0이 아니라 1로 시작
모든 하위 depth(공백) Selector
- 공백문자로 하위 엘리먼트를 셀렉트했을 때, 모든 하위 엘리먼트를 선택
- .contants h1를 선택하면 inner_1, inner_2가 선택
바로 아래 depth(>) Selector
- > 문자로 하위 엘리먼트를 셀렉트 했을때, 바로 아래 엘리먼트를 선택
- .contants > h1를 선택하면 inner_1이 선택
네이버 연관 검색어 수집 실습
import pandas as pd
import requests
from bs4 import BeautifulSoup #css-selector로 엘리먼트 선택 가능
query = '검색어'
url = f'https://search.naver.com/search.naver?query={query}'
# request(URL) > response:str(html)
response = requests.get(url)
#str(html) > bs object
dom = BeautifulSoup(response.text, 'html.parser')
# bs object > .select(css-selector), select_one(css-selector) > str(text)
selector = '개발자도구에서 copy한 css-selector'
elements = dom.select(selector)
len(elements), elements[0]
keywords = [element.text.strip() for element in elements]