Day 8

TIL - HTML ๋ถ„์„

๐Ÿ“‹ย ๊ณต๋ถ€ ๋‚ด์šฉ

requests

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ์›น ๋ธŒ๋ผ์šฐ์ €์™€ ๊ฐ™์ด ์›น ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๊ณ  ์‘๋‹ต์„ ๋ฐ›์•„์˜ด
  • ์‘๋‹ต ๋ฐ›์€ ๋ฌธ์„œ -> ๋ถ„์„ ํ•„์š”!

BeautifulSoup

HTML ์ฝ”๋“œ๋ฅผ ๋ถ„์„ ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (HTML Parser)

์„ค์น˜ ๋ฐฉ๋ฒ•

(mac, python3 ๊ธฐ์ค€)

1
2
3
pip install bs4
or
pip3 install bs4

HTML ๋ถ„์„ ์‹ค์Šต

  • ํ•„์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

    1
    2
    
    import requests
    from bs4 import BeautifulSoup #import bs library
    
  • ์‚ฌ์ดํŠธ๋ฅผ ์š”์ฒญํ•˜๊ณ  ์‘๋‹ต๋ฐ›๊ธฐ

    1
    2
    
    # requests.get์œผ๋กœ ์‚ฌ์ดํŠธ HTML์„ ๋ฐ›์•„ ์™€ ์ €์žฅ
    res = requests.get("https://www.example.com")
    
  • ์‘๋‹ต๋ฐ›์€ HTML์œผ๋กœ BeautifulSoup ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚ด์šฉ์„ ์ถœ๋ ฅํ•ด๋ณด๊ธฐ

    1
    2
    3
    4
    5
    6
    
    # bs ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , res.text์™€ "html.paser"๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ
    # HTML parser ์—ญํ• ์„ ํ•˜๊ฒŒ ๋งŒ๋“ฌ
    soup = BeautifulSoup(res.text, "html.parser")
    
    # HTML์˜ ๊ตฌ์กฐ๋ฅผ ์ž˜ ๋ณด์—ฌ์ฃผ๋„๋ก ๋“ค์—ฌ์“ฐ๊ธฐํ•˜์—ฌ ์ถœ๋ ฅ
    print(soup.prettify())
    
  • HTML์˜ ํŠน์ • ์š”์†Œ ์ฐพ๊ธฐ

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    # ํ•ด๋‹นํ•˜๋Š” ๊ฐ ํƒœ๊ทธ์˜ ๋‚ด์šฉ์„ ๋ณด์—ฌ์คŒ
    soup.title
    soup.head
    soup.body
    
    # h1ํƒœ๊ทธ๋“ค ์ค‘ ๊ฐ€์žฅ ๋จผ์ € ๋‚˜์˜ค๋Š”๊ฒƒ์„ ์ฐพ์•„ ๋ฐ˜ํ™˜
    h1 = soup.find("h1")
    # pํƒœ๊ทธ ๋ชจ๋‘๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜
    soup.find_all("p")
    
    # ํƒœ๊ทธ ์ด๋ฆ„๊ณผ (h1) ํƒœ๊ทธ ์•ˆ์˜ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ด
    h1.name
    h1.text
    
  • ํŠน์ • ์š”์†Œ๋ฅผ ์ฐพ์•„ ๊ทธ ์•ˆ์˜ ์›ํ•˜๋Š” ์ •๋ณด๋งŒ ์ถ”๋ ค๋‚ด๊ธฐ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    # ์ฑ… ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐพ์•„ ๊ทธ ์ œ๋ชฉ๋งŒ ์ถ”์ถœํ•˜๋Š” ์ฝ”๋“œ
    
    # ์ง์ ‘ ์‚ฌ์ดํŠธ๋ฅผ ํ™•์ธํ•˜๊ณ  h3ํƒœ๊ทธ์— ์ฑ… ์ด๋ฏธ์ง€, ์ €์ž, ์ œ๋ชฉ ๋“ฑ์ด ์žˆ๋Š”๊ฒƒ์„ ํ™•์ธํ•˜์—ฌ find_all๋กœ ๊ฐ€์ ธ์˜ด
    h3_results = soup.find_all('h3')
    
    # ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด, ๋‚ด๋ถ€ ํƒœ๊ทธ๋ฅผ ์†์„ฑ์ฒ˜๋Ÿผ ์“ธ ์ˆ˜ ์žˆ์Œ
    # h3 > a ํƒœ๊ทธ ๋‚ด์— title ์†์„ฑ value๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜
    for book in h3_results:
        print(book.a['title'])
    
  • id๋ฅผ ์ด์šฉํ•ด ์š”์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ

    1
    2
    3
    
    # .find(<tag_name>, id = <id_name>)
    soup.find_all("div", id="bo_list")
    soup.find("div", id="bo_cate")
    
  • class๋ฅผ ์ด์šฉํ•ด ์š”์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ

    1
    2
    3
    
    # .find(<tag_name>, <class_name>)
    soup.find_all("li", "questions") 
    soup.find("div", "question")
    
  • user-agent ์ •๋ณด๋ฅผ ๋„˜๊ธฐ๋ฉด์„œ ์š”์ฒญํ•˜๊ธฐ
    user agent ํ™•์ธ ์‚ฌ์ดํŠธ

    1
    2
    3
    4
    5
    
    user_agent = {"User-Agent": <๋ณธ์ธ์˜ user agent ์ •๋ณด>}
    import requests
    from bs4 import BeautifulSoup
    url = "https://qna.programmers.co.kr/"
    res = requests.get(url, user_agent)
    
  • ํŽ˜์ด์ง€๋„ค์ด์…˜ (Pagination)

    ์ •๋ณด๋ฅผ ์ธ๋ฑ์Šค๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ธฐ๋ฒ•

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    # ํ•ด๋‹น ์‚ฌ์ดํŠธ๋Š” query string์œผ๋กœ ๊ตฌ๋ถ„
    import time
    import requests
    from bs4 import BeautifulSoup
    
    url = "https://qna.programmers.co.kr/?page={}"
    
    for i in range(1, 6):
        res = requests.get(url.format(i), user_agent)
        soup = BeautifulSoup(res.text, "html.parser")
        questions = soup.find_all("li", "question-list-item")
        for question in questions:
            print(question.find("div", "question").find("div", "top").h4.a.text)
        # ๊ณผ๋„ํ•œ ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด 1์ดˆ๋งˆ๋‹ค ์š”์ฒญ
        time.sleep(1)
    

