У меня в репозитории ждут своего часа с десяток бибилиотек для парсинга html страниц. Здесь мы посмотрим, как работает grab.
С одной стороны, автор старается быть последовательным: сначала мы подготовили соглашение о наименованиях (файлов и директорий), потом, вот уже третий пост, последовательно кодируем процессы работы с файлами CSV.
Здесь подошла очередь процесса сохранения скачаных файлов. Очевидно, что сначала файлы нужно скачать. Потому здесь рассмотрим пример с библиотекой grab.
С одной стороны, автор старается быть последовательным: сначала мы подготовили соглашение о наименованиях (файлов и директорий), потом, вот уже третий пост, последовательно кодируем процессы работы с файлами CSV.
Здесь подошла очередь процесса сохранения скачаных файлов. Очевидно, что сначала файлы нужно скачать. Потому здесь рассмотрим пример с библиотекой grab.
Вставка из середины поста. Здесь мне просто необходимо сделать отступление... после вступления. Я начал этот пост неделю назад..., но пришлось ликвидровать собственную безграмотность в понимании кодировок (предыдущий пост). Естественно, здесь меня в первую очередь заботили кодировки скачиваемых файлов. Но оказалось, что в бибилиотеке Grab все эти вопрос решены (см. код класса Response в середине этого поста). Так что все до раздела "Объект response" можно пропустить. Я решил, что могу здесь попробовать подобрать фрагманты кода под мои задачи.
- Репозиторий Grab Bitbucket
- Документация Grab Grab - фреймворк для парсинга сайтов
- Google Grab Google Code
- Github Grab Unicode HOWTO
- Документации Python Standard Encodings
Вот типичный пример того, как не надо делать. Здесь автор взял чужой работающий пример и "воткнул туда" несколько кусков кода. Потом этот "временный" файл пролежал пару месяцев. И вот теперь надо вспоминать, что же он делал? Автор запускал его с дебаггером в редакторе Spyder, потому под кодом приведены копии сообщений из консоли Spyder
In []:
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 28 14:37:02 2013
@author: kiss
"""
import urllib
import csv
import logging
from grab.spider import Spider, Task
from grab import Grab
# Добавимв импорт Selector from grab.selector import Selector
from furl import furl #url parser
class Spider_mail_pages_1(Spider):
# Define Dictionaries
agegd = {"0":"Все", "1":"до 12","2":"12-18","3":"19-24","4":"25-30","5":"31-35","6":"36-40","7":"41-45","8":"46-50","9":"старше 50"}
genderd = {"0":"All", "1":"Male","2":"Female"}
autod = {"mercedes-benz":"Mercedes", "toyota":"Toyota","volkswagen":"VW"}
def prepare(self):
self.readme_file = open('C:\\Users\\kiss\\Documents\\IPython Notebooks\\web\\Spyder\\result.txt', "wb")
self.result_write = csv.writer(self.readme_file)
#### You have to edit this for every Spider #########
base_url='http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=nissan&gender=1&agegroup=0&'
# assign tulips with parts of URLs
all_auto=('mercedes-benz',)#,'nissan','toyota','volkswagen')#
all_gender=('1','2')#There is not comma between '1''2'
all_age=('4',)
#### Warning: the number of URL = len( all_auto)*len( all_gender)*len( all_age)
f = furl(base_url)
# Выставляем счетчик и объявляем список initial_urls
# в него будем добаилять новые URL
self.result_counter = 0 # couner for URL list
self.initial_urls=['NULL',] #URL list
for auto1 in all_auto:
f.args['filter']=auto1
for g in all_gender:
f.args['gender']=g
for age in all_age:
f.args['agegroup']=age
print '"это в цикле f.url= %s /n' % f.url
self.initial_urls.append(f.url)
del self.initial_urls[0]# remove first NULL from URL list
print 'self.initial_urls = %s ' % self.initial_urls
#return self.initial_urls
# Список страниц, с которых Spider начнёт работу
# для каждого адреса в этом списке будет сгенерировано
# задание с именем initial
#initial_urls = ['http://habrahabr.ru/']
#initial_urls=prepare.initial_urls
#print 'initial_urls = ' + self.initial_urls
def task_initial(self, grab, task):
print 'Save body %s' % self.result_counter
path = 'mail/mail_pages/%s.csv' % self.result_counter
grab.response.save(path)
self.folder_readme
self.result_counter += 1# is to after initial_urls
def folder_readme(self,grab):
#path_fr = 'mail/mail_pages/readme.csv'
fr= furl(grab.response.url)
print "fr="+fr
#ucsv="%s;%s;%s;%s;%s" % (fr.args['gender'],fr.args['gender'],fr.args['gender'],fr.args['gender'],fr.args['gender'])
ucsv="98787"
self.result_write.writerow(ucsv)
self.readme_file.close()
return None
#def open_readme (self):
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
# Запустим парсер в многопоточном режиме - два потока
# Можно больше, только вас яндекс забанит
# Он вас и с двумя то потоками забанит, если много будете его беспокоить
bot = Spider_mail_pages_1(thread_number=2)
bot.run()
In []:
Ниже сообщения из консоли Spyder (крякозябы из-за кириллицы в строке print '"это в цикле f.url= %s /n' % f.url)
In []:
>>> runfile('C:/Users/kiss/Documents/IPython Notebooks/web/Spyder/S-mail-csv-1.py', wdir=r'C:/Users/kiss/Documents/IPython Notebooks/web/Spyder')
"это в цикле f.url= http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=1&agegroup=4 /n
"это в цикле f.url= http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=2&agegroup=4 /n
self.initial_urls = ['http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=1&agegroup=4', 'http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=2&agegroup=4']
DEBUG:grab.spider.base:Using memory backend for task queue
DEBUG:grab.network:[01] GET http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=1&agegroup=4
DEBUG:grab.network:[02] GET http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=mercedes-benz&gender=2&agegroup=4
Save body 0
Save body 1
DEBUG:grab.spider.base:Job done!
Мы видим, что в окне консоли редактора есть проблемы с кодировкой. Пока для Python 2.x примем правило: "Строки Питона не поддерживают двухбайтные кодировки" и будем использовать только аглийский язык для комментариеа в скриптах. И будем переходит на третий Питон.
А ниже пример файла, с результатами работы скрипта, один из тех файлов, которые записывались в pages
Полный путь C:Notebookspages
Полный путь C:Notebookspages
In [1]:
filepath="C:\\Users\\kiss\\Documents\\IPython Notebooks\\web\\Spyder\\mail\\mail_pages\\0.csv"
f = open(filepath)
In [2]:
f.encoding
Эта команда должна выдавать кодировку, но она подвисает... Не до нее, но оставим этот баг, как напоминание...
In [3]:
f.readlines
Out[3]:
Так что скачаем на всякий случай документацию к объекту file (в з-ей версии такого объекта уже нет).
In [5]:
help (f)
Проблемы с открытием файлов с кириллицей решаются при помощи импорта библиотеки codecs. Обратите внимание, в выводе два варианта отображения строк (print line, repr(line)):
In [4]:
import codecs
f1 = codecs.open(filepath, encoding='cp1251')
for line in f1:
print line, repr(line)
Легко заметить, что вместо символов кириллицы здесь выводятся шестнадцатиричные последовательности. В предыдущем посте мы решили эту проблему. Здесь ниже приведем только решение, но тратить время на обсуждение не будем, поскольку в любом редакторе этот файл отображается нормально. Что нам, собственно, и нужно.
А ниже снова проверим кодировку, но у объекта f1
А ниже снова проверим кодировку, но у объекта f1
In [7]:
f1.encoding
Out[7]:
Обратите внимание команда f.encoding не выполнилась... Но пока мы не будем разбираться почему... Нас здесь интересуют более насущные проблемы.
Объект response¶
Как видно из первого примера, результаты работы парсера содержатся в объекте response, вот содержание файла документации из моего локального репозитория C:.rst
.. _response:
================
Работа с ответом
================
Объект Response
===============
Результатом обработки запроса является объект класса :class:`~grab.response.Response`. Вы можете получить к нему доступ через аттрибут `response`::
g.go('http://mail.ru')
print g.response.headers['Content-Type']
Смотрите полный перечень аттрибутов и методов объекта `Response` в API справочнике :class:`~grab.response.Response`. Самое важное, что вам нужно знать:
:body: оригинальное тело ответа
:unicode_body(): тело ответа, приведённое к unicode-представлению
:code: HTTP-статус ответа
:headers: HTTP-заголовки ответа
:charset: кодировка документа
:cookies: кукисы ответа
:url: URL документа. В случае автоматической обработки редиректов, этот URL может отличаться от запрашиваемого.
В контексте вопросов с кодировками файлов - атрибут unicode_body() надо использовать, кроме того, действительно, здесь есть все, что нас может понадобится на первых порах. На всякий случай, скопируем код из локального репозитория:
In [5]:
!type C:\\Users\\kiss\\Documents\\GitHub\\grab\\grab\\response.py
Из комментариев в коде ясно, что при скачивании файлов Grab тщательнейшим образом пытается определить кодировку документа. Если это не удается, то сохраняет файл в utf-8. А здесь и онлайн руководствоКодировка документа Причем, документ читается построчно как байтовый файл. Это сохраняет кодировку оригинала. Таким образом, кодировка cp1251 из примера файла в начале поста, скорее всего, взята с сервера. Проверить это предположение легко, вот заголовок:
In []:
Pragma: no-cache
Date: Tue, 04 Feb 2014 06:08:37 GMT
Server: nginx/1.2.9
Content-Type: text/comma-separated-values; charset=windows-1251
Cache-control: no-cache
Content-Disposition: attachment
Connection: keep-alive
Content-Length: 1406
Grab предоставляет возможность перекодировать файл "на лету", чтобы хранить его в utf-8. Стоит ли это делать? Пока не знаю. Так что займемся изучением объекта Response. Вот здесь есть документация на русском
И, оказывается, есть таки довольно подробное описание grab.response: класс ответа сервера
И, оказывается, есть таки довольно подробное описание grab.response: класс ответа сервера
In [11]:
from grab import Grab
g=Grab()
In [12]:
myurl='http://top.mail.ru/referers.csv?id=85837&period=2&date=&sf=0&pp=20&filter_type=0&filter=nissan&gender=1&agegroup=0&'
In [14]:
g.go(myurl)
print g.response.headers['Content-Type']
print g.response.charset
print g.response.url
print g.response.cookies
In [16]:
print g.response.body()
In [15]:
print g.response.unicode_body()
Пока все здорово, проблем с кодировкой не возникает, функция unicode_body() работает замечательно. Проверим ка еще функцию save. Здесь неудача, у объекта unicode_body() нет метода save. А стоит ли его дописывать самому? Пожалуй, что нет, вот, как этот объект будет выглядеть в текстовом файле
In [19]:
ftemp=g.response.unicode_body()
ftemp
Out[19]:
Здесь стоит сделать паузу. Пусть в этом посте будут только возможности Grab и немного Furl... Прежде, чем двигаться дальше надо будет порассуждать о соглашении о наименованиях...
Посты чуть ниже также могут вас заинтересовать
Комментариев нет:
Отправить комментарий