프로그래밍/Crawling

[WebCrawling] 웹 크롤링 기본 개념 및 문법 (BeautifulSoup사용법)

seojeon9 2022. 8. 2. 23:14

이 게시글은 데이터사이언스엔지니어링_전문가 과정을 수강하며 복습을 위해 정리한 글입니다.


 

웹 크롤링은 웹 브라우저를 통해 진행 됨

관련 패키지 : webbrowser

# 브라우저 컨트롤 모듈 import
import webbrowser

 

브라우저 실행 : webbrowser.open

매개변수 : url을 전달(접속하고자 하는 웹 사이트)

- url은 파라미터를 포함할 수 있음 

- https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=python 

- https://search.naver.com/search.naver? : 검색을 위한 필수 주소

- where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=python : 서버에 전달되는 파라미터 (query=검색어)

반환값이 True면 브라우저 실행이 정상 완료

# 네이버 사이트 브라우저 함수로 접속하기
url = 'www.naver.com' # 네이버 도메인
webbrowser.open(url)

# 검색어 적용
search_url = 'https://search.naver.com/search.naver?'
search = 'query=django'
url = search_url + search

webbrowser.open(url)

파이썬을 이용한 웹 데이터 수집

1. 문서 내용 요청 후 읽어오기 urllib 패키지

- urlopen()접속

- read() 데이터 읽어오기

- text 속성을 사용해서 데이터 읽어올 수도 있음

2. 문서에서 원하는 내용 추출하기(파싱)

import requests
# 파이썬 기본 패키지 : HTTP 요청을 보내는 모듈

url = 'http://www.tistory.com'

# request 패키지의 get(url) 함수 사용 : urllib의 open()과 read()를 한번에 진행
response = requests.get(url)

# URL 변수에 저장되어 있는 웹주소로 요청신호를 보냄
# 서버(tistory)는 해당 페이지의 소스코드를 클라이언트로 전송하면서 응답하게 됨
# response 변수에는 http://www.tistory.com의 소스코드(html)가 저장되어 있음
# 정상 응답인지 확인
response.status_code # 응답에대한 상태코드
# 200은 정상응답
# 400번대 코드 : 클라이언트의 요청이 잘못되었다의 의미(url주소가 틀렸거나 권한이 없는 페이지를 요청했거나...)
# 500번대 코드 : 클라이언트는 문법에 맞게 요청을 했는데 서버측에서 인증이 안되었거나, 서버가 망가졌거나의 상태

# 요청에 의해 전달된 코드 확인
# 응답객체.text 속성을 통해 확인
response.text
# 브라우저가 사이트에 요청해서 받은 결과 코드랑 동일한 코드가 문자열 형태로 반환됨

 

파라미터 전달 방법

- 파라미터 : 사이트의 문서를 요청할 때 서버로 전달되는 정보

함수의 파라미터처럼 문서를 찾기위한 정보나 명령을 수행하기 위한 정보를 같이 전달하게 되는데 그 정보를 파라미터라고함

-서버에 파라미터 전송방법 :

1. url뒤에 ?뒤에 파라미터=값&파라미터=값 으로 전송

2. 파라미터를 dict로 구성해서 get(params=dict)

base_url = "https://sports.news.naver.com/news" # 기본 url
param_url = 'https://sports.news.naver.com/news?oid=477&aid=0000312064' # 파라미터가 포함된 url

# 파라미터 전달 방식 1 : get방식 - url 파라미터 포함
# site에 따라 접근 거부할 수도 있음
res1 = requests.get(param_url)
res1.status_code

# 파라미터 전달 방식 2 : get방식 : params= dict사용
# oid=477&aid=0000312064
param = {'oid':477, 'aid':'0000312064'}
res2 = requests.get(base_url, params=param)
res2.status_code

 

urllib 패키지 사용한 소스 추출

from urllib.request import urlopen # 사이트에 요청 신호를 보내는 함수

url = 'https://www.naver.com'
html = urlopen(url)
html
#<http.client.HTTPResponse at 0x2a60cc87c70> : 상태코드와 소스코드가 있습니다.

# 응답객체에서 소스코드 읽어오기 
# - 응답객체.read() - 한번 읽어오면 그 후에는 읽어도 빈 문자열이 나옴
rawtext = html.read()

 

문서에서 원하는 내용 추출하기(파싱)

html문서에서 원하는 내용 추출

  • BeautifulSoup 라이브러리 사용 : 태그 형식으로 된 text를 파싱할 때 사용
  • find() / findAll() 등 함수 사용

 

Beautiful Soup

  • import bs4
  • 데이터를 추출하는데 필요한 기능이 들어 있는 라이브러리(파싱 라이브러리)
  • 외부라이브러리 : 설치해야함
  • 주피터 노트북은 기본패키지라 설치하지 않아도 됨
  • 파이참 설치방법
    • File/Settings
    • Project Interpreter에서 bs4 검색
    • [Install Package]
import bs4

url = 'http://www.naver.com'
html = urlopen(url)

########################################################
# 응답 객체인 html을 BeautofumSoup(응답객체, 파서기) 함수에 전달
# bs4 파싱객체를 반환
bs_obj = bs4.BeautifulSoup(html,'html.parser')

type(bs_obj) # bs4.BeautifulSoup
print(bs_obj.prettify()) # # html 소스코드를 들여쓰기 하여 계층적인 구조로 표현 (가독성이 높다)

 