์›น ์‚ฌ์ดํŠธ์™€ ์Šคํฌ๋ž˜ํ•‘

์ •์  or ๋™์  ์›น ์‚ฌ์ดํŠธ

  • ์›น ์‚ฌ์ดํŠธ๋Š” ์ •์ (Static) ์›น ์‚ฌ์ดํŠธ ์™€ ๋™์ (Dynamic) ์›น ์‚ฌ์ดํŠธ ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.
    ์›น ์‚ฌ์ดํŠธStaticDynamic
    HTML ๋‚ด์šฉ๊ณ ์ •๋ณ€๊ฒฝ
    HTML ๋ฐ์ดํ„ฐ ๋กœ๋“œ์‘๋‹ต ์ด์ „์— ์™„๋ฃŒ๋จ์‘๋‹ต ์ดํ›„์— ์™„๋ฃŒ๋˜๊ธฐ๋„ ํ•จ

๋™๊ธฐ or ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

๋™๊ธฐ ์ฒ˜๋ฆฌ (์ •์  ์›น ์‚ฌ์ดํŠธ)

  • ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ์ดํ›„์— ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰
  • ์š”์ฒญ์— ๋”ฐ๋ฅธ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ ค์•ผ ํ•จ

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ (๋™์  ์›น ์‚ฌ์ดํŠธ)

  • ๋ Œ๋”๋ง๊ณผ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋™์‹œ์— ์ง„ํ–‰๋จ
  • ์š”์ฒญ์— ๋”ฐ๋ฅธ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ
  • ์ƒํ™ฉ์— ๋”ฐ๋ผ ์‘๋‹ต ์‹œ ๋ฐ›์€ HTML ๋ฌธ์„œ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์™„์ „ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฐœ์ƒ

