본문 바로가기
국비 교육/데이터

[데이터 수집] 웹 스크래핑 예제 - 네이버 뉴스 페이지 언론사 목록, 네이버 웹툰 제목 가져오기, 다음 영화 사이트에서 영화 포스터 다운로드

by 육츠 2023. 11. 1.
Contents 접기

[실습] 네이버 뉴스 페이지 언론사 목록 가져오기

print(result.children) 은 안 나오는데, news_list = [ list(tag.children)[0] for tag in result] 는 나오는 이유:
BeautifulSoup 의 라이브러리인 find_all = 기준에 맞는 태그를 모두 가져와 리스트 타입으로 반환하는 함수이다.

print(type(result))  출력: <class 'bs4.element.ResultSet'> 는 list처럼 취급할 수 있다는 것이다.
그 값을 인덱싱 하기 위해서는 for문에 돌려 값을 빼와 사용해야하기 때문에, 리스트 형태로 묶인 것이 문자열 형태로 바뀌게 되어 다시 list 형태로 만들어 주는 것이다.
 

#  언론사에 이름만 가져와서 저장 -> 리스트에 저장

import re
import requests
from bs4 import BeautifulSoup

res = requests.get('https://news.naver.com/')
soup = BeautifulSoup(res.text,'lxml')

result = soup.find_all('h4',attrs={'class':'channel'}) 

# print(len(result))
# print(result[0].text)
# 한겨레11월 05일 16:40
# print(list(result[0].children)[0])

# print(result.children)
news_list = [ list(tag.children)[0] for tag in result]
print(news_list[:10])

# ['전자신문', '한겨레', '한국일보', '강원도민일보', '머니투데이', '경향신문', '디지털데일리', '연합뉴스TV', '대구MBC', '대전일보']

 

태그 내의 문장 가져오기 

검색: css 를 활용하여 원하는 정보를 가져옴. (css 선택지를 활용)

select_one : find 와 유사
select : find_all 과 유사

import requests
from bs4 import BeautifulSoup

res = requests.get('http://www.tradecampus.com')
soup = BeautifulSoup(res.text,'html.parser')

print(soup.select_one("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > div > h3")) 

# <h3 class="tit">공지사항</h3>

# 특정위치에 있는 값을 find() 보다 정확히 가져온다는 장점이 있음

무역 아카데미 사이트 활용

# tradecapus.com 메인페이지 공지사항 2번째 내용 가져오기
notice = soup.select("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > ul > li:nth-child(2)")
# find_all과 같다.

# li:nth-child(2) ul 태그 밑에 li 존재 -> 그 중 두 번째
print(notice[0].text)

# 오프라인/Live

# 제1기 전자상거래(B2C)수출실무 첫걸음 개강안내(10/27)
# 2023-10-24

# :nth-child(n) 을 지우면 모든 요소를 가져온다
notice = soup.select("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > ul > li")

 

css 선택자 

- .class: class = "intro"를 갖는 모든 요소 선택

- #id: id = "firstname" 갖는 요소 선택

- element: 모든 <p> 요소 선택

- :nth-child(n): 부모 요소의 n번째 자식요소 해당하는 모든 <p> 요소 선택  

 

활용) 이미지의 주소 추출하기
1. 태그이름이 'span' 이고, class = "img" 로 되어있는 처음으로 만나는 태그를 우선 검색

2. 그 아래에서 태그 이름이 'img' 인 태그의 'src(이미지주소)'를 요청하여 응답받는다.

ele = soup.find('span',attrs={'class':'img'})

# print(ele)
# <span class="img">
# <img alt="" src="/weven_template_repository/theme/KITAAC/1/resource/img/img_main_cate01.png"/>
# <img alt="" class="hover" src="/weven_template_repository/theme/KITAAC/1/resource/img/img_main_cate01_on.png"/>
# </span>

ele = ele.find('img').get('src')
print(ele)
# /weven_template_repository/theme/KITAAC/1/resource/img/img_main_cate01.png

get('속성'): 해당 속성 값을 출력한다.

 

[실습] 네이버 웹툰 제목 가져오기

 

해당 페이지는 동적페이지 이므로 selenium 패키지를 다운 받아 진행하였다.

 

0. 동적 페이지임을 알았던 방법

우선 BeautifulSoup 패키지를 이용하여 원하는 속성을 검색한다.

temp = soup.select_one("#container > div.component_wrap.type2 > div.WeekdayMainView__daily_all_wrap--UvRFc")
print(temp) # None 출력 -> 동적으로 만들어진 페이지 라는 의미

 

1. selenium 패키지를 활용하여 원하는 페이지의 요청을 전달한 다음 응답을 받으면

soup = BeautifulSoup(driver.page_source,'lxml') ->  driver.page_source : drivermanager 를 통해 동적 페이지를 얻어온다.


2. enumerate() 를 사용하여 for 문을 돌리는데 여기서 enumerate()를 사용한 이유는 리스트형태인 week 변수를 차례대로 얻어오기 위함이다.

그리고 큰 태그인 div > ul > li 안에 있는 class = "text" 라는 태그를 태그 없이 반환한다. 

import requests
from bs4 import BeautifulSoup
import time # sleep() 쓰기 위해
from selenium.webdriver.chrome.service import Service
from selenium.webdriver import Chrome, ChromeOptions
from webdriver_manager.chrome import ChromeDriverManager

