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

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

ただいまの
回答率

87.93%

Pythonでのスクレイピング時に何も取得できなかった場合の処理をしたいです。

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 4,049

score 19

Python初心者です。

PythonにてWebスクレイピングをしておりまして、取得した内容をCSVファイルに書き出すという処理をしたいと考えております。

複数のサイトにアクセスし、複数の項目(次の例では2つ)の内容を取得して、配列に格納し、書き出すプログラムを組んでみたのですが…

import requests
from bs4 import BeautifulSoup
import time
import os
import re
import csv
from urllib.parse import urljoin

#URL
url = ["http://hoge.co.jp", \
       "http://fuga.co.jp", \
       "http://piyo.co.jp"
      ]

#取得する値(配列)
arr1 = []
arr2 = []

def scraping()
  with open("output.csv", "w", errors="ignore") as f:
    #URLカウンタ(初期化)
    n = 0
    while n < len(url)
      res = requests.get(url[n])
      res.raise_for_status()

      #配列カウンタ(初期化)
      count = 0

      #指定のURLにアクセス
      html = BeautifulSoup(res.content, "lxml")

      #タグの内容を取得
      for tmp1 in html.find_all("h3", class_="hoge"):
         arr1.append([])
         arr1[n].append(tmp1.get_text())

      for tmp2 in html.find_all("p"):
         for pt in tmp2.find_all("a"):
            arr2.append([])
            arr2[n].append(pt.get("href"))

      #ファイル書き出し
      f.write(arr1[n][count] + "," + arr2[n][count])
      count += 1
     
    #次のURLへ  
      n+=1

   #ファイルをクローズ 
   f.close()

scraping()

上記のソースコードですと、
f.write(arr1[n][count] + "," + arr2[n][count])のところで、
IndexError: list index out of range
というエラーが起きてしまいます。

おそらく、arr1またはarr2のところで、取得した値がなく、配列に何も追加されず要素がないものを指定したということで起きたエラーなのではないかと思います。(違ってたらすみません)

そこで、if文で、何も取得できなかった場合は、空白("")を配列に追加することでこれを回避しようかと考えております。

結果として、ファイルに書き込まれる内容は、空白(カンマでは区切られる)ですが、エラーで処理が停まるのを防ぐことができるのではないかと考えています。

その場合、上記のようなソースコードですと、どのように指定したらよいのでしょうか。
(arr1の場合ですと、h3タグのクラス名"hoge"がない場合、arr1[n].append("")を処理するif文を追加したいです。)

Pythonはまだ不慣れでして、インデントにも多少の抵抗はあります。(実際エラーが起きて修正することが多々ありますし。)

他にも何かいい方法がありましたら、ご教示いただきますようお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

0

nとは何か、countとは何かが、はっきりしていない気がします。
全体的なロジックを考え直しては?

単に、エラーを回避するだけでいいなら、lenで長さを調べれば良いのですが。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/19 23:45

    申し訳ありません。
    全体的に説明不足でありました。

    複数のURLの情報を取得するため、URLを配列として、はじめにurlとして定義しています。
    nはその配列のナンバーとなります。

    さらに、countは、一つのサイトの中でのナンバーとなります。
    例えば、ランキングサイトで、1~10位の内容を取得したいとき、10回ループする必要がありますが、そ際に用いるカウンタとなります。

    エラー回避もそうですが、格納先もずれないようにしたいと考えています。
    例えば、1~10位の中で、4位のものが何も取得できなかったとき、5位のものが配列の4番目(count=3)に格納されないように、配列の4番目には空白として登録できれば良いのではないかと考えました。

    lenで長さを調べる処理で、上記のようなことは対応可能でしょうか。

    キャンセル

  • 2019/06/19 23:57

    ・countについてのループがない。
    ・nに関するwhileループの外でnをインクリメントしている。
    をまず直しましょう。

    キャンセル

0

list index out of rangeですが、存在しない配列の要素を参照した際に起こるエラーです。
エラーが発生すると、その行以降は実行されませんので、例外処理として、try~except文がよいかと思われます。
詳しくはこちらを読んでみてください。
https://docs.python.org/ja/3/tutorial/errors.html