์Šคํฌ๋ž˜ํ•‘

  • requests ์š”์ฒญ์˜ ๋ฌธ์ œ์ 

    1. ๋ถˆ์™„์ „ํ•˜๊ณ  ์›ํ•˜์ง€ ์•Š์€ ๋‚ด์šฉ์˜ ์‘๋‹ต์„ ๋ฐ›๊ฒŒ ๋˜์–ด ๋™์  ์›น์‚ฌ์ดํŠธ์— ์ ์šฉ์ด ์–ด๋ ค์›€
    2. ํ‚ค๋ณด๋“œ, ๋งˆ์šฐ์Šค ์ž…๋ ฅ ๋“ฑ UI์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๊ธฐ ์–ด๋ ค์›€
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

    1. ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ›„ ์‘๋‹ต์„ ๋ฐ›์•„์˜ค๋Š” ๋ฐฉ์‹ ์ ์šฉ
    2. UI Action์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
    3. ์›น ๋ธŒ๋ผ์šฐ์ € ์—ญํ• ์„ ํ•˜๋Š” ๋Œ€์‹  ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์กฐ์ž‘ -> Selenium

๐Ÿ‘€ย CHECK

(์–ด๋ ต๊ฑฐ๋‚˜ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ ๋œ ๊ฒƒ ๋“ฑ ๋‹ค์‹œ ํ™•์ธํ•  ๊ฒƒ๋“ค)

  • Pagination : ์ •๋ณด๋ฅผ ์ธ๋ฑ์Šค๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ธฐ๋ฒ•
    • Query String
  • .format method
    1
    
    "{}".format(a) -> "a"
    
  • ๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์˜ ๊ฐœ๋…์— ๋Œ€ํ•ด ํ•œ๋ฒˆ ๋” ๋ณต์Šต
  • bs .find ํ•จ์ˆ˜
    • ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ์ฐพ์„ ๋•Œ or์„ ์ ์šฉํ•˜์—ฌ A or B or C class ๋ชจ๋‘๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆ
    • class A, B / class A ์ธ ์š”์†Œ ๋‘ ๊ฐœ๊ฐ€ ์žˆ์„ ๋•Œ class B๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๋Š” ๋ฐฐ์ œํ•˜๊ณ  ์ฐพ๋Š” ๋ฐฉ๋ฒ•๋„ ๊ถ๊ธˆ
  • ์ •์  ์›น ์‚ฌ์ดํŠธ์—๋Š” requests๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๊ณ  ๋™์  ์›น ์‚ฌ์ดํŠธ์—๋Š” Selenium์„ ์“ฐ๋Š”์ง€ ์•„๋‹ˆ๋ฉด ์ผ๊ด„์ ์œผ๋กœ Selenium์ฒ˜๋Ÿผ ์›น ์‚ฌ์ดํŠธ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ํ˜•์‹์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์“ฐ๋Š”์ง€ ๊ถ๊ธˆํ•˜๋‹ค.
  • ์ค‘์ฒฉ๋œ div ๋‚ด์— h4๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์— (div>div>h4) ๊ฐ€์žฅ ๋ฐ”๊นฅ div ๋‚ด์— h4๊ฐ€ ํ•˜๋‚˜๋ฟ์ด๋ผ๋ฉด div_container.find("div").h4 ๋กœ ์“ฐ์ง€ ์•Š๊ณ  div_container.h4๋กœ ์ž‘์„ฑํ•ด๋„ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ•˜๋˜๋ฐ ์ด์œ ๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค.

