質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

PythonのスクレイピングでSSLエラー

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,072
退会済みユーザー

退会済みユーザー

起こっていること

Pythonのrequestsを使ってグーグルの検索結果のタイトルやHタグを抜き出すスクレイピングを試みています。ソースコードは以下です。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import requests
from bs4 import BeautifulSoup
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import time


URL = 'https://www.google.co.jp'
URL_TITLE = 'Google'

# 2つのAPIを記述しないとリフレッシュトークンを3600秒毎に発行し続けなければならない
scope = [
    'https://spreadsheets.google.com/feeds',
    'https://www.googleapis.com/auth/drive'
]

# 認証情報設定
# ダウンロードしたjsonファイル名をクレデンシャル変数に設定(秘密鍵、Pythonファイルから読み込みしやすい位置に置く)
credentials = ServiceAccountCredentials.from_json_keyfile_name(
    'google-multi-search-〇〇〇〇〇.json', scope)

# 共有設定したスプレッドシートキーを格納
SPREADSHEET_KEY = '〇〇〇〇〇〇'


def main():
    '''
    メインの処理
    Googleでキーワードを検索
    1ページ目の情報を取得し、Googleスプレッドシートに出力
    '''

    with open('keyword.txt', encoding='UTF-8') as f:
        keywords = [s.rstrip() for s in f.readlines()] # 検索キーワードが入力されたテキストファイルを読み込む

    options = Options()
    options.add_argument('--headless') # ヘッドレスモードを有効にする
    driver = webdriver.Chrome(options=options) # ChromeのWebDriverオブジェクトを作成
    driver.get(URL) # Googleのトップページを開く
    time.sleep(2) # 2秒待機
    assert URL_TITLE in driver.title # タイトルに'Google'が含まれていることを確認

    for keyword in keywords:
        print('検索キーワード:' + keyword)

        # Google検索処理
        search(driver, keyword)

        # 情報取得処理
        items = get_info(driver, keyword)

        # Googleスプレッドシート出力処理
        count = 0
        while True:
            if count == 3:
                break
            else:
                count = googlespreadsheets(items, keyword, count)

    driver.quit() # ブラウザーを閉じる


def search(driver, keyword):
    '''
    検索テキストボックスに検索キーワードを入力し、検索する
    '''

    input_element = driver.find_element_by_name('q') # 検索テキストボックスの要素をname属性から取得
    input_element.clear() # 検索テキストボックスに入力されている文字列を消去
    input_element.send_keys(keyword) # 検索テキストボックスにキーワードを入力
    input_element.send_keys(Keys.RETURN) # Enterキーを送信
    time.sleep(2) # 2秒待機


def get_info(driver, keyword):
    '''
    情報を取得
    '''

    items_num = 0
    items = {
        'keyword': keyword,
        'title': [],
        'url': [],
        'description': [],
        'h1': [],
        'h2': [],
        'h3': [],
        'h4': [],
        'h5': [],
        'h6': []
    }

    # url
    urls = driver.find_elements_by_css_selector('.r > a')
    if urls:
        for url in urls:
            if 'translate' not in url.get_attribute('href'):
                items['url'].append(url.get_attribute('href').strip())

    # title
    titles = driver.find_elements_by_css_selector('.r > a > .LC20lb')
    if titles:
        for title in titles:
            items['title'].append(title.text.strip())

    # description
    descriptions = driver.find_elements_by_css_selector('.s > div > .st')
    if descriptions:
        for description in descriptions:
            items['description'].append(description.text.strip())

    # h1〜h6
    for url in items['url']:
        r = requests.get(url)
        soup = BeautifulSoup(r.content, 'lxml')
        time.sleep(1) # 1秒待機

        # h1
        h1s = soup.find_all('h1')
        h1_list = []
        for h1 in h1s:
            if h1.text.strip():
                h1_list.append(h1.text.strip())
        items['h1'].append(h1_list)

        # h2
        h2s = soup.find_all('h2')
        h2_list = []
        for h2 in h2s:
            if h2.text.strip():
                h2_list.append(h2.text.strip())
        items['h2'].append(h2_list)

        # h3
        h3s = soup.find_all('h3')
        h3_list = []
        for h3 in h3s:
            if h3.text.strip():
                h3_list.append(h3.text.strip())
        items['h3'].append(h3_list)

        # h4
        h4s = soup.find_all('h4')
        h4_list = []
        for h4 in h4s:
            if h4.text.strip():
                h4_list.append(h4.text.strip())
        items['h4'].append(h4_list)

        # h5
        h5s = soup.find_all('h5')
        h5_list = []
        for h5 in h5s:
            if h5.text.strip():
                h5_list.append(h5.text.strip())
        items['h5'].append(h5_list)

        # h6
        h6s = soup.find_all('h6')
        h6_list = []
        for h6 in h6s:
            if h6.text.strip():
                h6_list.append(h6.text.strip())
        items['h6'].append(h6_list)

    return items


