Selenium Python 404 ๋งํฌ ์ฒดํฌ ๋ฐฉ๋ฒ• (์ž๋™ํ™”)

ํ…Œ์ŠคํŠธ ์ž๋™ํ™”

๐Ÿ“Œ ์ด๋Ÿฐ ๋ถ„๋“ค์„ ์œ„ํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค

  • ๐Ÿ”ง QA ์—”์ง€๋‹ˆ์–ด โ†’ ๊นจ์ง„ ๋งํฌ ํƒ์ง€๋ฅผ ์ž๋™ํ™”ํ•ด์„œ ํ…Œ์ŠคํŠธ ๊ณต์ˆ˜๋ฅผ ์ค„์ด๊ณ  ์‹ถ์€ ๋ถ„
  • ๐Ÿ” SEO ๋‹ด๋‹น์ž โ†’ ๊นจ์ง„ ๋งํฌ ๋ฐฉ์น˜๋กœ ์ธํ•œ SEO ์ ์ˆ˜ ํ•˜๋ฝ์„ ๋ง‰๊ณ  ์‹ถ์€ ๋ถ„
  • โš™๏ธ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ์—”์ง€๋‹ˆ์–ด โ†’ Selenium๊ณผ Python์œผ๋กœ ์‹ค๋ฌด ์ˆ˜์ค€์˜ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์€ ๋ถ„

๐Ÿ”ฅ ์ด ๊ธ€์„ ์ฝ์œผ๋ฉด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • SEO์— ์•…์˜ํ–ฅ์„ ์ฃผ๋Š” ใ€Œ๊นจ์ง„ ๋งํฌใ€๋ฅผ ์ž๋™์œผ๋กœ ํƒ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ˆ˜๋™ ๋งํฌ ํ™•์ธ ์ž‘์—…์ด ์™„์ „ํžˆ ํ•„์š” ์—†์–ด์ง„๋‹ค
  • 404๋ฟ๋งŒ ์•„๋‹ˆ๋ผ 4xx/5xx ์ „์ฒด ์ƒํƒœ ์ฝ”๋“œ์— ๋Œ€์‘ํ•œ ๊ฒ€์‚ฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค
  • QAยทํ…Œ์ŠคํŠธ ์ž๋™ํ™”ยทSEO ๋Œ€์ฑ… 3๊ฐ€์ง€ ๋ชจ๋‘์— ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ์‹ค๋ฌด ์ˆ˜์ค€์˜ ์ฝ”๋“œ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค

๐Ÿ‘ค

