Python Google検索の順位探索(PC版、スマホ版)

# Python Google検索の順位探索(PC版、スマホ版)

Google検索の順位はWebページがユーザーの目に触れる機会の多さと密接な関係にあり、SEO(検索エンジン最適化 : Search Engine Optimization)などにより上位にあげることができる。今回は対象のWebページがPC/スマホでのGoogle検索で、特定の検索ワードの何番目に出てくるのかを調査するスクレイピングプログラムを紹介する。

## Google検索の順位調査(PC版)
検索ワード「シュレディンガー　猫」で検索し、上位50件を取得するプログラムの例です。
検索結果はURLとタイトルがcsvに吐き出される。

```python:google_serarch.py
import time
import datetime
import numpy as np 
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

dt_now = datetime.datetime.now()

#googleで検索する文字
word = 'シュレディンガー　猫'

#Seleniumを使うための設定とgoogleの画面への遷移
INTERVAL = 2.5
URL = "https://www.google.com/"

options = Options()
options.add_argument('--incognito')
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
time.sleep(INTERVAL)
driver.get(URL)
driver.maximize_window()
time.sleep(INTERVAL)

#文字を入力して検索
driver.find_element(By.NAME, 'q').send_keys(word)
driver.find_element(By.NAME, 'q').send_keys(Keys.RETURN)
time.sleep(INTERVAL)

datray1 = np.array([])
datray1  = np.append(datray1 , "日時,対象サイト,対象記事のURL,検索キーワード,検索順位")

#検索結果の一覧を取得する
results = []
flag = False
while True:
    g_ary = driver.find_elements(By.CLASS_NAME,'g')
    for g in g_ary:
        result = {}
        try:
            url = g.find_element(By.CLASS_NAME,'yuRUbf').find_element(By.TAG_NAME,'a').get_attribute('href')
            title = g.find_element(By.TAG_NAME,'h3').text
        except:
            continue
        datray1  = np.append(datray1 , '%s,%s,%s,%s,%s'%(dt_now, title.replace(',', ''), url, word, len(datray1)))
        if len(datray1) >= 50: #抽出する件数を指定
            flag = True
            break
    if flag:
        break
    try:
        driver.find_element(By.ID,'pnnext').click()
    except:
        break
    time.sleep(INTERVAL)

DAT =  np.column_stack(datray1)
outname = './sample.csv'
np.savetxt(outname, DAT , delimiter=",\n", fmt="%s", encoding='utf8')

driver.close()

```


## Google検索の順位調査(スマホ版)

スマホ版はPC上でChromeのデベロッパーツールから対象のスマホ機器での検索結果を調べる。今回はiphone 12 Proでの検索結果を調べた。PC版とは異なり、googleのインターフェースがスマホ仕様のため、検索の仕方も工夫が必要である。

下記、google_serarch.pyを実行すると、複数の検索ワードで検索し、上位20件を取得するプログラムの例です。検索結果はURLとタイトルがcsvに吐き出される。

