크롤링(Crawling)이란, 원하는 사이트의 정보나 링크의 정보들을 긁어 모으는 작업을 말한다.
웹 크롤링을 위해서는 requests와 BeautifulSoup 라이브러리를 설치해야 하는데, 설치 명령어는 다음과 같다.
- pip install requests
- pip install bs4
- requests: 이 라이브러리는 웹 페이지에 HTTP 요청을 보내고 응답을 받는 데 사용된다. 즉, 웹 서버에 GET, POST 등의 요청을 보내고, 서버로부터 HTML, JSON 등의 응답을 받을 수 있다.
- bs4 (BeautifulSoup): 이 라이브러리는 HTML 및 XML 문서를 구문 분석하고 추출하는 데 사용된다. 웹 페이지의 구조를 탐색하고 필요한 정보를 추출하기 위해 사용된다. 주로 웹 크롤링 및 웹 스크래핑에 활용된다.
웹 크롤링을 할 때는 HTTP 상태 코드를 파악하여 요청의 성공 여부를 확인한다.
성공적인 처리를 의미하는 상태 코드는 200이다.
HTTP 상태 코드
: 웹 서버가 클라이언트에게 요청에 대한 처리 결과를 알려주는 데 사용되는 3자리 숫자
- 1xx (Informational): 요청을 받았으며, 처리가 계속되고 있음 (예를 들어, 100 코드는 "Continue"를 나타내며, 요청의 일부를 받았고 클라이언트는 계속해서 요청을 보낼 수 있음을 의미)
- 2xx (Success): 요청이 성공적으로 처리되었음
- 3xx (Redirection): 클라이언트가 요청을 완료하려면 추가 동작이 필요함 (예를 들어, 301 코드는 "Moved Permanently"를 나타내며, 리소스가 영구적으로 새 위치로 이동되었음을 의미)
- 4xx (Client Error): 클라이언트의 요청에 오류가 있음 (가장 많이 볼 수 있는 상태 코드는 404로, 요청한 리소스를 찾을 수 없음을 의미)
- 5xx (Server Error): 서버가 요청을 처리하는 동안 오류가 발생했음 (500 코드는 "Internal Server Error"를 나타내며, 서버가 처리할 수 없는 일반적인 오류를 의미)
추가적인 상태 코드는 아래를 참고하면 좋을 듯 하다.
[웹 프로그래밍] HTTP 상태 코드 표(100 ~ 500) 전체 요약 정리
서버에서의 처리 결과는 응답 메시지의 상태 라인에 있는 상태 코드(status code)를 보고 파악할 수 있습니다. 상태 코드는 세 자리 숫자로 되어 있는데 첫 번째 숫자는 HTTP 응답의 종류를 구분하는
hongong.hanbit.co.kr
다음은 BeautifulSoup 라이브러리를 사용하여 HTML 문서에서 원하는 요소를 찾는 데 사용되는 매개변수에 대한 설명이다.
클래스 요소를 찾을 때 class 예약어와의 충돌을 피하기 위해 'class_' 라는 매개변수를 사용한다는 점을 유의하자.
속성 | 설명 |
name | 찾고자 하는 태그의 이름을 지정합니다. 문자열, 정규표현식, 함수 등으로 지정할 수 있습니다. |
attrs | 태그의 속성과 속성 값으로 찾습니다. 딕셔너리 형태로 지정하며, 속성이 일치하는 태그를 찾습니다. 예: soup.find_all(attrs={'class': 'example'}) |
recursive | 기본적으로 find_all은 하위 태그까지 모두 검색합니다. recursive=False로 설정하면 직속 자식 태그만 검색합니다. |
string | 태그의 텍스트 내용을 기반으로 찾습니다. 문자열, 정규표현식, 리스트 등을 사용할 수 있습니다. 예: soup.find_all(string='example') |
limit | 검색 결과의 개수를 제한합니다. limit=1로 설정하면 첫 번째로 발견된 태그만 반환합니다. |
class_ | class는 파이썬의 예약어이므로, BeautifulSoup에서는 class_로 사용합니다. 클래스 이름으로 태그를 찾습니다. 예: soup.find_all(class_='example') |
id | 태그의 ID로 태그를 찾습니다. ID는 문서 전체에서 고유해야 합니다. 예: soup.find_all(id='example') |
limit | 검색 결과의 개수를 제한합니다. limit=1로 설정하면 첫 번째로 발견된 태그만 반환합니다. |
recursive | 기본적으로 find_all은 하위 태그까지 모두 검색합니다. recursive=False로 설정하면 직속 자식 태그만 검색합니다. |
limit | 검색 결과의 개수를 제한합니다. limit=1로 설정하면 첫 번째로 발견된 태그만 반환합니다. |
이해를 돕기 위한 웹 크롤링 예제는 다음과 같다.
from bs4 import BeautifulSoup
# HTML 내용
html_content = """
<html>
<body>
<a href="https://www.example.com/page1">Link 1</a>
<a href="https://www.example.com/page2">Link 2</a>
<a href="https://www.example.com/page3">Link 3</a>
</body>
</html>
"""
# BeautifulSoup 객체 생성
soup = BeautifulSoup(html_content, "lxml")
# 모든 <a> 태그 찾기
links = soup.find_all('a')
# 각 링크의 href 속성과 텍스트 출력
for link in links:
if 'href' in link.attrs: # 링크에 href 속성이 있는지 확인
href = link["href"] # href 속성 값 가져오기
print(f"{link.text} 링크: {href}")
# get() 메서드를 사용하여 href 속성 값 가져오기
for link in links:
href_value = link.get('href') # href 속성 값 가져오기
print(f"{href_value}")
ex_crawl.py
예제에서는 find_all() 메서드를 사용했으나 find() 메서드를 사용해 첫 번째 요소만 가져올 수도 있다.
find_all() 메서드는 모든 요소를 찾아 리스트 형태로 반환하기 때문에 조건에 맞는 요소가 없으면 빈 리스트([])를 반환하지만,
find() 메서드는 단일 요소를 반환하기 때문에 조건에 맞는 요소가 없으면 None을 반환한다는 차이가 있다.
import requests
from bs4 import BeautifulSoup
# URL
url = "https://sports.news.naver.com/news?oid=139&aid=0002168397"
# HTTP 요청 헤더 설정
headers = {
'User-Agent': 'Mozilla/5.0', # 가짜 User-Agent를 사용하여 서버에게 웹 브라우저인 것처럼 보이도록 함
'Content-Type': 'text/html; charset=utf-8' # 요청하는 문서의 인코딩을 지정
}
# HTTP GET 요청 보내기
req = requests.get(url, headers=headers)
# BeautifulSoup 객체 생성
soup = BeautifulSoup(req.text, "lxml") # lxml 파서를 사용하여 HTML 파싱
# 모든 <a> 태그 찾기
links = soup.find_all('a')
# 각 링크의 href 속성과 텍스트 출력
for link in links:
if 'href' in link.attrs: # 링크에 href 속성이 있는지 확인
href = link["href"] # href 속성 값 가져오기
print(f"{link.text} 링크: {href}")
crawl01.py
lxml 파서를 사용하기 위해서는 추가적으로 pip install lxml 명령어를 통한 라이브러리 설치가 필요하다.
여러 가지 파서 중 lxml 파서가 속도가 빠르고 유연하며 HTML 문서를 잘 처리하기 때문에 주로 사용된다고 한다.
모든 <a> 태그를 찾아 출력한 덕에 매우 긴 실행결과가 나오는 것을 확인할 수 있다.
import requests
from bs4 import BeautifulSoup
# URL
url = "http://www.boannews.com/"
# HTTP 요청 헤더 설정
headers = {
'User-Agent': 'Mozilla/5.0', # 가짜 User-Agent를 사용하여 서버에게 웹 브라우저인 것처럼 보이도록 함
'Content-Type': 'text/html; charset=utf-8' # 요청하는 문서의 인코딩을 지정
}
# HTTP GET 요청 보내기
req = requests.get(url, headers=headers)
# BeautifulSoup 객체 생성
soup = BeautifulSoup(req.text, "lxml") # lxml 파서를 사용하여 HTML 파싱
# CSS 선택자를 사용하여 원하는 요소 찾기
tags = soup.select('#headline2 > ul > li > p')
# 선택된 요소의 텍스트 출력
for tag in tags:
print(tag.text)
crawl02.py
보안뉴스에서 가장 중앙의 뉴스 제목만 긁어와 텍스트로 출력하는 예제이다.
가짜 User-Agent를 사용하는 이유
= 서버로부터 차단을 피하기 위해서
일부 웹 서버는 자동화된 요청을 차단하기 위해 User-Agent를 확인하고, 일반적인 브라우저의 User-Agent가 아닌 요청은 차단할 수 있다. 따라서 가짜 User-Agent를 사용하여 실제 웹 브라우저처럼 보이도록 설정하여 이러한 차단을 우회할 수 있다고 한다.
User-Agent를 지정하지 않은 경우에는 기본적으로 Python requests 모듈이나 BeautifulSoup 등의 라이브러리에서 사용하는 기본 User-Agent가 사용되는데, 이는 서버에게 스크레이핑이나 크롤링이라는 것을 알리는 신호가 될 수 있으며, 이로 인해 차단될 가능성이 있으니 주의해야 한다.
import requests
from bs4 import BeautifulSoup
# URL
url = "https://www.malware-traffic-analysis.net/2024/index.html"
# HTTP 요청 헤더 설정
headers = {
'User-Agent': 'Mozilla/5.0', # 가짜 User-Agent를 사용하여 서버에게 웹 브라우저인 것처럼 보이도록 함
'Content-Type': 'text/html; charset=utf-8' # 요청하는 문서의 인코딩을 지정
}
# HTTP GET 요청 보내기
req = requests.get(url, headers=headers)
try:
# BeautifulSoup 객체 생성
soup = BeautifulSoup(req.text, "lxml") # lxml 파서를 사용하여 HTML 파싱
# CSS 선택자를 사용하여 원하는 요소 찾기
tags = soup.select('a.main_menu')
# 선택된 링크의 텍스트와 URL 출력
for tag in tags:
print(tag.text) # 링크의 텍스트 출력
print(f"https://www.malware-traffic-analysis.net/2024/{tag['href']}") # 링크의 URL 출력
except Exception as e:
print("An error occurred:", e)
crawl03.py
Malware-Traffic-analysis.net 사이트를 크롤링해 제목과 링크 정보를 가져오는 예제이다.
여러 연도의 포스트 중 2024년의 포스트만 가져오도록 했다.
Malware-Traffic-Analysis.net 이란?
"Malware Traffic Analysis"는 악성 코드와 관련된 네트워크 트래픽에 대한 분석 및 연구를 제공하는 웹 사이트이다.
이 사이트는 실제로 발생한 악성 코드와 관련된 네트워크 활동을 분석하고 이를 포괄적으로 문서화하여 보안 커뮤니티에 공유한다.
pcap 파일 및 멀웨어 샘플을 위한 소스도 제공받을 수 있다고 한다.
'대외활동 > SK Shieldus rookies' 카테고리의 다른 글
[인프라 활용을 위한 파이썬] #5 슬랙 API 활용 및 자동화 (0) | 2024.03.22 |
---|---|
[인프라 활용을 위한 파이썬] #4 FTP 서비스 연결 및 자동화 (0) | 2024.03.12 |
[인프라 활용을 위한 파이썬] #3 엑셀파일 자동화 (0) | 2024.03.10 |
[인프라 활용을 위한 파이썬] #1 파일&디렉토리, 메일 자동화 (2) | 2024.03.07 |