해외 사이트를 크롤링하려고 시도 했을때 해외에서 접근을 막은 (한국의 접근을)막은 사이트의 경우 프록시를 우회하여

접근할 필요가 있다 그 경우에 사용했던 코드이다..

 

 

무료 프록시 제공사이트에서 포트와 ip를 받아오고 (크롤링) 그것을 적용하는 request코드이다.

from bs4 import BeautifulSoup
import pandas as pd
import requests
import re

headers = {'User-Agent':'--ari/537.36'}

import random

def random_us_proxy():
    
    proxy_url = "https://www.us-proxy.org/"

    res = requests.get(proxy_url)
    soup = BeautifulSoup(res.text,'lxml')

    table = soup.find('tbody')
    rows = table.find_all('tr')
    proxy_server_list = []

    for row in rows:
        https = row.find('td', attrs = {'class':'hx'})
        if https.text == 'yes':
            ip = row.find_all('td')[0].text
            port = row.find_all('td')[1].text
            server = f"{ip}:{port}"
            proxy_server_list.append(server)

    proxy_server = random.choices(proxy_server_list)[0]
    return proxy_server

proxy_server = random_us_proxy()
proxies = {"http": 'http://' + proxy_server, 'https': 'http://' + proxy_server}

res = requests.get(url, headers = headers, proxies=proxies)
res.raise_for_status() # 오류가 생기면 셀종료
soup = BeautifulSoup(res.text, 'lxml')

 

 

셀레니움 코드이다.

from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium import webdriver


proxy_ip_port = random_us_proxy()

proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = proxy_ip_port
proxy.ssl_proxy = proxy_ip_port

capabilities = webdriver.DesiredCapabilities.CHROME
proxy.add_to_capabilities(capabilities)

# replace 'your_absolute_path' with your chrome binary absolute path
driver = webdriver.Chrome(desired_capabilities=capabilities)

웹페이지를 크롤링하다보면 스크롤을 하지않으면 댓글이나 게시글이 보이지않는 경우가 있을것이다

예를 들어 유튜브의 댓글창 같은것 말이다. 스크롤을 하지않으면 댓글이 웹브라우저의 정보가 추가 되지않는다

그럴 경우에는 스크롤을 내려 정보를 모두 가져온 후에 웹페이지정보를 가져와 크롤링을 시도해야한다.

 

options = webdriver.ChromeOptions()
# options.add_argument('--headless') # 창 새로 띄우는 것 없이
options.add_argument('--disable-gpu') # gpu 사용안하겠다 괜한 오류나오니깐

browser = webdriver.Chrome(options=options)
browser.maximize_window()

browser.get(url)

sleep_time = 1

# 스크롤 높이 가져옴
last_height = browser.execute_script("return document.body.scrollHeight")

while True:
    # 끝까지 스크롤 내리기
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")

    # 대기
    time.sleep(sleep_time)

    # 스크롤 내린 후 스크롤 높이 다시 가져옴
    new_height = browser.execute_script("return document.body.scrollHeight")
    if new_height == last_height:
        break
    last_height = new_height

 그럴경우에는 이렇게 하면된다.

이 이후 크롤링을 계속하면되겠다..

 

하지만 유튜브의 댓글의 경우 

browser.execute_script("return document.body.scrollHeight") 

스크롤의 정보가 변했음에도 불구하고 위의 명령어가 같은 정보를 반환했다. 

그래서 찾은해결책이

from selenium.webdriver.common.keys import Keys

for i in range(300)    
        browser.find_element_by_tag_name('body').send_keys(Keys.PAGE_DOWN)
        time.sleep(0.5)

 

페이지다운을 누르는 것이다. 

하지만 이방법은 끝에 도달하지않아도 페이지다운을 누르는 것을 멈추고

끝에 도달했음에도 페이지다운을 누르는 문제점이 있어 시간적으로 비효율적이다.

유튜브 페이지에서 페이지의 스크롤 정보를 가져오는 것을 막아 놓아서 방법을 찾지못했다. 

 

chim_coments = []

