среда, 1 декабря 2010 г.

Как получить список директорий и файлов по отдельности

Как быстро получить по отдельности список директорий (папок) и список файлов в заданной директории.

root, dirs, files = os.walk(Path).next()

Path - путь к директории, по которой нужно вести поиск.

В root попадает значение Path, в переменные dirs, files попадают списки деректорий и файлов, соответственно.

Отправить уведомление по e-mail

Для этих нужд в Python есть специальная библиотека smtplib. В данном примере для пересылки используется сервер Gmail.

#!/usr/bin/env python
# -*- coding: utf-8 -*-  # это чтобы в файле можно было использовать кириллицу
# python '2.6.5'

import smtplib
import email.utils
from email.mime.text import MIMEText

from_addr = 'sender@gmail.com'
to_addrs  = 'recipient@gmail.com'

text = 'Текст сообщения'

# Указываем кодировку
msg = MIMEText(text, "", "utf-8")

# Создаем заголовок сообщения
msg['To'] = email.utils.formataddr(('Имя получателя', to_addrs))
msg['From'] = email.utils.formataddr(('Имя отправителя', from_addr))
msg['Subject'] = 'Тест'


# Параметры авторизации
username = 'username'
pwd = 'password'

# Отправка сообщения
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,pwd)
server.sendmail(from_addr, to_addrs, msg.as_string())
server.quit()

среда, 17 ноября 2010 г.

Определение даты отстоящей на определенное число дней, месяцев и лет от заданной

Очень полезная функция для определения даты, отстоящей на определенное число дней, месяцев и лет от заданной.

import time, datetime
from dateutil.relativedelta import relativedelta

def time_ago(date,delta_date):
  spl_date=date.split('.');
  spl_delta_date=delta_date.split('.');
  day=int(spl_date[0]);month=int(spl_date[1]);year=int(spl_date[2]);
  d_date = datetime.date(year,month,day);
  delta = relativedelta(days=int(spl_delta_date[0]),months=int(spl_delta_date[1]),years=int(spl_delta_date[2]));
  d_result=d_date+delta;
  return d_result.strftime('%d.%m.%Y');


Примеры использования:

>>time_ago('1.1.2010','-1.0.-1')
>>'31.12.2008'

Здесь '31.12.2008' - исходная дата, '-1.0.-1' - указание найти день на один год и один день раньше.

пятница, 5 ноября 2010 г.

Транслитерация кириллических URL или транслит для Google

Как известно, по стандартам консорциума W3C, в URL разрешается использовать только символы ASCII. Хоть это и запрещено, браузеры понимают кириллические урлы, понимают их и поисковики, но с такими именами постоянно появляются разные проблемы, например

  • adsense не понимает кириллические url-каналы,
  • не все хостинги корректно справляются с кириллическим именами html-файлов и директорий,
  • при копировании таких урлов в текстовые редакторы получается абракадабра из символов в шестнадцатеричном представлении,
в общем, постоянная головная боль. Выход из этой ситуации это транслитерация. Главная проблема в том, что систем транслитерации достаточно много, общепринятого стандарта нет. Добавляет проблем то, что существующие средства автоматической транслитерации далеко не всегда указывают какую именно систему они используют.

На сайте проекта Pypi есть два скрипта на python для для транслитерации - это pytils (версия 0.2.3) и trans (версия 1.3).

Плюсом pytils является то что он есть в репозитории ubuntu, но на этом его плюсы заканчиваются, скрипт немного недоделанный, на попытку выполнить пример
print pytils.translit.translify(u"рейтинг")

отвечает что-то вроде: ValueError: Unicode string doesn't transliterate completely, is it russian?

и запускается он только внутри отдельно написанного скрипта типа

# -*- coding: utf-8 -*-
import pytils
print pytils.translit.slugify(u"рейтинг")

