크롤링 가능 여부 확인

  • [사이트의 root url]/robots.txt 에서 확인!

카카오의 경우

User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php

Sitemap: https://tech.kakao.com/wp-sitemap.xml

Disallow: /wp-content/uploads/wpo-plugins-tables-list.json

원칙적으로 wp-admin 페이지는 크롤링하지 않는 것으로

1. kakao

  • 글 제목, 링크, 날짜는 메인 화면에서 바로 읽을 수 있음
  • 페이지도 url에 들어가기 때문에 가장 쉬웠던 사이트!
import requests
from bs4 import BeautifulSoup
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
import pandas as pd

user_agent = user_agent = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}

kakao = "https://tech.kakao.com/blog/"

kakao_title = []
kakao_link = []
kakao_date = []

# title, date, link
for i in range(1, 36):
    res = requests.get(kakao+"page/{}/#posts".format(i), user_agent)
    soup = BeautifulSoup(res.text, "html.parser")
    soup.find("section", class_="elementor-section elementor-top-section elementor-element elementor-element-2252c9ab elementor-section-boxed elementor-section-height-default elementor-section-height-default").decompose()

    ktitle = soup.find_all("article", "elementor-post")
    for title in ktitle:
        kakao_title.append(title.find("div", "elementor-post__text").find("h3", "elementor-post__title").a.text.strip())
        kakao_link.append(title.find("div", "elementor-post__text").find("h3", "elementor-post__title").a["href"])
        kakao_date.append(title.find("div", "elementor-post__text").find("div", "elementor-post__meta-data").find("span", "elementor-post-date").text.strip())
    time.sleep(1)


  • 태그는 글 속에 있어서 selenium을 이용하여 들어갔다 나왔다 반복했음
# tags
kakao_tag_list = []

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

for link in kakao_link:
    kakao_tag = []
    driver.get(link)

    soup = BeautifulSoup(driver.page_source, "html.parser")
    tags = soup.find_all("a", "elementor-post-info__terms-list-item")
    for tag in tags:
        kakao_tag.append(tag.text)
    kakao_tag_list.append(list(kakao_tag))

2. woowahan

from time import sleep
import pickle

import requests
from bs4 import BeautifulSoup

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# header            
user_agent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"}    

data = [] 
company_name = "우아한형제들"

with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    # 브라우저 창 최대화
    driver.maximize_window()
    driver.get("https://techblog.woowahan.com/")
    # post의 url 리스트 
    urls = []
    while True:
        sleep(1)
        for post in driver.find_element(By.CLASS_NAME, "posts").find_elements(By.CLASS_NAME, "item")[10:]:
            # post의 url 수집
            urls.append(post.find_element(By.TAG_NAME, "a").get_attribute("href"))
        # 스크롤을 최대로 내림
        prev_height = driver.execute_script("return document.body.scrollHeight")
        while True:
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
            sleep(2)
            curr_height = driver.execute_script("return document.body.scrollHeight")
            if curr_height == prev_height:
                break
            prev_height = curr_height
        # 다음 페이지 버튼 클릭
        button = driver.find_elements(By.CLASS_NAME, "nextpostslink")
        if button:
            ActionChains(driver).click(button[0]).perform()
        else:
            break
        
for url in urls:
    # post별 url 요청
    res = requests.get(url, user_agent)
    soup = BeautifulSoup(res.text, "html.parser")
    
    # title, date, tag
    title = soup.find("h1").text
    pub_date = soup.find("div", "author").find("span").text.strip()
    tags = []
    for tag in soup.find("span","cats").find_all("a", "cat-tag"):
        tags.append(tag.text.strip())

    sleep(0.5)
  • 페이지가 url에 뜨지 않고, post 방식으로 받아서 다음 페이지 버튼을 눌러서 이동해야 했던 사이트!
  • 버튼을 누를 때 창을 아래로 내려야 한다는 것을 몰라서 엄청 헤맸다,,!
  • 이 코드를 다시 보며 생각해보니 태그가 글 안에 있는 다른 사이트들도 그냥 페이지에 들어가서 제목, 날짜, 태그를 한 번에 긁어오는게 나을 것 같다!

3. 이스트소프트

import requests
from bs4 import BeautifulSoup
from time import sleep
import pickle

# header
user_agent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"}

data = []
company_name = "이스트소프트"