url = 'https://www.youtube.com/watch?v=hnanNlDbsE4'

options = webdriver.ChromeOptions()
options.add_argument('--headless') # 창 새로 띄우는 것 없이
options.add_argument('--disable-gpu') # gpu 사용안하겠다 괜한 오류나오니깐

browser = webdriver.Chrome(options=options)
browser.maximize_window()
browser.get(url)

time.sleep(3)
    
for i in range(300):
        browser.find_element_by_tag_name('body').send_keys(Keys.PAGE_DOWN)
        time.sleep(0.5)
     

    
soup = BeautifulSoup(browser.page_source,'lxml')
titles = soup.find_all('yt-formatted-string', attrs = {'id':'content-text'})
titles
for title in tqdm(titles):
    comment = title.get_text()
    chim_coments.append(comment)

침착맨 유튜브의 댓글을 크롤링한 코드이다. 

혹시 나와같은 상황이라면 도움이되었으면 좋겠다.

크롤링을 시도하다보면 분명 f12를 통해서 페이지검사를 실시했을 떄는 문제 없이 나오던 html형식이

request모듈을 사용해서 가져오면 파이썬 내에서 보이지않는 경우가 발생한다.

url = 'https://sports.news.naver.com/wfootball/index'

re = requests.get(url)
soup = BeautifulSoup(re.text, 'lxml')
soup.find_all('span','title')
[<span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 BBC 전문가 "손흥민 있으니 케인 팔아라" 2..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 '첼시 새 9번' 루카쿠 "경기장에서 증명해보이..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 황의조 이적료 137억!! 무조건 팔아야 하는 ..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 토트넘은 노났고, 맨시티는 절박해졌다 [달수네라..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             21/22시즌 맨시티가 챔스 조별리그에서 만날 상대는?
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             무리뉴의 전사된 타미, 로마 개막전 승리 견인
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             밀란 GK 메냥, 선방만큼 돋보였던 롱패스
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             첼시 루카쿠, 홀드 업 플레이 장착해 왕으로 돌아왔다
                         </span>]

내가 선택한 부분은 분명 span태그의 title클래스를 가지는 녀석인데 soup과 requests를 이용한 결과에는 확인되지않는다.

 

 

이 경우에는 정확하지는 않지만 자바스크립트로 작성된 웹페이지여서 그렇다고 한다.

움직이는 페이지나 움직여야만, 사용자가 동작을 해야만 페이지의 정보를 보여줘서, 가져올 수 있어서 

동적크롤링이라고 생각하면 편하다.

 

동적크롤링을 위해서는 셀레니움모듈을 사용한다.

 

 

!pip install selenium

셀레니움을 다른 모듈과 같은 방식으로 설치하고

사용을 위해서는 웹드라이버 설치가 요구된다.

크롬브라우저의 우측상단 점세개 > 도움말 >크롬정보로 가면 

위와같이 크롬브라우저의 버전을 확인할 수 있다.

 

https://chromedriver.chromium.org/downloads

 

ChromeDriver - WebDriver for Chrome - Downloads

Current Releases If you are using Chrome version 93, please download ChromeDriver 93.0.4577.15 If you are using Chrome version 92, please download ChromeDriver 92.0.4515.107 If you are using Chrome version 91, please download ChromeDriver 91.0.4472.101 For

chromedriver.chromium.org

위 사이트에 가서 자신의 버전과 가까운 크롬브라우저를 다운로드받으면 된다.

나는 92.0.4515.159 버전이기 때문에 ChromeDriver 92.0.4515.107를 다운했했다.

마지막 세자리를 제외하고 생각하는것이 편할 것이다.

그리고 자신의 버전보다 숫자가 높다면 호환이 문제가 생길 수 있기때문에 가까운버전을 선택하되 낮은 버전을 선택하는 것이 좋겠다.

 

다운을 완료했다면 압축을 해제하고 자신이 코딩할 파이썬 파일(.py , .ipynb )이 있는 폴더에 옮기면된다.

자신의 경로를 모르겠다면 

import os 
os.getcwd()