๊ธ€์“ด์ด ์†Œ๊ฐœ๏ผšSeleniumยทPythonยทํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ์‹ค๋ฌด์—์„œ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ๋Š” QA ์—”์ง€๋‹ˆ์–ด์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์˜ ์ฝ”๋“œ๋Š” ์‹ค์ œ ์—…๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ, GitHub์—์„œ ์ „์ฒด ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๊ณต๊ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์›น์‚ฌ์ดํŠธ ์šด์˜์—์„œ ์€๊ทผํžˆ ๊ณจ์น˜ ์•„ํ”ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐ”๋กœ ๊นจ์ง„ ๋งํฌ(404 ์—๋Ÿฌ)์˜ ์กด์žฌ์ž…๋‹ˆ๋‹ค. ์ˆ˜๋™์œผ๋กœ ํ™•์ธํ•˜๊ธฐ์—” ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ , ๊ทธ๋ ‡๋‹ค๊ณ  ๋ฐฉ์น˜ํ•˜๋ฉด SEO ํ‰๊ฐ€์™€ ์‹ ๋ขฐ์„ฑ์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ๊ทธ ๊ณ ๋ฏผ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  QA ์ž๋™ํ™” ๋„๊ตฌ๊ฐ€ ๋ฐ”๋กœ LinkChecker์ž…๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” Selenium ร— Python์œผ๋กœ ๊ตฌํ˜„ํ•œ ๋งํฌ ์ฒด์ปค์˜ ์ฝ”๋“œ๋ฅผ, ์„ค๊ณ„ ์˜๋„ยท๊ฐ ๋ฉ”์„œ๋“œ์˜ ์—ญํ• ยท์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๊นŒ์ง€ ํฌํ•จํ•ด ์ฒ ์ €ํ•˜๊ฒŒ ํ•ด์„คํ•ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ ๊ฒฐ๊ณผ ์ƒ˜ํ”Œ๊ณผ CSV ์ถœ๋ ฅ ์˜ˆ์‹œ๋„ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋‹ˆ ๋ฐ”๋กœ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”.



  1. 00. ๊นจ์ง„ ๋งํฌ๊ฐ€ SEO์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ
  2. 01. ์‹คํ–‰ ๊ฒฐ๊ณผ ์ƒ˜ํ”Œ๏ผˆ๋จผ์ € ๋™์ž‘ ํ™•์ธ๏ผ๏ผ‰
    1. ์ž๋™ ์ƒ์„ฑ๋˜๋Š” CSV ๋ฆฌํฌํŠธ๏ผˆ์ €์žฅํ•ด์„œ ์žฌํ™œ์šฉ๏ผ‰
  3. 02. ์™œ Selenium + requests ์กฐํ•ฉ์ธ๊ฐ€๏ผŸ
  4. 03. ๋Œ€์‘ํ•˜๋Š” ์—๋Ÿฌ ์ƒํƒœ ์ฝ”๋“œ ์ผ๋žŒ
  5. 04. ํ™˜๊ฒฝ ๊ตฌ์ถ•๊ณผ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  6. 05. ํด๋ž˜์Šค ๊ตฌ์กฐ์™€ ์„ค๊ณ„
  7. 06. __init__ ์™€ ์ดˆ๊ธฐ ์„ค์ •
  8. 07. get_all_links โ€” ๋งํฌ ์ˆ˜์ง‘ ์ „๋žต
  9. 08. check_link_status โ€” 2๋‹จ๊ณ„ ์ƒํƒœ ์ฝ”๋“œ ํ™•์ธ
  10. 09. take_screenshot โ€” ์ฆ๊ฑฐ ์ˆ˜์ง‘
  11. 10. handle_cookie_popup โ€” GDPR ๋Œ€์‘
  12. 11. save_results โ€” CSV ๋ฆฌํฌํŠธ ์ถœ๋ ฅ
  13. 12. ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ์™€ ๋Œ€์ฒ˜๋ฒ•
    1. โ‘  ChromeDriver ๋ฒ„์ „ ๋ถˆ์ผ์น˜
    2. โ‘ก TimeoutException โ€” ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋”ฉ๋˜์ง€ ์•Š์Œ
    3. โ‘ข a ํƒœ๊ทธ๊ฐ€ 0๊ฑด๋ฐ–์— ์ทจ๋“๋˜์ง€ ์•Š์Œ
  14. 13. ๋Œ€๋Ÿ‰ URL์„ ํ™•์ธํ•  ๋•Œ์˜ ๊ณ ์†ํ™”
    1. โ‘  concurrent.futures๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
    2. โ‘ก ํƒ€์ž„์•„์›ƒ ์„ค์ •๊ณผ ์žฌ์‹œ๋„ ์ฒ˜๋ฆฌ
  15. 14. ๋” ๋ฐœ์ „์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๊ฐœ์„  ์•„์ด๋””์–ด
  16. 15. ์ž์ฃผ ๊ฒช๋Š” ๋ฌธ์ œ & ํ•ด๊ฒฐ๋ฒ•
    1. โ‘  Selenium๋งŒ์œผ๋กœ๋Š” HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋‹ค
    2. โ‘ก StaleElementReferenceException ๋ฐœ์ƒ
    3. โ‘ข aํƒœ๊ทธ๊ฐ€ 0๊ฑด๋ฐ–์— ๊ฐ€์ ธ์™€์ง€์ง€ ์•Š๋Š”๋‹ค
    4. โ‘ฃ HEAD ์š”์ฒญ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ์„œ๋ฒ„๊ฐ€ ์žˆ๋‹ค
    5. โ‘ค ChromeDriver ๋ฒ„์ „ ๋ถˆ์ผ์น˜
  17. 16. ์ •๋ฆฌ
    1. ์ด ๋„๊ตฌ์˜ 3๊ฐ€์ง€ ํ™œ์šฉ ์žฅ๋ฉด