Транслитерирует этот скрипт по системе ISO 9:1995 или GOST 7.79B, точно я не разбирался: обе системы транслитерируют слово рейтинг как rejting.
Главное, что Google такую транслитерацию понимает, но не очень уверенно и отвечает Возможно, вы имели в виду: рейтинг  Показаны первые результаты: 2

В интернете можно встретить указания, что Google использует транслитерацию по системе BGN. Однако это не совсем так, гугл выбрал скользкую дорожку собственного стандарта, так, в отличие от системы BGN, гугл транслитерирует кириллическую букву e идущую после гласной как e, а не ye, как того требует BGN.

Так вот, таблица транслитерации скрипта trans кажется как раз соответствует стандарту гугла, например то же слово рейтинг он транслитерирует как ryayting, что, кажется очень нравится гуглу, потому советую взять этот скрипт на вооружение. Скрипт можно немного доработать, чтобы получать строчные переменные, а не юникод, с автоматической заменой пробелов на дефисы и перевода всех букв в нижний регистр.

def u8(string):
  return unicode(string,'utf-8')

def eu8(string):
  return string.encode('utf-8')

def ful_trans(string):
  return eu8(u8(string.replace(' ','-')).encode('trans').lower())

print ful_trans('рейтинг')

понедельник, 21 июня 2010 г.

Парсинг html таблиц

Есть задача: извлечь данные из таблицы на некотором сайте.

Есть следующее решение.

libxml2dom

Скачиваем библиотеку libxml2dom.

Распаковываем архив, ищем файл setup.py, и устанавливаем командой sudo python setup.py install (конечно если у вас стоит Ubuntu ;) )

Далее создаем файл table_parser.py, со следующим содержимым:

# -*- coding: utf-8 -*-

#Извлечение данных из заданных столбцов html-таблицы

#Данные: html-исходник любого сайта
#Аргументы: список заголовков или номера столбцов (начиная с нулевого)
#Результат: список данных по рядам

import libxml2dom

def parse_tables(source, headers, table_index):
    """headers может быть списком строк, если таблица содержит заголовки или
       headers может быть списком целых чисел, если заголовки не заданы.

       Этот метод возвращает вложенные списки.
    """

    #Determine if the headers list is strings or ints and make sure they
    #are all the same type
    j = 0

    #print 'Printing headers: ',headers

    #route to the correct function
    #if the header type is int
    if type(headers[0]) == type(1):

        #run no_header function
        return no_header(source, headers, table_index)

    #if the header type is string
    elif type(headers[0]) == type('a'):

        #run the header_given function
        return header_given(source, headers, table_index)

    else:
        #return none if the headers aren't correct
        return None


#This function takes in the source code of the whole page a string list of
#headers and the index number of the table on the page. It returns a list of
#lists with the scraped information
def header_given(source, headers, table_index):
    #initiate a list to hole the return list
    return_list = []

    #initiate a list to hold the index numbers of the data in the rows
    header_index = []

    #get a document object out of the source code
    doc = libxml2dom.parseString(source,html=1)

    #get the tables from the document
    tables = doc.getElementsByTagName('table')

    try:
        #try to get focue on the desired table
        main_table = tables[table_index]
    except:
        #if the table doesn't exits then return an error
        return ['The table index was not found']

    #get a list of headers in the table
    table_headers = main_table.getElementsByTagName('th')


    #need a sentry value for the header loop
    loop_sentry = 0

    #loop through each header looking for matches
    for header in table_headers:

        #if the header is in the desired headers list
        if header.textContent in headers:

            #add it to the header_index
            header_index.append(loop_sentry)

        #add one to the loop_sentry
        loop_sentry+=1

    #get the rows from the table
    rows = main_table.getElementsByTagName('tr')

    #sentry value detecting if the first row is being viewed
    row_sentry = 0

    #loop through the rows in the table, skipping the first row
    for row in rows:

        #if row_sentry is 0 this is our first row
        if row_sentry == 0:

            #make the row_sentry not 0
            row_sentry = 1337
            continue
        #get all cells from the current row
        cells = row.getElementsByTagName('td')

        #initiate a list to append into the return_list
        cell_list = []

        #iterate through all of the header index's
        for i in header_index:

            #append the cells text content to the cell_list
            cell_list.append(cells[i].textContent)

        #append the cell_list to the return_list
        return_list.append(cell_list)

    #return the return_list
    return return_list