โ—ย ๋Š๋‚€ ์ 

์˜ค๋Š˜์€ ๊ฝค๋‚˜ ๋งŒ์กฑ์Šค๋Ÿฌ์šด ํ•˜๋ฃจ๋ฅผ ๋ณด๋ƒˆ๋‹ค.

๊ฐ•์˜์™€ ์‹ค์Šต์€ ๋‚ด ๊ธฐ์ค€์—” ์–ด๋ ต์ง€ ์•Š์•„ 2์‹œ๊ฐ„ ๋‚ด๋กœ ์™„๋ฃŒํ–ˆ๋‹ค. ์‹ค์Šต์„ ํ•˜๋ฉด์„œ ์ฃผ์–ด์ง„ ์‚ฌ์ดํŠธ ์™ธ์— ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ๋„ ๋“ค์–ด๊ฐ€์„œ ๋ถ„์„ํ•˜๊ณ  ๋ฐฐ์šด ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ด๋ณด์•˜๋‹ค. ์‹ค์Šต์„ ํ•˜๋ฉด์„œ ๊ถ๊ธˆํ•œ ์ ์ด ๋ช‡๊ฐœ ์ƒ๊ฒผ๊ณ  CHECK์—๋„ ์ ์–ด๋†จ๋Š”๋ฐ, TIL์„ ๋‹ค ์ ์€ ํ›„์— ๊ตฌ๊ธ€๋ง ๋ฐ ์Šฌ๋ž™ ์ฑ„๋„์„ ํ†ตํ•ด์„œ ํ•ด๊ฒฐํ•ด ๋ณผ ์ƒ๊ฐ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ฝ”์–ดํƒ€์ž„ ์ค‘๊ฐ„์— hugo๋กœ ๋งŒ๋“  ๋ธ”๋กœ๊ทธ์— ๊ธ€์„ ์˜ฌ๋ ค๋ดค๋Š”๋ฐ ๋‚ด๊ฐ€ ์ƒ๊ฐํ–ˆ๋˜๊ฒƒ ๋ณด๋‹ค ๋” ์‰ฌ์›Œ์„œ ์•ˆ์‹ฌํ–ˆ๋‹ค. ๋ธ”๋กœ๊ทธ์™€ ๊ด€๋ จํ•ด ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ชฉํ‘œ๋ฅผ ์„ธ์› ๋‹ค.

  1. github action ์ˆ˜์ •ํ•ด์„œ push ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ๋ฐฐํฌ๋˜๊ฒŒ ๋งŒ๋“ค๊ธฐ
  2. hugo new post ํ…œํ”Œ๋ฆฟ ๋งŒ๋“ค๊ธฐ
  3. ํ•œ๊ตญ์–ด ์„ค์ •ํ•˜๊ธฐ

์ด๋ ‡๊ฒŒ ์„ธํŒ…์„ ์™„๋ฃŒํ•œ ํ›„์—, ๊ธฐ์กด์— ์ž‘์„ฑํ–ˆ๋˜ TIL์„ ์˜ฌ๋ฆฌ๊ณ  ์นดํ…Œ๊ณ ๋ฆฌ ์„ค์ •๊นŒ์ง€ ํ•˜๋ฉด ์™„์„ฑ์ด๋‹ค. ๋‚˜์ค‘์—” ๋ธ”๋กœ๊ทธ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์ปค์Šคํ…€, ๋Œ“๊ธ€ ์œ„์ ฏ ์„ค์ •, SEO ์„ธํŒ… ๋“ฑ์„ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

Hugo๋กœ ๋งŒ๋“ฆ
Jimmy์˜ Stack ํ…Œ๋งˆ ์‚ฌ์šฉ ์ค‘