00. ๊นจ์ง„ ๋งํฌ๊ฐ€ SEO์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ

ใ€Œ๊นจ์ง„ ๋งํฌ๋Š” ์‚ฌ์šฉ์ž๋งŒ ๋ถˆํŽธํ•œ ๊ฒƒใ€์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ์‰ฝ์ง€๋งŒ, ์‹ค์€ SEO์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ๋„ ์‹ฌ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊นจ์ง„ ๋งํฌ๋Š” ์‚ฌ์ดํŠธ์˜ ํ’ˆ์งˆยท๊ฒ€์ƒ‰ ํ‰๊ฐ€ยท์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๋ชจ๋‘์— ์•…์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.

โš ๏ธ Google์˜ ๊ณต์‹ ์ž…์žฅ๏ผš ํฌ๋กค๋งํ•  ์ˆ˜ ์—†๋Š” ํŽ˜์ด์ง€๋‚˜ ๊นจ์ง„ ๋งํฌ๊ฐ€ ๋งŽ์€ ์‚ฌ์ดํŠธ๋Š” ์‚ฌ์ดํŠธ ์ „์ฒด์˜ ํ’ˆ์งˆ ํ‰๊ฐ€๊ฐ€ ๋‚ฎ์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊นจ์ง„ ๋งํฌ ๋ฐฉ์น˜๋Š” SEO์— ์žˆ์–ด ๋ฆฌ์Šคํฌ์ž…๋‹ˆ๋‹ค.
๐Ÿ“‰
ํฌ๋กค ํšจ์œจ ์ €ํ•˜

Googlebot์ด ๊นจ์ง„ ๋งํฌ๋ฅผ ๋งŒ๋‚˜๋ฉด ํฌ๋กค ๋ฒ„์ง“์„ ๋‚ญ๋น„ํ•ด ๋‹ค๋ฅธ ํŽ˜์ด์ง€๊ฐ€ ์ˆœํšŒ๋˜๊ธฐ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค

โญ
์‚ฌ์ดํŠธ ํ‰๊ฐ€ ํ•˜๋ฝ

404 ํŽ˜์ด์ง€๊ฐ€ ๋งŽ์€ ์‚ฌ์ดํŠธ๋Š” Google๋กœ๋ถ€ํ„ฐ ใ€Œํ’ˆ์งˆ์ด ๋‚ฎ๋‹คใ€๊ณ  ํŒ๋‹จ๋˜์–ด SEO ํ‰๊ฐ€๊ฐ€ ๋‚ฎ์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๐Ÿ˜ž
UX ์•…ํ™”ยท์ดํƒˆ๋ฅ  ์ƒ์Šน

๊นจ์ง„ ๋งํฌ๋ฅผ ๋งŒ๋‚œ ์‚ฌ์šฉ์ž๋Š” ๋ฐ”๋กœ ์ดํƒˆํ•ฉ๋‹ˆ๋‹ค. ์ดํƒˆ๋ฅ  ์ƒ์Šน์ด ๊ฐ„์ ‘์ ์œผ๋กœ 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์™€ ์Šคํฌ๋ฆฐ์ƒท์ด ์ž๋™์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Selenium ๋งํฌ ์ฒด์ปค ํ„ฐ๋ฏธ๋„ ์‹คํ–‰ ๊ฒฐ๊ณผ ์ด ๋งํฌ ์ˆ˜ 128 ์—๋Ÿฌ ๋งํฌ ์ˆ˜ 2

โ–ฒ ์‹ค์ œ ํ„ฐ๋ฏธ๋„ ์ถœ๋ ฅ. 128๊ฑด ํ™•์ธ ์ค‘ 2๊ฑด์˜ ์—๋Ÿฌ ๋งํฌ๋ฅผ ๊ฐ์ง€. CSV์™€ ์Šคํฌ๋ฆฐ์ƒท์ด ์ž๋™ ์ €์žฅ๋จ

