๐ŸPyppeteer ๋ฐ๋ชจ

36670 ๋‹จ์–ด Pythonpyppeteertech

Pypeter๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

  • ์ž…๋ ฅ ํ˜•์‹์˜ ์กฐ์ž‘์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ž๋™ ์ž…๋ ฅ์„ ์‹œ๋„
  • ์กฐ์‚ฌ ๊ฒฐ๊ณผPython ์‚ฌ์šฉSelenium์˜ ๊ธฐ์‚ฌ๊ฐ€ ๋Œ€๋Ÿ‰์œผ๋กœ ํŒ”๋ ธ๊ณ , ๊ทธ ์ค‘์—์„œ๋„ ์‚ฌ์šฉPyppeteer๋œ Qita ๋ณด๋„
  • ๊ฐ€ ์žˆ์—ˆ๋‹ค.
  • ๋‹ค๋ฅธ Pyppetter์— ๊ด€ํ•œ Qita ๋ฌธ์žฅ ๋ช‡ ๊ถŒ์„ ์ฝ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •
  • Pyppetter ์ด์‹Puppeteer๏ผˆJavascript๏ผ‰์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Puppeteer์˜ ๋ฌธ์žฅ ๋ช‡ ํŽธ์„ ํ™•์ธํ–ˆ๋‹ค
  • ์ฐธ๊ณ  ์ž๋ฃŒ

  • ํŒŒ์ด์ฌ ์Šคํฌ๋ ˆ์ดํผ - ์…€๋ ˆ๋‹ˆ์›€์€ ์‹œ๋Œ€์— ๋’ค๋–จ์–ด์กŒ๋‹ค!?/ / Pyppeteer ์‚ฌ์šฉ๋ฒ• - Qita
  • ๋‹ค์–‘ํ•œ ์–ธ์–ด๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋œ Pupeteer์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์š”์•ฝ - Qita
  • ์„ค์น˜ํ•˜๋‹ค.


    pip3 install
    $ pip3 install pyppeteer
    

    ๊ฐ€์ ธ์˜ค๊ธฐ


    ๋ชจ๋“ˆ ๊ฐ€์ ธ์˜ค๊ธฐ
    import asyncio
    from pyppeteer import launch
    

    ์‚ฌ์ „ ์ค€๋น„


    ์–‘์‹ ์ž…๋ ฅ ํ™•์ธ

  • ์ด ๊ธ€์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์š”์†Œ์˜ ์ž…๋ ฅ ํ•ญ๋ชฉ์„ ๊ฐ€์ •ํ•˜๊ณ  ๊ณ„์† ํ† ๋ก 
  • ๊ธฐ์‚ฌ์— ๊ธฐ์žฌ๋œ HTML์˜ ์ถœ์ฒ˜์™€ ์‹ค์ œ ์‚ฌ์šฉ์— ์•ฝ๊ฐ„์˜ ๋ณ€ํ™”๊ฐ€ ์žˆ๋‹ค.์›€์ง์ผ ์ˆ˜ ์—†๋Š” ๋ถ€๋ถ„๋„ ์žˆ๊ฒ ์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์“ฐ๋ฉด ์›€์ง์ผ ๊ฑฐ๊ณ  ์ƒ๋Œ€๋ฐฉ์—๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด๐Ÿ˜“
  • ์ด๋ฆ„ใƒ†ใ‚ญใ‚นใƒˆใƒœใƒƒใ‚ฏใ‚น
  • ๊ฐ€๋ช…ใƒ†ใ‚ญใ‚นใƒˆใƒœใƒƒใ‚ฏใ‚น
  • ์ด๋ฉ”์ผ ์ฃผ์†Œใƒ†ใ‚ญใ‚นใƒˆใƒœใƒƒใ‚ฏใ‚น
  • ๋…„๋Œ€ใƒ‰ใƒญใƒƒใƒ—ใƒ€ใ‚ฆใƒณใƒชใ‚นใƒˆ
  • ์ด ์ด๋ฒคํŠธ๋ฅผ ์–ด๋””์„œ ์•„์„ธ์š”?ใ€์—ฌ๋Ÿฌ ๊ฐœ ์„ ํƒ ๊ฐ€๋Šฅใ€‘ใƒใ‚งใƒƒใ‚ฏใƒœใƒƒใ‚ฏใ‚น
  • ์ˆ˜๋™ ์ž…๋ ฅ ํ™•์ธ ์ปจ๋•ํ„ฐ

  • ์ž…๋ ฅ ํ™”๋ฉด -> ์ „์†ก
  • ์ž…๋ ฅ ๋‚ด์šฉ ํ™•์ธ -> ํ™•์ธ
  • ์™„์„ฑํ™”๋ฉด ๋ณด๋‚ด๊ธฐ
  • ์ž๋™ ์ž…๋ ฅ ์ˆœ์„œ ์ •๋ฆฌํ•˜๊ธฐ

  • ๋ธŒ๋ผ์šฐ์ € ์‹œ์ž‘
  • ์ž…๋ ฅ ํ˜•์‹ ์—ด๊ธฐ
  • ์ž…๋ ฅ ํ˜•์‹์˜ ๋‚ด์šฉ
  • ์ž…๋ ฅ ๋‚ด์šฉ ํ™•์ธ ํ™”๋ฉด์œผ๋กœ ํฌ๋งท ์ด๋™
  • ํ™•์ธ ํ™”๋ฉด ์บก์ฒ˜ ์ €์žฅ
  • ๋ฐœ์†ก ๋‚ด์šฉ
  • ์–‘์‹ ์ž…๋ ฅ ์ค€๋น„

  • ์ด๋ฒˆ ์ž…๋ ฅ ํ˜•์‹์€ ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ด์šฉํ•œ ์ค‘๋ณต ๋กœ๊ทธ์ธ ํ™•์ธ ๊ธฐ๋Šฅ
  • ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • faker ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ ์Šคํƒ€์ผ์˜ ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
  • ๋ณ„๋„๋กœ ์ž๋™์œผ๋กœ ๋‹ค๋ฅธ ๋‚ด์šฉ(์ด๋ฆ„ ๋“ฑ)
  • ์ƒ์„ฑ
    ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ [๋ถ€๊ฐ€]๋กœ ์„ค๋ช…ํ•œ๋‹ค

    ํ•จ์ˆ˜ ๋งŒ๋“œ๋Š” ๋ฒ•

  • pyppeteer์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ๋ชจ๋“  ์›น ์ž‘์—…์€ ๋น„๋™๊ธฐ์‹
  • ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œasync def ้–ขๆ•ฐๅ(...)
  • ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œawait ้–ขๆ•ฐๅ(...)

  • ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์–ด๋ฒ„๋ฆฌ๊ฑฐ๋‚˜async ๋˜๋Š” await ์‹คํ–‰ํ•˜๋ฉด ์š•์„ ๋จน๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ๋ธŒ๋ผ์šฐ์ € ์‹œ์ž‘

  • ๋จผ์ € ๋ธŒ๋ผ์šฐ์ € ์‹œ์ž‘
  • headless Chrome ์‹œ๋™์ด ๊ฑธ๋ฆฐ ๊ฒƒ ๊ฐ™์€๋ฐ
  • ์ฒ˜์Œ ์‹œ์ž‘ํ•  ๋•Œ ๋‹ค์šด๋กœ๋“œํ•œ
  • ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์ด ํ‘œ์‹œ๋˜์ง€ ์•Š์Œ
  • ๋ธŒ๋ผ์šฐ์ € ์ˆจ๊ธฐ๊ธฐ(headless ๋ชจ๋“œ)
    browser = await launch()
    
  • ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์ด ํ‘œ์‹œ๋œ ์ƒํƒœ์—์„œ ์ง€์ •headless=False
  • ์ฒ˜์Œ ์‚ฌ๋žŒ์—๊ฒŒ ๋™์ž‘์„ ํ™•์ธํ•˜๋ฉด์„œ ๋””๋ฒ„๊น…์„ ํ•  ๋•Œ ๋งŽ์€ ๋ฐฐ๋ ค๋ฅผ ๋ฐ›์•˜๋‹ค
  • ๋ธŒ๋ผ์šฐ์ € ํ‘œ์‹œ
    browser = await launch(headless=False)
    

    ํŽ˜์ด์ง€ ์•ก์„ธ์Šค

  • ์ฝ”๋“œ์— ์ ํžŒ ๋Œ€๋กœ
  • ์ƒˆ ํŽ˜์ด์ง€(๋ ˆ์ด๋ธ”?)URL์„ ์—ด๊ณ  ์ž…๋ ฅํ•˜๋Š” ์ž‘์—…
  • ํƒœ๊ทธ ์ž…๋ ฅ URL ์—ด๊ธฐ
    page = await browser.newPage()
    await page.goto(url)
    

    ์ž…๋ ฅ ํ…์ŠคํŠธ ์ƒ์ž

  • ใ‚ปใƒฌใ‚ฏใ‚ฟ ๋ฐ ๅ…ฅๅŠ›ใ™ใ‚‹ๅ€ค๋ฅผ ์ง€์ •ํ•˜๋ฉด OK
  • ๊ธฐ๋ณธํ˜•
    await page.type('ใ‚ปใƒฌใ‚ฏใ‚ฟ', 'ๅ…ฅๅŠ›ใ™ใ‚‹ๅ€ค')
    
  • ํŽ˜์ด์ง€ ์›๋ณธ์„ ํ™•์ธํ•จ์œผ๋กœ์จ ์–ด๋–ค ์„ ํƒ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธ
  • ์ž…๋ ฅ ์ฐฝ (ํ…์ŠคํŠธ ์ƒ์ž)
    <input type="text" name="name_last" id="name_last" value="">
    <input type="text" name="name_first" id="name_first" value="">
    <input type="text" name="kana_last" id="kana_last" value="">
    <input type="text" name="kana_first" id="kana_first" value="">
    <input type="text" name="mail" id="mail" value="">
    <input type="text" name="mail_re" id="mail_re" value="">
    
  • ์ด ๊ฒฝ์šฐ id ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•  ๊ฒƒ ๊ฐ™๋‹ค
  • input[name="name"]๋„ OK(์•„๋งˆ)
  • ์ž๋™ ์ž…๋ ฅ (ํ…์ŠคํŠธ ์ƒ์ž)
    await page.type('#name_last', '็ซˆ้–€')
    await page.type('#name_first', '็‚ญๆฒป้ƒŽ')
    await page.type('#kana_last', 'ใ‹ใพใฉ')
    await page.type('#kana_first', 'ใŸใ‚“ใ˜ใ‚ใ†')
    await page.type('#mail', '[email protected]')
    await page.type('#mail_re', '[email protected]')
    

    ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด์—์„œ ์„ ํƒ

  • ํ…์ŠคํŠธ ์ƒ์ž์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ใ‚ปใƒฌใ‚ฏใ‚ฟ ๋ฐ ้ธๆŠžใ™ใ‚‹ๅ€ค๋ฅผ ์ง€์ •ํ•˜๋ฉด OK
  • ๊ธฐ๋ณธํ˜•
    await page.select('ใ‚ปใƒฌใ‚ฏใ‚ฟ', '้ธๆŠžใ™ใ‚‹ๅ€ค')
    
  • ํŽ˜์ด์ง€ ์›๋ณธ์„ ํ™•์ธํ•จ์œผ๋กœ์จ ์–ด๋Š ๊ฒƒ์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ํŒ๋‹จใ‚ปใƒฌใ‚ฏใ‚ฟ
  • ้ธๆŠžใ™ใ‚‹ๅ€ค ํ™•์ธvalue์— ์„ค์ •๋œ ๊ฐ’
  • ์ž…๋ ฅ ํ˜•์‹(์—ฐ๋Œ€)
    <select id="age" name="age">
        <option value="" selected="selected">้ธๆŠžใ—ใฆใใ ใ•ใ„</option>
        <option value="1">10ไปฃ</option>
        <option value="2">20ไปฃ</option>
        <option value="3">30ไปฃ</option>
    </select>
    
  • ์ด ๊ฒฝ์šฐ์—๋„ ์†์„ฑid์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •
  • ์ž…๋ ฅ ํ…Œ์ŠคํŠธ(์—ฐ๋Œ€)
    await page.select('#age', '1')
    
  • ๋žœ๋ค์œผ๋กœ ์ž…๋ ฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • ์ž…๋ ฅ ํ…Œ์ŠคํŠธ(๋žœ๋ค ์ž…๋ ฅ ์—ฐ๋Œ€)
    import random
    await page.select('#age', str(random.randint(1, 3)))
    

    ํ™•์ธ๋ž€์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

  • ์„ ํƒ๊ธฐ๋งŒ ์ง€์ •ํ•˜๋ฉด OK
  • await page.click('ใ‚ปใƒฌใ‚ฏใ‚ฟ')
    
  • ํŽ˜์ด์ง€ ์›๋ณธ์„ ํ™•์ธํ•จ์œผ๋กœ์จ ์–ด๋Š ๊ฒƒ์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ํŒ๋‹จใ‚ปใƒฌใ‚ฏใ‚ฟ
  • <input type="checkbox" name="web" id="web" value="1">
    <label for="web">ใ‚ฆใ‚งใƒ–ใ‚ตใ‚คใƒˆ</label>
    <br>
    <input type="checkbox" name="tw" id="tw" value="2">
    <label for="tw">Twitter</label>
    <br>
    <input type="checkbox" name="fb" id="fb" value="3">
    <label for="fb">Facebook</label>
    <br>
    <input type="checkbox" name="mz" id="mz" value="4">
    <label for="magazine">ใƒกใƒผใƒซใƒžใ‚ฌใ‚ธใƒณ</label>
    
  • ์ด ๊ฒฝ์šฐ์—๋„ ์†์„ฑid์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •
  • ์—ฌ๋Ÿฌ ์˜ต์…˜
  • await page.click('#web')
    await page.click('#fb')
    

    ์ž…๋ ฅ ์ฐฝ ๋ณด๋‚ด๊ธฐ

  • page.click๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค
  • .
    ์ž…๋ ฅ ์ฐฝ (๋ณด๋‚ด๊ธฐ ์ฐฝ)
    <input type="submit" name="__send" id="__send" value="ๆฌกใธ โ†’">
    
  • name ์†์„ฑ ์ง€์ • ์‚ฌ์šฉ
  • ์ด๋ฒˆ์—๋Š” ๋ชฉ์ ์ง€๋ฅผ ์˜ฎ๊ธด ํŽ˜์ด์ง€์˜ ์บก์ฒ˜๋ฅผ ๊ธฐ๋ก์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์‹ถ๋‹ค
  • ํŽ˜์ด์ง€ ์ „ํ™˜ ์ข…๋ฃŒ ๋Œ€๊ธฐ page.waitForNavigation()
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋Œ€๊ธฐ
    await asyncio.wait([
        page.click('input[name="__send"]'),
        page.waitForNavigation(),
    ])
    

    ์บก์ฒ˜ ์ €์žฅ

  • ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ์บก์ฒ˜๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—ด๋ฆด ๋•Œ๋งŒ ํ‘œ์‹œ๋˜๋Š” ์„น์…˜๋งŒ ์ €์žฅ
  • ๋ชจ๋‘ ์ €์žฅ๋œ ์ƒํƒœ์—์„œ page.setViewport๋กœ ํ™”๋ฉด ํฌ๊ธฐ ์„ค์ •
  • await page.setViewport({'width': 800, 'height': 1000})
    await page.screenshot(path='ss/screenshot.png', fullPage=True)
    

    ์ž…๋ ฅ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰

  • ๋””๋ฒ„๊น… ๊ณผ์ •์—์„œ ์ƒ˜ํ”Œ ์ค‘์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Œ
  • ๊ฒฌ๋ณธ๊ณผ ๊ฐ™์ด
    if __name__ == "__main__":
        asyncio.get_event_loop().run_until_complete(main())
    
  • ์—ฌ๋Ÿฌ ๋ฒˆ ์ž…๋ ฅํ•˜๋ฉด ๋ฃจํ”„๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค
  • .
    if __name__ == "__main__":
        N = 5  ## <== ใ“ใ“ใฎๅ€คใ‚’็›ดๆŽฅๆ›ธใๆ›ใˆใฆใ€ๆง˜ๅญใ‚’ใฟใชใŒใ‚‰ใƒ†ใ‚นใƒˆ
        for i in range(N):
            print('=' * 30)
            print(f'TEST [{i+1:3d}/{N:3d}]')
            asyncio.get_event_loop().run_until_complete(main())
            time.sleep(random.randint(1, 30))  ## <== ๆ”ปๆ’ƒใ—ใฆใ‚‹ใ‚ใ‘ใ˜ใ‚ƒใชใ„ใ‚ˆใจใ„ใ†ๆฐ—ๆŒใกใฎ่กจใ‚Œ
    
    ์‹คํŒจํ•œ ์ด์•ผ๊ธฐ๋กœ์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์“ฐ๋ฉด์„œ ๋””๋ฒ„๊น…์„ ํ•  ๋•Œ ๋ฐฉ๋ฌธ ํŽ˜์ด์ง€๊ฐ€ ์ฐจ๋‹จ๋˜์—ˆ๋‹ค.ํ•  ๋•Œ๋Š” ๊ด€๊ณ„์ž์—๊ฒŒ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๊ณ  ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

    ๊ฒฝํ’ˆ: ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ

  • ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ ์ค€๋น„ ํ•„์š”
  • ์ด๋ฒˆ์— faker,pykakasi,randomํฌ์žฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์งœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค
  • ์„ค์น˜ ๋ชจ๋“ˆ
    $ pip3 install faker
    $ pip3 install pykakasi
    

    ์ˆซ์ž ์ƒ์„ฑ

  • ์ด๋ฒˆ ์ž…๋ ฅ ํ˜•์‹์€ ์—ฐ๋Œ€์˜ ์ž…๋ ฅ ๋ถ€๋ถ„
  • ์ด๋‹ค.
  • input์˜ value ๋ฒ”์œ„ ๋‚ด์—์„œ ๋ฌด์ž‘์œ„ ์ƒ์„ฑ ์ •์ˆ˜
  • ๋ฌด์ž‘์œ„๋กœ 1~5์˜ ์ •์ˆ˜์น˜ ์ƒ์„ฑ
    import random
    
    random.randint(1, 5)
    

    ์ƒ์„ฑ ์ด๋ฆ„

  • faker.Faker ์ผ๋ณธ์–ด ๋ชจ๋“œja_JP๋กœ ๊ฐ์ฒด ๋งŒ๋“ค๊ธฐ
  • ์„ฑ์”จlast_name์™€ ์ด๋ฆ„first_name
  • ์„ฑ๊ณผ ์ด๋ฆ„์„ ๋ฌด์ž‘์œ„๋กœ ์ƒ์„ฑ
    from faker import Faker
    
    fakejp = Faker('ja_JP')
    name_last = fakejp.last_name()
    name_first = fakejp.first_name()
    
    await page.type('#name_last', name_last)
    await page.type('#name_first', name_first)
    

    ์ฃผ์Œ๊ฐ€๋ช…์„ ๋งŒ๋“ค๋‹ค


  • ๋ฌธ์ œ์ 
  • faker์—์„œ ์ƒ์„ฑ๋œ ๋กœ์ผ“ํฌ๋Š” ๊ฐ€๋ช…์ด๋‹ค
  • ์—์„œ ์ƒ์„ฑ๋œ ๋ช…์นญ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ƒ์„ฑ

  • ํ•ด๊ฒฐ์ฑ…
  • ์‚ฌ์šฉpykakasi ์œ„์—์„œ ์ƒ์„ฑ๋œ ์ด๋ฆ„์„ ํ‰๊ฐ€๋ช…์œผ๋กœ ๋ณ€ํ™˜
  • ํ•œ์žโ†’๊ฐ€๋ช…์œผ๋กœ ๋ณ€ํ™˜
    import pykakasi
    
    kks = pykakasi.kakasi()
    kana_last = kks.convert(name_last)[0]['hira']
    kana_first = kks.convert(name_first)[0]['hira']
    
    await page.type('#kana_last', kana_last)
    await page.type('#kana_first', kana_first)
    

    ์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