В пауке XMLFeed... решил поробовать создать новый столбец из свежеспарсенного словаря списков... Пишем файл pipeline.py в котором фильтруем записи о серверах по времени, типу прокси и стране. Потом записываем результат в виде строк "http://777.77.77.77:8080" в текстовый файл. В процессе работы выскакивают ошибки, я быстро ликивидирую безграмотность перечитывая и конспектируя книгу Лутца. Сроки поджимают, стоит ли продолжать ликбез? Насколько он необходим?
Действительно, для работы "в продакшене" нужен минимальный набор навыков. Очевидно, что его у меня нет (иначе бы не складывал строку со списком). Очевидно, что поиск ошибки при таких знаниях должен быть сопряжен с изрядной тратой времени на закрепление основ.
Например, здесь я перечитал главы о списках, словарях, кортежах - это околло 100 страниц... Причем, пока читаешь, чувствуешь себя очень умным... поскольку все понимаешь.
В то же время, когда надо понять, как работает def process_item в pipelines scrapy, то чувствуешь себя полудурком, поскольку надо уметь сначала распечатать стек, понять назначение классов, посмотреть атрибуты функций... Потом разобраться, как эта "process_item " вызывается из twisted ...
Здесь могло бы помочь знание общих правил (например, как работаю итераторы...), может быть выделить на занятия по 1 часу в день? Но что читать и в какой последовательности?
Еще одно направление - дебаггинг... Но здесь пока все ясно: where, break, next, continue, return, args, ... dir(), help, file
Методические ошибки. В выводе результатов дебаггера распечатывалось не один словарь "Item", а штук 8... Поначалу я этого не заметил, потому что экспериментировал с файлом в 100 строк, ... вывод на умещался на экране... Ошибка очень плохая, потому, что я искал причину ошибки в филах pipeline, а она была в spider C:\Users\kiss\Documents\GitMyScrapy\scrapy_xml_1\XMLFeedSpider\XMLFeedSpider\proxylists.py
# Сначала я использовал
itertag = 'prx:proxy'
# Потом
itertag = 'item'
# Потом понял, что количесво повторов (вывода) соответствуют количеству тегов 'item'
#instead of online ['http://www.proxylists.net/feed.xml']
namespaces = [('prx','http://www.proxyrss.com/specification.html'),('content','http://purl.org/rss/1.0/modules/content/')]
iterator = 'xml' # you can change this; see the docs
itertag = 'channel' # change it accordingly
#pdb.set_trace()
Выше правильное решение, но я нашел его "случайно", а большую часть времени потратил на поиск "кошки в пустой черной комнате...". Конечно, было небезполезно разобраться с тем, как вызывается "process_item", но я начал предполагать, что эта функция вызывается для каждой итерации в пауке... оказалось, что все проще - вызов происходит один раз... естественно, после того, как сфомирован объект Item (словарь списков). Т.е., первоначальный код для pipeline работал нормально.
Вот она, первоначальная "детская ошибка"¶
<item>
<title>AL proxies on 2014/10/15 04:21:41</title>
<link>http://www.proxylists.net/al_0.html</link>
<description>AL proxies are in the database at the moment.</description>
<guid>http://www.proxylists.net/al_0.html</guid>
<prx:proxy>
<prx:ip>77.242.22.254</prx:ip>
<prx:port>8741</prx:port>
<prx:type>Socks4</prx:type>
<prx:country>Albania</prx:country>
<prx:check_timestamp>10/13 03:36:32</prx:check_timestamp>
</prx:proxy>
</item>
Далее процесс поиска и программирования.¶
Эти фрагменты ниже - примеры промежуточного кода. С "временными строчками", которые конечно загромождают модули..., но как быть, когда пробуешь разные варианты... Потом я сотру "все лишнее"... Очевидно, что по этому коду можно легко понять степень моей чайниковости. Но она снижается с каждым днем.
Зачем же я оставляю этот компромат? Уповаю на память "на припоминание", может помочь вспомнить не только, "как я это выводил", но и активизировать соответствующие "забытые" синаптические ассоциации.
%load 'C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\pipelines.py'
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from datetime import datetime
#import pdb
class XmlfeedspiderPipeline(object):
"""
This pipeline forms strings like
"http://777.78.79.67:8080"
in field item['proxylist']
There are three filters here: timestamp, country, proxytype
"""
print "In === XmlfeedspiderPipeline"
#Select last proxy onlytimestanmp=FROMPREVIOUS_LIST_TIMESTAMP
timestamp =0
year='2014/' # for stringto_time method
#Country filter
country = 'Brazil'
# Type - 'prxtype' (Anonymous, Distorting, Transparent, Socks4, Socks5)
proxytype = 'Anonymous'
def process_item(self, item, spider):
#pdb.set_trace()
# if self.stringto_time(item['prxcheck_timestamp']) != 0:
# if item['prxcountry'] == self.country:
# if item['prxtype'] == self.proxytype:
# #make new linestring for proxyfile
item['proxylist'] = item['prxtype'] + item['prxip'] +':'+ item['port']
print item['proxylist']
#else:
#raise DropItem("Missing price in %s" % item)
return item
def stringto_time(self,timestring):
"""
Convert string like '10/15 05:09:21'
to datetime oblect
year - is added here by hands
"""
your_string =self.year + timestring
return datetime.strptime(your_string, "%Y/%m/%d %H:%M:%S")
Далее я прочитал про словари и списки в Лутце и понял, что нужны итерации внутри функции, поскольку элементы словаря выдают не строки, а списки. Результаты будут в отдельном посте..., далее я отвлекся на решение другой задачи
Далее посмотрим, как нам преобразоват строку в дату для поля check_timestamp¶
Это совершенно другая задача, она решается довольно просто
import datetime
year='2014/'
your_string =year +'10/15 05:09:21'
dt=datetime.datetime.strptime(your_string, "%Y/%m/%d %H:%M:%S")
dt.isoformat()
Как лучше формировать список? Можно добавлять к пустому списку по одному элементу, а можно сгенерировать список заранее, а потом изменить каждый элемент списка.¶
port=[u'8741', u'1080', u'8080', u'8080']
ip=[u'77.242.22.254', u'41.191.253.146', u'191.103.9.210', u'191.103.9.209']
s=ip.append(port)
s
print s
ip.append('xxx')
ip
ip[1]
len(ip)
len(ip[4])
for i in ip:
print i.index()
help(list)
ip.extend(port)
ip
col2 = [row[1] for row in ip]
print col2
col3 = [rw[1] for rw in ip]
col3
dir(ip)
len(ip)
il=[i for i in range(len(ip)) ]
il
help(ip.__add__)
for x in ip: print x, ip.index(x)
ip[ip.index('77.242.22.254')]+':'+ ip[4][0]
item['proxylist'] =item['prxip']
ipdb.set_trace()
ip=item['prxip'] #the list of IP
for ipadr in ip:
j=ip.index(ipadr)
item['proxylist'][j] = ipadr +':'+ item['prxport'][ip.index(ipadr)]
ddd={'p':[8741, 1080, 8080, 8080], 'ip':[u'77.242.22.254', u'41.191.253.146', u'191.103.9.210', u'191.103.9.209']}
ddd['pp']=ddd['p']
ddd['pp'][1]=1111
ddd['p'][1]
d1=ddd['p']
dir(d1)
dir(list)
Здесь и далее цитаты из Лутца (начиная со стр. 257)¶
Несмотря на то что оператор + со списками работает точно так же, как и со строками, очень важно знать, что с обеих сторон оператора должны находиться последовательности одного и того же типа, в противном случае во время работы программы вы получите сообщение об ошибке.
Например, нельзя выполнить операцию конкатенации для списка и строки, если предварительно не преобразовать список в строку (используя, например, функцию str или оператор форматирования %) или строку в список (с помощью встроенной функцией list):
>>> str([1, 2]) + '34' # То же, что и “[1, 2]” + “34”
[1, 2] + list('34') # То же, что и [1, 2] + [“3”, “4”]
Подробнее об операции обхода элементов списка в цикле for и о встроенной функции range мы поговорим в главе 13, потому что они имеют отношениюк синтаксису инструкций. Говоря коротко, оператор цикла for выбирает элементы последовательности в порядке слева направо и выполняет одну или более инструкций для каждого из них.
Последняя строка в табл. 8.1 представляет генератор списков и вызов встроенной функции map, которые подробно описываются в главе 14 и расширенное обсуждение которых приводится в главе 20. Как говорилось в главе 4, генераторы списков – это способ построить новый список, применяя выражение к каждому элементу последовательности; они являются близкими родственниками инструкции цикла for.
...в версии 3.0 метод keys возвращает итератор, а не список. Вызов функции list принудительно выполняет обход всех значений итератора, что позволяет вывести их все сразу. В версии 2.6 метод keys конструирует и возвращает обычный список, поэтому для отображения результатов вызов функции list в этой версии интерпретатора не требуется. Подробнее об этом рассказывается ниже, в этой главе.
>>> sorted(open(‘script1.py’))
[‘import sys\n’, ‘print(2 ** 33)\n’, ‘print(sys.path)\n’, ‘x = 2\n’]
>>> list(zip(open(‘script1.py’), open(‘script1.py’)))
[(‘import sys\n’, ‘import sys\n’), (‘print(sys.path)\n’, ‘print(sys.path)\n’),
(‘x = 2\n’, ‘x = 2\n’), (‘print(2 ** 33)\n’, ‘print(2 ** 33)\n’)]
>>> list(enumerate(open(‘script1.py’)))
[(0, ‘import sys\n’), (1, ‘print(sys.path)\n’), (2, ‘x = 2\n’),
(3, ‘print(2 ** 33)\n’)]
>>> list(filter(bool, open(‘script1.py’)))
[‘import sys\n’, ‘print(sys.path)\n’, ‘x = 2\n’, ‘print(2 ** 33)\n’]
>>> import functools, operator
>>> functools.reduce(operator.add, open(‘script1.py’))
‘import sys\nprint(sys.path)\nx = 2\nprint(2 ** 33)\n’
стр 430 ...\Все они являются инструментами итераций, но каждая из них играет свою, уникальную роль. С функциями zip и enumerate мы встречались в предыдущей главе. С функциями filter и reduce мы познакомимся в главе 19, когда будем рассматривать тему функционального программирования, поэтому пока мы не будем погружаться в их особенности.
С функцией sorted мы встретились в первый раз в главе 4 и использовали ее при работе со словарями в главе 8. sorted – это встроенная функция, использующая протокол итераций, – она похожа на метод sort списков, но возвращает новый отсортированный список и принимает любые итерируемые объекты. Обратите внимание, что в отличие от функции map и других, в версии Python 3.0 функция sorted возвращает фактический список, а не итерируемый объект.
стр. 439
Еще больше о генераторах списков и об итераторах мы узнаем в главе 20, когда будем рассматривать их в контексте функций, и еще раз вернемся к ним в главе 29, когда будем знакомиться с классами. Позднее вы узнаете, что:
•• С помощью инструкции yield пользовательские функции можно превратить в итерируемые функции-генераторы.
•• Генераторы списков можно трансформировать в итерируемые выражения-генераторы, заключив их в круглые скобки.
•• В пользовательские классы можно добавить поддержку итераций с помощью методов перегрузки операторов iter или getitem.
В частности, реализация пользовательских итераторов с помощью классов позволяет обеспечить возможность использования произвольных объектов в любых итерацонных контекстах, с которыми мы познакомились здесь
стр. 440 В число итерационных контекстов языка Python входят: цикл for, генераторы списков, встроенная функция map, оператор in проверки вхождения, а также встроенные функции sorted, sum, any и all.
В эту категорию также входят встроенные функции list и tuple, строковый метод join и операции присваивания последовательностей – все они следуют итерационному протоколу (метод next) для обхода итерируемых объектов.
Генераторы списков и матрицы (p. 564)¶
>>> M = [[1, 2, 3],
... [4, 5, 6],
... [7, 8, 9]]
>>> N = [[2, 2, 2],
... [3, 3, 3],
... [4, 4, 4]]
[row[1] for row in M]
>>> open(‘myfile’).readlines()
[‘aaa\n’, ‘bbb\n’, ‘ccc\n’]
#Если требуется удалить символы конца строки, их можно отсечь сразу во всех строках за одно действие с помощью генератора списков или
#функции map (в Python 3.0 функция map возвращает итерируемый объект, поэтому нам пришлось использовать функцию list, чтобы полу-
#чить весь список с результатами в интерактивном сеансе):
>>> [line.rstrip() for line in open(‘myfile’).readlines()]
[‘aaa’, ‘bbb’, ‘ccc’]
>>> [line.rstrip() for line in open(‘myfile’)]
[‘aaa’, ‘bbb’, ‘ccc’]
>>> list(map((lambda line: line.rstrip()), open(‘myfile’)))
[‘aaa’, ‘bbb’, ‘ccc’]
#В последних двух случаях используются файловые итераторы (по сути это означает, что вам не требуется вызывать метод, который будет чи-
#тать строки из файла). Вызов функции map выглядит немного длиннее, чем генератор списков, но ни в одном из этих двух случаев
#не требуется явно управлять конструированием списка результатов.
p. 566
Производительность этих тестов может зависеть от вида решаемой задачи, а также от изменений и оптимизаций в самом интерпретаторе
языка Python. Например, в последних версиях Python была увеличена скорость выполнения инструкции цикла for.
Тем не менее генераторы списков обычно показывают более высокую скорость работы, чем циклы for, и даже более высокую, чем функция map
(хотя функция map может выйти победителем в состязании среди встроенных функций).
Чтобы проверить скорость работы альтернативных реализаций, можно использовать функции time.clock и time.time в модуле time.
В версии Python 2.4 появился новый модуль timeit, который рассматривается в разделе «Хронометраж итерационных альтернатив»
далее в этой главе.
Еще раз об итераторах: генераторы (p. 568)¶
В частности, существуют две языковые конструкции, откладывающие создание ре- зультатов, когда это возможно:
Функции-генераторы – выглядят как обычные инструкции def, но для возврата результатов по одному значению за раз используют инструкцию yield, которая приостанавливает выполнение функции.
Выражения-генераторы – напоминают генераторы списков, о которых рассказывалось в предыдущем разделе, но они не конструируют список с результатами, а возвращают объект, который будет воспроизводить результаты по требованию.
Чтобы по-настоящему понять функции-генераторы, вы должны знать, что они тесно связаны с понятием протокола итераций в языке Python. Как мы уже знаем, итерируемые объекты определяют метод _next _, который либо воз- вращает следующий элемент в итерации, либо возбуждает особое исключение StopIteration по окончании итераций. Доступ к итератору объекта можно по- лучить с помощью встроенной функции iter.
Как отмечалось в главе 14, в Python 2.6 и в более ранних версиях итерируемые объекты объявляют метод next, а не _nex . То же относится и к объектам генераторов, которые мы обсуждаем. В версии 3.0 метод next был переименован в next__ . Для обе- спечения удобства и совместимости предоставляется встроен- ная функция next: вызов next(I) – это то же самое, что вызов I. next _() в версии 3.0 и I.next() в версии 2.6. Для реализации итераций вручную в более ранних версиях Python программы должны вызывать метод I.next().
Как использовать словарь для "перевода" - замены строки 'Anonymous' на строку 'http://'¶
# Type - 'prxtype' (Anonymous, Distorting, Transparent, Socks4, Socks5)
ltype=['Anonymous', 'Distorting', 'Transparent', 'Socks4']
dtype={'Anonymous':'http://', 'Distorting':'http://', 'Transparent':'http://', 'Socks4':'', 'Socks5':''}
ltranslate= dtype[ltype[2]]
ltranslate
di
Посты чуть ниже также могут вас заинтересовать
Комментариев нет:
Отправить комментарий