driver = Chrome(service=Service(ChromeDriverManager().install()),options = ChromeOptions())
driver.get("https://comic.naver.com/webtoon")

time.sleep(5) # 동적으로 생성되는 페이지의 내용이 완성될 때 까지 대기

soup = BeautifulSoup(driver.page_source,'lxml') 
# driver.page_source : drivermanager 를 통해 동적 페이지를 얻음

# 요일별 전체 웹툰 css 선택자
temp = soup.select_one("#container > div.component_wrap.type2 > div.WeekdayMainView__daily_all_wrap--UvRFc")
# print(temp) # None => 동적 페이지 라는 의미
# BeaBeautifulSoup 동적 페이지를 할 수 없음

# 요일별 div 태그 (find_all() 함수로 동일 클래스 선택자를 사용하는 모든 태그 검색)
temp = temp.find_all("div",attrs={"class":"WeekdayMainView__daily_all_item--DnTAH"})
# 각각 요일별 태그 정보가 리스트로 넘어옴
# print(len(temp))

week = ['월','화','수','목','금','토','일']
for i, w in enumerate(temp):
    print(f'======= {week[i]}요 웹툰=======')
    week_list = w.select('ul > li') # = find_all # 상위 태그가 ul인 모든 li태그를 선택
    for li_tag in week_list: 
        print(li_tag.find('span', attrs={'class','text'}).text)

 

[실습] 다음 영화 사이트에서 영화 포스터 다운로드 하기

 

0. 파일 저장을 위한 import os 패키지 호출

raise_for_status() : 만약 에러가 있다면 멈추고 해당 에러를 알려주는 역할.

 

1. 저장을 위한 폴더 생성

if not os.path.exists(img_dir): -> 유효성 검사 후 존재하지 않는다면  os.makedirs(img_dir) 로 만든다.

 

2.  if ':' in title: ->  영화제목중 ':' 이 들어가는 제목이 존재. 하지만 파일 이름으로는 ':'이 사용될 수 없어 공백으로 대체하였다.

import os
import requests
from bs4 import BeautifulSoup

res = requests.get("https://movie.daum.net/ranking/reservation")
res.raise_for_status()

soup = BeautifulSoup(res.text,'lxml')
poster_img = soup.find_all('img',attrs={'class':'img_thumb'})
# 20개 이미지 태그 

# 이미지 저장할 폴더 생성(폴더가 있으면 안 만들고, 없으면 만든다)
img_dir = './poster_img/' # 현쟈파일/poster_img 만든다

if not os.path.exists(img_dir):
    os.makedirs(img_dir)
    print('폴더 생성 완료')
else:
    print('폴더 이미 존재')

for i,movie in enumerate(poster_img,1): # 스타트값: 1, 안 정하면 0
    title = movie.get('alt')
    img_url = movie.get('src')
    
    print(i,":",img_url)
    img_res = requests.get(img_url)

    if ':' in title:
        title = title.replace(':',' ')

    with open(img_dir+f'm{i:02d}_{title}.jpg','wb') as f:
        f.write(img_res.content)

print('포스터저장완료')

[실습] 네이버 뉴스에서 경제 관련 언론사별 랭킹뉴스 추출하기
- 네이버 뉴스 랭킹 페이지에서 언론사의 이름에 '경제' 단어가 들어간 언론사의 기사 제목 추출

 

0. 동적 페이지 확인 + 원하는 속성을 찾기 위해 BeautifulSoup 패키지 활용.

0-1. 사이트에 들어가지지 않아, headers 에 user-agent 헤더값을 사용하여 속성을 얻어왔다.

 

아래 코드는 내가 직접 작성한 코드이다.( 웹툰의 제목을 얻어오는 코드를 활용하여 작성)

한 번 find_all() 을 사용하여 검색한 것은, 리스트 형태이므로 for문을 활용하여 값을 한 번 더 자세히 검색해야한다.

=> 넓은 단락 나눠짐과 시간이 같이 나와 적합하지 않은 코드였다. 

import requests
from bs4 import BeautifulSoup

headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"}

res = requests.get('https://news.naver.com/main/ranking/popularDay.naver?mid=etc&sid1=111', headers = headers)
soup = BeautifulSoup(res.text,'lxml')

name = soup.find_all('div',attrs={'class':'rankingnews_box'})

#e = []

for title in name:
    title_name = title.find('strong',attrs={'class':'rankingnews_name'})
    if '경제' in title_name.text: 
        print(title.text)

아래 코드는 교수님과 함께 진행하며 작성된 코드이다.

=> 중첩으로 for문을 돌리며 내 코드에 과하게 있던 단락나누기가 정리 되었다.

import requests
from bs4 import BeautifulSoup

headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"}

res = requests.get('https://news.naver.com/main/ranking/popularDay.naver?mid=etc&sid1=111', headers = headers)
soup = BeautifulSoup(res.text,'lxml')

news_list = soup.find_all('div',attrs={'class':'rankingnews_box'})
print('등록 언론사 개수:', len(news_list))
for news in news_list:
    press_title = news.find('strong').string
    if '경제' in press_title:
        print()
        print('언론사 :',press_title)
        news_title = news.find_all('div',attrs={'class':'list_content'})
        for i, rankingNews in enumerate(news_title,1):
            print(f"{i}:{rankingNews.find('a').text}")