[1주차_데이터분석] 개발일지 (웹스크래핑, bs4, Newspaper3k, requests)

Coastby·2022년 5월 11일
0

크롤링 (crawling)은 웹 페이지로 부터 데이터를 추출하는 행위를 말한다.

선택자 (selector)는 html 문서의 특정 부분에 이름을 붙인 것이다. 그래서 우리는 그 이름을 가지고 html 문서 내의 특정 부분 (우리가 필요한 데이터)을 찾을 수 있다.

패키지란 이미 만들어져 있는 함수들의 묶음이다. 만들어놓은 함수를 우리가 설치하여 바로 사용할 수 있다.

○ 파이썬에서는 다음 명령어를 통해 패키지를 설치하고 사용할 수 있다.
모듈은 패키지 안에 저장되어 있는 함수 등을 말한다.

!pip install 패키지이름
from 패키지이름 import 모듈		#패키지로부터 모듈을 임포트

✍️ BeautifulSoup (package)

파이썬에서 크롤링을 할 때 주로 사용되는 패키지로 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('.클래스명') : 클래스를 입력으로 사용할 경우

  • soup.select('#아이디') : ID를 입력으로 사용할 경우

  • soup.select('상위태그명 하위태그명') : 자손 관계 (어떤 태그 내부에 있는 모든 태그를 자손이라고 함)

  • soup.select('상위태그명 > 하위태그명') : 자식 관계 (어떤 태그 내부에 있는 태그 중 바로 한 단계 아래에 있는 태그를 자식이라고 함)

  • soup.태그명
    : 해당 태그를 포함하여 그 태그가 끝날 때까지의 문장을 가지고 온다. 단, 해당 태그가 여러개 있다면 첫번째 태그만 가지고 온다.

  • soup.태그명.get('속성명')
    : 해당 속성의 값을 가져온다. aaa = "bbb"라는 형식ㅇ르 가진 경우, get('aaa')를 하면 'bbb'의 값을 가져온다.

soup.a.get('href')

http://www.naver.com

✍️ Newspaper3k (package)

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 (package)

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에 접속해서 뉴스 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문 이해부족이었다. 변수의 자리에는 리스트의 요소들을 하나하나를 지칭하는 것이며 이를 수행할 문장에 적용하기 위해 이름을 붙인 것이다.

👉 dictionary 만들기

idx2word = {'101' : '경제', '102' : '사회', '103' : '생활/문화', '105' : 'IT/과학'}

'key' : 'value'의 묶음이며, key를 이용해 value를 불러낼 수 있다.

👉 url이용해 뉴스 데이터프레임 생성하기

# 데이터프레임을 생성하는 함수.
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
  • 이 코드를 통해 2020년 5월 6일, code_list의 카테고리에서 10개 페이지를 크롤링한다.
df = make_total_data(10, code_list, 20200506)

# Result
102번 코드에 대한 데이터를 만들었습니다.
103번 코드에 대한 데이터를 만들었습니다.
105번 코드에 대한 데이터를 만들었습니다.

# 데이터 프레임의 이름에 .sample(숫자)를 사용하면 해당 숫자만큼 랜덤으로 뽑아서 출력
df.sample(10)
  • 크롤링이 완료되면 csv파일로 별도 저장
df.to_csv('news_data.csv', index=False)
profile
훈이야 화이팅

0개의 댓글