見出し画像

【Python(2)】複数のGoogleキーワード検索ボリュームを自動取得しよう。

こんにちは(@t_kun_kamakiri)

前回の記事では、Googleキーワード検索ボリュームの自動取得の手順について解説を行いました。

今回は前回のコードのアップデート版です。前回紹介したコードは「ひとつのキーワード」に対して検索ボリュームを調査するという内容でしたが、今回のコードはテキストファイルに書き込んだ複数のキーワードの検索ボリュームを順に調査するというブロガーがほしがるプログラムとなっております。

前回の記事でGoogleAdsAPIの取得やアクセスキーの取得が済んでいることが前提となります。

ファイル構成
generate_keyword.py
geotargets-2020-11-18.csv
main.py
post.py
検索キーワードリスト.txt

使用しているPythonとライブラリのバージョンも記載しておきます。

Python 3.8.5
google-ads==8.2.0
numpy==1.19.5
pandas==1.1.5

1.「検索キーワードリスト.txt」に調査したい単語を並べる

検索キーワードを手テキストファイルに書きます。

今回調査するキーワードは「数学、熱力学、力学」とします。

検索キーワードリスト.txt

画像1

キーワードボリュームとデータフレームを作成するモジュールを用意します。

generate_keyword.py

#!/usr/bin/env python
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This example generates keyword ideas from a list of seed keywords."""

import sys
from google.ads.google_ads.client import GoogleAdsClient
from google.ads.google_ads.errors import GoogleAdsException
import os # add
import pandas as pd #add


class KeywordVolume:
   def __init__(self,
               client, customer_id, location_ids, 
               language_id, keyword_texts, page_url):
       print(keyword_texts)
       self.client = client
       self.customer_id = customer_id
       self.location_ids = location_ids
       self.language_id = language_id
       self.keyword_texts = keyword_texts
       self.page_url = page_url

   # [START generate_keyword_ideas]
   def generate_keyword(self):
       keyword_plan_idea_service = self.client.get_service(
           "KeywordPlanIdeaService", version="v6"
       )
       keyword_competition_level_enum = self.client.get_type(
           "KeywordPlanCompetitionLevelEnum", version="v6"
       ).KeywordPlanCompetitionLevel
       keyword_plan_network = self.client.get_type(
           "KeywordPlanNetworkEnum", version="v6"
       ).GOOGLE_SEARCH_AND_PARTNERS

       gtc_service = self.client.get_service("GeoTargetConstantService", version="v6")
       locations = [
           gtc_service.geo_target_constant_path(location_id)
           for location_id in self.location_ids
       ]
       language = self.client.get_service(
           "LanguageConstantService", version="v6"
       ).language_constant_path(self.language_id)

       # Only one of these values will be passed to the KeywordPlanIdeaService
       # depending on whether keywords, a page_url or both were given.
       url_seed = None
       keyword_seed = None
       keyword_url_seed = None

       # Either keywords or a page_url are required to generate keyword ideas
       # so this raises an error if neither are provided.
       if not (self.keyword_texts or self.page_url):
           raise ValueError(
               "At least one of keywords or page URL is required, "
               "but neither was specified."
           )

       # To generate keyword ideas with only a page_url and no keywords we need
       # to initialize a UrlSeed object with the page_url as the "url" field.
       if not self.keyword_texts and self.page_url:
           url_seed = client.get_type("UrlSeed", version="v6")
           url_seed.url = self.page_url

       # To generate keyword ideas with only a list of keywords and no page_url
       # we need to initialize a KeywordSeed object and set the "keywords" field
       # to be a list of strings.
       if self.keyword_texts and not self.page_url:
           keyword_seed = self.client.get_type("KeywordSeed", version="v6")
           keyword_seed.keywords.extend(self.keyword_texts)

       # To generate keyword ideas using both a list of keywords and a page_url we
       # need to initialize a KeywordAndUrlSeed object, setting both the "url" and
       # "keywords" fields.
       if self.keyword_texts and self.page_url:
           keyword_url_seed = client.get_type("KeywordAndUrlSeed", version="v6")
           keyword_url_seed.url = self.page_url
           keyword_url_seed.keywords.extend(self.keyword_texts)

       try:
           keyword_ideas = keyword_plan_idea_service.generate_keyword_ideas(
               self.customer_id,
               locations,
               False,
               keyword_plan_network,
               language=language,
               url_seed=url_seed,
               keyword_seed=keyword_seed,
               keyword_and_url_seed=keyword_url_seed,
           )

           # 空リストを用意
           keyword = []
           keyword_ave = []
           keyword_competition_value = []
           for idea in keyword_ideas:
               competition_value = keyword_competition_level_enum.Name(
                   idea.keyword_idea_metrics.competition
               )

               keyword.append(idea.text)
               keyword_ave.append(idea.keyword_idea_metrics.avg_monthly_searches)
               keyword_competition_value.append(competition_value)

               # print(
               #     f'Keyword idea text "{idea.text}" has '
               #     f'"{idea.keyword_idea_metrics.avg_monthly_searches}" '
               #     f'average monthly searches and "{competition_value}" '
               #     "competition.\n"
               # )
       except GoogleAdsException as ex:
           print(
               f'Request with ID "{ex.request_id}" failed with status '
               f'"{ex.error.code().name}" and includes the following errors:'
           )
           for error in ex.failure.errors:
               print(f'\tError with message "{error.message}".')
               if error.location:
                   for field_path_element in error.location.field_path_elements:
                       print(f"\t\tOn field: {field_path_element.field_name}")
           sys.exit(1)
           # [END generate_keyword_ideas]

       # 辞書型に変換
       keyword_dict = {
           'keyword': keyword,
           'volume' :keyword_ave,
           'keyword_competition' :keyword_competition_value
           }

       # DataFrameに変換する
       df_all = self.make_df(keyword_dict)
       # print(df_all) # ファイルに出力

       return df_all


   def make_df(self,keyword_dict):
       df = pd.DataFrame(keyword_dict)
       df = df.sort_values(by='volume', ascending=False).reset_index(drop=True)
       df_hight = df[df['keyword_competition'] == 'HIGH']
       df_midium = df[df['keyword_competition'] == 'MEDIUM']
       df_low = df[df['keyword_competition'] == 'LOW']
       
       df_all = pd.concat([df_low,df_midium]).reset_index(drop=True)
       return df_all

こちらのファイルをモジュールとしてメインファイルから呼び出すようにします。

main.py

from generate_keyword import KeywordVolume
from google.ads.google_ads.client import GoogleAdsClient
from google.ads.google_ads.errors import GoogleAdsException
import argparse
import pandas as pd
import os
from time import sleep

# データフレームごとのキーワードの存在判定、キーワードがない場合はvolume = 0
def search_key_volume(df, search_key):
   dfx = df[df['keyword'].isin([search_key])]

   if len(dfx):
       index = dfx.index[0]
   else:
       index = 0
       dfx.at[index, 'volume'] = 0
   return index, dfx 

def keyword_volueme_JP(search_keyword):
     # customer_id
   customer_id = "53483*****" #自身のキーワードプランナーのid(テストアカウントを使用すること)
   # A language criterion ID. For example, specify 1000 for English. For more
   # information on determining this value, see the below link:
   # https://developers.google.com/adwords/api/docs/appendix/codes-formats#languages.
   _DEFAULT_LANGUAGE_ID = "1005"  # language ID for English

   df_ParentIDs = pd.read_csv('geotargets-2020-11-18.csv', encoding="cp932")
   df_JP = df_ParentIDs[df_ParentIDs['Country Code']=='JP']
   ParentIDs = df_JP['Parent ID'].unique().astype(int)
   ParentIDs_list = ParentIDs[:-1]

   ParentIDs_lists = []
   for i in range(round(len(ParentIDs_list)/10)):
       ParentIDs_lists.append(ParentIDs_list[10*i:10*(i+1)])

   df_volume = pd.DataFrame()
   for i in range(len(ParentIDs_lists)):
       ParentIDs = ParentIDs_lists[i]
       # Location IDs are listed here: https://developers.google.com/adwords/api/docs/appendix/geotargeting
       # and they can also be retrieved using the GeoTargetConstantService as shown
       # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
       _DEFAULT_LOCATION_IDS = ParentIDs  # location ID for New York, NY


       # GoogleAdsClient will read the google-ads.yaml configuration file in the
       # home directory if none is specified.
       google_ads_client = GoogleAdsClient.load_from_storage()

       parser = argparse.ArgumentParser(
           description="Generates keyword ideas from a list of seed keywords."
       )

       # The following argument(s) should be provided to run the example.
       # parser.add_argument(
       #     "-c",
       #     "--customer_id",
       #     type=str,
       #     required=True,
       #     help="The Google Ads customer ID.",
       # )
       parser.add_argument(
           "-k",
           "--keyword_texts",
           nargs="+",
           type=str,
           required=False,
           default=[],
           help="Space-delimited list of starter keywords",
       )
       
       # To determine the appropriate location IDs, see:
       # https://developers.google.com/adwords/api/docs/appendix/geotargeting.
       parser.add_argument(
           "-l",
           "--location_ids",
           nargs="+",
           type=str,
           required=False,
           default=_DEFAULT_LOCATION_IDS,
           help="Space-delimited list of location criteria IDs",
       )
       # To determine the appropriate language ID, see:
       # https://developers.google.com/adwords/api/docs/appendix/codes-formats#languages.
       parser.add_argument(
           "-i",
           "--language_id",
           type=str,
           required=False,
           default=_DEFAULT_LANGUAGE_ID,
           help="The language criterion ID.",
       )
       # Optional: Specify a URL string related to your business to generate ideas.
       parser.add_argument(
           "-p",
           "--page_url",
           type=str,
           required=False,
           help="A URL string related to your business",
       )

       args = parser.parse_args()

       keyword_texts = search_keyword

       # KeywordVolumeインスタンス化
       keyword = KeywordVolume(
           google_ads_client,
           customer_id,
           args.location_ids,
           args.language_id,
           keyword_texts,
           args.page_url,
       )

       # キーワード検索ボリュームのデータフレーム
       df_all = keyword.generate_keyword() 
       df_all = df_all[:100]
   
       for index in range(df_all.shape[0]):
           search_key = df_all['keyword'][index]
           # データフレームごとのキーワードの存在判定、キーワードがない場合はvolume = 0
           index_, dfx_ = search_key_volume(df_all, search_key)

           # df0にvolumeの合計数値を計算
           df_all['volume'][index] = df_all['volume'][index] + dfx_.at[index_, 'volume']
       
       sleep(1)

   # csvファイルの保存先
   path = os.getcwd()
   csv_dir = rf'{path}\volume_csv'
   if not os.path.isdir(csv_dir):
       os.mkdir(csv_dir)
   
   # csvファイルの保存
   df_all.to_csv(rf'{csv_dir}\search_{keyword_texts}.csv',columns=['keyword'],header=False, index=False)
   df_all.to_csv(rf'{csv_dir}\search_{keyword_texts}_all.csv',header=False, index=False)

if __name__ == '__main__':
   # 検索キーワードリストの読み込み
   with open('検索キーワードリスト.txt', encoding='utf-8') as f:
       keyword_list = [s.rstrip() for s in f.readlines()]
   print(keyword_list)
   for search_keyword in keyword_list:
       keyword_volueme_JP([search_keyword]) #キーワードはリストにする
       sleep(1)

※最大10件の地域の検索キーワードの合計値が取得できるが、日本の全体の地域は48件になるため「for i in range(len(ParentIDs_lists)):」で5回ループを回しています。
※5回に分けて取得した検索ボリュームの合計値を「for index in range(df_all.shape[0]):」のループで足し合わせています。
※面倒なのが5回に分けて取得した検索ボリュームの順番がバラバラなので各キーワードに対応したボリュームを足すというプログラムを「def search_key_volume(df, search_key):」を使って判定しています。

メインファイルがこんなに長くなってしまった('ω')
ちゃんと構成考えなきゃ_(._.)_

2.コマンドプロンプトで`python main.py`と打ち計算実行

これで準備ができたのでコマンドプロンプトでファイルを実行してみましょう。

python main.py

画像2

こんな感じで計算が進みます。

3.「volume_csv」フォルダが作成され単語ごとにcsvファイルが出力。

計算が終わると「volume_csv」フォルダにcsvファイルが出力されます。

画像3

_all.csvというファイルが検索ボリュームの結果が書かれたファイルです。
※csvファイルをExcelで開くと文字コードの関係でエラーが出るのでテキストファイルで開いてください。

例)search_['力学']_all.csv

画像4

例)search_['力学'].csv
こちらは単語だけを抜き取ったファイルです。
※スクレイピングに使用するファイルです(別途紹介)。

画像5

オプションとして検索キーワードボリューム上位に並び変えるプログラムも作成しました。

4.コマンドプロンプトで`python post.py`と打ち計算実行

ここまでで各キーワードに対してバラバラでキーワード検索ボリュームを出すことができました。

画像6

できればどのキーワードが検索ボリュームとして上位に来ているのかが知りたいですよね。

なので各csvファイルをひとつにまとめてソートするプログラム作成しました。

post.py

import pandas as pd
import glob
import os

csv_list = glob.glob(r'volume_csv\*_all.csv')

df = pd.DataFrame()

for csv_file in csv_list:
   df_ = pd.read_csv(csv_file, names=["keyword", "volume", "keyword_competition"])
   df = pd.concat([df,df_], axis=0)

df_sort = df.sort_values('volume', ascending=False)

# csvファイルの保存先
path = os.getcwd()
csv_dir = rf'{path}\volume_csv'
df_sort.to_csv(rf'{csv_dir}\search_sort.csv',header=False, index=False)

こちらを実行します。

python post.py

「volume_csv」にsearch_sort.csvが出力されます。

画像7


5.「volume_csv」にsearch_sort.csvが出力される。テキストで閲覧。

では今回調査した「数学、熱力学、力学」に関する検索ボリュームボリュームの全体順位を見てみましょう。

画像8

このようになりました(^^)/

こーしさんにも試しに使ってもらいました。

前回の記事を読んで自身のキーワードプランナーのidとアクセスキーでお試しください。

Pythonはこちらの参考書を辞書代わりに使っています。

Twitter➡@t_kun_kamakiri
Instagram➡kamakiri1225
ブログ➡宇宙に入ったカマキリ(物理ブログ)
ココナラ➡物理の質問サポートサービス
コミュニティ➡製造業ブロガー

この記事が気に入ったらサポートをしてみませんか?