이렇게 입력하고 실행하면 현재 경로를 알려준다.

웹드라이버가 제 위치를 찾았다면 동적크롤링의 준비가 완료된  것이다. 

 

이제 아까와 같은 페이지를 셀레니움을 활용해 동적크롤링해보자.

 

url = 'https://sports.news.naver.com/wfootball/index'

options = webdriver.ChromeOptions()
options.add_argument('--headless') # 창 새로 띄우는 것 없이
options.add_argument('--disable-gpu') # gpu 사용안하겠다 괜한 오류나오니깐

browser = webdriver.Chrome(options=options)
browser.maximize_window()

browser.get(url)

soup = BeautifulSoup(browser.page_source,'lxml')
soup.find_all('span','title')
[<span class="title">[UCL PO 2차] PSV 에인트호번 vs 벤피카 MVP 코디 학포</span>,
 <span class="title">[UCL PO 2차] PSV에게 '제로'라고 답하는 블라호디모스</span>,
 <span class="title">[UCL PO 2차] '무리한 도전' 베리시무의 경고 누적 퇴장!</span>,
 <span class="title">이강인 이적 상황 급변...발렌시아와 계약 종료 협상 중</span>,
 <span class="title">'단 1분도 못 뛰었는데' 벤치 신세로 전락한 에이스, 솔샤르의 희망고문인가</span>,
 <span class="title">토트넘 팬들 속탄다...케인, 이적설 파동 속에 골프 삼매경</span>,
 <span class="title">"호날두 만나고 싶다" 손흥민 꿈 이루어지나…토트넘 이적설 화제</span>,
 <span class="title">토트넘, 233억 원에 파페 마타르 사르 영입 합의</span>,
 <span class="title">AI가 예측한 EPL 최종 순위는? 1위는 맨시티, 아스널은 8위?</span>,
 <span class="title"> PSV 에인트호번 vs 벤피카 MVP 코디 학포</span>,
 <span class="title"> PSV에게 '제로'라고 답하는 블라호디모스</span>,
 <span class="title"> '이해를 못한 것이냐? ' 극대노하는 제주스 감독</span>,
 <span class="title"> 골대 강타에 멘탈이 무너지는 자하비</span>,
 <span class="title"> 과한 욕심에 역습 찬스 놓치는 야렘추크</span>,
 <span class="title"> PSV 에인트호번 vs 벤피카 전반 주요장면</span>,
 <span class="title"> '무리한 도전' 베리시무의 경고 누적 퇴장!</span>,
 <span class="title"> 괴체의 테크닉, 기회를 놓치는 필립 막스</span>,
 <span class="title"> '아직 죽지 않았다' 벤피카의 돌격 대장 타랍!</span>,
 <span class="title"> 슈팅의 침묵을 깨는 필립 막스</span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 BBC 전문가 "손흥민 있으니 케인 팔아라" 2..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 '첼시 새 9번' 루카쿠 "경기장에서 증명해보이..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 황의조 이적료 137억!! 무조건 팔아야 하는 ..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                                 토트넘은 노났고, 맨시티는 절박해졌다 [달수네라..
                             </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             21/22시즌 맨시티가 챔스 조별리그에서 만날 상대는?
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             '완전체로 돌아온' 루카쿠, 골-연계-몸싸움까지 다 보여주다
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             밀란 GK 메냥, 선방만큼 돋보였던 롱패스
                         </span>,
 <span class="title" onclick="clickcr(this, 'stb.txt', '', '', event);">
                             
                             
                             무리뉴의 전사된 타미, 로마 개막전 승리 견인
                         </span>]

일반 크롤링에서는 찾아볼 수 없었던 정보를 가져오고 있는 것을 확인할 수 있다.

지도를 활용하여 시각화를 하고싶은데 주소는 있지만 위경도 정보가 없어서 시각화하기 제한되는 경우 사용

 

먼저 카카오 api에 접속 및 가입

 

https://developers.kakao.com/

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

바로보이는 시작하기

어플 추가하기 클릭

 

형식에 맞게 입력!

