python url csv python-3.x

Lea el archivo.csv de la URL en Python 3.x-_csv.Error: el iterador debe devolver cadenas, no bytes(¿abrió el archivo en modo texto?)



python-3.x (3)

He estado luchando con este simple problema por mucho tiempo, así que pensé en pedir ayuda. Estoy tratando de leer una lista de artículos de revistas del sitio ftp de la Biblioteca Nacional de Medicina en Python 3.3.2 (en Windows 7). Los artículos de revistas están en un archivo .csv.

He intentado con el siguiente código:

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]

Resulta en el siguiente error:

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?)

Supongo que debería trabajar con cadenas, no con bytes. Cualquier ayuda con el problema simple, y una explicación de lo que está pasando sería muy apreciada.


A pesar de que ya existe una respuesta aceptada, pensé que agregaría al conjunto de conocimientos al mostrar cómo logré algo similar usando el paquete de requests (que a veces se ve como una alternativa a urlib.request ).

La base de usar codecs.itercode() para resolver el problema original sigue siendo la misma que en la respuesta aceptada .

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

Aquí también vemos el uso de la transmisión proporcionada a través del paquete de requests para evitar tener que cargar todo el archivo a través de la red en la memoria primero (lo que podría llevar mucho tiempo si el archivo es grande).

Pensé que podría ser útil ya que me ayudó, ya que estaba usando requests lugar de urllib.request en Python 3.6.

Algunas de las ideas (por ejemplo, usando closing() ) se recogen de esta post similar


El problema depende de los bytes que regresan urllib . Como prueba, puede intentar descargar el archivo csv con su navegador y abrirlo como un archivo normal y el problema desaparecerá.

Un problema similar fue tratado here .

Puede resolverse decodificando bytes en cadenas con la codificación adecuada. Por ejemplo:

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]

La última línea también podría ser: data = list(csvfile) que puede ser más fácil de leer.

Por cierto, dado que el archivo csv es muy grande, puede ralentizarse y consumir memoria. Tal vez sería preferible usar un generador.

EDITAR: utilizando códecs como lo propone Steven Rumbalski, por lo que no es necesario leer todo el archivo para decodificar. El consumo de memoria se reduce y la velocidad aumenta.

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

Tenga en cuenta que la lista no se crea por el mismo motivo.


urlopen devolverá una instancia urllib.response.addinfourl para una solicitud ftp.

Para ftp, archivos y URL de datos y solicitudes manejadas explícitamente por las clases URLopener heredadas y FancyURLopener, esta función devuelve un objeto urllib.response.addinfourl que puede funcionar como gestor de contexto ...

>>> urllib2.urlopen(url) <addinfourl at 48868168L whose fp = <addclosehook at 48777416L whose fp = <socket._fileobject object at 0x0000000002E52B88>>>

En este punto, ftpstream es un archivo como objeto, usando .read() devolvería el contenido; sin embargo, csv.reader requiere un iterable en este caso:

Definir un generador así:

def to_lines(f): line = f.readline() while line: yield line line = f.readline()

Podemos crear nuestro lector csv así:

reader = csv.reader(to_lines(ftps))

Y con una url

url = "http://pic.dhe.ibm.com/infocenter/tivihelp/v41r1/topic/com.ibm.ismsaas.doc/reference/CIsImportMinimumSample.csv"

El código:

for row in reader: print row

Huellas dactilares

>>> [''simpleci''] [''SCI.APPSERVER''] [''SRM_SaaS_ES'', ''MXCIImport'', ''AddChange'', ''EN''] [''CI_CINUM''] [''unique_identifier1''] [''unique_identifier2'']