def googlespreadsheets(items, keyword, count):
    '''
    Googleスプレッドシート出力
    '''

    # 制限
    # ①ユーザーごとに100秒あたり100件のリクエスト
    # ②1回のプログラムで設定できる最大値は1,000件まで
    # ③1秒あたり10件まで

    # OAuth2の資格情報を使用してGoogleAPIにログイン
    gc = gspread.authorize(credentials)

    # シートが作成されているか確認するためのフラグ
    flag = False

    try:
        # 共有設定したスプレッドシートのシート1を開く
        workbook = gc.open_by_key(SPREADSHEET_KEY)
        worksheet = workbook.add_worksheet(title=keyword, rows='100', cols='100')

        # シートが作成されたらフラグを立てる
        flag = True

        # スプレッドシート書き込み処理
        # キーワード
        worksheet.update_cell(1, 1, keyword)
        time.sleep(1) # 1秒待機

        # 順位
        ranking = 1
        row = 2
        column = 1
        for title in items['title']:
            worksheet.update_cell(row, column, ranking)
            ranking += 1
            column += 1
        time.sleep(3) # 3秒待機

        # 「タイトル」
        row = 3
        column = 1
        for title in items['title']:
            worksheet.update_cell(row, column, title)
            column += 1
        time.sleep(3) # 3秒待機

        # 「URL」
        row = 4
        column = 1
        for url in items['url']:
            worksheet.update_cell(row, column, url)
            column += 1
        time.sleep(3) # 3秒待機

        # 「ディスクリプション」
        row = 5
        column = 1
        for description in items['description']:
            worksheet.update_cell(row, column, description)
            column += 1
        time.sleep(3) # 3秒待機

        # 「h1」
        row = 6
        column = 1
        for h1s in items['h1']:
            if h1s:
                h1_str = '*****'.join(h1s)
                worksheet.update_cell(row, column, h1_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        # 「h2」
        row = 7
        column = 1
        for h2s in items['h2']:
            if h2s:
                h2_str = '*****'.join(h2s)
                worksheet.update_cell(row, column, h2_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        # 「h3」
        row = 8
        column = 1
        for h3s in items['h3']:
            if h3s:
                h3_str = '*****'.join(h3s)
                worksheet.update_cell(row, column, h3_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        # 「h4」
        row = 9
        column = 1
        for h4s in items['h4']:
            if h4s:
                h4_str = '*****'.join(h4s)
                worksheet.update_cell(row, column, h4_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        # 「h5」
        row = 10
        column = 1
        for h5s in items['h5']:
            if h5s:
                h5_str = '*****'.join(h5s)
                worksheet.update_cell(row, column, h5_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        # 「h6」
        row = 11
        column = 1
        for h6s in items['h6']:
            if h6s:
                h6_str = '*****'.join(h6s)
                worksheet.update_cell(row, column, h6_str)
                column += 1
            else:
                worksheet.update_cell(row, column, 'なし')
                column += 1
        time.sleep(3) # 3秒待機

        count = 3
        return count

    # エラー処理
    except gspread.exceptions.APIError as e:
        # 制限に達した場合
        if '"code": 429' in str(e):
            if flag:
                workbook.del_worksheet(worksheet)
            print('100秒待機してリトライします')
            time.sleep(100) # 100秒待機
            count += 1
            return count
        # スプレッドシートに既にデータが存在している場合
        elif '"code": 400' in str(e):
            print('既に同じキーワードが存在します')
            count = 3
            return count


if __name__ == '__main__':
    main()

今までは何回か普通にスクレイピングできていたのですが、今回下のようなSSLエラーが起こりました。

    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='www.isc.meiji.ac.jp', port=443): Max retries exceeded with url: /~mizutani/python/intro7_python.html (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)'),))

実行環境
MacとWindowsの両方でやりました。
Mac OSX Mojave 
Windows 10

やってみたこと

下記のコードを追加してみたが、同じエラーが出てしまいます。

import ssl

ssl._create_default_https_context = ssl._create_unverified_context

お分かりになる方いらっしゃいませんでしょうか?どうぞよろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yodel

    2019/07/18 21:03

    「Pythonのrequestsを使ってグーグルの検索結果のタイトルやHタグを抜き出すスクレイピング」についてコード記載したほうが再現~原因確認しやすい気がします。 SSL認証で失敗するサイトがいくつかあるようにエラーログからは見えます。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2019/07/18 21:09

    そうですね。失礼しました。今ソースコードを記載いたしました。

    キャンセル

  • yodel

    2019/07/23 18:08

    ・今再実施しても再現しますか?
    ・エラーが発生する接続先は常に同じですか?
    同じIP接続元から短時間に数回接続された場合にエラーとされていないかを気にしています。

    キャンセル

回答 2

+1

リクエストしている実装部分のソースコードが無いから
分からないですけれど、この辺の情報は参考になりませんか?

https://qiita.com/j_tamura/items/5a22b102a58d1fa93a78

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/18 16:03

    ありがとうございます。
    こちらも実行してみましたが、また同じエラーが出てしまいました。

    キャンセル

0

利用規約に反した行動をとったためGoogleにブロックされてしまった可能性も考えられます。
利用規約を確認ください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/18 13:51 編集

    エラーが出たときの接続先がwww.isc.meiji.ac.jpなら、とりあえずグーグルは関係ないのでは。
    ……なんでグーグル叩いて明治大学なんだろう?(検索結果のページを個別に叩いた?)
    どのみち、本当にグーグルを使っているならルール的にはだめですね。

    キャンセル

  • 2019/07/18 14:11

    あ、接続先を見逃してました。Google関係ないですね。
    ↓のページを取得しようとしてたようですね
    https://www.isc.meiji.ac.jp/~mizutani/python/intro7_python.html

    キャンセル

  • 2019/07/18 14:19 編集

    すみません。私が説明不足でした。seleniumを使って「python 関数」というキーワードの1位から10位へそれぞれアクセスしてもらい、その個別のサイトごとにhttpリクエストをrequests関数を使ってかけていたという形でした。

    https://www.isc.meiji.ac.jp/~mizutani/python/intro7_python.html
    のページだけからはじかれたという感じみたいですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る