์—๋Ÿฌ ๊ฐ์ง€ ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—๋Ÿฌ ํŽ˜์ด์ง€์˜ ์Šคํฌ๋ฆฐ์ƒท์ด ์ž๋™์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

404 ์—๋Ÿฌ ๊ฐ์ง€ ์‹œ ์ž๋™ ์ €์žฅ๋˜๋Š” ์Šคํฌ๋ฆฐ์ƒท ์˜ˆ์‹œ

โ–ฒ 404 ์—๋Ÿฌ ๊ฐ์ง€ ์‹œ ์ž๋™ ์ €์žฅ๋˜๋Š” ์Šคํฌ๋ฆฐ์ƒท

๐Ÿ’ก ์‹ค๋ฌด์—์„œ์˜ ํ™œ์šฉ๏ผš ์‹ค๋ฌด์—์„œ๋Š” ์ด ๊ฒฐ๊ณผ๋ฅผ ๋กœ๊ทธ๋‚˜ CSV๋กœ ์ €์žฅํ•ด ์ •๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” 1ํŽ˜์ด์ง€๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜์ง€๋งŒ, ์ด ๋กœ์ง์€ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋‚˜ ์‚ฌ์ดํŠธ ์ „์ฒด ํ™•์ธ์—๋„ ์‘์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ž๋™ ์ƒ์„ฑ๋˜๋Š” 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ยท์ƒํƒœ ์ฝ”๋“œยท์Šคํฌ๋ฆฐ์ƒท ๊ฒฝ๋กœ๊ฐ€ ํ•œ๋ˆˆ์— ์ •๋ฆฌ๋˜์–ด ์žˆ์–ด ๊ทธ๋Œ€๋กœ ๋ฒ„๊ทธ ํ‹ฐ์ผ“์— ์ฒจ๋ถ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Selenium ๋งํฌ ์ฒด์ปค CSV ๋ฆฌํฌํŠธ Excel๋กœ ์—ด๊ธฐ ์—๋Ÿฌ ๋งํฌ ๋ชฉ๋ก ์ƒํƒœ ์ฝ”๋“œ 404

โ–ฒ ์ž๋™ ์ƒ์„ฑ๋œ CSV๋ฅผ Excel๋กœ ์—ด๊ธฐ. ๋งํฌ ํ…์ŠคํŠธยทURLยท์ƒํƒœ ์ฝ”๋“œยท์Šคํฌ๋ฆฐ์ƒท ๊ฒฝ๋กœ๊ฐ€ ๋ชจ๋‘ ์ •๋ฆฌ๋˜์–ด ์žˆ์Œ

๐Ÿ“Š ์ „์ฒด ๊ฒฐ๊ณผ CSV
๋ชจ๋“  ๋งํฌ ํ™•์ธ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅ. ํ†ต๊ณ„ยท๋ถ„์„์— ํ™œ์šฉ ๊ฐ€๋Šฅ
โŒ ์—๋Ÿฌ ์ „์šฉ CSV
์—๋Ÿฌ๋งŒ ์ถ”์ถœํ•ด ๋ณ„๋„ ํŒŒ์ผ๋กœ ์ €์žฅ. ์ˆ˜์ • ์ž‘์—…์„ ๋ฐ”๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Œ

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
๐Ÿ
Python 3.8+

๋™์ž‘ ํ™•์ธ ์™„๋ฃŒ. Windows/Mac ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋Œ€์‘

๐ŸŒ
selenium 4.6+

ChromeDriver ์ž๋™ ๊ฐ์ง€ ๋Œ€์‘. ์ˆ˜๋™ ๊ด€๋ฆฌ ๋ถˆํ•„์š”

