📄

pythonでスクレイピングしてスプレッドシートに書きだす方法

2023/12/06に公開

手順

  1. Google cloud APIsの必要なAPIを有効化する
  2. 検索するコードを書く
  3. スクレイピングするコードを書く
  4. スプレッドシートに書きだすコードを書く
  5. スプレッドシートに権限を付与する
  6. 実行する

Google cloud APIsの設定を行う

有効化するAPI

・Google Sheets API
・Custom Search API
Google cloudコンソールのAPIとサービスから、上記2つを検索し、有効化する。

検索するコードを書く

search.py
from googleapiclient.discovery import build
import sys,os
from dotenv import load_dotenv

# envファイルの読み込み
load_dotenv()

# Google Custom Search APIを使うための情報
API_KEY = os.environ.get('API_KEY')
CSE_ID = os.environ.get('CSE_ID')

def get_search_results(query, start_index):				
    # Google Custom Search API				
    service = build("customsearch",				
                    "v1",				
                    cache_discovery=False,				
                    developerKey=API_KEY)				
    # CSEの検索結果を取得				
    result = service.cse().list(q=query,				
                                cx=CSE_ID,
                                num=10,
                                start=start_index).execute()				
    # 検索結果(JSON形式)				
    return result


def get_link_list():
    # 検索結果を格納する配列
    results_arr = []
    # 10件ずつしか検索できないので、100件取得するためのインデックスの配列(1~10、11~20、、、というように10回に分けて取得する)
    # index_arr = [1,11,21,31,41,51,61,71,81,91]
    index_arr = [1]
    # 検索する文字列(このpythonファイルを実行するときに入力する引数)
    search_args = sys.argv[1]

    for index in index_arr:
        result_search = get_search_results(search_args,index)
        for item in result_search["items"]:
            if not "/rank/" in item["link"] and not "/rstLst/" in item["link"]:
                results_arr.append(item["link"])

    return results_arr
	

スクレイピングするコードを書く

scraping.py
from bs4 import BeautifulSoup
import requests
import bs4

def get_store_info(url):

    try:
        # urlにリクエストを送る
        res = requests.get(url)
        #取得したresをパース
        soup = BeautifulSoup(res.content, "html.parser")

        store_info = {}

        # 食べログのリンク
        store_info["link"] = url

        # 店舗名
        store_name = soup.find('div', class_='rstinfo-table__name-wrap').contents[1].contents[0]
        # print(store_name)
        store_info["store_name"] = store_name

        # ジャンル
        Genre = soup.find('th', string="ジャンル", class_='').next_sibling.next_sibling.contents[1].contents[0]
        # print(Genre)
        store_info["Genre"] = Genre

        # 電話番号
        tel_arr = soup.find_all('strong', class_='rstinfo-table__tel-num')
        tel = ""
        for tel_item in tel_arr:
            if tel_item.contents[0].startswith("03-"):
                tel = tel_item.contents[0]
                break
            else:
                tel = tel_item.contents[0]
        # print(tel)
        store_info["tel"] = tel

        # 住所
        address_arr = soup.find('p', class_='rstinfo-table__address').contents
        address = ""
        for address_parts in address_arr:
            if not type(address_parts) is bs4.element.NavigableString:
                for address_parts_item in address_parts.contents:
                    if not type(address_parts_item) is bs4.element.NavigableString:
                        address += address_parts_item.contents[0]
                    else:
                        address += address_parts_item
            else:
                address += address_parts
        # print(address)
        store_info["address"] = address

        return store_info
    
    except:
        return False

スクレイピングするコードに関しては、対象のサイトによって書くコードが変わるので、スクレイピングしたいサイトに応じてコードを書き換える。

スプレッドシートに書きだすコードを書く

output.py
import gspread
from gspread.exceptions import *
from oauth2client.service_account import ServiceAccountCredentials
import sys,os
from dotenv import load_dotenv

# envファイルの読み込み
load_dotenv()


def output_spreadsheet(store_info):
    
    if not store_info:
        return
    
    secret_key = os.environ.get('SECRET_KEY')
    book_name = '食べログスクレイピング'
    sheet_name = 'シート1'
    try:
        sheet = get_gspread_book(secret_key, book_name).worksheet(sheet_name)
    except SpreadsheetNotFound:
        print('Spreadsheet: ' + book_name + 'が見つかりませんでした')
        sys.exit()
    except WorksheetNotFound:
        print('Worksheet: ' + sheet_name + 'が見つかりませんでした')
        sys.exit()
    
    row = next_available_row(sheet)
    print(row)
    sheet.update_acell('A' + str(row), store_info["link"])
    sheet.update_acell('B' + str(row), store_info["store_name"])
    sheet.update_acell('C' + str(row), store_info["Genre"])
    sheet.update_acell('D' + str(row), store_info["tel"])
    sheet.update_acell('E' + str(row), store_info["address"])


def get_gspread_book(secret_key, book_name):
    scope = ['https://spreadsheets.google.com/feeds',
            'https://www.googleapis.com/auth/drive']
    credentials = ServiceAccountCredentials.from_json_keyfile_name(secret_key, scope)
    gc = gspread.authorize(credentials)
    book = gc.open(book_name)
    return book


def next_available_row(worksheet):
    str_list = list(filter(None, worksheet.col_values(1)))  # fastest
    return str(len(str_list) + 1)

スプレッドシートに権限を付与する

スプレッドシートに書きだすためには、
Google cloudのプロジェクト>APIとサービス>認証情報
から、サービスアカウントを作成し、作成したサービスアカウントからjsonのキーを発行する。
このjsonファイルの中に、client_emailがあるので、このメールアドレスに、書きだしたいスプレッドシートの共有から権限を付与する。
こうすることで、APIがスプレッドシートを見つけ出しているんだと思う。

実行する

最後に実行するためのファイルを作成する。

index.py
from search import get_link_list
from scraping import get_store_info
from output import output_spreadsheet
from time import sleep

link_list = get_link_list()

for link in link_list:
    store_info = get_store_info(link)
    output_spreadsheet(store_info)
    sleep(2)

このファイルを実行するのだが、実行時に引数を一つ渡して実行する。
この引数は、検索したい文字列になる。

terminal
python index.py "居酒屋 吉祥寺"

今回は検索エンジンを食べログに限定しているため、このように検索すると、吉祥寺の居酒屋の食べログのサイトが検索できる。

Discussion