[DEV] Web Crawling
[DEV] Web Crawling
크롤링 가능 여부 확인
- [사이트의 root url]/robots.txt 에서 확인!
카카오의 경우
1
2
3
4
5
6
7
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에 들어가기 때문에 가장 쉬웠던 사이트!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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을 이용하여 들어갔다 나왔다 반복했음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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. 이스트소프트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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_all
와find_element
구분!
- url에 페이지 번호가 안 뜨는 곳은 버튼 등으로 페이지를 이동해야 한다
- 이 때 페이지 크기와 아래로 스크롤 하는 것 꼭 고려할 것!
- 크롤링을 세 명이서 사이트를 나누어서 진행했는데, 코드를 모아보니 사람마다 스타일이 다른 것 같아 신기했다
- 그 중 내 코드가 가장 뭔가 정리되고 구조화되지 않은 것 같아서 이 부분에서 많이 배웠다!
- 다음부터는 더 코드를 깔끔하고 규칙적으로 작성하려고 노력해봐야겠다
Github
https://github.com/bokyung124/tech_dashboard/tree/main/crawler
This post is licensed under CC BY 4.0 by the author.