๐Ÿ“ก
requests

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 โ€” ๋งํฌ ์ˆ˜์ง‘ ์ „๋žต

  1. ํŽ˜์ด์ง€ ์ ‘์† & ์ดˆ๊ธฐ ๋Œ€๊ธฐ โ€” time.sleep(2)๋กœ ๋™์  ์ฝ˜ํ…์ธ  ๋กœ๋”ฉ์„ ๊ธฐ๋‹ค๋ฆผ
  2. Cookie ํŒ์—… ์ฒ˜๋ฆฌ โ€” handle_cookie_popup()์œผ๋กœ GDPR ๋™์˜ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ž๋™ ํด๋ฆญ
  3. ์ „์ฒด a ํƒœ๊ทธ ์ทจ๋“ โ€” find_elements(By.TAG_NAME, "a")๋กœ HTTP๋กœ ์‹œ์ž‘ํ•˜๋Š” URL๋งŒ ํ•„ํ„ฐ๋ง
  4. ์š”์†Œ ์ •๋ณด ์‚ฌ์ „ ์ €์žฅ๏ผˆStale Element ๋Œ€์ฑ…๏ผ‰ โ€” location/size/XPath ๋“ฑ์„ dict์— ์ €์žฅ
  5. ํ…์ŠคํŠธ ์—†๋Š” ๋งํฌ ๋ณด์™„ โ€” title โ†’ alt โ†’ aria-label ์ˆœ์œผ๋กœ ํด๋ฐฑ ์ทจ๋“
๐Ÿ’ก Stale Element๋ž€๏ผš Selenium์œผ๋กœ ์š”์†Œ๋ฅผ ์ทจ๋“ํ•œ ํ›„ DOM์ด ์—…๋ฐ์ดํŠธ๋˜๋ฉด, ์ด์ „์— ์ทจ๋“ํ•œ ์š”์†Œ ์ฐธ์กฐ๊ฐ€ ๋ฌดํšจํ™”๋˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ์š”์†Œ์˜ ์†์„ฑ ์ •๋ณด๋ฅผ dict์— ๋ฏธ๋ฆฌ ๋ณต์‚ฌํ•ด ๋‘๋Š” ๊ฒƒ์œผ๋กœ ํšŒํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
# โŒ ๋‚˜์œ ์˜ˆ๏ผš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๋กœ ์ €์žฅ

๐Ÿ”ด
์—๋Ÿฌ ์ „ ์Šคํฌ๋ฆฐ์ƒท๏ผˆBEFORE๏ผ‰

์›๋ž˜ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€ ๋ฌธ์ œ ๋งํฌ์— ๋นจ๊ฐ„ ํ…Œ๋‘๋ฆฌ ํ•˜์ด๋ผ์ดํŠธ๏ผ‹ใ€Œ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ใ€ ๋ฐฐ๋„ˆ๊ฐ€ ํ™”๋ฉด ์ƒ๋‹จ์— ์ž๋™ ์ฃผ์ž…๋˜์–ด ์–ด๋А ๋งํฌ๊ฐ€ ์›์ธ์ธ์ง€ ํ•œ๋ˆˆ์— ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Selenium ๋งํฌ ์ฒด์ปค ์—๋Ÿฌ ๋งํฌ ๋นจ๊ฐ„ ํ…Œ๋‘๋ฆฌ ํ•˜์ด๋ผ์ดํŠธ ERROR LINK ๋ฐฐ๋„ˆ JS ์ž๋™ ์ฃผ์ž…

โ–ฒ ์—๋Ÿฌ ๋งํฌ๋ฅผ ๋นจ๊ฐ„ ํ…Œ๋‘๋ฆฌ๋กœ ํ•˜์ด๋ผ์ดํŠธ๏ผ‹ใ€Œ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}๊ฑด")
๐Ÿ’ก ์‹ค๋ฌด ํฌ์ธํŠธ๏ผš ์—๋Ÿฌ ์นด์šดํŠธ๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ฒจ๋‘๋ฉด ์ด์ „ ํ™•์ธ๊ณผ์˜ ๋น„๊ต๊ฐ€ ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค. ใ€Œ์ง€๋‚œ์ฃผ๋ณด๋‹ค 404๊ฐ€ 3๊ฑด ๋Š˜์—ˆ๋‹คใ€์™€ ๊ฐ™์€ ํŠธ๋ Œ๋“œ ๊ด€๋ฆฌ๋„ ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

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
๐Ÿ’ก ๋ชฉํ‘œ ์ˆ˜์น˜๏ผš 100๊ฑด์ด๋ฉด ๋ณดํ†ต ์•ฝ 5๋ถ„ โ†’ 10๋ณ‘๋ ฌ ์Šค๋ ˆ๋“œ๋กœ ์•ฝ 30์ดˆ๋กœ ๋‹จ์ถ• ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

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_๏ผ‰์ด ์„ธํŠธ๋กœ ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ๋ช…์— ๋งํฌ ํ…์ŠคํŠธ์™€ ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ๋‚˜์ค‘์— ์ฐพ์•„๋ณด๊ธฐ๋„ ์‰ฝ์Šต๋‹ˆ๋‹ค.

