크롤링 (crawling)
은 웹 페이지로 부터 데이터를 추출하는 행위를 말한다.
○ 선택자 (selector)
는 html 문서의 특정 부분에 이름을 붙인 것이다. 그래서 우리는 그 이름을 가지고 html 문서 내의 특정 부분 (우리가 필요한 데이터)을 찾을 수 있다.
○ 패키지
란 이미 만들어져 있는 함수들의 묶음이다. 만들어놓은 함수를 우리가 설치하여 바로 사용할 수 있다.
○ 파이썬에서는 다음 명령어를 통해 패키지를 설치하고 사용할 수 있다.
모듈
은 패키지 안에 저장되어 있는 함수 등을 말한다.
!pip install 패키지이름
from 패키지이름 import 모듈 #패키지로부터 모듈을 임포트
파이썬에서 크롤링을 할 때 주로 사용되는 패키지로 BeautifulSoup이 있다. 이 를 통해 HTML 이나 XML 문서로부터 원하는 정보를 추출할 수 있다.
!pip install beautifulSoup4
#bs4라는 패키지로부터 BeautifulSoup이라는 모듈을 임포트
from bs4 import BeautifulSoup
설치할 때는 beautifulSoup4라는 이름으로 설치했지만, 임포트할 때는 from bs4라고 적는다. 설치할 때의 이름과 임포트할 때의 패키지 이름이 항상 동일하지는 않는다.
# html 문서를 문자열 html로 저장
html = ```
...
```
# BeautifulSoup 인스턴스 생성. 두번째 매개변수는 분석할 분석기 (parser)의 종류.
# html 문법으로 작성되어 있으니 html 문법을 기반으로 파싱하라는 의미이다.
soup = BeautifulSoup (html, 'html.parser')
soup.select('태그명') : 태그를 입력으로 사용할 경우
soup.select('.클래스명') : 클래스를 입력으로 사용할 경우
soup.select('#아이디') : ID를 입력으로 사용할 경우
soup.select('상위태그명 하위태그명') : 자손 관계 (어떤 태그 내부에 있는 모든 태그를 자손이라고 함)
soup.select('상위태그명 > 하위태그명') : 자식 관계 (어떤 태그 내부에 있는 태그 중 바로 한 단계 아래에 있는 태그를 자식이라고 함)
soup.태그명
: 해당 태그를 포함하여 그 태그가 끝날 때까지의 문장을 가지고 온다. 단, 해당 태그가 여러개 있다면 첫번째 태그만 가지고 온다.
soup.태그명.get('속성명')
: 해당 속성의 값을 가져온다. aaa = "bbb"라는 형식ㅇ르 가진 경우, get('aaa')를 하면 'bbb'의 값을 가져온다.
soup.a.get('href')
http://www.naver.com
newspaper3k
는 뉴스 데이터를 크롤링을 위한 패키지이다. 사용자가 인터넷 뉴스 기사의 url을 전달해주면, 이로부터 뉴스 기사 제목과 텍스트를 추출한다.
!pip install newspaper3k
# newspaper라는 패키지로부터 Article이라는 모듈을 임포트
from newspaper import Article
# 파싱할 뉴스 기사 주소
url = 'https://news.naver.com/main/read.nhn?mode=LSD&mid=sec&sid1=101&oid=030&aid=0002881076'
# 언어가 한국어이므로 language='ko'로 설정
article = Article(url, language='ko')
# 해당 뉴스를 다운로드하고,
article.download()
# 뉴스 제목과 본문을 찾아내는 분석 진행!
article.parse()
# 기사 제목
article.title
# 기사 내용
article.text
requests
패키지의 get 이라는 모듈 (함수)에 url을 입력하고, 이를 변수에 저장한 후에 변수.content를 하면 하면 해당 url의 html 코드를 받아올 수 있다.
import requests
크롤링 차단 방지 코드
접근이 크롤링 행위를 하는 것으로 추정되어 차단당하여 사용자인 척하는 편법이다.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'} news = requests.get(url, headers=headers)
!pip install newspaper3k
# requests라는 패키지를 임포트
import requests
# newspaper라는 패키지로부터 Article이라는 모듈을 임포트
from newspaper import Article
# pandas라는 패키지를 임포트하는데 앞으로 pd로 부르겠음
import pandas as pd
# bs4라는 패키지로부터 BeautifulSoup라는 모듈을 임포트
from bs4 import BeautifulSoup
# 'import 패키지이름' 형식으로 '임포트'했다면 '패키지이름.모듈이름' 으로 모듈을 호출.
# requets 패키지의 get이라는 모듈을 호출.
news = requests.get(url)
news.content
# 위 코드로만 작성하면 오류나서 크롤리 차단 방지 코드가 필요하다.
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
news = requests.get(url, headers=headers)
news.content
b'<html><body>\n<h1>Access Denied</h1>\nRequest is denied\n</body></html>\n'
원하는 페이지 수와 카테고리 번호, 날짜를 입력하면 해당 url에 접속해서 뉴스 url 리스트를 리턴하는 함수이다.
# 크롤링할 페이지 수, 카테고리, 날짜
def make_urllist(page_num, code, date):
urllist= []
# 1 ~ page_num까지 정해진 페이지만큼 반복.
for i in range(1, page_num + 1):
# 함수의 입력으로 된 변수들로 주소를 조합
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
url = 'https://news.naver.com/main/list.nhn?mode=LSD&mid=sec&sid1='+str(code)+'&date='+str(date)+'&page='+str(i)
news = requests.get(url, headers=headers)
# BeautifulSoup 모듈을 사용하여 HTML 페이지를 분석
soup = BeautifulSoup(news.content, 'html.parser')
# case1
news_list = soup.select('.newsflash_body .type06_headline li dl')
# case2
news_list.extend(soup.select('.newsflash_body .type06 li dl'))
# 뉴스 리스트에 있는 각 뉴스로부터 a 태그인 <a href ='주소'> 에서 '주소'만을 가져온다.
for line in news_list:
urllist.append(line.a.get('href'))
return urllist
# 함수 실행해보기 (2020년 5월 6일, 경제 (코드101)기사, 페이지 2번까지)
url_list = make_urllist(2, 101, 20200506)
print('뉴스 기사 갯수 :', len(url_list))
# Result : 한 페이지 당 뉴스 20개
뉴스 기사 갯수 : 40
# 5개만 출력해보기
url_list[:5]
✋ 어려웠던 점
실습하면서 위의 함수가 제일 이해가 안 갔었다. 왜 for 문에서 line이 어디서 나왔지????
이는 python의 for문 이해부족이었다. 변수의 자리에는 리스트의 요소들을 하나하나를 지칭하는 것이며 이를 수행할 문장에 적용하기 위해 이름을 붙인 것이다.
idx2word = {'101' : '경제', '102' : '사회', '103' : '생활/문화', '105' : 'IT/과학'}
'key' : 'value'의 묶음이며, key를 이용해 value를 불러낼 수 있다.
# 데이터프레임을 생성하는 함수.
def make_data(urllist, code):
text_list = []
for url in urllist:
article = Article(url, language='ko')
article.download()
article.parse()
text_list.append(article.text)
df = pd.DataFrame({'news': text_list})
df['code'] = idx2word[str(code)]
return df
# 앞서 저장한 경제 카테고리 40개 url을 이용하여 데이터 프레임 만들어보기
data = make_data(url_list, 101)
data[:10]
✋ 어려웠던 점
마지막 줄에 df 생성하면서 왜 마지막 줄에 column이 생긴 이유
⭐️ Pandas를 이용해 원래 있던 DataFrame에 열 추가하는 방법
- Method #1: By declaring a new list as a column.
# Import pandas package import pandas as pd # Define a dictionary containing Students data data = {'Name': ['Jai', 'Princi', 'Gaurav', 'Anuj'], 'Height': [5.1, 6.2, 5.1, 5.2], 'Qualification': ['Msc', 'MA', 'Msc', 'Msc']} # Convert the dictionary into DataFrame df = pd.DataFrame(data) # Declare a list that is to be converted into a column address = ['Delhi', 'Bangalore', 'Chennai', 'Patna'] # Using 'Address' as the column name # and equating it to the list df['Address'] = address # Observe the result df
- Method #2: By using a dictionary
# Import pandas package import pandas as pd # Define a dictionary containing Students data data = {'Name': ['Jai', 'Princi', 'Gaurav', 'Anuj'], 'Height': [5.1, 6.2, 5.1, 5.2], 'Qualification': ['Msc', 'MA', 'Msc', 'Msc']} # Define a dictionary with key values of # an existing column and their respective # value pairs as the # values for our new column. address = {'Delhi': 'Jai', 'Bangalore': 'Princi', 'Patna': 'Gaurav', 'Chennai': 'Anuj'} # Convert the dictionary into DataFrame df = pd.DataFrame(data) # Provide 'Address' as the column name df['Address'] = address # Observe the output df
code_list = [102, 103, 105]
make_urllist
, make_data
함수를 호출한다.def make_total_data(page_num, code_list, date):
df = None
for code in code_list:
url_list = make_urllist(page_num, code, date)
df_temp = make_data(url_list, code)
print(str(code)+'번 코드에 대한 데이터를 만들었습니다.')
if df is not None:
df = pd.concat([df, df_temp])
else:
df = df_temp
return df
df = make_total_data(10, code_list, 20200506)
# Result
102번 코드에 대한 데이터를 만들었습니다.
103번 코드에 대한 데이터를 만들었습니다.
105번 코드에 대한 데이터를 만들었습니다.
# 데이터 프레임의 이름에 .sample(숫자)를 사용하면 해당 숫자만큼 랜덤으로 뽑아서 출력
df.sample(10)
df.to_csv('news_data.csv', index=False)