Ошибка: массивы должны быть одинаковой длины

Я пытаюсь написать скрипт для загрузки изображений из Reddit, используя praw, сохраняя изображения в папку по своему выбору и экспортируя .csv результатов.

Я думаю, что закодировал его правильно с момента загрузки изображений, я просто получаю сообщение об ошибке «Массивы должны быть одинаковой длины», когда пытаюсь запустить скрипт.

Я думаю, что это может иметь какое-то отношение к полю «путь» в моем словаре, но циклы выглядят так, как будто они правильно добавляют информацию, поэтому я не знаю. Мне не хватает 2 записей из «пути». Я понятия не имею, куда они падают.

Мой код ниже:

#! python3
import praw
import pandas as pd
import requests

path = r'C:\\Scripts\\IMG\\'

#Reddit API Tokens
reddit = praw.Reddit(client_id='x', \
                client_secret='x', \
                user_agent='x', \
                username='x', \
                password='x')

x_dict = {"id":[], \
            "title":[], \
            "url":[], \
            "path":[]}
submissions = reddit.subreddit('x').hot(limit=100)

for submission in submissions:
    x_dict["id"].append(submission.id)
    x_dict["title"].append(submission.title)
    x_dict["url"].append(submission.url)

    if submission.url.endswith(".gifv"):
        submission.url = submission.url.replace('.com/', '.com/download/')
        submission.url = (submission.url + ".mp4")
        r = requests.get(submission.url, allow_redirects=True)
        if "gif" in r.headers['Content-Type']:
            dir2 = os.path.join(path, submission.id + ".gif")
            submission.url = (submission.url + ".gif")
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
        else:
            dir2 = os.path.join(path, submission.id + ".mp4")
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
    elif "gfycat" in submission.url:
        if "https://" in submission.url:
            dir2 = os.path.join(path, submission.id + ".mp4")
            submission.url = submission.url.replace('https://', 'https://giant.')
            submission.url = (submission.url + ".mp4")
            r = requests.get(submission.url, allow_redirects=True)
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
        else:
            dir2 = os.path.join(path, submission.id + ".mp4")
            submission.url = submission.url.replace('http://', 'http://giant.')
            submission.url = (submission.url + ".mp4")
            r = requests.get(submission.url, allow_redirects=True)
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
    elif "i.redd" in submission.url:
        if submission.url.endswith(".jpg"):
            dir2 = os.path.join(path, submission.id + ".jpg")
            r = requests.get(submission.url, allow_redirects=True)
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
        elif submission.url.endswith(".jpeg"):
            dir2 = os.path.join(path, submission.id + ".jpeg")
            r = requests.get(submission.url, allow_redirects=True)
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)         
        elif submission.url.endswith(".png"):
            dir2 = os.path.join(path, submission.id + ".png")
            r = requests.get(submission.url, allow_redirects=True)
            open(dir2, 'wb').write(r.content)
            print ("downloading " + submission.id + " to " + dir2)
            x_dict["path"].append(dir2)
    elif "v.redd" in submission.url:
        dir2 = os.path.join(path, submission.id + ".mp4")
        r = requests.get(submission.media['reddit_video']['fallback_url'], allow_redirects=True)
        open(dir2, 'wb').write(r.content)
        print ("downloading " + submission.id + " to " + dir2)
        x_dict["path"].append(dir2)
    elif submission.url is None:
        print ("\\ " + submission.id + " url is none")
        x_dict["path"].append('')
    else:
        print ("\\" + submission.id + " not supported")
        x_dict["path"].append('')
        continue
print (len(x_dict["id"]))
print (len(x_dict["title"]))
print (len(x_dict["url"]))
print (len(x_dict["path"]))
x_data = pd.DataFrame(x_dict)
x_data.to_csv(os.path.join(path,'xscrape.csv'))

Выход выглядит следующим образом

downloading 99rdbf to C:\\Scripts\\IMG\\99rdbf.jpg
100
100
100
98
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-434-0d78dff7cb84> in <module>()
 89 print (len(x_dict["url"]))
 90 print (len(x_dict["path"]))
---> 91 x_data = pd.DataFrame(x_dict)
     92 x_data.to_csv(os.path.join(path,'xscrape.csv'))

d:\Users\localuser\AppData\Local\Continuum\anaconda3\lib\site-    packages\pandas\core\frame.py in __init__(self, data, index, columns, dtype, copy)
346                                  dtype=dtype, copy=copy)
347         elif isinstance(data, dict):
--> 348             mgr = self._init_dict(data, index, columns, dtype=dtype)
    349         elif isinstance(data, ma.MaskedArray):
    350             import numpy.ma.mrecords as mrecords