Selenium ๋งํฌ ์ฒด์ปค screenshots ํด๋” 404 ์—๋Ÿฌ SS BEFORE ์ด๋ฏธ์ง€ ์ž๋™ ์ €์žฅ ํŒŒ์ผ ๋ชฉ๋ก

โ–ฒ ์ž๋™ ์ €์žฅ๋˜๋Š” screenshots ํด๋” ๋‚ด์šฉ. ์—๋Ÿฌ ํŽ˜์ด์ง€ ์Šคํฌ๋ฆฐ์ƒท๏ผˆ404_๏ผ‰๊ณผ ์—๋Ÿฌ ์ „ ์Šคํฌ๋ฆฐ์ƒท๏ผˆBEFORE_๏ผ‰์ด ์„ธํŠธ๋กœ ์ €์žฅ๋จ

  • Selenium์€ ๋งํฌ ์ถ”์ถœยทDOM ์กฐ์ž‘๏ผŒrequests๋Š” ์ƒํƒœ ํ™•์ธ์ด๋ผ๋Š” ์—ญํ•  ๋ถ„๋‹ด์ด ์‹ค๋ฌด์˜ ์ •๋‹ต
  • 404๋ฟ๋งŒ ์•„๋‹ˆ๋ผ 4xx/5xx ์ „์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๊ฒƒ์ด ์‹ค๋ฌด์ ์ธ ์ ‘๊ทผ
  • ์ฆ๊ฑฐ๋กœ์„œ ์—๋Ÿฌ ์ „ํ›„์˜ ์Šคํฌ๋ฆฐ์ƒท์„ ์ž๋™ ์ƒ์„ฑ ๊ฐ€๋Šฅ
  • ๊ฒฐ๊ณผ๋Š” Excel ๋Œ€์‘ CSV๋กœ ์ž๋™ ์ถœ๋ ฅ๋˜์–ด ๋ฒ„๊ทธ ํ‹ฐ์ผ“์— ๋ฐ”๋กœ ์ฒจ๋ถ€ ๊ฐ€๋Šฅ
  • SEO ๊ฐœ์„  โ†’ ๋งํฌ ๋Š๊น€์„ ์ •๊ธฐ ๊ฐ์‹œํ•ด ๊ฒ€์ƒ‰ ํ‰๊ฐ€ ํ•˜๋ฝ์„ ๋ฐฉ์ง€ ๊ฐ€๋Šฅ
  • CI/CD์— ํ†ตํ•ฉ โ†’ ๋ฆด๋ฆฌ์Šค ์ „์— ์ž๋™์œผ๋กœ ๋งํฌ ํ™•์ธ์ด ์‹คํ–‰๋˜๋Š” ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ๊ตฌ์ถ• ๊ฐ€๋Šฅ
  • ์ •๊ธฐ ๊ฐ์‹œ โ†’ cron์ด๋‚˜ ์ž‘์—… ์Šค์ผ€์ค„๋Ÿฌ์™€ ์กฐํ•ฉํ•ด ์ฃผ๊ฐ„ยท์›”๊ฐ„์œผ๋กœ ์ž๋™ ์‹คํ–‰ ๊ฐ€๋Šฅ

์ด ๋„๊ตฌ์˜ 3๊ฐ€์ง€ ํ™œ์šฉ ์žฅ๋ฉด

๐Ÿ”
SEO ๊ฐœ์„ ์— ํ™œ์šฉ

