山傘のプログラミング勉強日記

プログラミングに関する日記とどうでもよい雑記からなるブログです。

Pythonクローリング&スクレイピング[増補改訂版] ―データ収集・解析のための実践開発ガイドー その14

第5章 クローリング・スクレイピングの実践とデータの活用

今回は、Web ページを自動的にクローリングします。

MechanicalSoup

MechanicalSoup を使って Google 検索をします。

import mechanicalsoup

browser = mechanicalsoup.StatefulBrowser()
browser.open("https://www.google.com/")

browser.select_form('form[action="/search"]')

browser['q'] = 'Python'
browser.submit_selected(btnName="btnG")

page = browser.get_current_page()

for a in page.select('a'):
    print(a.text)
    print(browser.absolute_url(a.get('href')))

テキストに書いてあるものだと動かなかったので、CSS セレクタを "a" にしてリンクを全て出力するようにしています。HTML が複雑すぎてどのようにして切り出せばいいのか分かりませんでした。

Selenium

ブラウザをヘッドレスモードを有効にして、検索結果を表示します。

from selenium.webdriver import Chrome, ChromeOptions, Remote
from selenium.webdriver.common.keys import Keys

options = ChromeOptions()
options.headless = True
#driver = Remote('http://10.0.2.2:4444', options=options)
driver = Chrome(options=options)
driver.get('https://www.google.co.jp/')

assert 'Google' in driver.title

input_element = driver.find_element_by_name('q')
input_element.send_keys('Python')
input_element.send_keys(Keys.RETURN)

assert 'Python' in driver.title

# スクリーンショット
driver.save_screenshot('img/search_result.png')

for h3 in driver.find_elements_by_css_selector('a > h3'):
  a = h3.find_element_by_xpath('..')
  print(h3.text)
  print(a.get_attribute('href'))

driver.quit()

Pyppeteer

Chrome を自動操作するために Pyppteer を使ってクローリングを行います。

import asyncio
from pyppeteer import launch


async def main():
    browser = await launch()
    # browser = await launch(headless=False)
    
    page = await browser.newPage()
    await page.goto('https://www.google.co.jp/')

    assert 'Google' in (await page.title())

    input_element = await page.querySelector('[name="q"]')
    await input_element.type('Python')

    await asyncio.wait([
        input_element.press('Enter'),
        page.waitForNavigation(),
    ])

    assert 'Python in (await page.title())'
    
    # スクショ
    await page.screenshot({'path': 'img/search_results1.png'})

    for h3 in await page.querySelectorAll('a > h3'):
        text = await page.evaluate('(e) => e.textContent', h3)
        print(text)
        a = await page.evaluateHandle('(e) => e.parentElement', h3)
        url = await page.evaluate('(e) => e.href', a)
        print(url)
    
    await browser.close()

if __name__ == '__main__':
    asyncio.run(main())

感想

Googleの検索結果に対してクローリングを行いました。Chrome を経由することで、実際のユーザの感覚でクローリングを行えるようになるなかな?と思いました。