너무 긴장할 필요없이 지오코딩 / 실명 이런식으로 입력해도됌

생성 후 클릭하면 

이렇게 인증키들이 생성된다.

여기서 우리가 사용할 것은 restapikey입니다.

 

이제 파이썬을 활용해서 위도와 경도를 가져와보자

https://developers.kakao.com/docs/latest/ko/local/dev-guide

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

개발가이드를 참고하면 어떤정보 입력하고 어떤정보를 돌려받는지 확인가능.

def get_coordinate(address): # 주소를 입력받음
    result = "" 
 
    url = 'https://dapi.kakao.com/v2/local/search/address.json?query=' + address # 카카오 api 서버스를 이용하여 접근
    rest_api_key = 'api키이빈다' # 사용자 api key
    header = {'Authorization': 'KakaoAK ' + rest_api_key} 

    r = requests.get(url, headers=header) 
 
    if r.status_code == 200: # 정보를 오류없이 받아왔다면
        if len(r.json()['documents']) != 0: # 길이가 0이 아니라면
            try : 
                result_address = r.json()["documents"][0]["address"]
                result = (result_address["y"],result_address["x"])  # 좌표 정보에 접근
            except:  # 예외처리
                result = np.nan
            
        else: # 길이가 0인경우 na처리
            result = np.nan
            
    else:
        result = "ERROR[" + str(r.status_code) + "]"
    return result # 좌표를 반환
    
get_coordinate('수원시 권선구 권선로669번길 34(권선동) (권선동 1011-15)')
('37.2612803904025', '127.025397892703')

주소를 텍스트로 입력하면 위도와 경도를 튜플형태로 순차적으로 반환하는 함수를 정의

 

개발가이드를 보면 여러가지가 가능

웹크롤링 도중 서버에서 크롤링 시도를 거부할 경우

직접 웹을 열지않고 html형식을 가져오려면 

첫시도 몇번에는 가능했지만 여러번이 되면 html형식을 가져오려는 것을 거부하는 경우가 생긴다.

이럴경우에는 user agent 정보를 넣어주면된다.

간단히 말하면 내가 사람이고 직접접속하는 것이라고 말해주는 것이다.

접속하는 사람의 정보를 알려주는것이다.

 

https://www.whatismybrowser.com/detect/what-is-my-user-agent

 

What is my user agent?

Every request your web browser makes includes your User Agent; find out what your browser is sending and what this identifies your system as.

www.whatismybrowser.com

url = 'https://nadocoding.tistory.com'
headers = {'User-Agent':'에이전트'}
res = requests.get(url, headers = headers)

res.raise_for_status()

res.text

헤더에 유저에이전트를 명시하고 requests의 get함수를 이용할 때 헤더 정보를 입력하면 된다.

모든 사용자가 자신마의 유저에이전트 정보를 가지는 데 자신의 유저에이전트는 

상단의 웹링크에서 확인할 수 있다.

여기서파란박스에 해당하는 녀석들이 고유한 유저정보이다.

 

서버에서 정보를 가져오는 것을 막았던 경우라면 유저에이전트를 넣으면 해결될 것이다.

공공데이터에 있어 api란 실시간정보가 조회가 가능한 것이다

공공데이터 포털에 들어가게되면 csv, excel형식의 파일데이터와  open api형태의 데이터로 나누어 정보를 제공하는데 

 

보통 파일데이터로 제공하기에는 너무 정보가 방대하거나 데이터의 업데이트주기가 짧아야할 경우에 api로 제공하는모양새이다.

 

api는 사용자가 요청한 정보만을 반환한다.

 

한국천문연구원의 특일정보 api를 이용해서 공휴일 정보를 가져와보겠다.

먼저 로그인후에 원하는 데이터를 검색하고 우측상단에 활용신청을 누르면 다음과 같은페이지가 나온다. 

활용신청을 누른뒤 형식에 맞게 입력후 신청을 하면 api에 따라 다르겠지만 보통은 금방 승인이 난다. 

 

후에 마이페이지 -> 오픈 api

아까 신청했던 목록이 보일 것이다 이것을 클릭하면 