๋งํฌ ๋Š๊น€์€ Google ํ‰๊ฐ€๋ฅผ ๋‚ฎ์ถ”๋Š” ์š”์ธ. ์ฃผ๊ฐ„ยท์›”๊ฐ„์œผ๋กœ ์ •๊ธฐ ์‹คํ–‰ํ•ด ํ•ญ์ƒ ๊ฑด์ „ํ•œ ์‚ฌ์ดํŠธ๋ฅผ ์œ ์ง€

๐Ÿงช
QA ํ…Œ์ŠคํŠธ์— ํ™œ์šฉ

๋ฆด๋ฆฌ์Šค ์ „ ๋งํฌ ํ’ˆ์งˆ ํ™•์ธ์„ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์— ํ†ตํ•ฉ. ์ฆ๊ฑฐ ์Šคํฌ๋ฆฐ์ƒท๋„ ์ž๋™ ์ƒ์„ฑ๋จ

โš™๏ธ
CI/CD์— ํ†ตํ•ฉ ๊ฐ€๋Šฅ

GitHub Actions๋‚˜ Jenkins์— ํ†ตํ•ฉํ•˜๋ฉด ๋งค ๋ฐฐํฌ ์ „์— ์ž๋™์œผ๋กœ ๋งํฌ ํ™•์ธ์ด ์‹คํ–‰๋˜๋Š” ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ๊ตฌ์ถ• ๊ฐ€๋Šฅ

๐Ÿ“…
์ •๊ธฐ ๊ฐ์‹œ ๊ฐ€๋Šฅ

cron์ด๋‚˜ Windows ์ž‘์—… ์Šค์ผ€์ค„๋Ÿฌ์™€ ์กฐํ•ฉํ•ด ์ฃผ๊ฐ„ยท์›”๊ฐ„์œผ๋กœ ์ž๋™ ์‹คํ–‰. ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌ ๊ฐ€๋Šฅ

๐Ÿš€ ํ–ฅํ›„ ํ™•์žฅ ์˜ˆ์‹œ

  • ์ „์ฒด ํŽ˜์ด์ง€ ํฌ๋กค ๋Œ€์‘ โ†’ ๋‚ด๋ถ€ ๋งํฌ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋”ฐ๋ผ๊ฐ€ ์‚ฌ์ดํŠธ ์ „์ฒด๋ฅผ ์ผ๊ด„ ํ™•์ธ
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๏ผˆconcurrent.futures๏ผ‰ โ†’ 1000๊ฑด์˜ URL์„ ๊ณ ์† ์ฒ˜๋ฆฌ
  • ์ •๊ธฐ ์‹คํ–‰ + Slack ์•Œ๋ฆผ โ†’ cron์œผ๋กœ ์ž๋™ ์‹คํ–‰ํ•˜๊ณ  ์—๋Ÿฌ ๊ฐ์ง€ ์‹œ Slack์— ์ฆ‰์‹œ ์•Œ๋ฆผ
๐Ÿ’ก ๊ธ€์“ด์ด ํ•œ๋งˆ๋””๏ผš ์ด ๋„๊ตฌ๋Š” ใ€Œ๋งํฌ ๋Š๊น€์„ ์ฐพ๋Š”๋‹คใ€๋Š” ๋‹จ์ผ ๋ชฉ์ ์— ํŠนํ™”๋œ ์„ค๊ณ„๊ฐ€ ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. Selenium์˜ ๋ณต์žก์„ฑ์„ ํด๋ž˜์Šค ์•ˆ์— ๊ฐ€๋‘์–ด, ํ˜ธ์ถœ ์ธก์€ ์‹ฌํ”Œํ•˜๊ฒŒ ์œ ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŒ€ ์ „์ฒด์— ๋ฐฐํฌํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ธฐ๋„ ์‰ฌ์šด ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. SEOยทQAยทCI/CD ๋ชจ๋‘์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ GitHub์˜ ์†Œ์Šค์ฝ”๋“œ๋„ ๊ผญ ์ฐธ๊ณ ํ•ด ๋ณด์„ธ์š”๏ผ
ใ‚ฟใ‚คใƒˆใƒซใจURLใ‚’ใ‚ณใƒ”ใƒผใ—ใพใ—ใŸ