#This function takes in the source code of the whole page an int list of
#headers indicating the index number of the needed item and the index number
#of the table on the page. It returns a list of lists with the scraped info
def no_header(source, headers, table_index):

    #initiate a list to hold the return list
    return_list = []

    #get a document object out of the source code
    doc = libxml2dom.parseString(source, html=1)

    #get the tables from document
    tables = doc.getElementsByTagName('table')

    try:
        #Try to get focus on the desired table
        main_table = tables[table_index]
    except:
        #if the table doesn't exits then return an error
        return ['The table index was not found']

    #get all of the rows out of the main_table
    rows = main_table.getElementsByTagName('tr')

    #loop through each row
    for row in rows:

        #get all cells from the current row
        cells = row.getElementsByTagName('td')

        #initiate a list to append into the return_list
        cell_list = []

        #loop through the list of desired headers
        for i in headers:
            try:
                #try to add text from the cell into the cell_list
                cell_list.append(cells[i].textContent)
            except:
                #if there is an error usually an index error just continue
                continue
        #append the data scraped into the return_list
        return_list.append(cell_list)

    #return the return list
    return return_list



Чтобы воспользоваться этими функциями, можно использовать импорт:

from parse_tables import *

Непосредственно для извлечения данных можно использовать следующую команду python:

parse_tables(html_string, [0,1], 0)

где html_string - это строка, куда записан исходный код сайта с таблицей, [0,1] - номера колонок с данными для извлечения и третий аргумент 0 - это порядковый номер таблицы на сайте.

По материалам http://www.dreamincode.net

четверг, 17 июня 2010 г.

Как конвертировать xls в csv?

xls2csv и xls2tsv в одном флаконе ;)


Вот простая функция, которая конвертирует файл Excel в CSV:

def xls2csv(xls_file,csv_file,input_encoding,output_encoding,separator):
  data=[]
  for sheet_name, values in parse_xls(xls_file, input_encoding): # parse_xls(arg) -- default encoding
       matrix = [[]]
       for row_idx, col_idx in sorted(values.keys()):
    v = values[(row_idx, col_idx)]
    if isinstance(v, unicode):
        v = v.encode(output_encoding, 'backslashreplace')
    else:
        v = str(v)
    last_row, last_col = len(matrix), len(matrix[-1])
    while last_row < row_idx:
        matrix.extend([[]])
        last_row = len(matrix)

    while last_col < col_idx:
        matrix[-1].extend([''])
        last_col = len(matrix[-1])

    matrix[-1].extend([v])

       for row in matrix:
    csv_row = separator.join(row)
    #print csv_row
    data.append(csv_row+'\n')
  outfile = open(tsv_file, 'w')
  for i in data:
    outfile.write(i)
    #print tsv_file
    #print 'i=',i
    #print
  outfile.close()

Для использования этой функции нужно задать в качестве параметров xls_file и сsv_file соответственно путь к исходному и новому файлам, input_encoding output_encoding - кодировка этих файлов (например 'cp1251' и 'utf-8'), separator - разделитель данных в получаемом csv, например запятая: ',' или знак табуляции '\t' (в этом случае, вообще говоря, это будет уже не csv, а tsv файл). Здесь используется библиотека pyExcelerator. В Ubuntu ee можно установить следующим образом: sudo apt-get install python-excelerator

Альтернативные варианты

Теоретически для преобразования xls в csv можно также использовать библиотеки xlrd и xlwrt.
Также в Ubuntu можно использовать программу xls2csv из пакета catdoc, но к сожалению xls2csv из catdoc не умеет использовать табулятор в качестве разделителя и к тому же она не очень хорошо отлажена и иногда дает сбои.