Beautiful Soup 패키지의 파싱 함수

  • find(태그,[{속성명:속성값}])
    • 지정한 태그 중 첫번째 만나는 태그만 추출 또는 지정한 태그 중 해당 속성과 속상값을 갖고있는 태그의 첫번째 태그
  • findAll(태그,[{속성명:속성값}])
    • 지정한 태그 모두 찾아서 추출
    • 첫번째 이외의 태그를 추출할 때 사용
    • list 형태로 반환
  • find_all(태그,[{속성명:속성값}])
    • findAll 함수와 동일
# html코드를 파싱 객체로 변환
bs_obj = bs4.BeautifulSoup(html_str,"html.parser")
print(type(bs_obj))
#<class 'bs4.BeautifulSoup'> bs4 객체 - 관련함수 사용 가능

bs_obj.find('div')
# 여는 div 태그부터 닫는 div 태그까지
bs_obj.find('div').text
# html코드에서 첫번째 만나는 div태그의 내부 문자열 반환

bs_obj.find('li')
# fing() : ul_t 객체 안에서 첫번째 만나는 li 태그를 반환

uls = bs_obj.findAll('li') # ul_t 객체 안의 모든 li태그를 반환
uls # list형태로 반환

uls[2].text

# 반복문을 이용해서 모든 원소의 text 추출
for li in uls : 
    print(li.text)

 

class 속성값을 이용하여 태그 추출

html_str = """
<html>
    <body>
        <ul class="greet">
            <li>hello</li>
            <li>bye</li>
            <li>welcome</li>
        </ul>
        <ul class="reply">
            <li>ok</li>
            <li>no</li>
            <li>sure</li>
        </ul>
    </body>
</html>
"""

bs_obj = bs4.BeautifulSoup(html_str,'html.parser')

# 태그중에 특정 속성을 갖고 있는 태그를 추출
# ul 태그중 class 속성값이 greet인 태그를 추출
# 첫번째 ul 태그를 추출
bs_obj.find('ul')
# ul태그를 찾고 class 속성값이 greet인걸 확인 후에 반환
bs_obj.find('ul',{'class':'greet'})

# ul 태그중 class 속성값이 reply인 태그를 추출
bs_obj.findAll('ul')[1]
bs_obj.find('ul',{'class':'reply'})  # ul 태그중에 class 속성값이 reply인 첫번째 ul태그 반환
bs_obj.findAll('ul',{'class':'reply'})  # ul 태그중에 class 속성값이 reply인 모든 ul태그 반환
# 해당 ul태그가 여러개면 모두 반환

# bs_obj 객체의 모든 li태그 추출
bs_obj.find_all('li') # lit로 반환
bs_obj.findAll('li') # 6개의 li객체 반환

# class속성값이 greet 인 ul 태그 내의 모든 li 태그 추출
bs_obj.find('ul').find_all('li')
bs_obj.find_all('ul')[0].find_all('li')
bs_obj.find('ul',{'class':'greet'}).find_all('li')
bs_obj.find_all('ul',{'class':'greet'})[0].find_all('li')

 

id 속성값을 이용하여 태그 추출

html_str = """
<html>
    <body>
        <h1 id='title'>Hello Python</h1>
        <p id="crawling">웹 크롤링</p>
        <p id="parsing">파싱</p>
    </body>
</html>"""

bs_obj = bs4.BeautifulSoup(html_str, 'html.parser')

# h1태그 중 id가 title인 태그 추출
bs_obj.find('h1',{'id':'title'})
# p태그 중 id가 parsing인 태그
bs_obj.find('p',{'id':'parding'})

 

bs4 형제 노드 찾기

p1 = bs_obj.find('p')
p1.next_sibling # 첫번째 p태그를 기준으로 다음 p태그를 반환

p1.next_sibling.next_sibling # 첫번째 p태그를 기준으로 다음 다음 p태그를 반환

 

속성값 추출하기

- a태그의 href속성

bs_obj.find_all('a')[0]['href']
# 'https://www.naver.com/'
bs_obj.find_all('a')[0].text
# '네이버'

hrefs=[]
name=[]
for a in bs_obj.find_all('a') :
    name.append(a.text)
    hrefs.append(a['href'])

 

- select사용

# selector : select(태그) 함수에 인수로 전달
# 해당 태그를 모두 찾아서 list로 반환
bs_obj.select('a')
bs_obj.select('div')

# id 선택자 : #을 활용
bs_obj.select('div #mainMenuBox') # 리스트로 반환
# id는 유일
bs_obj.select('#mainMenuBox')

bs_obj.select('#mainMenuBox li') # 자손 li를 찾음 : ul 태그 내부의 li가 반환
bs_obj.select('#mainMenuBox > li') # 자식태그인 li를 찾음

# 클래스 선택자(.클래스명)를 이용
bs_obj.select('.box') # 클래스 속성값이 box인 태그 모두를 추출

# 클래스 속성값이 box인 태그 중 두번째 태그안의 모든 a태그
bs_obj.select('.box')[1].select('a')
# 클래스 속성값이 box인 태그 중 두번째 태그안의 모든 a태그의 첫번째 태그
bs_obj.select('.box')[1].select('a')[0]
bs_obj.select('.box')[0].select('a')[0].text

 

웹 크롤링 시 주의사항

- 웹 사이트는 언제든지 변경될 수 있기 때문에 지금 실행하는 코드가 실행되지 않을 수 있다

728x90