파이썬과 셀레늄으로 웹 스크래핑 연습

소개



이번에는 파이썬과 현재 공부중인 셀레늄을 사용하여 웹 스크래핑을하고 싶습니다.
타겟 사이트는 Google에서, 지정한 키워드로 검색을 실시해, 지정한 수의 항목을 취득해, 「타이틀, URL, 개요」의 3항목을 데이타베이스에 내보낸다, 라고 하는 것입니다.

다음과 같은 항목을 가져옵니다.

최종 목표는 SQLite 데이터베이스에 정보를 씁니다.


개발 환경



Python 3.7.1을 사용합니다.
개발 환경은 Visual Studio 2019입니다.
Firefox용 드라이버는 geckodriver에서 다운로드했습니다.

코드



소스 코드는 다음과 같습니다.
덧붙여 아래의 코드는 「2020년 4월 23일」의 시점에서 동작하는 것입니다만, 향후의 사이트의 사양 변경등에 의해, 동작하지 않게 될 가능성도 있으므로 양해 바랍니다.

google.py
import urllib.parse
import records
from selenium.webdriver import Firefox, FirefoxOptions
from sqlalchemy.exc import IntegrityError

# 以下のキーワードを使って検索する
keywd = ['Python','機械学習']

# 取得したデータを保存するファイルの名前
db = records.Database('sqlite:///google_search.db')

db.query('''CREATE TABLE IF NOT EXISTS items (
            url text PRIMARY KEY,
            title text,
            summary text NULL)''')

def store_data(url, title, summary):
    try:
        db.query('''INSERT INTO items (url, title, summary)
                    VALUES (:url, :title, :summary)''', 
                    url=url, title=title, summary=summary)
    except IntegrityError:
        # この項目はすでに存在するのでスキップ
        print("it's already exist.")
        return False

    return True

def visit_next_page(driver, url):
    driver.get(url)

    items = driver.find_elements_by_css_selector('#rso > div.g')

    for item in items:
        tag = item.find_element_by_css_selector('div.r > a')
        link = tag.get_attribute('href')
        title = tag.find_element_by_tag_name('h3').text.strip()

        summary = item.find_element_by_css_selector('div.s span.st').text.strip()

        if store_data(link, title, summary):
            print(title, link, sep='\n', end='\n\n')

def main():
    # ターゲットサイトと検索個数(search_unit * search_loop)
    base_url = "https://www.google.co.jp/"
    search_unit = 20 # 1ページの表示件数(100以上を指定しても無理っぽい)
    search_loop = 5
    start = 0

    # キーワードを1つの文字列に結合する
    target = ' '.join(keywd)

    # URLエンコード(デフォルトのエンコードは"utf-8")
    target = urllib.parse.quote(target)

    opt = FirefoxOptions()

    # 自分でブラウザの動作を観察したければ、コメントにしてください
    opt.add_argument('-headless')
    driver = Firefox(options=opt)

    # 待機時間を設定する
    driver.implicitly_wait(10)

    # 1ページ分づつ読み込んでいく
    for i in range(search_loop):
        url = "{0}search?num={1}&start={2}&q={3}".format(base_url, search_unit, start, target)
        start += search_unit

        print("\npage count: {0}...".format(i + 1), end='\n\n')
        visit_next_page(driver, url)

    driver.quit()

if __name__ == '__main__':
    main()

해설



소스에 코멘트를 넣고 있기 때문에 대체로 알고 있다고 생각합니다만, 스크래핑의 중심적인 부분을 해설합니다.

1건분의 데이터가 들어 있는 부분은 다음과 같습니다.

이것을 이하와 같은 코드로 취득합니다.
items = driver.find_elements_by_css_selector('#rso > div.g')

제목과 링크 URL은 다음과 같습니다.

개요 부분은 다음과 같습니다.


이것을 이하와 같은 코드로 취득합니다.
tag = item.find_element_by_css_selector('div.r > a')
link = tag.get_attribute('href')
title = tag.find_element_by_tag_name('h3').text.strip()

summary = item.find_element_by_css_selector('div.s span.st').text.strip()

사용법



실제로 사용하는 경우 소스 코드의 시작 부분에서 검색하려는 키워드와 저장할 파일 이름을 지정합니다.
# 以下のキーワードを使って検索する
keywd = ['Python', '機械学習']

# 取得したデータを保存するファイルの名前
db = records.Database('sqlite:///google_search.db')

또한 동일한 프로그램을 여러 번 실행해도 동일한 URL은 등록하지 않고 건너뜁니다.
def store_data(url, title, summary):
    try:
        db.query('''INSERT INTO items (url, title, summary)
                    VALUES (:url, :title, :summary)''', 
                    url=url, title=title, summary=summary)
    except IntegrityError:
        # この項目はすでに存在するのでスキップ
        print("it's already exist.")
        return False

    return True

보충



처음에는 1회의 페이지 표시로 정리해 1000건분 정도 취득하려고 생각했습니다만, 아무래도 Google의 사양으로 1페이지당 100건 정도가 한도인 것 같았습니다.
그래서 search_unitsearch_loop 의 2 개의 변수를 사용해 다음 페이지로 전환하면서 스크레이핑 하고 있습니다.

또한 왜 BeautifulSoup을 사용하지 않습니까?
라는 목소리가 들릴 것 같습니다만, Selenium을 사용한 연습을 하고 싶었던 것과, 최근에는 JavaScript를 사용한 사이트가 많기 때문에 Selenium을 사용할 기회가 늘어날 것 같기 때문에, 이번은 이런 방법으로 스크래핑 시도했습니다.

끝에



이번에 소개한 소스 코드는 자유롭게 사용해도 상관 없습니다만, 그 때에는 자기 책임으로 부탁합니다.

참고 기사, 참고 서적



Python 스크래핑의 기본과 실천 / Seppe vanden Broucke 외

좋은 웹페이지 즐겨찾기