-
웹 크롤링 - BeautifulSoup+Pandas를 이용한 데이터 분석빅데이터/BeautifulSoup 2021. 12. 11. 14:41
인프런 '코딩교양스쿨' 강의 참조
01 웹 크롤링 VS 웹 스크래핑
- 웹 크롤링 OR 웹 스크래핑은 거의 같은 개념이라고 보면 된다. 위키피디아에 따르면 웹 스크래핑은 웹에 있는 데이터를 수집하는 기술을 뜻하고 웹 크롤링은 스크래핑시에 사용되는 기술(봇, 크롤러 등)을 의미
- 웹 스크래핑/크롤링 시에는 데이터를 수집하고자 하는 사이트에 무리가 가지않도록 조금씩 작게 천천히 해야하며 저작권과 금지되어있는(로봇 배제 표준 robots.txt)것들은 하지 않아야 한다
02 웹의 구성
- HTML/CSS/JS 로 구성되어있으며 HTML - 뼈대, CSS - 꾸미는 용도, JS - 동적으로 만드는 기술이라고 알면 쉽다
- HTML은 요소(elements)와 속성(attributes)로 이루어져 있다.
요소 - 태그를 의미 (<h1>, <br> 등)
속성 - 요소의 추가적인 정보 (href=, src=, width= 등) - 웹 스크래핑/크롤링 시에는 주로 HTML 혹은 CSS 구성 요소들로 데이터를 찾아서 긁게 된다
03 Chrome 개발자 도구
- 웹 스크래핑/크롤링 시에 반드시 필요한 웹 플랫폼이며 이 플랫폼 자체가 검사(F12)를 지원하는데 이 기능으로 어떠한 웹사이트들이 어떻게 구성되어있는지 보여 준다.
04 BeautifulSoup 라이브러리 훑어 보기
- 파이썬 기능 중 하나인 'Requests'를 사용할때 같이 쓰는 라이브러리
정적 페이지 vs 동적 페이지 냐에 따라 쓰는 도구가 달라지게 된다 - BeautifulSoup을 사용하다보면 웹사이트들이 봇으로 인식하는 경우가 있고 이때 차단을 당하는 경우가 발생 한다. 이럴때 계속해서 스크래핑을 진행 할 수 있게 하는것이 'User-Aagent'라는 정보인데 이를 바꿔주면 웹사이트가 사람으로 인식하여 스크래핑을 계속해서 진행이 가능해 진다.
나의 user-agent 정보는 구글에서 'my user agent' 라고 검색하면 나온다. - BeautifulSoup 간단 실습
- find - 찾고자 하는 조건의 결과물중 첫번째 결과물만 들고 옴
- find_all - 찾고자 하는 조건의 결과물을 모두 들고 옴 (리스트 형식으로 리턴)
from bs4 import BeautifulSoup as bs html_doc = """<html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """ soup = bs(html_doc, "html.parser")
print("find: ", soup.find('p')) print() print("find_all: ", soup.find_all('p')) print() find: <p class="title"><b>The Dormouse's story</b></p> find_all: [<p class="title"><b>The Dormouse's story</b></p>, <p class="story">Once upon a time there were three little sisters; and their names were <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well.</p>, <p class="story">...</p>] a_list = soup.find_all('a') for i in a_list: print("<a> 태그들이 가진 링크들: ", i.attrs['href']) <a> 태그들이 가진 링크들: http://example.com/elsie <a> 태그들이 가진 링크들: http://example.com/lacie <a> 태그들이 가진 링크들: http://example.com/tillie for i in a_list: print(i.string) Elsie Lacie Tillie * 태그안의 문자열은 .string/.text로 바로 들고 오면 된다.
* .string vs .text 차이는 다음 링크에서 확인! (https://www.inflearn.com/questions/3945)
- requests 신호 확인 하기
import requests from bs4 import BeautifulSoup as bs headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'} url = "https://www.transfermarkt.com/" r = requests.get(url, headers=headers) r.status_code
05 트랜스퍼 마켓 스크래핑
※ 기본 세팅
import requests from bs4 import BeautifulSoup as bs import pandas as pd import time headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'} url = "https://www.transfermarkt.com/spieler-statistik/wertvollstespieler/marktwertetop" r = requests.get(url, headers=headers) r.status_code
어떠한 패턴이 있는지 찾아야 한다 soup = bs(r.content, 'html.parser') player_info = soup.find_all('tr', class_=['odd', 'even']) print(player_info[0]) print("------------------------") print(len(player_info)) <tr class="odd"> <td class="zentriert">1</td> ... --------------------- 25
- 모든 선수들은 <tbody>에 들어 가 있다
- 각각의 선수들은 <tr> 태그에 클래스 이름이 'even' or 'odd' 로 분류 되어 있다
- 정보들은 <tbody> → <tr> → '<td>' 태그에 담겨 있다
- 총 25명이 나오는것을 볼 수 있다.
- 7개 정보 들고와보기 number, name, position, age, nation, team, value
한 스텝씩 진행하는 습관이 중요하다.
1) '<td>' 정보 들고 와 보기
*<td> 태그안에 정보가 있다는걸 알고 있다 number = [] name = [] position = [] age = [] nation = [] team = [] value = [] for info in player_info: player = info.find_all("td") print(player[0]) <td class="zentriert">1</td> <td class="zentriert">2</td> <td class="zentriert">3</td> ...
2) 각각의 부분을 준비된 리스트 변수에 담기
for info in player_info: player = info.find_all("td") # print(player[0]) number.append(player[0].get_text()) print(number) ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25']
3) 생각지 못한 데이터 형태 확인
번호는 잘 뽑았으나 이름을 뽑으려고 하니 몇 번째의 '<td>' 태그인지 찾아보아야 한다 → 이름 데이터의 경우 너무나도 보기가 어렵고 이런식의 데이터일 경우 몇번째 인덱스에 문자열이 들어가 있는지 잘 보아야 한다
for info in player_info: player = info.find_all("td") # print(player[0]) number.append(player[0].get_text()) # print(player[3].get_text()) name.append(player[3].get_text()) # print(number) print(name) ['Kylian Mbappé', 'Erling Haaland', 'Harry Kane', 'Jack Grealish', 'Mohamed Salah', 'Romelu Lukaku', 'Kevin De Bruyne', 'Neymar', 'Jadon Sancho', 'Frenkie de Jong', 'Bruno Fernandes', 'Joshua Kimmich', 'Raheem Sterling', 'Marcus Rashford', 'Sadio Mané', 'Heung-min Son', 'Pedri', 'Phil Foden', 'Lautaro Martínez', 'Marcos Llorente', 'Lionel Messi', 'Mason Mount', 'Trent Alexander-Arnold', 'Rúben Dias', 'Marquinhos']
4) 포지션, 나이는 위와 동일. 하지만 국가와 팀은 사진으로 되어있다. 해결해 보자.
어떤 정보를 긁어 올 수 있을까? for info in player_info: player = info.find_all("td") ... print(player[6])
<img> 태그에 문자열로 국가가 표시 되어 있는것을 볼 수 있다 for info in player_info: player = info.find_all("td") ... nation.append(player[6].img['alt']) ['France', 'Norway', 'England', 'England', 'Egypt', 'Belgium', 'Belgium', 'Brazil', 'England', 'Netherlands', 'Portugal', 'Germany', 'England', 'England', 'Senegal', 'Korea, South', 'Spain', 'England', 'Argentina', 'Spain', 'Argentina', 'England', 'England', 'Portugal', 'Brazil']
→[6]번 인덱스 값에 <img> 태그를 찾고 그 안의 속성인 'alt'가 가진 내용을 들고 오면 된다
5) 팀 정보도 같은 이슈 이다.
for info in player_info: player = info.find_all("td") .. print(player[7])
for info in player_info: player = info.find_all("td") ... team.append(player[7].a['title']) ['Paris Saint-Germain', 'Borussia Dortmund', 'Tottenham Hotspur', 'Manchester City', 'Liverpool FC', 'Chelsea FC', 'Manchester City', 'Paris Saint-Germain', 'Manchester United', 'FC Barcelona', 'Manchester United', 'Bayern Munich', 'Manchester City', 'Manchester United', 'Liverpool FC', 'Tottenham Hotspur', 'FC Barcelona', 'Manchester City', 'Inter Milan', 'Atlético de Madrid', 'Paris Saint-Germain', 'Chelsea FC', 'Liverpool FC', 'Manchester City', 'Paris Saint-Germain']
→ 이번에는 <a> 태그가 가진 'title' 값으로 찾아 왔다
6) 시장가치 부분도 눈여겨 볼 필요가 있다
for info in player_info: player = info.find_all("td") .. print(player[8])
<td> 태그안에 <b> 태그로도 값이있고 <span> 태그에도 값이 있기 때문 for info in player_info: player = info.find_all("td") .. # value.append(player[8].b.get_text()) value.append(player[8].span['title']) ['€160.00m', '€130.00m', '€120.00m', '€65.00m', '€110.00m', '€90.00m', '€100.00m', '€110.00m', '€100.00m', '€80.00m', '€90.00m', '€90.00m', '€100.00m', '€85.00m', '€100.00m', '€85.00m', '€70.00m', '€70.00m', '€70.00m', '€70.00m', '€80.00m', '€60.00m', '€100.00m', '€70.00m', '€70.00m']
'빅데이터 > BeautifulSoup' 카테고리의 다른 글
웹 크롤링 - BeautifulSoup+Pandas를 이용한 데이터 분석 3 (0) 2021.12.13 웹 크롤링 - BeautifulSoup+Pandas를 이용한 데이터 분석 2 (0) 2021.12.11 웹 크롤링 - BeautifulSoup 기초 개념 (0) 2021.12.09 BeautifulSoup 03 - Basics of data science tasks (2) - 위키피디아 영화 관련 스크래핑 (0) 2021.12.08 BeautifulSoup 03 - Basics of data science tasks (1) - 위키피디아 영화 관련 스크래핑 (0) 2021.12.01