웹크롤링이란
자동으로 웹을 탐색하고 필요한 정보를 긁어(crawl)오는 것을 말한다.
파싱부터 데이터베이스까지 아주 단순한 웹 크롤러를 만들어보고자 한다.
1. HTML 파싱
거의 모든 웹사이트들은 HTML이라는 파일로 어떻게 보여질 지 정해진다.
이 HTML 파일을 통해서 우리가 필요한 정보를 얻을 수 있다.
파싱이란 특정한 의미를 갖는 부분을 자르는 방법을 말한다.
우리가 받아올 HTML 정보는 따로 파싱이 되어있지 않는 형태이기 때문에 파이썬으로 파싱하는 것이 필요하다.
HTML 파싱에 필요한 라이브러리는 다음과 같다
- pip install requests
- pip install bs4
reqeusts는 HTTP 프로토콜 명령(GET, POST 등)을 통해 웹사이트의 소스파일(html)을 얻기위해 쓰는 라이브러리라서 필요하다
!pip install bs4
bs4는 html파일을 다루기 쉽게 정리해주는 라이브러리이다
import requests
from bs4 import BeautifulSoup
이제 크롤링하기 좋을만한 사이트를 찾아보자
여러 링크들을 쉽고 많이 찾을 수 있는 사이트면 좋다
나는 한국경제를 선택했다.
왜냐하면 사이트맵에서 모든 뉴스들을 볼 수 있고 편하게 접근가능하게 링크들이 모여있기 때문이다
적당히 오늘 나온 뉴스중 하나를 찾아서 본다.
https://www.hankyung.com/finance/article/202012045610Y
크롤링하고 싶은 부분을 생각해본다.
나는 제목이랑 내용들을 따로 떼오고 싶다.
html 코드는 아래의 requests.get(url)을 통해 얻을 수 있다.[
results = requests.get('https://www.hankyung.com/finance/article/202012045610Y')
print(results.content)
Ctrl+F 로 제목(뉴욕증시,...)를 검색해보면
<title>뉴욕증시 美고용 부진에도 재정 부양책 기대 상승 출발 | 한경닷컴</title> 라는 부분이 나온다.
원하는 내용을 얻기 위해서는 원하는 내용이 들어가는 패턴이나 태그를 찾아야한다.
딱히 정해져있는 방법이 아니기 때문에 눈썰미가 좀 필요할 수 있다.
위에서 찾은 뉴스 제목은 title이라는 이름의 tag 로부터 구할 수 있다
title = bs_obj.title.string
print(title)
뉴욕증시 美고용 부진에도 재정 부양책 기대 상승 출발 | 한경닷컴
뉴스의 제목을 얻었으니 이제 뉴스의 내용을 얻어보자
Ctrl+F로 내용의 일부인
"뉴욕증시에서 주요 지수는 4일 미국 고용 부진에도" 라는 부분을 검색하면 대충 아래와 같은 부분이 나온다.
대충 <div id="articletxt"> 라는 태그를 찾으면 될 것처럼 보인다.
<div id="articletxt">
라는 건 div 라는 tag인데 id 라는 속성이 articletxt로 설정되어있는 것을 말한다.
속성값을 특정해서 태그를 찾는 방법은 BeautifulSoup의 find함수를 사용하면 된다.
content_obj = bs_obj.find('div', {'id':'articletxt'})
print(content_obj.text.strip())
뉴욕증시에서 주요 지수는 ~~ 움직였다. /연합뉴스
이로써 뉴스의 제목과 내용을 파싱하는데 성공하였다.
위 예시에는 태그만으로 쉽게 정보를 파싱할 수 있었지만 그럴 수 없는 경우에는 string의 split이라는 함수가 참 유용하게 쓰인다.
예시를 하나 살펴보자
"삼성전자 주식 가격: 70000원, LG전자 주식 가격: 440000원, 현대자동차 주식 가격: 1133000원"
대충 위와 같은 문자열이 있다고 하고 주식 가격들만 숫자로 뽑아와야한다고 치자
숫자들은 모두 "주식 가격: "이라는 문자열과 "원"이라는 문자열 사이에 있다는 공통점을 발견할 수 있다
split은 인자로 들어가는 문자열을 기준으로 문자열을 여러개의 문자열 리스트로 자르는 것이다.
string = "삼성전자 주식 가격: 70000원, LG전자 주식 가격: 440000원, 현대자동차 주식 가격: 1133000원"
l = string.split('주식 가격: ') # "주식 가격: "으로 문자열을 분해
print(l)
print(l[1:]) # 맨 앞 "삼성전자" 제외해서 출력
print(l[1].split('원')[0]) # 원 이전의 70000 이라는 숫자를 추출해본력
prices_string = [p.split('원')[0] for p in l[1:]] # 나머지 종목에 대해서도 반복 적용한다
print(prices_string)
prices_integer = [int(p) for p in prices_string] # 문자열을 정수타입으로 변환한다
print(prices_integer)
['삼성전자 ', '70000원, LG전자 ', '440000원, 현대자동차 ', '1133000원'] ['70000원, LG전자 ', '440000원, 현대자동차 ', '1133000원'] 70000 ['70000', '440000', '1133000'] [70000, 440000, 1133000]
2. 데이터베이스에 저장하기
파싱만 하는 것이 크롤링이 아니다. 주기적으로 어떤 url을 파싱할지 스케줄링하고 파싱된 정보를 데이터베이스에 저장하되 이미 저장된 사이트는 피하는 과정이 필요하다.
파이썬 데이터베이스 라이브러리인 sqlite3는 보통 깔려있다
import sqlite3
sqlite3.connect는 db를 새로 만들거나 같은 이름의 db가 있는 경우 db를 읽어온다.
con = sqlite3.connect('test.db')
데이터베이스에 접근하기 위해서는 SQL이라는 쿼리명령어를 사용한다. 직관적이여서 별로 어렵지는 않은 것 같다.
- CREATE TABLE news (title text, content text): news 라는 이름의 테이블을 만든다. 데이터베이스들은 여러 개의 테이블이 모여있는 구조이다. 테이블의 column에 해당하는 필드의 이름과 필드의 타입을 괄호 안에 나열해준다. title이라는 text 타입의 column과 content라는 이름의 text타입 column을 생성
- INSERT INTO news VALUES ('news title test', 'test contents'): 테이블에 데이터 한 행을 추가하는 명령어이다.
- SELECT * FROM news WHERE title='news title test': 특정 데이터 행을 가져오는 명령어. SELECT와 FROM 사이에 가지고 오고 싶은 column을 넣고 WHERE 뒤에는 가지고 오고 싶은 데이터의 조건을 추가한다.
cursor = con.cursor()
cursor.execute('CREATE TABLE news (title text, content text)')
cursor.execute('INSERT INTO news values (?, ?)', (title, content_obj.text.strip()))
con.commit()
cursor.execute('SELECT * FROM news')
newses = cursor.fetchone()
print(newses)
뉴스를 데이터베이스에 저장하고 접근하는데 성공했다..
3. 스케줄링
구글이나 빙에서 사용하고 있는 웹크롤러들은 가능한 많은 url을 가지고 가야하기 때문에 하나의 url에서 링크하고 있는 모든 링크를 크롤링하고 또 각각의 링크에서 또다른 링크들을 크롤링하는 식으로(트리 구조) 탐색한다.
하지만 우리는 그렇게나 많은 url을 탐색할 필요가 없으니까 일부 url만 특정해서 탐색하면 되겠다.
한국경제 뉴스의 사이트맵으로 가본다. https://www.hankyung.com/sitemap/
F12를 누르면 개발자 도구가 나오고 검사기 왼쪽에 있는 버튼을 클릭해서 아무 년도나 클릭해본다.
<u1 class='news_archive_list'> 안에 있는 모든 href들을 뽑아오면 모든 년도에 대한 링크를 얻어올 수 있을 것 같다.
results = requests.get('https://www.hankyung.com/sitemap/')
bs_obj = BeautifulSoup(results.content)
news_archive_list = bs_obj.find('ul', attrs={'class': 'news_archive_list'})
year_links = [a['href'] for a in news_archive_list.findAll('a')]
print(year_links)
같은방식으로 일별 뉴스 링크를 뽑아오자
<div class='news_archive_day'> 안에 있는 모든 href 값을 긁어오면 될것같다.
results = requests.get('https://www.hankyung.com/sitemap/1989')
bs_obj = BeautifulSoup(results.content)
news_archive_day = bs_obj.find('div', attrs={'class': 'news_archive_day'})
day_links = [a['href'] for a in news_archive_day.findAll('a')]
print(day_links)
<div class='archive_article'> 안에 있는 모든 href를 긁어오자.
results = requests.get('https://www.hankyung.com/sitemap/2020/12/06')
bs_obj = BeautifulSoup(results.content)
archive_article = bs_obj.find('div', attrs={'class': 'archive_article'})
news_links = [a['href'] for a in archive_article.findAll('a')]
print(news_links)
이제 링크의 구조를 적당히 알았으니 트리구조로 잘 탐색하면 되겠다. for 문으로 모든 뉴스를 탐색하면서 위에서 배운 파싱하고 데이터베이스에 저장하면 되겠다.
'파이썬' 카테고리의 다른 글
cv2.remap 함수에 대한 고찰 (0) | 2023.03.24 |
---|---|
WSL 현재 시간 동기화 문제 (0) | 2023.03.22 |
FATAL: exception not rethrown (0) | 2023.03.21 |
과제를 성공시키기 위해 한 짓거리 (0) | 2021.07.22 |