이렇게 인증키를 확인할 수 있다. 

 

이제 데이터를 받아 저장하는 파이썬 코드를 알아보자 이 api는 xml이다.

 

본문 상단에 있는 기본정보란에 상세설명을 누르면 

이렇게 요청하는해야하는 변수와 출력해주는 결과를  보여준다.

위의 경우에는 

api주소 + 기능이름 + 년 + 월 + 서비스키를 입력하면

출력결과를 가져오게된다. 

 

import requests
import datetime
from bs4 import BeautifulSoup
import urllib.parse as urlparse


def get_request_query(url, operation, params, serviceKey):
    params = urlparse.urlencode(params)
    request_query = url + '/' + operation + '?' + params + '&' + 'serviceKey' + '=' + serviceKey
    return request_query

먼저 주소,  기능, 요청변수, 서비스키를 입력하면 이들을 합쳐 하나의 url로  이어주는 함수를 정의하였다.

params는 파라미터들의 준말로 여기서는 solYear,solMonth 이다. 

 

mykey = "인증키는 노출불가능"
url = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService'
operation = 'getRestDeInfo'
year = '2021'
month = '09'

params = {'solYear':year, 'solMonth':month}

request_query = get_request_query(url, operation, params, mykey)
print(request_query)

이렇게 나의 인증키, 주소, 요청인자들을 활용해 하나의 url을 만들고 해당 html구문을 분석하여

원하는 정보를 가져오면 된다.

res = requests.get(request_query)
soup = BeautifulSoup(res.text, 'lxml')
items = soup.find_all('item')

for item in items:
    day = item.locdate.get_text()
    name = item.datename.get_text()
    
    print(name, ' : ', day)
추석  :  20210920
추석  :  20210921
추석  :  20210922

포문을 활용하면 이렇게 

import requests
import datetime
import pandas as pd
from bs4 import BeautifulSoup
import urllib.parse as urlparse


def get_request_query(url, operation, params, serviceKey):
    params = urlparse.urlencode(params)
    request_query = url + '/' + operation + '?' + params + '&' + 'serviceKey' + '=' + serviceKey
    return request_query

mykey = "인증키공개 ㄴㄴ"
url = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService'
operation = 'getRestDeInfo'

df = pd.DataFrame()

for year in [2019,2020,2021]:
    for month in range(1,13):

        if month < 10:
            month = '0' + str(month)
        else:
            month = str(month)
        params = {'solYear':year, 'solMonth':month}

        request_query = get_request_query(url, operation, params, mykey)

        res = requests.get(request_query)
        soup = BeautifulSoup(res.text, 'lxml')
        items = soup.find_all('item')

        for item in items:
            day = item.locdate.get_text()
            name = item.datename.get_text()
            data = {'휴일이름':name , '날짜':day}
            df = df.append(data, ignore_index=True)
    
display(df.head())
display(df.tail())
  휴일이름 날짜
0 11 20190101
1 설날 20190204
2 설날 20190205
3 설날 20190206
4 삼일절 20190301
  휴일이름 날짜
47 개천절 20211003
48 대체공휴일 20211004
49 한글날 20211009
50 대체공휴일 20211011
51 기독탄신일 20211225

 

웹에 있는 정보를 가져오는 것

 

!pip install requests, BeautifulSoup

from bs4 import BeautifulSoup
import requests

requests, BeautifulSoup 모듈을 이용

 

1. html 

- 웹사이트에 요청, html이라는 형식으로 유저에게 정보를 보내줌 파싱(해석),랜더링(가공)을 거처 우리 눈으로 보게됨 이것을 http프로토콜이라고함. 
- html

<학교 이름 = 'xx대학교'>
	<학과 value = '통계학과'>
		<학번 value = '16학번'>
			<학생 value = '201611460'> 이우현 </학생>
			<학생 value = '201611461'> 이지은 </학생>
			<학생 value = '201611463'> 이예지 </학생>
		</반>	
		<학번 value = '17학번'>
			<학생 value = '2010711562'> 황예지 </학생>	
		</반>
		<학번 value = '3반'> ...</반>
	</학과>

	<학과 value = '경영학과'> ....


