๐ ์ด๋ฐ ๋ถ๋ค์ ์ํ ๊ธ์ ๋๋ค
- ๐ง QA ์์ง๋์ด โ ๊นจ์ง ๋งํฌ ํ์ง๋ฅผ ์๋ํํด์ ํ ์คํธ ๊ณต์๋ฅผ ์ค์ด๊ณ ์ถ์ ๋ถ
- ๐ SEO ๋ด๋น์ โ ๊นจ์ง ๋งํฌ ๋ฐฉ์น๋ก ์ธํ SEO ์ ์ ํ๋ฝ์ ๋ง๊ณ ์ถ์ ๋ถ
- โ๏ธ ํ ์คํธ ์๋ํ ์์ง๋์ด โ Selenium๊ณผ Python์ผ๋ก ์ค๋ฌด ์์ค์ ๋๊ตฌ๋ฅผ ๋ง๋ค๊ณ ์ถ์ ๋ถ
๐ฅ ์ด ๊ธ์ ์ฝ์ผ๋ฉด ์ป์ ์ ์๋ ๊ฒ
- SEO์ ์ ์ํฅ์ ์ฃผ๋ ใ๊นจ์ง ๋งํฌใ๋ฅผ ์๋์ผ๋ก ํ์งํ ์ ์๋ค
- ์๋ ๋งํฌ ํ์ธ ์์ ์ด ์์ ํ ํ์ ์์ด์ง๋ค
- 404๋ฟ๋ง ์๋๋ผ 4xx/5xx ์ ์ฒด ์ํ ์ฝ๋์ ๋์ํ ๊ฒ์ฌ๋ฅผ ๊ตฌํํ ์ ์๋ค
- QAยทํ ์คํธ ์๋ํยทSEO ๋์ฑ 3๊ฐ์ง ๋ชจ๋์ ํ์ฉ ๊ฐ๋ฅํ ์ค๋ฌด ์์ค์ ์ฝ๋๋ฅผ ์ป์ ์ ์๋ค
์น์ฌ์ดํธ ์ด์์์ ์๊ทผํ ๊ณจ์น ์ํ ๋ฌธ์ ๊ฐ ๋ฐ๋ก ๊นจ์ง ๋งํฌ(404 ์๋ฌ)์ ์กด์ฌ์ ๋๋ค. ์๋์ผ๋ก ํ์ธํ๊ธฐ์ ์๊ฐ์ด ๋๋ฌด ์ค๋ ๊ฑธ๋ฆฌ๊ณ , ๊ทธ๋ ๋ค๊ณ ๋ฐฉ์นํ๋ฉด SEO ํ๊ฐ์ ์ ๋ขฐ์ฑ์๋ ์ํฅ์ ๋ฏธ์นฉ๋๋ค. ๊ทธ ๊ณ ๋ฏผ์ ํด๊ฒฐํ๊ธฐ ์ํด ๋ง๋ QA ์๋ํ ๋๊ตฌ๊ฐ ๋ฐ๋ก LinkChecker์ ๋๋ค.
์ด ๊ธ์์๋ Selenium ร Python์ผ๋ก ๊ตฌํํ ๋งํฌ ์ฒด์ปค์ ์ฝ๋๋ฅผ, ์ค๊ณ ์๋ยท๊ฐ ๋ฉ์๋์ ์ญํ ยท์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๊น์ง ํฌํจํด ์ฒ ์ ํ๊ฒ ํด์คํฉ๋๋ค. ์คํ ๊ฒฐ๊ณผ ์ํ๊ณผ CSV ์ถ๋ ฅ ์์๋ ํฌํจ๋์ด ์์ผ๋ ๋ฐ๋ก ์คํํด ๋ณด์ธ์.
- 00. ๊นจ์ง ๋งํฌ๊ฐ SEO์ ๋ฏธ์น๋ ์ํฅ
- 01. ์คํ ๊ฒฐ๊ณผ ์ํ๏ผ๋จผ์ ๋์ ํ์ธ๏ผ๏ผ
- 02. ์ Selenium + requests ์กฐํฉ์ธ๊ฐ๏ผ
- 03. ๋์ํ๋ ์๋ฌ ์ํ ์ฝ๋ ์ผ๋
- 04. ํ๊ฒฝ ๊ตฌ์ถ๊ณผ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- 05. ํด๋์ค ๊ตฌ์กฐ์ ์ค๊ณ
- 06. __init__ ์ ์ด๊ธฐ ์ค์
- 07. get_all_links โ ๋งํฌ ์์ง ์ ๋ต
- 08. check_link_status โ 2๋จ๊ณ ์ํ ์ฝ๋ ํ์ธ
- 09. take_screenshot โ ์ฆ๊ฑฐ ์์ง
- 10. handle_cookie_popup โ GDPR ๋์
- 11. save_results โ CSV ๋ฆฌํฌํธ ์ถ๋ ฅ
- 12. ์์ฃผ ๋ฐ์ํ๋ ์๋ฌ์ ๋์ฒ๋ฒ
- 13. ๋๋ URL์ ํ์ธํ ๋์ ๊ณ ์ํ
- 14. ๋ ๋ฐ์ ์ํค๊ธฐ ์ํ ๊ฐ์ ์์ด๋์ด
- 15. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
- 16. ์ ๋ฆฌ
00. ๊นจ์ง ๋งํฌ๊ฐ SEO์ ๋ฏธ์น๋ ์ํฅ
ใ๊นจ์ง ๋งํฌ๋ ์ฌ์ฉ์๋ง ๋ถํธํ ๊ฒใ์ด๋ผ๊ณ ์๊ฐํ๊ธฐ ์ฝ์ง๋ง, ์ค์ SEO์ ๋ฏธ์น๋ ์ํฅ๋ ์ฌ๊ฐํฉ๋๋ค. ๊นจ์ง ๋งํฌ๋ ์ฌ์ดํธ์ ํ์งยท๊ฒ์ ํ๊ฐยท์ฌ์ฉ์ ๊ฒฝํ ๋ชจ๋์ ์ ์ํฅ์ ์ค๋๋ค.
Googlebot์ด ๊นจ์ง ๋งํฌ๋ฅผ ๋ง๋๋ฉด ํฌ๋กค ๋ฒ์ง์ ๋ญ๋นํด ๋ค๋ฅธ ํ์ด์ง๊ฐ ์ํ๋๊ธฐ ์ด๋ ค์์ง๋๋ค
404 ํ์ด์ง๊ฐ ๋ง์ ์ฌ์ดํธ๋ Google๋ก๋ถํฐ ใํ์ง์ด ๋ฎ๋คใ๊ณ ํ๋จ๋์ด SEO ํ๊ฐ๊ฐ ๋ฎ์์ง ์ ์์ต๋๋ค
๊นจ์ง ๋งํฌ๋ฅผ ๋ง๋ ์ฌ์ฉ์๋ ๋ฐ๋ก ์ดํํฉ๋๋ค. ์ดํ๋ฅ ์์น์ด ๊ฐ์ ์ ์ผ๋ก SEO์๋ ์ ์ํฅ์ ์ค๋๋ค
01. ์คํ ๊ฒฐ๊ณผ ์ํ๏ผ๋จผ์ ๋์ ํ์ธ๏ผ๏ผ
์ค์ ๋ก ์คํํ๋ฉด URLยท์ํ ์ฝ๋ยท๊ฒฐ๊ณผ๊ฐ ๋ชฉ๋ก์ผ๋ก ์ถ๋ ฅ๋ฉ๋๋ค. ๊นจ์ง ๋งํฌ๋ฅผ ํ๋์ ํ์ ํ ์ ์๋ ํ์์ ๋๋ค.
| URL | ์ํ ์ฝ๋ | ๊ฒฐ๊ณผ |
|---|---|---|
| /top | 200 | โ ์ ์ |
| /about | 200 | โ ์ ์ |
| /careers | 404 | โ ๋งํฌ ๋๊น |
| /old-page | 410 | โ ์ญ์ ๋ ํ์ด์ง |
| /contact | 200 | โ ์ ์ |
ํฐ๋ฏธ๋์๋ ๊ฐ์ ํ์์ผ๋ก ์ค์๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
=== ์คํ ๊ฒฐ๊ณผ ์์ ===
[ํ์ธ ์ค] /top โ 200 OK
[ํ์ธ ์ค] /about โ 200 OK
[ํ์ธ ์ค] /careers โ 404 Not Found โ ๋งํฌ ๋๊น ๊ฐ์ง๏ผ
[ํ์ธ ์ค] /old-page โ 410 Gone โ ์ญ์ ๋ ํ์ด์ง ๊ฐ์ง๏ผ
=== ์์ฝ ===
์ด ๋งํฌ ์: 89 / ์๋ฌ ๋งํฌ ์: 3
๐ธ ์คํฌ๋ฆฐ์ท ์ ์ฅ ์๋ฃ
๐ CSV ์ถ๋ ฅ ์๋ฃ โ Desktop/LinkChecker/
์ค์ ๋ก ํ์์ ํ๊ฒฝ์์ ์คํํ ๊ฒฐ๊ณผ์ ๋๋ค. ์ด 128๊ฐ ๋งํฌ ์ค ์๋ฌ ๋งํฌ 2๊ฐ๋ฅผ ๊ฐ์งํ๊ณ , CSV์ ์คํฌ๋ฆฐ์ท์ด ์๋์ผ๋ก ์ ์ฅ๋์์ต๋๋ค.
โฒ ์ค์ ํฐ๋ฏธ๋ ์ถ๋ ฅ. 128๊ฑด ํ์ธ ์ค 2๊ฑด์ ์๋ฌ ๋งํฌ๋ฅผ ๊ฐ์ง. CSV์ ์คํฌ๋ฆฐ์ท์ด ์๋ ์ ์ฅ๋จ
์๋ฌ ๊ฐ์ง ์ ๋ค์๊ณผ ๊ฐ์ด ์๋ฌ ํ์ด์ง์ ์คํฌ๋ฆฐ์ท์ด ์๋์ผ๋ก ์ ์ฅ๋ฉ๋๋ค.
โฒ 404 ์๋ฌ ๊ฐ์ง ์ ์๋ ์ ์ฅ๋๋ ์คํฌ๋ฆฐ์ท
์๋ ์์ฑ๋๋ CSV ๋ฆฌํฌํธ๏ผ์ ์ฅํด์ ์ฌํ์ฉ๏ผ
์ค๋ฌด์์๋ ใ๊ฒฐ๊ณผ๋ฅผ ๋จ๊ธฐ๋ ๊ฒใ์ด ํ์์ ๋๋ค. ์ด ๋๊ตฌ๋ ํ์ธ ์๋ฃ ํ CSV๋ฅผ ์๋ ์์ฑํ๋ฏ๋ก, ๋ฒ๊ทธ ํฐ์ผ ์ฒจ๋ถยทSEO ๋ด๋น์์์ ๊ณต์ ยท์์ ์์ ์ ์ฐ์ ์์ ๊ฒฐ์ ์ ๋ฐ๋ก ํ์ฉํ ์ ์์ต๋๋ค.
๋งํฌ ํ
์คํธ,URL,์ํ ์ฝ๋,์คํฌ๋ฆฐ์ท ๊ฒฝ๋ก,ํ์ธ ์๊ฐ
์๋จ ํ์ด์ง,/top,200,,2026-03-19 14:30:01
ํ์ฌ ์๊ฐ,/about,200,,2026-03-19 14:30:03
์ฑ์ฉ ์ ๋ณด,/careers,404,screenshots/404_careers.png,2026-03-19 14:30:22
๊ตฌ ํ์ด์ง,/old-page,410,screenshots/410_old-page.png,2026-03-19 14:30:24
๋ฌธ์ํ๊ธฐ,/contact,200,,2026-03-19 14:30:25
์๋ ์์ฑ๋ CSV๋ฅผ Excel๋ก ์ด๋ฉด ์ด๋ ๊ฒ ํ์๋ฉ๋๋ค. ๋งํฌ ํ ์คํธยทURLยท์ํ ์ฝ๋ยท์คํฌ๋ฆฐ์ท ๊ฒฝ๋ก๊ฐ ํ๋์ ์ ๋ฆฌ๋์ด ์์ด ๊ทธ๋๋ก ๋ฒ๊ทธ ํฐ์ผ์ ์ฒจ๋ถํ ์ ์์ต๋๋ค.
โฒ ์๋ ์์ฑ๋ CSV๋ฅผ Excel๋ก ์ด๊ธฐ. ๋งํฌ ํ ์คํธยทURLยท์ํ ์ฝ๋ยท์คํฌ๋ฆฐ์ท ๊ฒฝ๋ก๊ฐ ๋ชจ๋ ์ ๋ฆฌ๋์ด ์์
๋ชจ๋ ๋งํฌ ํ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅ. ํต๊ณยท๋ถ์์ ํ์ฉ ๊ฐ๋ฅ
์๋ฌ๋ง ์ถ์ถํด ๋ณ๋ ํ์ผ๋ก ์ ์ฅ. ์์ ์์ ์ ๋ฐ๋ก ์์ํ ์ ์์
02. ์ Selenium + requests ์กฐํฉ์ธ๊ฐ๏ผ
ใSelenium๋ง์ผ๋ก๋ ๋์ง ์๋๏ผใ๋ผ๊ณ ์๊ฐํ๋ ๋ถ๋ ๋ง์ ํ ๋ฐ์. ์ฌ์ค Selenium ๋จ๋ ์ผ๋ก๋ HTTP ์ํ ์ฝ๋๋ฅผ ์ง์ ๊ฐ์ ธ์ฌ ์ ์๋ค๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
๐ก ์ค๋ฌด์์์ ์ ๋ต
Selenium์ ๋งํฌ ์ถ์ถยทDOM ์กฐ์์ ๋ด๋นํ๊ณ , ์ํ ์ฝ๋ ํ์ธ์ requests๊ฐ ๋ด๋นํ๋ ๊ฒ์ด ์ค๋ฌด์ ์ธ ์ญํ ๋ถ๋ด์ ๋๋ค.
| ๋๊ตฌ | ์ํ๋ ๊ฒ | ๋ชปํ๋ ๊ฒ |
|---|---|---|
| Selenium | DOM ์กฐ์ยทJS ์คํยทCookie ์ฒ๋ฆฌยทํ์ด์ง ๋ ๋๋ง | HTTP ์ํ ์ฝ๋ ์ง์ ์ทจ๋ |
| requests | HTTP ์ํ ์ฝ๋ ๊ณ ์ ํ์ธยท๊ฒฝ๋ | JS ์ธ์ฆยทCookie ์ฒ๋ฆฌยท๋์ ์ฝํ ์ธ |
03. ๋์ํ๋ ์๋ฌ ์ํ ์ฝ๋ ์ผ๋
| ์ํ ์ฝ๋ | ์๋ฏธ | ๋์ |
|---|---|---|
| 404 | ํ์ด์ง๊ฐ ์กด์ฌํ์ง ์์ | โ ๊ฐ์งยท์คํฌ๋ฆฐ์ท |
| 410 | ํ์ด์ง๊ฐ ์๊ตฌ์ ์ผ๋ก ์ญ์ ๋จ | โ ๊ฐ์งยท์คํฌ๋ฆฐ์ท |
| 500 | ์๋ฒ ๋ด๋ถ ์๋ฌ | โ ๊ฐ์งยท์คํฌ๋ฆฐ์ท |
| 502/503/504 | ๊ฒ์ดํธ์จ์ดยท์๋น์ค ์ด์ฉ ๋ถ๊ฐ | โ ๊ฐ์งยท์คํฌ๋ฆฐ์ท |
| 403 | ์ ๊ทผ ์ ํ๏ผํ์ด์ง ์์ฒด๋ ์กด์ฌ๏ผ | โญ๏ธ ์คํต๏ผ์ ์์ผ๋ก ์ฒ๋ฆฌ๏ผ |
| 200/301/302 | ์ ์ยท๋ฆฌ๋ค์ด๋ ํธ | โ ์ ์์ผ๋ก ํ์ |
04. ํ๊ฒฝ ๊ตฌ์ถ๊ณผ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
# ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ผ๊ด ์ค์น
pip install selenium requests
๋์ ํ์ธ ์๋ฃ. Windows/Mac ํฌ๋ก์ค ํ๋ซํผ ๋์
ChromeDriver ์๋ ๊ฐ์ง ๋์. ์๋ ๊ด๋ฆฌ ๋ถํ์
HTTP ์ํ ์ฝ๋ ๊ณ ์ ํ์ธ์ ์ฌ์ฉ. ๋๋ ๋งํฌ ์ฒ๋ฆฌ์ ๋์
05. ํด๋์ค ๊ตฌ์กฐ์ ์ค๊ณ
# ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋จ 3์ค
checker = LinkChecker("https://example.com")
results = checker.run_check()
checker.close()
class LinkChecker:
__init__ # ์ด๊ธฐํยท์ถ๋ ฅ ๊ฒฝ๋กยทWebDriver ์คํยท์๋ฌ ๋ชฉ๋ก ์ด๊ธฐํ
โ
โโโ setup_output_directory # ํด๋ ์์ฑ
โโโ setup_driver # Chrome ์ต์
์ค์ ยท๋๋ผ์ด๋ฒ ์คํ
โ
โโโ run_check # โ
๋ฉ์ธ ๋ฃจํ๏ผ์ ์ฒด ์ ์ด๏ผ
โ โโโ get_all_links # Selenium์ผ๋ก ๋งํฌ ์์ง
โ โโโ check_link_status # HTTP ์ํ ํ์ธ
โ โโโ take_screenshot # ์๋ฌ ํ์ด์ง ์คํฌ๋ฆฐ์ท ์ดฌ์
โ
โโโ save_results # CSV ์ ์ฅ
โโโ close # WebDriver ์ข
๋ฃ
06. __init__ ์ ์ด๊ธฐ ์ค์
def __init__(self, base_url, output_dir=None):
if output_dir is None:
desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
output_dir = os.path.join(desktop_path, "LinkChecker")
self.base_url = base_url
self.output_dir = output_dir
self.setup_output_directory()
self.setup_driver()
self.error_links = []
07. get_all_links โ ๋งํฌ ์์ง ์ ๋ต
- ํ์ด์ง ์ ์ & ์ด๊ธฐ ๋๊ธฐ โ
time.sleep(2)๋ก ๋์ ์ฝํ ์ธ ๋ก๋ฉ์ ๊ธฐ๋ค๋ฆผ - Cookie ํ์
์ฒ๋ฆฌ โ
handle_cookie_popup()์ผ๋ก GDPR ๋์ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์๋ ํด๋ฆญ - ์ ์ฒด a ํ๊ทธ ์ทจ๋ โ
find_elements(By.TAG_NAME, "a")๋ก HTTP๋ก ์์ํ๋ URL๋ง ํํฐ๋ง - ์์ ์ ๋ณด ์ฌ์ ์ ์ฅ๏ผStale Element ๋์ฑ ๏ผ โ location/size/XPath ๋ฑ์ dict์ ์ ์ฅ
- ํ
์คํธ ์๋ ๋งํฌ ๋ณด์ โ
title โ alt โ aria-label์์ผ๋ก ํด๋ฐฑ ์ทจ๋
# โ ๋์ ์๏ผDOM์ด ๋ณ๊ฒฝ๋๋ฉด ์์ธ ๋ฐ์
elements = driver.find_elements(By.TAG_NAME, "a")
do_something()
elements[0].click() # โ StaleElementReferenceException๏ผ
# โ
์ข์ ์๏ผํ์ํ ์ ๋ณด๋ฅผ ๋จผ์ dict์ ๋ชจ๋ ์ ์ฅ
element_data = {
'location': element.location,
'classes': element.get_attribute("class") or "",
'id': element.get_attribute("id") or "",
'xpath': self.get_element_xpath(element)
}
08. check_link_status โ 2๋จ๊ณ ์ํ ์ฝ๋ ํ์ธ
# ๋จผ์ HEAD ์์ฒญ์ผ๋ก ๊ฒฝ๋ ํ์ธ
response = requests.head(url, timeout=8, allow_redirects=True, headers=headers)
status_code = response.status_code
# 400๋ฒ๋๏ผ403/404 ์ ์ธ๏ผ๋ GET์ผ๋ก ์ฌํ์ธ
if 400 <= status_code < 500 and status_code not in [403, 404]:
response = requests.get(url, timeout=8, allow_redirects=True, headers=headers)
return response.status_code
09. take_screenshot โ ์ฆ๊ฑฐ ์์ง
404/500 ๋ฑ ์๋ฌ ํ์ด์ง๋ฅผ ์บก์ฒ. 404_ํ
์คํธ_ํ์์คํฌํ.png๋ก ์ ์ฅ
์๋ ํ์ด์ง๋ก ๋์๊ฐ ๋ฌธ์ ๋งํฌ์ ๋นจ๊ฐ ํ ๋๋ฆฌ ํ์ด๋ผ์ดํธ๏ผใERROR LINKใ๋ฐฐ๋๋ฅผ JS๋ก ์ฃผ์ ํด ์บก์ฒ
# ์์์ ๋นจ๊ฐ ํ
๋๋ฆฌยท๊ธ๋ก์ฐ ์ดํํธ ์ ์ฉ
element.style.cssText += `
border: 3px solid #ff0000 !important;
box-shadow: 0 0 15px rgba(255, 0, 0, 0.8) !important;
z-index: 999999 !important;
`;
# ํ๋ฉด ์๋จ์ ๊ณ ์ ๋ฐฐ๋ ์ถ๊ฐ
var label = document.createElement('div');
label.innerHTML = 'ERROR LINK: ' + linkText.substring(0, 30);
label.style.cssText = `
position: fixed; top: 20px; left: 50%;
background: #ff0000; color: white; padding: 10px 20px;
`;
์ค์ ๋ก ์์ฑ๋๋ BEFORE ์คํฌ๋ฆฐ์ท์ ๋๋ค. ๋ฌธ์ ๋งํฌ๊ฐ ๋นจ๊ฐ ํ ๋๋ฆฌ๋ก ํ์ด๋ผ์ดํธ๋๊ณ ใERROR LINKใ ๋ฐฐ๋๊ฐ ํ๋ฉด ์๋จ์ ์๋ ์ฃผ์ ๋์ด ์ด๋ ๋งํฌ๊ฐ ์์ธ์ธ์ง ํ๋์ ์ ์ ์์ต๋๋ค.
โฒ ์๋ฌ ๋งํฌ๋ฅผ ๋นจ๊ฐ ํ ๋๋ฆฌ๋ก ํ์ด๋ผ์ดํธ๏ผใERROR LINKใ๋ฐฐ๋๋ฅผ JS๋ก ์ฃผ์ ํด ์บก์ฒ. ์ด๋ ๋งํฌ๊ฐ ๋ฌธ์ ์ธ์ง ํ๋์ ํ์ ๊ฐ๋ฅ
10. handle_cookie_popup โ GDPR ๋์
cookie_selectors = [
"//button[contains(text(), 'Accept all cookies')]",
"//button[contains(text(), 'Accept')]",
"//button[contains(text(), 'Accetta')]",
"//button[contains(@class, 'accept')]",
]
self.driver.execute_script("arguments[0].click();", button)
11. save_results โ CSV ๋ฆฌํฌํธ ์ถ๋ ฅ
# utf-8-sig = BOM ํฌํจ UTF-8๏ผExcel์์ ๊นจ์ง ๋ฐฉ์ง๏ผ
with open(csv_path, 'w', newline='', encoding='utf-8-sig') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=results[0].keys())
writer.writeheader()
writer.writerows(results)
error_count = sum(1 for r in results if r['์ํ ์ฝ๋'] in [404, 410, 500, 502, 503])
print(f"โ
์ ์ ๋งํฌ : {len(results) - error_count}๊ฑด")
print(f"โ ์๋ฌ ๋งํฌ: {error_count}๊ฑด")
12. ์์ฃผ ๋ฐ์ํ๋ ์๋ฌ์ ๋์ฒ๋ฒ
โ ChromeDriver ๋ฒ์ ๋ถ์ผ์น
pip install --upgrade selenium
โก TimeoutException โ ํ์ด์ง๊ฐ ๋ก๋ฉ๋์ง ์์
self.driver.set_page_load_timeout(30) # 15์ด โ 30์ด๋ก ์ฐ์ฅ
โข a ํ๊ทธ๊ฐ 0๊ฑด๋ฐ์ ์ทจ๋๋์ง ์์
time.sleep(5) # 2์ด โ 5์ด๋ก ๋๋ ค์ ์ฌ์๋
13. ๋๋ URL์ ํ์ธํ ๋์ ๊ณ ์ํ
โ concurrent.futures๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ
from concurrent.futures import ThreadPoolExecutor, as_completed
def check_links_parallel(links, max_workers=10):
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_link = {
executor.submit(check_link_status, link['url']): link
for link in links
}
for future in as_completed(future_to_link):
link = future_to_link[future]
status = future.result()
results.append({'url': link['url'], 'status': status})
return results
โก ํ์์์ ์ค์ ๊ณผ ์ฌ์๋ ์ฒ๋ฆฌ
def check_with_retry(url, max_retries=3, timeout=8):
for attempt in range(max_retries):
try:
response = requests.head(url, timeout=timeout, allow_redirects=True)
return response.status_code
except requests.exceptions.Timeout:
if attempt < max_retries - 1:
time.sleep(2)
return 0
14. ๋ ๋ฐ์ ์ํค๊ธฐ ์ํ ๊ฐ์ ์์ด๋์ด
concurrent.futures๋ก ๋ณ๋ ฌ ์คํ. 1000๊ฑด ๋งํฌ๋ฅผ ๊ณ ์ ํ์ธ ๊ฐ๋ฅ
๋ด๋ถ ๋งํฌ๋ฅผ ์ฌ๊ท์ ์ผ๋ก ๋ฐ๋ผ๊ฐ ์ฌ์ดํธ ์ ์ฒด๋ฅผ ์ผ๊ด ํ์ธ ๊ฐ๋ฅํ๊ฒ
ํ์์์ ์ ์๋ ์ฌ์๋๋ก ์ค๊ฒ์ถ์ ๋ฐฉ์ง
mailto:๋ tel:์ ์คํต ๋ชฉ๋ก์ ์ถ๊ฐํด ๋ถํ์ํ ํ์ธ์ ์๋ต
15. ์์ฃผ ๊ฒช๋ ๋ฌธ์ & ํด๊ฒฐ๋ฒ
๊ตฌํ ์ค ์ค์ ๋ก ๊ฒช์๋ ๋ฌธ์ ๋ค์ ์ ๋ฆฌํ์ต๋๋ค. ๊ฐ์ ๊ณณ์์ ๋งํ๋ ๋ถ๋ค๊ป ๋์์ด ๋๋ฉด ์ข๊ฒ ์ต๋๋ค.
โ Selenium๋ง์ผ๋ก๋ HTTP ์ํ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค
์ฒ์์ "Selenium๋ง์ผ๋ก ๋งํฌ ์ค๋ฅ ๊ฒ์ถ์ด ๊ฐ๋ฅํ๊ฒ ์ง!"๋ผ๊ณ ์๊ฐํ๊ณ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์์ํ์ต๋๋ค. ํ์ง๋ง Selenium์ DOM ์กฐ์์๋ ๋ฅ์ํ์ง๋ง, HTTP ์ํ ์ฝ๋๋ฅผ ์ง์ ๊ฐ์ ธ์ค๋ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
ํด๊ฒฐ์ฑ
์ผ๋ก requests ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์กฐํฉํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค.
# โ Selenium๋ง์ผ๋ก๋ HTTP ์ํ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์
# โ
requests์ ์กฐํฉํ์ฌ ํด๊ฒฐ
response = requests.head(url, timeout=8, allow_redirects=True)
status_code = response.status_code
๐ก ํฌ์ธํธ๏ผ Selenium์ ๋งํฌ ์ถ์ถยทDOM ์กฐ์, requests๋ ์ํ ์ฝ๋ ํ์ธ์ผ๋ก ์ญํ ์ ๋ถ๋ดํ๋ ๊ฒ์ด ์ค๋ฌด์์์ ์ ๋ต์ ๋๋ค.
โก StaleElementReferenceException ๋ฐ์
๋งํฌ๋ฅผ ๊ฐ์ ธ์จ ํ DOM์ด ์
๋ฐ์ดํธ๋๋ฉด, ์ด๋ฏธ ๊ฐ์ ธ์จ ์์์ ์ฐธ์กฐ๊ฐ ๋ฌดํจํ๋์ด StaleElementReferenceException ์ด ๋ฐ์ํฉ๋๋ค. ์ฒ์์๋ ์ ์ค๋ฅ๊ฐ ๋๋์ง ์ ํ ์ดํดํ ์ ์์์ต๋๋ค.
ํด๊ฒฐ์ฑ ์ ์์ ์ ๋ณด๋ฅผ ๋ฏธ๋ฆฌ dict์ ์ ์ฅํด๋๋ ๊ฒ์ ๋๋ค.
# โ ๋์ ์: ๋์ค์ ์์๋ฅผ ์กฐ์ํ๋ ค๊ณ ํ๋ฉด ์ค๋ฅ ๋ฐ์
elements = driver.find_elements(By.TAG_NAME, "a")
do_something()
elements[0].click() # โ StaleElementReferenceException!
# โ
์ข์ ์: ๋ฏธ๋ฆฌ dict์ ์ ๋ณด๋ฅผ ์ ์ฅ
element_data = {
'href': element.get_attribute("href"),
'text': element.text,
}
๐ก ํฌ์ธํธ๏ผ ์์ ์ฐธ์กฐ๋ฅผ ๋์ค์ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋๋ผ, ํ์ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ ์งํ์ dict์ ์ ์ฅํ๋ ์ต๊ด์ ๋ค์ด๋ฉด ์์ ์ ์ผ๋ก ๋์ํฉ๋๋ค.
โข aํ๊ทธ๊ฐ 0๊ฑด๋ฐ์ ๊ฐ์ ธ์์ง์ง ์๋๋ค
find_elements(By.TAG_NAME, "a") ๋ฅผ ์คํํด๋ 0๊ฑด๋ฐ์ ๊ฐ์ ธ์ค์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์์ธ์ JavaScript๋ก ๋์ ์ผ๋ก ์์ฑ๋๋ ์ฝํ
์ธ ์ ๋ก๋ฉ ๋๊ธฐ ์๊ฐ์ด ๋ถ์กฑํ๋ ๊ฒ์ด์์ต๋๋ค.
# โ 0๊ฑด์ด ๋ ์ ์์
driver.get(url)
elements = driver.find_elements(By.TAG_NAME, "a")
# โ
๋๊ธฐ ์๊ฐ์ ๋๋ ค์ ํด๊ฒฐ
driver.get(url)
time.sleep(5) # 2์ด โ 5์ด๋ก ๋๋ฆผ
elements = driver.find_elements(By.TAG_NAME, "a")
โ ๏ธ ์ฃผ์๏ผ time.sleep() ์ ๊ฐ์ ์ฌ์ดํธ์ ๋ก๋ฉ ์๋์ ๋ฐ๋ผ ์กฐ์ ์ด ํ์ํฉ๋๋ค. ๋ฌด๊ฑฐ์ด ์ฌ์ดํธ์์๋ 10์ด ์ด์ ํ์ํ ๊ฒฝ์ฐ๋ ์์ต๋๋ค.
โฃ HEAD ์์ฒญ์ ๊ฑฐ๋ถํ๋ ์๋ฒ๊ฐ ์๋ค
requests.head() ๋ก ๊ฐ๋ฒผ์ด ์ฒดํฌ๋ฅผ ์๋ํ๋๋, ์๋ฒ์ ๋ฐ๋ผ์๋ HEAD ๋ฉ์๋๋ฅผ ๊ฑฐ๋ถํ๊ณ 405 ์ค๋ฅ๋ฅผ ๋ฐํํ๋ ๊ฒฝ์ฐ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ์ฑ ์ HEAD๊ฐ ์คํจํ๋ฉด GET์ผ๋ก ์ฌํ์ธํ๋ 2๋จ๊ณ ์ฒดํฌ๋ก ๋ง๋๋ ๊ฒ์ ๋๋ค.
# ๋จผ์ HEAD๋ก ๊ฐ๋ฒผ์ด ์ฒดํฌ
response = requests.head(url, timeout=8)
# 400๋ฒ๋๊ฐ ๋ฐํ๋๋ฉด GET์ผ๋ก ์ฌํ์ธ
if 400 <= response.status_code < 500:
response = requests.get(url, timeout=8)
๐ก ํฌ์ธํธ๏ผ HEAD โ GET ํด๋ฐฑ ๊ตฌ์ฑ์ผ๋ก ์๋ฒ ์ฐจ์ด์ ์ํ ์คํ์ง๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
โค ChromeDriver ๋ฒ์ ๋ถ์ผ์น
Chrome์ ์ ๋ฐ์ดํธํ ํ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ ๊ฐ์๊ธฐ ๋์ํ์ง ์๊ฒ ๋์ต๋๋ค. ์์ธ์ Chrome๊ณผ ChromeDriver์ ๋ฒ์ ์ด ๋ง์ง ์๊ฒ ๋ ๊ฒ์ด์์ต๋๋ค.
# โ
selenium 4.6 ์ด์์ผ๋ก ์
๊ทธ๋ ์ด๋ํ๋ฉด ์๋ ํด๊ฒฐ
pip install --upgrade selenium
๐ก ํฌ์ธํธ๏ผ Selenium 4.6 ์ด์์ ChromeDriver๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ด ๋ฌธ์ ๊ฐ ๊ทผ๋ณธ์ ์ผ๋ก ํด๊ฒฐ๋ฉ๋๋ค. ์๋ ๋ฒ์ ๊ด๋ฆฌ๊ฐ ํ์ ์์ด์ง๋ฏ๋ก ๋จผ์ ์ต์ ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
16. ์ ๋ฆฌ
์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ฉด screenshots ํด๋์ ์๋ฌ ํ์ด์ง ์คํฌ๋ฆฐ์ท๏ผ404_๏ผ๊ณผ ์๋ฌ ์ ์คํฌ๋ฆฐ์ท๏ผBEFORE_๏ผ์ด ์ธํธ๋ก ์๋ ์ ์ฅ๋ฉ๋๋ค. ํ์ผ๋ช ์ ๋งํฌ ํ ์คํธ์ ํ์์คํฌํ๊ฐ ํฌํจ๋์ด ์์ด ๋์ค์ ์ฐพ์๋ณด๊ธฐ๋ ์ฝ์ต๋๋ค.
โฒ ์๋ ์ ์ฅ๋๋ screenshots ํด๋ ๋ด์ฉ. ์๋ฌ ํ์ด์ง ์คํฌ๋ฆฐ์ท๏ผ404_๏ผ๊ณผ ์๋ฌ ์ ์คํฌ๋ฆฐ์ท๏ผBEFORE_๏ผ์ด ์ธํธ๋ก ์ ์ฅ๋จ
- Selenium์ ๋งํฌ ์ถ์ถยทDOM ์กฐ์๏ผrequests๋ ์ํ ํ์ธ์ด๋ผ๋ ์ญํ ๋ถ๋ด์ด ์ค๋ฌด์ ์ ๋ต
- 404๋ฟ๋ง ์๋๋ผ 4xx/5xx ์ ์ฒด๋ฅผ ๋์์ผ๋ก ํ๋ ๊ฒ์ด ์ค๋ฌด์ ์ธ ์ ๊ทผ
- ์ฆ๊ฑฐ๋ก์ ์๋ฌ ์ ํ์ ์คํฌ๋ฆฐ์ท์ ์๋ ์์ฑ ๊ฐ๋ฅ
- ๊ฒฐ๊ณผ๋ Excel ๋์ CSV๋ก ์๋ ์ถ๋ ฅ๋์ด ๋ฒ๊ทธ ํฐ์ผ์ ๋ฐ๋ก ์ฒจ๋ถ ๊ฐ๋ฅ
- SEO ๊ฐ์ โ ๋งํฌ ๋๊น์ ์ ๊ธฐ ๊ฐ์ํด ๊ฒ์ ํ๊ฐ ํ๋ฝ์ ๋ฐฉ์ง ๊ฐ๋ฅ
- CI/CD์ ํตํฉ โ ๋ฆด๋ฆฌ์ค ์ ์ ์๋์ผ๋ก ๋งํฌ ํ์ธ์ด ์คํ๋๋ ํ์ง ๊ฒ์ดํธ ๊ตฌ์ถ ๊ฐ๋ฅ
- ์ ๊ธฐ ๊ฐ์ โ cron์ด๋ ์์ ์ค์ผ์ค๋ฌ์ ์กฐํฉํด ์ฃผ๊ฐยท์๊ฐ์ผ๋ก ์๋ ์คํ ๊ฐ๋ฅ
์ด ๋๊ตฌ์ 3๊ฐ์ง ํ์ฉ ์ฅ๋ฉด
๋งํฌ ๋๊น์ Google ํ๊ฐ๋ฅผ ๋ฎ์ถ๋ ์์ธ. ์ฃผ๊ฐยท์๊ฐ์ผ๋ก ์ ๊ธฐ ์คํํด ํญ์ ๊ฑด์ ํ ์ฌ์ดํธ๋ฅผ ์ ์ง
๋ฆด๋ฆฌ์ค ์ ๋งํฌ ํ์ง ํ์ธ์ ํ ์คํธ ์ค์ํธ์ ํตํฉ. ์ฆ๊ฑฐ ์คํฌ๋ฆฐ์ท๋ ์๋ ์์ฑ๋จ
GitHub Actions๋ Jenkins์ ํตํฉํ๋ฉด ๋งค ๋ฐฐํฌ ์ ์ ์๋์ผ๋ก ๋งํฌ ํ์ธ์ด ์คํ๋๋ ํ์ง ๊ฒ์ดํธ ๊ตฌ์ถ ๊ฐ๋ฅ
cron์ด๋ Windows ์์ ์ค์ผ์ค๋ฌ์ ์กฐํฉํด ์ฃผ๊ฐยท์๊ฐ์ผ๋ก ์๋ ์คํ. ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ
๐ ํฅํ ํ์ฅ ์์
- ์ ์ฒด ํ์ด์ง ํฌ๋กค ๋์ โ ๋ด๋ถ ๋งํฌ๋ฅผ ์ฌ๊ท์ ์ผ๋ก ๋ฐ๋ผ๊ฐ ์ฌ์ดํธ ์ ์ฒด๋ฅผ ์ผ๊ด ํ์ธ
- ๋ณ๋ ฌ ์ฒ๋ฆฌ๏ผconcurrent.futures๏ผ โ 1000๊ฑด์ URL์ ๊ณ ์ ์ฒ๋ฆฌ
- ์ ๊ธฐ ์คํ + Slack ์๋ฆผ โ cron์ผ๋ก ์๋ ์คํํ๊ณ ์๋ฌ ๊ฐ์ง ์ Slack์ ์ฆ์ ์๋ฆผ
