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

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

新規登録して質問してみよう
ただいま回答率
85.48%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

3回答

3493閲覧

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

umai29

総合スコア19

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

3クリップ

投稿2019/06/19 14:26

編集2019/06/19 15:18

Python初心者です。

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

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

Python

1import requests 2from bs4 import BeautifulSoup 3import time 4import os 5import re 6import csv 7from urllib.parse import urljoin 8 9#URL 10url = ["http://hoge.co.jp", \ 11 "http://fuga.co.jp", \ 12 "http://piyo.co.jp" 13 ] 14 15#取得する値(配列) 16arr1 = [] 17arr2 = [] 18 19def scraping() 20 with open("output.csv", "w", errors="ignore") as f: 21 #URLカウンタ(初期化) 22 n = 0 23 while n < len(url) 24 res = requests.get(url[n]) 25 res.raise_for_status() 26 27 #配列カウンタ(初期化) 28 count = 0 29 30 #指定のURLにアクセス 31 html = BeautifulSoup(res.content, "lxml") 32 33 #タグの内容を取得 34 for tmp1 in html.find_all("h3", class_="hoge"): 35 arr1.append([]) 36 arr1[n].append(tmp1.get_text()) 37 38 for tmp2 in html.find_all("p"): 39 for pt in tmp2.find_all("a"): 40 arr2.append([]) 41 arr2[n].append(pt.get("href")) 42 43 #ファイル書き出し 44 f.write(arr1[n][count] + "," + arr2[n][count]) 45 count += 1 46   47    #次のURLへ   48 n+=1 49 50 #ファイルをクローズ 51 f.close() 52 53scraping()

上記のソースコードですと、
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はまだ不慣れでして、インデントにも多少の抵抗はあります。(実際エラーが起きて修正することが多々ありますし。)

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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

スクレイピングの部分

python

1from itertools import zip_longest 2 3soup = BeautifulSoup(res.content, 'html.parser') 4 5arr1 = [i.get_text() for i in soup.find_all("h3", class_="hoge")] 6arr2 = [j.get("href") for j in soup.select("p a")] 7 8for i in zip_longest(arr1, arr2): 9 print(i)

サンプル

python

1from itertools import zip_longest 2 3arr1 = [1, 2, 3] 4arr2 = ['A', 'B', 'C', 'D'] 5 6for i in zip_longest(arr1, arr2): 7 print(i)

結果

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

投稿2019/07/01 15:44

barobaro

総合スコア1286

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

Python

1import requests 2from bs4 import BeautifulSoup 3import time 4import os 5import re 6import csv 7from urllib.parse import urljoin 8 9#URL 10url = ["http://hoge.co.jp", \ 11 "http://fuga.co.jp", \ 12 "http://piyo.co.jp" 13 ] 14 15def scraping(): 16 #URLカウンタ(初期化) 17 n = 0 18 while n < len(url): 19 #取得する値(配列) 20 res = requests.get(url[n]) 21 res.raise_for_status() 22 #配列カウンタ(初期化) 23 count = 0 24 #指定のURLにアクセス 25 html = BeautifulSoup(res.content, "lxml") 26 #タグの内容を取得 27 arr1 = [] 28 arr2 = [] 29 data1 = html.find_all("h3", class_="hoge") 30 data2 = html.find_all("p") 31 if data1 is not None: 32 for tmp1 in data1: 33 arr1.append(tmp1.get_text()) 34 35 if data2 is not None: 36 for tmp2 in data2: 37 data3 = tmp2.find_all("a") 38 if data3 is not None: 39 for pt in data3: 40 arr2.append(pt.get("href")) 41 42 #ファイル書き出し 43 filename = "output" + str(n) + ".csv" 44 with open(filename, 'w') as f: 45 writer = csv.writer(f) 46 writer.writerow(arr1) 47 writer.writerow(arr2) 48 49 #次のURLへ   50 n += 1 51 52if __name__ == '__main__': 53 54 scraping()

投稿2019/06/19 17:53

編集2019/06/24 06:43
7vvXi

総合スコア24

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umai29

2019/06/20 11:24

ご回答ありがとうございます。 例外処理の件、実施してみたいと思います。 空白をappendしたいと思うのですが、肝心の指定の仕方がわからなくて困っています。 調べたところ、find_allは値を取得できなかった場合、空のリストを返すとのことなのですが、それをif文で指定するとき、どのように記述すればよいかがわからないのですが、もしおわかりであれば教えていただけないでしょうか。 たびたび申し訳ありません。
7vvXi

2019/06/24 06:44

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

2019/06/24 15:12

再びご回答ありがとうございます。 なるほど、一度data1やdata2にfind_allの結果を格納しておき、後でif文でis not Noneと指定するのですね。勉強になります。 こちらも説明不足で大変恐縮なのですが、結果が得られなかった場合は、""(空白)をappendしたいのですが(""ではなく、Noneになるのですかね?)、この場合、else文などで指定しなくても、エラーは起きずに無事ファイルに出力されるのでしょうか。 例えば3回ループするとして、2回目のループで値を取得できなかった場合に、配列になにも要素が追加されないことで、エラーが発生するのでは…と思いまして。 度々すみません。
7vvXi

2019/07/01 04:35

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

0

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

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

投稿2019/06/19 14:36

otn

総合スコア84499

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umai29

2019/06/19 14:45

申し訳ありません。 全体的に説明不足でありました。 複数のURLの情報を取得するため、URLを配列として、はじめにurlとして定義しています。 nはその配列のナンバーとなります。 さらに、countは、一つのサイトの中でのナンバーとなります。 例えば、ランキングサイトで、1~10位の内容を取得したいとき、10回ループする必要がありますが、そ際に用いるカウンタとなります。 エラー回避もそうですが、格納先もずれないようにしたいと考えています。 例えば、1~10位の中で、4位のものが何も取得できなかったとき、5位のものが配列の4番目(count=3)に格納されないように、配列の4番目には空白として登録できれば良いのではないかと考えました。 lenで長さを調べる処理で、上記のようなことは対応可能でしょうか。
otn

2019/06/19 14:57

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問