이런 형식으로 구성됨 하위로 가지를 뻗는 형태 json형식과도 비슷함

이우현 학생에게 접근하려면 xx대학교의 통계학과 16학번인 201611460의 이우현학생 이렇게 접근할 수 있음

각각 부모 자식의 관계로 구성이 됨 

네이버 월요웹툰의 html구조를보면 

이런식으로 나옴 

크롬브라우저의 경우 f12를 누르면 우측에 저런식으로 창이 뜨는데 

그 창이 해당 브라우저의 html문서, 화살표를 누르면 자식태그를 볼 수 있게 확장됨

 

만약 백수세끼라는 제목에 대한 태그를 확인하고 싶다면

빨간펜으로 표시된 버튼을 누르고 백수세끼라는 제목으로 누르면 우측창에 해당하는 태그 경로를 알 수 있음

이런 경로를 xpath라고도 함

원하는 부분에 마우스 우클릭을하면 copy - > copy xpath를 클릭하면 해당 하는 부분의 xpath를 복사해준다

 

//*[@id="content"]/div[1]/ul/li[2]/dl/dt/a/strong 

이게 백수세끼의 xpath이다.

 

<strong title="백수세끼">백수세끼</strong>

이 구문을 보면 스트롱이라는 태그는 title이라는 속성을 가진다. title값은 '백수세끼'이다.

그리고 </strong>으로 strong이라는  태그의 정보를 종료하는데 주황값으로 표시한

부분에는 택스트가 들어간다. 보통 우리가 접근하는 값은 이 값이다.

 

이런 속성정보는 태그이름이 동일한경우 내가 원하는 태그에 접근하고 싶을 때 유용하게 사용된다.

td 태그가 많은데 제목에 접근하고 싶다면 td태그이면서 class가 title인 녀석에 접근하면 될  것이다.

2. requests

import requests

url = 'https://comic.naver.com/webtoon/list?titleId=733074&weekday=mon'
res = requests.get(url)
res.raise_for_status() # 오류가 생기면 셀종료
res.text

리퀘스트 패키지를 이용해서 이렇게 네이버 월요웹툰의 html을 가져올 수 있다.

requests의 get명령어를 사용하면 해당 url의 html을 가져올 수 있고

res.raise_for_status() 명령어로 성공적으로 페이지를 가져왔는지 확인할 수 있다. 

만약 제대로 가져오지못했다면 에러가 생겼을 것이다

text 명령어를 통해 html을 확인할 수 있다.

 

 

 

 

3. BeautifulSoup

html에서 검색의 역할을 해준다고 생각하면 편하다,

 

from bs4 import BeautifulSoup


url = 'https://comic.naver.com/webtoon/list?titleId=733074&weekday=mon'
res = requests.get(url)
res.raise_for_status() # 오류가 생기면 셀종료
soup = BeautifulSoup(res.text, 'lxml')
print(soup.find('td', attrs = {'class':'title'}))

print('-'*50)

print(soup.find_all('td', attrs = {'class':'title'}))
<td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=89&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','89')">89화 롤케이크</a>
<img alt="UP" height="15" src="https://ssl.pstatic.net/static/comic/images/2012/ico_toonup.png" width="27"/>
</td>
--------------------------------------------------
[<td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=89&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','89')">89화 롤케이크</a>
<img alt="UP" height="15" src="https://ssl.pstatic.net/static/comic/images/2012/ico_toonup.png" width="27"/>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=88&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','88')">88화 컵과일</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=87&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','87')">87화 짜장라면</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=86&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','86')">86화 찐만두</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=85&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','85')">85화 어묵</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=84&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','84')">84화 로제 떡볶이</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=83&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','83')">83화 하ㅇ정식</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=82&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','82')">82화 김치부침개</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=81&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','81')">81화 크로플</a>
</td>, <td class="title">
<a href="/webtoon/detail?titleId=733074&amp;no=80&amp;weekday=mon" onclick="nclk_v2(event,'lst.title','733074','80')">80화 주꾸미 볶음</a>
</td>]