d:\Users\localuser\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\frame.py in _init_dict(self, data, index, columns, dtype)
    457             arrays = [data[k] for k in keys]
    458 
--> 459         return _arrays_to_mgr(arrays, data_names, index, columns, dtype=dtype)
    460 
    461     def _init_ndarray(self, values, index, columns, dtype=None, copy=False):

d:\Users\localuser\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\frame.py in _arrays_to_mgr(arrays, arr_names, index,     columns, dtype)
   7313     # figure out the index, if necessary
   7314     if index is None:
-> 7315         index = extract_index(arrays)
   7316 
   7317     # don't force copy because getting jammed in an ndarray anyway

d:\Users\localuser\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\frame.py in extract_index(data)
   7359             lengths = list(set(raw_lengths))
   7360             if len(lengths) > 1:
-> 7361                 raise ValueError('arrays must all be same length')
   7362 
   7363             if have_dicts:

ValueError: arrays must all be same length

person cipherbear    schedule 23.08.2018    source источник
comment
Включите вывод программы и особенно исключение, которое вы видите; это позволит людям видеть, а не гадать, какая строка кода вызывает ошибку. Кроме того, я почти уверен, что ваш отступ здесь не соответствует вашей реальной программе; попробуйте использовать тройные обратные кавычки выше и ниже кода в уценке и вставить сам код, не меняя отступ.   -  person cjs    schedule 24.08.2018
comment
Строка, создающая исключение (вызов pd.DataFrame()), кажется комментарием в опубликованном вами коде. Не верьте себе, что опубликованный вами код вызовет ошибку, если его изменить; удалите весь блок кода в своем сообщении и снова вставьте его, используя точное содержимое файла, который вы запустили и который произвел вывод.   -  person cjs    schedule 24.08.2018
comment
Обновлено в соответствии с просьбой, извините за ожидание, так как я здесь новый пользователь. Спасибо за терпеливость.   -  person cipherbear    schedule 24.08.2018
comment
Без проблем. Хорошо писать вопросы непросто; обучение этому является частью обучения программированию и займет некоторое время.   -  person cjs    schedule 24.08.2018


Ответы (1)


Основная проблема здесь заключается в структуре вашей структуры данных: она позволяет легко совершать ошибки программирования, а не помогает их предотвратить.

В этом ответе я собираюсь использовать стандартный трюк программиста: я даже не буду пытаться выяснить, в чем проблема в текущем коде, а просто реструктурирую все так, чтобы проблема больше не появлялась.

В файле CSV каждая строка представляет собой последовательность тесно связанных элементов. В свою очередь, весь файл представляет собой последовательность этих строк. Вы хотите, чтобы более тесно связанные элементы были ближе друг к другу в структуре данных, поэтому ваша «внутренняя» структура данных двух списков должна быть последовательностью полей в одной строке, а «внешняя» структура данных должна быть последовательностью линий, что противоположно тому, что вы сделали.

В Python есть две очень распространенные структуры данных последовательности: list, которые вы уже знаете и используете здесь, и tuple который похож на список, но неизменяем.

Для этой программы стоит изучить и понять структуру данных namedtuple. , который является кортежем, но дополнен именами полей и конструктором, который гарантирует, что вы всегда используете одно и то же количество аргументов. Последнее является еще одним решением при проектировании структуры данных, которое поможет вам избежать ошибок при программировании.

Определите свою структуру данных для строки CSV следующим образом:

from collections import namedtuple
Download = namedtuple('Download', 'id title url path')

(Стоит ввести это непосредственно в интерпретатор Python (python -i или ipython) и немного поиграть с ним, пока вы не освоитесь с созданием и отображением именованных кортежей.)

Затем вы можете составить список из них по мере загрузки. Поскольку кортеж неизменяем, нам нужно построить его одним вызовом конструктора, мы можем создать его только после того, как у нас будет вся необходимая для этого информация. Затем мы добавляем его в список.

def download(id, url):
    # All the stuff you need to do an individual download here.
    return path

downloads = []
for s in submissions:
    path = download(s.id, s.url)
    dl = Download.new(s.id, s.title, s.url, path)
    downloads.append(dl)

Вам не нужно устанавливать Pandas для записи файлов CSV; в стандартной библиотеке есть модуль csv, который прекрасно справляется со своей задачей. Работая с примером в его документации:

import csv

with open(os.path.join(path,'xscrape.csv'), 'w', newline='') as out:
    writer = csv.writer(out)
    writer.writerows(downloads)

(Это создает CSV-файл без строки заголовка; добавление одного я оставляю читателю в качестве упражнения.)

person cjs    schedule 23.08.2018