順位を正しくするには、空白をappendするのが現状一番かと思われます。
ループやインクリメント処理については、インデントやスコープをよく見て書いてみるといいかもしれません。

※ここから追記です
このようなことでしょうか?
一応ですが、実行確認済みです。

import requests
from bs4 import BeautifulSoup
import time
import os
import re
import csv
from urllib.parse import urljoin

#URL
url = ["http://hoge.co.jp", \
       "http://fuga.co.jp", \
       "http://piyo.co.jp"
      ]

def scraping():
    #URLカウンタ(初期化)
    n = 0
    while n < len(url):
        #取得する値(配列)
        res = requests.get(url[n])
        res.raise_for_status()
        #配列カウンタ(初期化)
        count = 0
        #指定のURLにアクセス
        html = BeautifulSoup(res.content, "lxml")
        #タグの内容を取得
        arr1 = []
        arr2 = []
        data1 = html.find_all("h3", class_="hoge")
        data2 = html.find_all("p")
        if data1 is not None:
            for tmp1 in data1:
                arr1.append(tmp1.get_text())

        if data2 is not None:
            for tmp2 in data2:
                data3 = tmp2.find_all("a")
                if data3 is not None:
                    for pt in data3:
                        arr2.append(pt.get("href"))

        #ファイル書き出し
        filename = "output" + str(n) + ".csv"
        with open(filename, 'w') as f:
            writer = csv.writer(f)
            writer.writerow(arr1)
            writer.writerow(arr2)

        #次のURLへ  
        n += 1

if __name__ == '__main__':

    scraping()

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/20 20:24

    ご回答ありがとうございます。
    例外処理の件、実施してみたいと思います。

    空白をappendしたいと思うのですが、肝心の指定の仕方がわからなくて困っています。
    調べたところ、find_allは値を取得できなかった場合、空のリストを返すとのことなのですが、それをif文で指定するとき、どのように記述すればよいかがわからないのですが、もしおわかりであれば教えていただけないでしょうか。

    たびたび申し訳ありません。

    キャンセル

  • 2019/06/24 15:44

    追記しましたので、見ていただければ幸いです。
    何か、勘違い等ありましたら、処理の一部について詳細にお願いいたします。

    キャンセル

  • 2019/06/25 00:12

    再びご回答ありがとうございます。
    なるほど、一度data1やdata2にfind_allの結果を格納しておき、後でif文でis not Noneと指定するのですね。勉強になります。

    こちらも説明不足で大変恐縮なのですが、結果が得られなかった場合は、""(空白)をappendしたいのですが(""ではなく、Noneになるのですかね?)、この場合、else文などで指定しなくても、エラーは起きずに無事ファイルに出力されるのでしょうか。
    例えば3回ループするとして、2回目のループで値を取得できなかった場合に、配列になにも要素が追加されないことで、エラーが発生するのでは…と思いまして。

    度々すみません。

    キャンセル

  • 2019/07/01 13:35

    返信が遅くなり、申し訳ありません。

    まず、空白の追加ですが、append(" ")です。append("")としてしまうと、空要素となってしまいます。
    本題ですが、タグで取得等する場合、Beautifulsoupのfind_allですと、見つかったものを先頭から詰めてリストに格納します。ですので、現在のやり方ですと、存在しない場所を空白で補完というのは厳しいです。
    h3タグやpタグといったものを含む、もう1段上(スコープが1段広い)タグを指定し、1行ずつ実行していくのが良いかと思われます。

    キャンセル

0

itertools.zip_longestで不足分を埋めるのは?
https://docs.python.org/ja/3/library/itertools.html#itertools.zip_longest

スクレイピングの部分

from itertools import zip_longest

soup = BeautifulSoup(res.content, 'html.parser')

arr1 = [i.get_text() for i in soup.find_all("h3", class_="hoge")]
arr2 = [j.get("href") for j in soup.select("p a")]

for i in zip_longest(arr1, arr2):
    print(i)

サンプル

from itertools import zip_longest

arr1 = [1, 2, 3]
arr2 = ['A', 'B', 'C', 'D']

for i in zip_longest(arr1, arr2):
    print(i)

結果

(1, 'A')
(2, 'B')
(3, 'C')
(None, 'D')

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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