requests 모듈로 가져온 html정보로부터 soup객체를 생성하고

find를 사용하면 td태그 중에서class가 title인 첫번째 태그를 가져온다

find_all을 사용하면 td태그 중에서class가 title인 태그들을 리스트형태로 가져온다.

만약 우리가 제목을 알고 싶다면.

 

titles = soup.find_all('td', attrs = {'class':'title'})

for title in titles:
    print(title.a.get_text())
89화 롤케이크
88화 컵과일
87화 짜장라면
86화 찐만두
85화 어묵
84화 로제 떡볶이
83화 하ㅇ정식
82화 김치부침개
81화 크로플
80화 주꾸미 볶음

titles에 td태그 중에서class가 title인 태그들을 저장하고 포문으로 각각 타이틀들의 a태그의 텍스트에 접근한다.

 

각각 회차의 링크를 가져오고 싶다면

 

for title in titles:
    print('https://comic.naver.com' + title.a['href'])
https://comic.naver.com/webtoon/detail?titleId=733074&no=89&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=88&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=87&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=86&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=85&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=84&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=83&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=82&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=81&weekday=mon
https://comic.naver.com/webtoon/detail?titleId=733074&no=80&weekday=mon

이렇게 된다.

 

하위 태그로 들어가고 싶다면 td.a 이렇게 .으로 이어서 접근하면되고 해당태그안의 속성에 관심이 있다면;

a['href']이렇게 접근하면 된다.

 

4. 최종실습

from bs4 import BeautifulSoup
import pandas as pd
df= pd.DataFrame()

url = 'https://comic.naver.com/webtoon/list?titleId=733074&weekday=mon'
res = requests.get(url)
res.raise_for_status() # 오류가 생기면 셀종료
soup = BeautifulSoup(res.text, 'lxml')

titles = soup.find_all('td', attrs = {'class':'title'})

for title in titles:
    name = title.a.get_text()
    link = 'https://comic.naver.com' + title.a['href']
    data = {'title':name, 'link':link}
    df = df.append(data, ignore_index=True)
    
display(df)
  title link
0 89화 롤케이크 https://comic.naver.com/webtoon/detail?titleId...
1 88화 컵과일 https://comic.naver.com/webtoon/detail?titleId...
2 87화 짜장라면 https://comic.naver.com/webtoon/detail?titleId...
3 86화 찐만두 https://comic.naver.com/webtoon/detail?titleId...
4 85화 어묵 https://comic.naver.com/webtoon/detail?titleId...
5 84화 로제 떡볶이 https://comic.naver.com/webtoon/detail?titleId...
6 83화 하ㅇ정식 https://comic.naver.com/webtoon/detail?titleId...
7 82화 김치부침개 https://comic.naver.com/webtoon/detail?titleId...
8 81화 크로플 https://comic.naver.com/webtoon/detail?titleId...
9 80화 주꾸미 볶음 https://comic.naver.com/webtoon/detail?titleId...
10 89화 롤케이크 https://comic.naver.com/webtoon/detail?titleId...
11 88화 컵과일 https://comic.naver.com/webtoon/detail?titleId...
12 87화 짜장라면 https://comic.naver.com/webtoon/detail?titleId...
13 86화 찐만두 https://comic.naver.com/webtoon/detail?titleId...
14 85화 어묵 https://comic.naver.com/webtoon/detail?titleId...
15 84화 로제 떡볶이 https://comic.naver.com/webtoon/detail?titleId...
16 83화 하ㅇ정식 https://comic.naver.com/webtoon/detail?titleId...
17 82화 김치부침개 https://comic.naver.com/webtoon/detail?titleId...
18 81화 크로플 https://comic.naver.com/webtoon/detail?titleId...
19 80화 주꾸미 볶음 https://comic.naver.com/webtoon/detail?titleId...

판다스에서 나타내는 과정에서짤려서 실제로 접근은안되지만 풀데이터프레임으로 접근시 가능

+ Recent posts