web-dev-qa-db-ja.com

URLから.csvファイルをPython 3.x-_csv.Error:イテレータはバイトではなく文字列を返す必要があります(ファイルをテキストモードで開きましたか?)

私はこの単純な問題に長い間苦労してきたので、私は助けを求めると思いました。国立医学図書館のftpサイトからジャーナル記事のリストをPython 3.3.2(Windows 7))に読み込もうとしています。ジャーナル記事は.csvファイルにあります。

私は次のコードを試しました:

import csv
import urllib.request

url = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/file_list.csv"
ftpstream = urllib.request.urlopen(url)
csvfile = csv.reader(ftpstream)
data = [row for row in csvfile]

次のエラーが発生します。

Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
data = [row for row in csvfile]
File "<pyshell#4>", line 1, in <listcomp>
data = [row for row in csvfile]
_csv.Error: iterator should return strings, not bytes (did you open the file in text mode?)

私はバイトではなく文字列を扱うべきだと思いますか?単純な問題に関するヘルプ、および何が問題なのかについての説明をいただければ幸いです。

29
Chris

問題はurllibがバイトを返すことに依存しています。証拠として、ブラウザでcsvファイルをダウンロードして、通常のファイルとして開くと、問題はなくなります。

同様の問題が解決されました here

適切なエンコーディングを使用して、バイトを文字列にデコードすることで解決できます。例えば:

_import csv
import urllib.request

url = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/file_list.csv"
ftpstream = urllib.request.urlopen(url)
csvfile = csv.reader(ftpstream.read().decode('utf-8'))  # with the appropriate encoding 
data = [row for row in csvfile]
_

最後の行はdata = list(csvfile)である可能性もあります。これは読みやすい場合があります。

ちなみに、csvファイルは非常に大きいため、遅くなり、メモリを消費する可能性があります。たぶん、ジェネレータを使用することが望ましいでしょう。

EDIT:Steven Rumbalskiが提案したコーデックを使用するため、ファイル全体を読み取ってデコードする必要はありません。メモリ消費が削減され、速度が向上しました。

_import csv
import urllib.request
import codecs

url = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/file_list.csv"
ftpstream = urllib.request.urlopen(url)
csvfile = csv.reader(codecs.iterdecode(ftpstream, 'utf-8'))
for line in csvfile:
    print(line)  # do something with line
_

同じ理由でリストも作成されないことに注意してください。

43
Diego Herranz

すでに受け入れられている答えがありますが、requestsパッケージ(_urlib.request_ )。

codecs.itercode()を使用して元の問題を解決する基礎は、 受け入れられた回答 と同じです。

_import codecs
from contextlib import closing
import csv
import requests

url = "ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/file_list.csv"

with closing(requests.get(url, stream=True)) as r:
    reader = csv.reader(codecs.iterdecode(r.iter_lines(), 'utf-8'))
    for row in reader:
        print row   
_

ここでは、ネットワーク経由でファイル全体をロードすることを回避するために、requestsパッケージを通じて提供されるstreamingの使用も参照してください。最初にメモリ(ファイルが大きい場合は時間がかかる可能性があります)。

Python 3.6。で_urllib.request_ではなくrequestsを使用していたので、それが役立ったので便利だと思いました。

いくつかのアイデア(たとえばclosing()を使用)は、この類似の post から選択されます

8
Irvin H.