base_url = "https://blog.est.ai/"
for page_url in ([base_url] + [base_url + f"page{i}" for i in range(2, 5)]):
    # 페이지 별 url 요청
    page_res = requests.get(page_url, user_agent)
    page_soup = BeautifulSoup(page_res.text, "html.parser")
    
    for post in page_soup.find_all("li", "post-preview"):
        # 각 post별 url
        url = post.find("article").find("a")["href"]
        # post별 url 요청
        res = requests.get(url, user_agent)
        soup = BeautifulSoup(res.text, "html.parser")
        
        # title, date, tag
        title = soup.find("h1").text
        pub_date = soup.find("span", "post-meta").text

        tags = []
        for tag in soup.find("div","blog-tags").find_all("li"):
            tags.append(tag.find("a").text)
            
  • 페이지도 url에 바로 들어가고, 태그가 div 안에 li 태그로 들어가있어서 뽑아내기 쉬웠음!

4. Devocean

import requests
from bs4 import BeautifulSoup
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
import pandas as pd

user_agent = user_agent = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}

devocean = "https://devocean.sk.com/blog/sub/index.do?ID=&boardType=&searchData=&page={}&subIndex=%EC%B5%9C%EC%8B%A0+%EA%B8%B0%EC%88%A0+%EB%B8%94%EB%A1%9C%EA%B7%B8"

devo_title = []
devo_link = []
devo_date = []

# title, date, link
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

for i in range(1, 62):
    driver.get(devocean.format(i))

    page = driver.find_element(By.XPATH, '//*[@id="wrapper"]/div/section[2]/div/div[2]/div/ul')
    for j in range(1, 11):
        driver.implicitly_wait(5)
        title = page.find_element(By.CSS_SELECTOR, "#wrapper > div > section.sub-sec.blog.cont01 > div > div.sub-sec-aside > div > ul > li:nth-child({}) > div > a > h3".format(j))
        devo_title.append(title.get_attribute("textContent").strip())

        link = page.find_element(By.CSS_SELECTOR, "#wrapper > div > section.sub-sec.blog.cont01 > div > div.sub-sec-aside > div > ul > li:nth-child({}) > div > a".format(j))
        devo_link.append(link.get_attribute("href").strip())

        date = page.find_element(By.CSS_SELECTOR, "#wrapper > div > section.sub-sec.blog.cont01 > div > div.sub-sec-aside > div > ul > li:nth-child({}) > div > div.sec-box > div > div.author-area.pc_view > span.date".format(j))
        devo_date.append(date.get_attribute("textContent").strip())

# tags
devo_tag_list = []

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

for i in range(1, 62):
    driver.get(devocean.format(i))

    for j in range(1, 11):
        devo_tag = []
        title = driver.find_element(By.XPATH, '//*[@id="wrapper"]/div/section[2]/div/div[2]/div/ul/li[{}]/div/div[1]/div/div[1]'.format(j))
        tags = title.find_elements(By.TAG_NAME, "span")

        for tag in tags:
            devo_tag.append(tag.get_attribute("textContent").replace("#", ""))
        devo_tag_list.append(list(devo_tag))

    time.sleep(1)
  • selenium의 find_element로 XPATH와 CSS_SELECTOR를 사용해보니 편해서 사용하긴 했는데 이게 좋은 방법인지는 모르겠다!
  • 목록 페이지에 태그도 같이 나와있어서 어렵지 않았던 사이트

느낀 점

  • 사실 타고타고 들어가는거라 구조를 잘 파악하기만 하면 어디든 적용할 수 있을 것 같다
  • request와 selenium 중 각각 어떤 것을 사용하면 좋을지 판단해야 할 듯
    • find, find_allfind_element 구분!
  • url에 페이지 번호가 안 뜨는 곳은 버튼 등으로 페이지를 이동해야 한다
    • 이 때 페이지 크기와 아래로 스크롤 하는 것 꼭 고려할 것!
  • 크롤링을 세 명이서 사이트를 나누어서 진행했는데, 코드를 모아보니 사람마다 스타일이 다른 것 같아 신기했다
    • 그 중 내 코드가 가장 뭔가 정리되고 구조화되지 않은 것 같아서 이 부분에서 많이 배웠다!
    • 다음부터는 더 코드를 깔끔하고 규칙적으로 작성하려고 노력해봐야겠다

Github

https://github.com/bokyung124/tech_dashboard/tree/main/crawler