```python:google_serarch.py
import time
import datetime
import numpy as np 
import re
import random
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.alert import Alert
import warnings
warnings.simplefilter('ignore')

now = datetime.datetime.now()
dt_now = now.strftime('%Y/%m/%d %H:%M:%S')

#googleで検索する文字
wordlist = ['おはよう','12345','abcde','あいう　えお','123 45','abc de','山田','山田　太郎','あいうえお abc 山田','あ']

#Seleniumを使うための設定とgoogleの画面への遷移
INTERVAL = 1.5
URL = "https://www.google.com/"

options = Options()
options.add_argument('--incognito')
options.add_argument("--disable-notifications")
options.add_argument('--disable-desktop-notifications')
options.add_argument("--disable-extensions")
options.add_argument('--ignore-certificate-errors')
options.add_argument("--disable-infobars")
prefs = {"profile.default_content_setting_values.notifications" : 2}
options.add_experimental_option("prefs",prefs)
############ Bacground 実行 ############
#options.add_argument('--headless') 
########################################
############ 上位何件 ############
rank = 20 
##################################
options.add_experimental_option("mobileEmulation", { "deviceName": "iPhone 12 Pro" })
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
time.sleep(INTERVAL*random.uniform(1, 1.5))
driver.get(URL)
time.sleep(INTERVAL*random.uniform(1, 1.5))
try:
    Alert(driver).accept()
except:
    pass
try:
    alert = driver.switch_to.alert
    alert.accept()
except:
    pass
driver.maximize_window()
time.sleep(INTERVAL*random.uniform(1, 1.5))
for i, word in enumerate(wordlist):
    #文字を入力して検索
    driver.find_element(By.NAME, 'q').send_keys(word)
    time.sleep(INTERVAL*random.uniform(1, 1.5))
    driver.find_element(By.NAME, 'q').send_keys(Keys.RETURN)
    time.sleep(INTERVAL*random.uniform(1, 1.5))
    try:
        Alert(driver).accept()
    except:
        pass
    try:
        alert = driver.switch_to.alert
        alert.accept()
    except:
        pass

    #検索結果の一覧を取得する
    flag = False
    datray1 = np.array([])
    datray1  = np.append(datray1 , "%s,対象サイト,対象記事のURL,検索キーワード,検索順位"%dt_now)
    time.sleep(INTERVAL*random.uniform(1, 1.5))
    for g in range(1, 20):
        print(datray1)
        adddiv = '/'
        for j in range(10):
            try: 
                title = driver.find_element(By.XPATH, '//*[@id="rso"]/div[%s]/div/div/div/div/div[1]/div/a%sdiv[2]/div'%(g, adddiv)).text
                url = driver.find_element(By.XPATH,'//*[@id="rso"]/div[%s]/div/div/div/div/div[1]/div/a'%g).get_attribute("href")
                datray1  = np.append(datray1 , ',%s,%s,%s,%s'%(title.replace(',', ''), url, word, len(datray1)))
            except:
                try:
                    title = driver.find_element(By.XPATH,'//*[@id="kp-wp-tab-overview"]/div[%s]/div/div/div/div/div/div/div/div/div/div[1]/div/a%sdiv[2]/div'%(g, adddiv)).text
                    url = driver.find_element(By.XPATH,'//*[@id="kp-wp-tab-overview"]/div[%s]/div/div/div/div/div/div/div/div/div/div[1]/div/a'%g).get_attribute("href")
                    datray1  = np.append(datray1 , ',%s,%s,%s,%s'%(title.replace(',', ''), url, word, len(datray1)))
                except:        
                    for k in range(5):
                        try:
                            title = driver.find_element(By.XPATH,'//*[@id="rso"]/div[%s]/div/div/div/div[%s]/div/div/div/div/div[1]/div/a%sdiv[2]/div'%(g, k, adddiv)).text
                            url = driver.find_element(By.XPATH,'//*[@id="rso"]/div[%s]/div/div/div/div[%s]/div/div/div/div/div[1]/div/a'%(g, k)).get_attribute("href")
                            datray1  = np.append(datray1 , ',%s,%s,%s,%s'%(title.replace(',', ''), url, word, len(datray1)))
                        except:
                            pass    
            adddiv = adddiv + 'div/'

    num = 0 
    while num < 20:
        driver.find_element(By.TAG_NAME, 'body').send_keys(Keys.END)
        time.sleep(3*random.uniform(1, 1.5))
        try:
            driver.find_element(By.XPATH, '//*[@id="botstuff"]/div/div[2]/div[4]/a[1]/h3/div/div').click()
        except:
            try:
                driver.find_element(By.XPATH, '//*[@id="kp-wp-tab-cont-overview"]/div/div[2]/div/div/div[3]/a[1]/h3/div/div').click()
            except:
                try:
                    driver.find_element(By.XPATH, '//*[@id="ofr"]/i/a').click()
                    num = 0
                except:
                    pass
        num = num + 1

    for k in range(1, int(rank/5) + 1):
        if len(datray1) > rank:
            continue
        for m in range(1, 19):
            adddiv = '/'
            for j in range(1, 9):
                try:
                    title = driver.find_element(By.XPATH,'//*[@id="arc-srp_1%s0"]/div/div[%s]/div/div/div/div/div[1]/div/a%sdiv[2]/div'%(k, m, adddiv)).text
                    url = driver.find_element(By.XPATH,'//*[@id="arc-srp_1%s0"]/div/div[%s]/div/div/div/div/div[1]/div/a'%(k, m)).get_attribute("href")
                    l_re_match = [s for s in datray1 if re.match('.*%s.*'%url, s)]
                    if l_re_match != []:
                        continue
                    else:                    
                        datray1  = np.append(datray1 , ',%s,%s,%s,%s'%(title.replace(',', ''), url, word, len(datray1)))
                except:
                    for n in range(1, 5):
                        try:
                            title = driver.find_element(By.XPATH,'//*[@id="arc-srp_1%s0"]/div/div[%s]/div/div/div/div[%s]/div/div/div/div/div[1]/div/a%sdiv[2]/div'%(k, m, n, adddiv)).text
                            url = driver.find_element(By.XPATH,'//*[@id="arc-srp_1%s0"]/div/div[%s]/div/div/div/div[%s]/div/div/div/div/div[1]/div/a'%(k, m, n)).get_attribute("href")
                            l_re_match = [s for s in datray1 if re.match('.*%s.*'%url, s)]
                            if l_re_match != []:
                                continue
                            else:                             
                                datray1  = np.append(datray1 , ',%s,%s,%s,%s'%(title.replace(',', ''), url, word, len(datray1)))
                        except:                
                            pass
                adddiv = adddiv + 'div/'
            if len(datray1) > rank:
                break
    DAT =  np.column_stack(datray1)
    outname = './search_result%s.csv'%(i + 1)
    np.savetxt(outname, DAT , delimiter=",\n", fmt="%s", encoding='utf8')
    time.sleep(INTERVAL*random.uniform(1, 1.5))
    driver.get(URL)
    time.sleep(INTERVAL*random.uniform(1, 1.5))

driver.close()
```

## Google reCAPTCHA

「ロボットではありませんか？」と人であることを確認されたことはないだろうか。今回、何度がスクレイピングをしていると、自身のIP adressでアクセスする際に、このようなメッセージが表示されるようになった。対策としては、自動化で突破するか、ロボットと認識されないような動きにする、主にこの二つの方法がある。

![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/6ae97583-b97a-2189-0fd6-9ff8ce872abc.png)

一つ目の自動化は下記サイトのような有料サービスを利用して、世界中のだれかに突破してもらう方法。下記サイトは、1000件当たり￥143のようだ。

https://2captcha.com/ja

二つ目の方法として、ロボットとして認識されないようにするには、

 - 速度を落とす
 - IPアドレスを変える
 - スクレイピングパターンを変則的にする

などの方法があり(下記サイト参照)、上記プログラムは、速度を落として、ブラウザを動かす時間に乱数を振って変則的にした。

https://www.octoparse.jp/blog/scrape-websites-without-being-blocked/

## まとめ
今回はGoogle検索における検索結果の順位を調査するスクレイピングプログラムを紹介した。
Googleのタグの変更があるのか不明なため、xpathの変更をする必要があるかもしれない。
その点を今後の課題とする。