вторник, 25 декабря 2012 г.

MySQL CheatSheet


Query

SELECT * FROM table
SELECT * FROM table1, table2, ...
SELECT field1, field2, ... FROM table1, table2, ...
SELECT ... FROM ... WHERE condition
SELECT ... FROM ... WHERE condition GROUP BY field
SELECT ... FROM ... WHERE condition GROUP BY field HAVING condition2
SELECT ... FROM ... WHERE condition ORDER BY field1, field2
SELECT ... FROM ... WHERE condition ORDER BY field1, field2 DESC
SELECT ... FROM ... WHERE condition LIMIT 10
SELECT DISTINCT field1 FROM ...
SELECT DISTINCT field1, field2 FROM ...
SELECT ... FROM t1 JOIN t2 ON t1.id1 = t2.id2 WHERE condition
SELECT ... FROM t1 LEFT JOIN t2 ON t1.id1 = t2.id2 WHERE condition
SELECT ... FROM t1 JOIN (t2 JOIN t3 ON ...) ON ...
SELECT ... FROM t1 JOIN t2 USING(id) WHERE condition

Conditionals

field1 = value1
field1 <> value1
field1 LIKE 'value _ %'
field1 IS NULL
field1 IS NOT NULL
field1 IN (value1, value2)
field1 NOT IN (value1, value2)
condition1 AND condition2
condition1 OR condition2

Data Manipulation

INSERT INTO table1 (field1, field2, ...) VALUES (value1, value2, ...)
INSERT table1 SET field1=value_1, field2=value_2 ...
DELETE FROM table1 / TRUNCATE table1
DELETE FROM table1 WHERE condition
-- join:
DELETE FROM table1, table2 WHERE table1.id1 = table2.id2 AND condition
UPDATE table1 SET field1=new_value1 WHERE condition
-- join:
UPDATE table1, table2 SET field1=new_value1, field2=new_value2, ...
WHERE table1.id1 = table2.id2 AND condition

Browsing

SHOW DATABASES
SHOW TABLES
SHOW FIELDS FROM table / SHOW COLUMNS FROM table / DESCRIBE table / DESC table / EXPLAIN table
SHOW CREATE TABLE table
SHOW CREATE TRIGGER trigger
SHOW TRIGGERS LIKE '%update%'
SHOW PROCESSLIST
KILL process_number
$ mysqlshow
$ mysqlshow database

Create / delete / select / alter database

CREATE DATABASE [IF NOT EXIST] mabase [CHARACTER SET charset] [COLLATE collation]
CREATE DATABASE mabase CHARACTER SET utf8
DROP DATABASE mabase
USE mabase
ALTER DATABASE mabase CHARACTER SET utf8

Create/delete/modify table

CREATE TABLE table (field1 type1, field2 type2, ...)
CREATE TABLE table (field1 type1, field2 type2, ..., INDEX (field))
CREATE TABLE table (field1 type1, field2 type2, ..., PRIMARY KEY (field1))
CREATE TABLE table (field1 type1, field2 type2, ..., PRIMARY KEY (field1, field2))
CREATE TABLE table1 (fk_field1 type1, field2 type2, ...,
  FOREIGN KEY (fk_field1) REFERENCES table2 (t2_fieldA))
    [ON UPDATE|ON DELETE] [CASCADE|SET NULL]
CREATE TABLE table1 (fk_field1 type1, fk_field2 type2, ...,
  FOREIGN KEY (fk_field1, fk_field2) REFERENCES table2 (t2_fieldA, t2_fieldB))
CREATE TABLE table IF NOT EXISTS (...)
CREATE TABLE new_tbl_name LIKE tbl_name
  [SELECT ... FROM tbl_name ...]
CREATE TEMPORARY TABLE table (...)
DROP TABLE table
DROP TABLE IF EXISTS table
DROP TABLE table1, table2, ...
DROP TEMPORARY TABLE table
ALTER TABLE table MODIFY field1 type1 
ALTER TABLE table MODIFY field1 type1 NOT NULL ... 
ALTER TABLE table CHANGE old_name_field1 new_name_field1 type1
ALTER TABLE table CHANGE old_name_field1 new_name_field1 type1 NOT NULL ...
ALTER TABLE table ALTER field1 SET DEFAULT ...
ALTER TABLE table ALTER field1 DROP DEFAULT
ALTER TABLE table ADD new_name_field1 type1
ALTER TABLE table ADD new_name_field1 type1 FIRST
ALTER TABLE table ADD new_name_field1 type1 AFTER another_field
ALTER TABLE table DROP field1
ALTER TABLE table ADD INDEX (field);
ALTER TABLE table ADD PRIMARY KEY (field);
-- Change field order:
ALTER TABLE table MODIFY field1 type1 FIRST
ALTER TABLE table MODIFY field1 type1 AFTER another_field
ALTER TABLE table CHANGE old_name_field1 new_name_field1 type1 FIRST
ALTER TABLE table CHANGE old_name_field1 new_name_field1 type1 AFTER another_field
ALTER TABLE old_name RENAME new_name;

Keys

CREATE TABLE table (..., PRIMARY KEY (field1, field2))
CREATE TABLE table (..., FOREIGN KEY (field1, field2) REFERENCES table2 (t2_field1, t2_field2))
ALTER TABLE table ADD PRIMARY KEY (field);

Privileges

GRANT ALL PRIVILEGES ON base.* TO 'user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, DELETE ON base.* TO 'user'@'localhost' IDENTIFIED BY 'password';
REVOKE ALL PRIVILEGES ON base.* FROM 'user'@'host'; -- one permission only
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'user'@'host'; -- all permissions
SET PASSWORD = PASSWORD('new_pass')
SET PASSWORD FOR 'user'@'host' = PASSWORD('new_pass')
SET PASSWORD = OLD_PASSWORD('new_pass')
DROP USER 'user'@'host'

Main data types

TINYINT (1o: -127+128) SMALLINT (2o: +-65 000)
  MEDIUMINT (3o: +-16 000 000) INT (4o: +- 2 000 000 000)
  BIGINT (8o: +-9.10^18)
  Precise interval: -(2^(8*N-1)) -> (2^8*N)-1
  /!\ INT(2) = "2 digits displayed" -- NOT "number with 2 digits max"
INT NOT NULL auto_increment PRIMARY KEY -- auto-counter for PK
FLOAT(M,D) DOUBLE(M,D) FLOAT(D=0->53) 
  /!\ 8,3 -> 12345,678 -- NOT 12345678,123!
TIME (HH:MM) YEAR (AAAA) DATE (AAAA-MM-JJ) DATETIME (AAAA-MM-JJ HH:MM; années 1000->9999)
  TIMESTAMP (like DATETIME, but 1970->2038, compatible with Unix)
VARCHAR (single-line; explicit size)  TEXT (multi-lines; max size=65535)  BLOB (binary; max size=65535)
  Variants for TEXT&BLOB: TINY (max=255) MEDIUM (max=~16000) LONG (max=4Go)
 Ex: VARCHAR(32), TINYTEXT, LONGBLOB, MEDIUMTEXT
ENUM ('value1', 'value2', ...) -- (default NULL, or '' if NOT NULL)

Forgot root password?

$ /etc/init.d/mysql stop
$ mysqld_safe --skip-grant-tables
$ mysql # on another terminal
mysql> UPDATE mysql.user SET password=PASSWORD('nouveau') WHERE user='root';
## Kill mysqld_safe from the terminal, using Control + \
$ /etc/init.d/mysql start

Repair tables after unclean shutdown

mysqlcheck --all-databases
mysqlcheck --all-databases --fast

понедельник, 24 декабря 2012 г.

Отличия TCP от UDP

В чем отличия TCP от UDP?

Прежде всего, TCP и UDP - это протоколы передачи данных по сети. Основное их отличие в том, что TCP - это протокол с гарантированной доставкой пакетов, а UDP - нет.

TCP - предоставляет "гарантированный" транспортный механизм с предварительным установлением соединения, предоставляющий приложению надёжный поток данных, дающий уверенность в безошибочности получаемых данных, перезапрашивающий данные в случае потери и устраняющий дублирование данных. TCP позволяет регулировать нагрузку на сеть, а также уменьшать время ожидания данных при передаче на большие расстояния. Более того, TCP гарантирует, что полученные данные были отправлены точно в такой же последовательности. В этом его главное отличие от UDP.

UDP - это протокол передачи датаграмм без установления соединения. Также его называют протоколом "ненадёжной" передачи, в смысле невозможности удостовериться в доставке сообщения адресату, а также возможного перемешивания пакетов. В приложениях, требующих гарантированной передачи данных, используется протокол TCP.

UDP обычно используется в таких приложениях, как потоковое видео и компьютерные игры, где допускается потеря пакетов, а повторный запрос затруднён или не оправдан, либо в приложениях вида запрос-ответ (например, запросы к DNS), где создание соединения занимает больше ресурсов, чем повторная отправка.

И TCP, и UDP используют для определения протокола верхнего уровня число, называемое портом.

пятница, 14 декабря 2012 г.

Python Keyboardinterrupt

Python Threading и KeyboardInterrupt.

Как организовать пул потоков?

Допустим у вас есть 100 данных (пусть они находятся в каком-либо списке) и все их нужно переработать в 10 потоков.

За всё время работы скрипта стартует только 10 потоков. Каждый из них, отработав со своим 1 данным, не дохнет, а берет из списка следующее 1 данное и делает с ним эту же работу:

# -*- coding: utf-8 -*-
import threading
inporty Queue
import time
import traceback

class Worker(threading.Thread):
    def __init__(self,queue):
        threading.Thread.__init__(self)
        self.__queue = queue

    def run(self):
        while True:

            try: item = self.__queue.get_nowait() # ждём данные
            except Queue.Empty: break # данные закончились, прекращаем работу

            try: self.work(item) # работа с данными
            except Exception: traceback.print_exc()

            time.sleep(0.5)
            self.__queue.task_done() # задача завершена
        return
    def work(self,item):
        print item
        #pass


def main():
    # выводим в 5 потоков цифры от 1 до 100.
    queue = Queue.Queue()
    num_threads = 5 # 5 потоков

    for x in xrange(100):
        queue.put(x) # заносим данные в очередь
    for i in xrange(num_threads):
        t = Worker(queue) # создаем поток
        t.start() # стартуем
        time.sleep(0.1) # чтобы в консоли друг на друга не накладывались

    queue.join() #блокируем выполнение программы, пока не будут израсходованы данные

    print "Done!"
if __name__ == '__main__':
    main()

Хотелось бы еще, чтобы было можно все это в любой момент остановить. Но поскольку выполнение программы блокируем с помощью queue.join(), то скрипт не обратит внимание на нажатия клавиш Ctrl+C для перерывания программы. Воспользуемся данным решением.

# -*- coding: utf-8 -*-
import threading
import Queue
import time
import traceback



class Worker(threading.Thread):
    def __init__(self,queue):
        threading.Thread.__init__(self)
        self.__queue = queue

        self.kill_received = False # флаг прекращения работы
    def run(self):
        while not self.kill_received:

            try: item = self.__queue.get_nowait() # ждём данные
            except Queue.Empty: break

            try: self.work(item)
            except Exception: traceback.print_exc()

            time.sleep(0.5)
            self.__queue.task_done() # задача завершена
            self.__queue.put(item) # зациклим
        return
    def work(self,item):
        print item


def main():
    queue = Queue.Queue()
    num_threads = 5 # 5 потоков

    threads = []
    for x in xrange(100):
        queue.put(x) # заносим данные в очередь
    for i in xrange(num_threads):
        t = Worker(queue) # создаем нить
        threads.append(t)
        t.start() # стартуем
        time.sleep(0.1)


    # ждем пока в живых не останется только главный поток
    while threading.activeCount()>1:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            print 'Ctrl+C received! Sending kill to threads...'
            for t in threads:
                t.kill_received = True # даем сигнал о завершении всем потокам
    print "Done!"
if __name__ == '__main__':
    main()

вторник, 11 декабря 2012 г.

Python Многопоточный клиент

Касательно активности главного потока. Когда, как вам кажется, вы запускаете ОДИН поток, фактически работает уже ДВА потока. Нужно понимать, что количество потоков, активных в данный момент равняется количеству потоков, запущенных в данный момент вами +1 поток, в котором работает основное тело приложения. Лично я стараюсь писать таким образом, чтобы четко отделять основной поток от запущенных мной. Если этого не делать, то возможно преждевременное (как вам кажется) завершение работы приложения, хотя на самом деле приложение отработает именно так, как вы его написали.

Вроде на словах понятно, теперь приступаем к практике. На практике в CPython есть такое понятние как GIL (Global Interpreter Lock). Под сим подразумевается глобальная блокировка интерпритатора в тот момент когда потоки вашего приложения обращаются к процессору. Фактически, в каждый отдельно взятый момент с процессором работает только один поток. В связи с этим максимальное количество потоков, которое вообще можно запустить в стандартном CPython колеблется в районе 350 штук.
В качестве примера будет сделана попытка реализовать многопоточный парсер www.google.com. Как я уже написал выше, для работы будут использованы исключительно стандартные модули, для выполнения задачи понадобятся модули urllib2, urllib, queue, threading, re.

По порядку:

#==================<Имортирование необходимых модулей>==================
import urllib2
#Модуль для работы с протоколом HTTP, высокоуровневый
import urllib
#Модуль для работы с протоколом HTTP, более низкоуровневый чем urllib2,
#фактически из него необходима одна функция - urllib.urlquote
from Queue import Queue
#Модуль, который представляет собой "Pool", фактически это список, в
#котором на нужных местах вставлены замки таким образом, чтобы к нему
#одновременно мог обращаться только один поток
import threading
#Модуль для работы с потоками, из него понадобится только
#threading.active_count, threading.Thread, threading.Thread.start,
#threading.Rlock
import re
#Модуль для работы с регулярными выражениями, его использование выходит
#за пределы статьи
import time
#Модуль для работы со временем, из него нужна только функция sleep
queue = Queue()
#Обязательное присваивание, нужно делать именно так (т.е. импортировать
#класс Queue из модуля Queue и инициализировать его)
#==================</Имортирование необходимых модулей>=================

#==============================<Настройки>==============================
PROXY = "10.10.31.103:3128"
#Во время написания статьи сижу за прокси-сервером, поэтому в статье
#затрагивается и этот вопрос, этой строкой обьявляется глобальная
#переменная PROXY, в которой находится адрес прокси-сервера. Для работы
#напрямую необходимо указать значение None
HEADERS = {"User-Agent" : "Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1",
           "Accept" : "text/html, application/xml;q=0.9, application/xhtml+xml, image/ png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1",
           "Accept-Language" : "ru,uk-UA;q=0.9,uk;q=0.8,en;q=0.7",
           "Accept-Charset" : "iso-8859-1, utf-8, utf-16, *;q=0.1",
           "Accept-Encoding" : "identity, *;q=0",
           "Connection" : "Keep-Alive"}
#Для того чтобы получить страницу с www.google.com НЕОБХОДИМО использовать
#заголовки браузера, они представлены выше в ассоциативном массиве HEADERS,
#соответствуют реальным заголовкам браузера Opera с маленько модификацией, эти
#заголовки означают что клиент не может принимать zlib compressed data, т.е.
#сжатые данные - не хотел я заморачиваться еще и с разархивироанием страниц, тем
#более что не все сайты их сжимают...
THREADS_COUNT = 10
#В принципе это все настройки приложения, это-количество потоков
DEEP = 30
#Это - значение, которое отвечает за глубину страниц поиска, которые
#нужно просматривать, фактически же определяет собой количество ссылок,
#которые будут собраны сборщиком.
ENCODING = "UTF-8"
#Кодировка ваших файлов (для загрузки данных из файла с запросами и
#последующего их перевода в юникод)
#==============================</Настройки>===================================

LOCK = threading.RLock()
# Вот тут то впервые и затрагивается модуль threading
#создается обьект LOCK, который представляет собой класс threading.RLock из
#модуля threading, это -простейший замок, который запрещает исполнение
#несколькими потоками участка кода который идет после вызова его метода
#acquire() Основным отличием threading.RLock от threading.Lock (тоже класс из
#модуля threading) является то, что каждый поток может обращаться к обьекту
#threading.RLock неограниченное количество раз, обьект threading.Lock может
#вызываться каждым потоком только единожды.

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

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

    def worker():
    # Обьявление функции worker, входных аргументов нет
        global queue
        #Здесь и далее я буду обьявлять функции из глобального пространства
        #имен в локальном для лучшей читабельности кода, хотя в написании
        #софта такое делать строго не рекомендую (!)
        while True:
        #Запуск бесконечного цикла, в котором будет происходить работа
            try:
            #Обработка ошибок, блок try/except, когда обработается
            #ошибка QueueEmpty это значит, что список задач пуст, и поток
            #должен завершить свою работу
                target_link =  queue.get_nowait()
                #Эта строчка олицетворяет собой получение задачи потоком из
                #списка задач queue
            except Exception, error:
            #сам перехват ошибки
                return
                #Завершение работы функции
            parsed_data = get_and_parse_page(target_link)
            #Позже будет реализована функция, которая будет получать
            #страницу и доставать из нее необходимые значения
            if parsed_data != "ERROR":
            #Проверка на то, была ли получена страница
                write_to_file(parsed_data)
                #Также будет реализована функция для записи собранных данных в файл
            else:
                queue.put(target_link)
                #Если страница не была получена, то забрасываем ее обратно в queue

Главное, что нужно четко усвоить — это алгоритм работы самого потока, и что именно потоки должны обрабатывать независимо друг от друга. Итого, задачи потока очень просты — получить ссылку на страницу поиска, передать ее в функцию-обработчик, из которой вернутся ссылки на найденные сайты а также title этих сайтов, после записать ссылки и title в файл (все это будет находиться в parsed_data).

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

def write_to_file(parsed_data):
#Обявление функции write_to_file, аргумент –массив данных для записи
    global LOCK
    global ENCODING
    LOCK.acquire()
    #"Накидывание замка", следующий далее участок кода может выполнятся
    #только одним потоком в один и тот же момент времени
    with open("parsed_data.txt", "a") as out:
    #Используется with statement, открывается файл parsed_data.txt с
    #правами "a", что означает дозапись в конец файла, и присваиваевается
    #хэндлеру на файл имя out (я так привык)
        for site in parsed_data:
        #Проход циклом по всем элементам parsed data, имя активного в
        #данный момент элемента будет site
            link, title = site[0], site[1]
            #Присваивание переменным link и title значений из кортежа site
            title = title.replace("<em>", "").replace("</em>", "").replace("<b>", "").replace("</b>", "")
            #.replace -это замена HTML-тэгов, которые проскакивают в title и совершено не нужны
            out.write(u"{link}|{title}\n".format(link=link, title=title).encode("cp1251"))
            #Производится сама запись в файл, используется оператор форматирования
            #строк .format, в отличие от % он поддерживает именованные аргументы, чем я и не
            #преминул воспользоваться, таким образом в файл пишется строка вида:
            #ссылка на сайт | title страницы\n -символ переноса строки(все это переводится
            #из юникода в cp1251)
    LOCK.release()
    #"Отпирание"  замка, в противном случае ни один из следующих
    #потоков не сможет работать с этим участком кода. По-хорошему, тут тоже нужно
    #сделать обработку ошибок, но это учебный пример, да и ошибка там может
    #возникнуть (после добавки замка в этот участок кода) только если во время
    #работы приложения выставить атрибут “только чтение” для данного пользователя
    #относительно файла parsed_data.txt

Далее идет реализация функции get_and_parse_page:

def get_and_parse_page(target_link):
#Обьявление функции, аргумент – ссылка на страницу
    global PROXY
    #Указывает на то, что в данной функции используется переменная PROXY
    #из глобального пространства имен
    global HEADERS
    #То же и для переменной Headers
    if PROXY is not None:
    #Если значение PROXY не равно None
        proxy_handler = urllib2.ProxyHandler( { "http": ""+PROXY+"/" } )
        #Создается Прокси-Хэндлер с указанным прокси
        opener = urllib2.build_opener(proxy_handler)
        #Далее создается opener c созданным ранее Прокси-Хэндлером
        urllib2.install_opener(opener)
        #И наконец-то он устанавливается, теперь нет необходимости в
        #шаманствах, все запросы в которых будет использоваться urllib2
        #(в пределах этой функции будут направляться через указанный ранее
        #PROXY)
    page_request = urllib2.Request(url=target_link, headers=HEADERS)
    #Создается обьект Request, который олицетворяет собой Request instance,
    #фактически это GET запрос к серверу с указанными параметрами, мне
    #же необходимо использовать заголовки...
    try:
    #Обработка всех возможных ошибок, возникающих во время получения
    #страницы, это нехорошо, но лучше чем полное отсутствие обработки
        page = urllib2.urlopen(url=page_request).read().decode("UTF-8", "replace")
        #Переменной page присваиваем прочитанное значение страницы запроса, переведенное
        #в unicode из кодировки UTF-8 (кодировка, используемая на www.google.com) (в
        #Python 2.6 unicode -это отдельный тип данных(!))
    except Exception ,error:
    #Сам перехват ошибки и сохранение ее значения в переменную error
        print str(error)
        #Вывод ошибки в консоль, прведварительно переведя ее в строку
        #(просто на всякий случай)
        return "ERROR"
        #Возврат из функции в том случае, если во время работы возникла ошибка
    harvested_data = re.findall(r'''\<li\ class\=g\>\<h3\ class\=r\>\<a\ href\=\"(.*?)".*?>(.*?)\<\/a\>\<\/h3\>''', page)
    #Сбор со страницы поиска ссылок и title найденных страниц
    #Очистка данных от результатов поиска по блогам, картинкам и др. сервисам гугла
    for data in harvested_data:
    #Для каждого элемента массива harvested_data присвоить ему имя data
        if data[0].startswith("/"):
        #Если нулевой элемент массива data(ссылка) начинается с символа /
            harvested_data.remove(data)
            #Удаляем его из массива harvested_data
        if ".google.com" in data[0]:
        #Если нулевой элемент массива data(ссылка) имеет в себе .google.com
            harvested_data.remove(data)
            #Также удаляем его из массива harvested_data
    return harvested_data
    #Возвращаем собранные значения из функции

Наконец-то дошла очередь до реализации основного тела приложения:

def main():
#Обявление функции, входных аргментов нет
    print "STARTED"
    #Вывод в консоль о начале процесса
    global THREADS_COUNT
    global DEEP
    global ENCODING
    #Обьявляние о том что эти переменные будут использоваться
    #из глобального пространства имен
    with open("requests.txt") as requests:
    #Открываем файл requests в котором находятся запросы к поисковику
         for request in requests:
         #На данном файлхэндлере доступен итератор, поэтому можно
         #пройтись по файлу циклом, без загрузки файл в оперативку, но это
         #тоже не важно, я все равно его туда загружу:)
                request = request.translate(None, "\r\n").decode(ENCODING, "replace")
                #Очистка запроса от символов конца строки а также их
                #перевод в юникод (с заменой конфликтных символов)
                empty_link = "www.google.com/search?hl=ru&client=opera&rls=ru&hs=67v&q={request}&start={N}&sa=N"
                #Это пустой адрес страницы поиска, отформатирован
                for i in xrange(0, DEEP, 10):
                #Проход итератором по диапазону #чисел от 0 до DEEP,
                #который представляет собой максимальную глубину поиска с
                #шагом в 10, т.е. получаем из этого диапазона только
                #числа десятков, т.е. 10, 20, 30 (как идет поиск у гугла)
                     queue.put(empty_link.format(request=request.encode("UTF-8"), N=i))
                     #Добавление в очередь каждой сгенерированной ссылки
                     #и перевод её в кодировку UTF-8 (для гугла)
    for _ in xrange(THREADS_COUNT):
    #Проход циклом по диапазону чисел количества потоков
        thread_ = threading.Thread(target=worker)
        #Создается поток, target-имя функции, которая являет собой
        #участок кода, выполняемый многопоточно
        thread_.start()
        #Вызывается метод start() , таким образом поток запускается
    while threading.active_count() >1:
    #До тех пор, пока количество активных потоков больше 1 (значит,
    #запущенные потоки продолжают работу)
        time.sleep(1)
        #Основной поток засыпает на 1 секунду
    print "FINISHED"
    #Вывод в консоль о завершении работы приложения

В итоге получаем нормально работающий многопоточный парсер.

Код:
Эта статья + исходники: sendspace.com/file/mw0pac
Код с русскими коментариями: dumpz.org/15202/

Добавлю свои 5 копеек: никто не запрещает разделить задачу еще больше — сделать 3 очереди и 3 типа потоков (скачка, парсинг, сохранение). Это позволит начать обработку входных данных не дожидаясь окончательной их загрузки (если потоки запустить до начала загрузки данных в очередь) и сильнее разнести части кода (например для увеличения кол-ва разработчиков). Дальше можно нитки демонизировать, что позволит оставить их болтаться до тех пор пока не закончится основной поток. Забирать данные в этом случае можно и проще и надежнее:
в нитке остаётся только бесконечно крутить:
while True:
# забираем данные из очереди дожидаясь пока они там будут
data = queueN.get()
# делаем свое грязно дело
do_some_work(data)
# помечаем сделанную работу в очереди
queue.task_done()

в основной поток будет выглядеть примерно вот так:
queue1 = Queue()
queue2 = Queue()
queue3 = Queue()

# тип 1
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker)
thread_.setDaemon(True) # для питона 2.5 (в 2.6+ есть обратносовместимый метод)
thread_.start()
# тип 2
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker2)
thread_.setDaemon(True)
thread_.start()

# тип 3
for _ in range(NUMBER):
thread_ = threading.Thread(target=worker3)
thread_.setDaemon(True)
thread_.start()

# тут запихиваем в очереди данные, и по мере того как очередь будет наполнятся она начнет обрабатываться

# ждем когда закончатся все очереди
queue1.join()
queue2.join()
queue3.join()

Все, после того как третья очередь завершится основной поток выйдет и грохнет все висящие нитки. 

Python Многопоточный сервер

Прежде чем мы приступим к разбору кода, работающего с потоками, нам нужно рассмотреть наиболее важную вещь — глобальную блокировку интерпретатора (global interpreter lock, GIL) Python. Если два или более потока попытаются манипулировать одним и тем же объектом в одно и то же время, то неизбежно возникнут проблемы. Глобальная блокировка интерпретатора исправляет это. В любой момент времени действия может выполнять только один поток. Python автоматически переключается между потоками, когда в этом возникает необходимость.

Использование модуля Threading.

Модуль threading предоставляет нам простой способ работы с потоками. Его класс Thread может быть унаследован (subclassed) для создания потока или нескольких потоков. Метод run должен содержать код, который вы желаете выполнить при выполнении потока. Звучит просто, не так ли? Вот, посмотрите:

import threading
class MyThread(threading.Thread):
    def run(self):
        print 'Insert some thread stuff here.'
        print 'It'll be executed...yeah....'
        print 'There's not much to it.'

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

import threading
class MyThread(threading.Thread):

    def run(self):
        print 'You called my start method, yeah.'
        print 'Were you expecting something amazing?'

 MyThread().start()

Конечно, всего один поток это не бог весть что. Как и люди, потоки через некоторое время остаются в одиночестве. Давайте создадим группу потоков:

import threading

theVar = 1

class MyThread(threading.Thread):
    def run ( self ):
        global theVar
        print 'This is thread ' + str(theVar) + ' speaking.'
        print 'Hello and good bye.'
        theVar = theVar + 1

for x in xrange ( 20 ):
    MyThread().start()

Давайте теперь сделаем с помощью модуля threading нечто условно-полезное. Сервера часто используют потоки для работы в одно и то же время с несколькими клиентами. Давайте создадим простой, но расширяемый сервер. Когда клиент подключится к нему, сервер создаст новый поток для обслуживания этого клиента. Чтобы отправлять потоку данные клиента нам понадобится перекрыть метод __init__ класса Thread, чтобы он принимал параметры. Отныне сервер будет отправлять поток своей дорогой и ждать новых клиентов. Каждый поток будет посылать упакованный (pickled) объект соответствующему клиенту, после чего печатать не более десяти строк, полученных от клиента. (Упакованный объект в общем случае является объектом, уменьшенным до нескольких символов. Это полезно при сохранении объектов для последующего использования или для передачи объектов по сети).

import pickle
import socket
import threading

# We'll pickle a list of numbers:
someList = [1, 2, 7, 9, 0]
pickledList = pickle.dumps(someList)

# Our thread class:
class ClientThread(threading.Thread):

    # Override Thread's __init__ method to accept the parameters needed:
    def __init__(self, channel, details):
        self.channel = channel
        self.details = details
        threading.Thread.__init__(self)

    def run(self):
        print 'Received connection:', self.details[0]
        self.channel.send(pickledList)
        for x in xrange(10):
            print self.channel.recv(1024)
        self.channel.close()
        print 'Closed connection:', self.details[0]

# Set up the server:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', 2727))
server.listen(5)

# Have the server serve "forever":
while True:
    channel, details = server.accept()
    ClientThread(channel, details).start()

Теперь нам нужно создать клиента, который будет подключаться к серверу, получать от него упакованный объект, распаковывать (reconstructs) объект и, наконец, посылать десять сообщений и закрывать соединение:

import pickle
import socket

# Connect to the server:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 2727))

# Retrieve and unpickle the list object:
print pickle.loads(client.recv(1024))

# Send some messages:
for x in xrange(10):
    client.send('Hey. ' + str(x) + '\n')

# Close the connection
client.close()

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

import pickle
import socket
import threading

# Here's our thread:
class ConnectionThread(threading.Thread):

    def run(self):
        # Connect to the server:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect (('localhost', 2727))

        # Retrieve and unpickle the list object:
        print pickle.loads(client.recv(1024))

        # Send some messages:
        for x in xrange(10):
            client.send('Hey. ' + str(x) + '\n')

        # Close the connection
        client.close()

# Let's spawn a few threads:
for x in xrange(5):
    ConnectionThread().start()

Пулы потоков (pooling threads).

Важно помнить, что потоки не появляются мгновенно. Создание большого их числа может замедлить ваше приложение. Чтобы создать поток и, позднее, уничтожить его, требуется время. Потоки могут также потреблять много ценных системных ресурсов в больших приложениях. Эта проблема легко решается путём создания ограниченного числа потоков (set number of threads) (пула потоков) и назначения им новых задач, в общем, повторного их использования. Соединения будут приниматься и передаваться тому потоку, который раньше всех закончит работу с предыдущим клиентом.

Если вы по-прежнему не понимаете, сравните это с больницей. Скажем, у нас есть пятеро врачей. Это наши потоки. Пациенты (клиенты) приходят в больницу и, если врачи заняты, сидят в приёмном покое.

Очевидно, нам нужно нечто, что сможет передавать данные клиента в наши потоки, не вызывая при этом проблем (оно должно быть потокобезопасным). Модуль Queue Python делает это для нас. Клиентская информация сохраняется в объекте Queue, откуда потоки извлекают её по мере надобности.

Давайте переделаем наш сервер, чтобы оценить преимущества пула потоков:

import pickle
import Queue
import socket
import threading

# We'll pickle a list of numbers, yet again:
someList = [1, 2, 7, 9, 0]
pickledList = pickle.dumps(someList)

# A revised version of our thread class:
class ClientThread(threading.Thread):

# Note that we do not override Thread's __init__ method.
# The Queue module makes this not necessary.

    def run(self):
        # Have our thread serve "forever":
        while True:
            # Get a client out of the queue
            client = clientPool.get()

            # Check if we actually have an actual client in the client variable:
            if client != None:
                print 'Received connection:', client[1][0]
                client[0].send(pickledList)
                for x in xrange(10):
                    print client[0].recv(1024)
                client[0].close()
                print 'Closed connection:', client[1][0]

# Create our Queue:
clientPool = Queue.Queue(0)

# Start two threads:
for x in xrange(2):
    ClientThread().start()
# Set up the server:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', 2727))
server.listen(5)

# Have the server serve "forever":
while True:
    clientPool.put(server.accept())

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

Дополнительные хитрости.

Работа с потоками не заключается только в их создании и уничтожении. Класс Thread из модуля threading содержит ещё несколько методов, которые могут вам пригодиться. Первые два предназначены для именования потоков. Метод setName присваивает потоку имя, а метод getName возвращает имя потока:

import threading

class TestThread(threading.Thread):
    def run(self):
        print 'Hello, my name is', self.getName()

cazaril = TestThread()
cazaril.setName('Cazaril')
cazaril.start()

ista = TestThread()
ista.setName('Ista')
ista.start()

TestThread().start()

Ничего удивительного. Также, как вы можете видеть, у потоков есть имена, даже если вы их не задавали.

Мы также можем проверить, является ли поток живым, воспользовавшись методом isAlive. Если поток ещё не закончил выполняться, независимо от того, что происходит в его методе run, то он классифицируется как живой:

import threading
import time

class TestThread(threading.Thread):

    def run(self):
        print 'Patient: Doctor, am I going to die?'

class AnotherThread(TestThread):

    def run (self):
        TestThread.run(self)
        time.sleep(10)

dying = TestThread()
dying.start()
if dying.isAlive():
    print 'Doctor: No.'
else:
    print 'Doctor: Next!'

living = AnotherThread()
living.start()
if living.isAlive():
    print 'Doctor: No.'
else:
    print 'Doctor: Next!'

Второй поток остаётся в живых, поскольку мы заставили его ждать, воспользовавшись методом sleep модуля time.

Если нам нужно, чтобы поток дождался завершения другого потока, можно воспользоваться методом join:

import threading
import time

class ThreadOne(threading.Thread):

    def run(self):
        print 'Thread', self.getName(), 'started.'
        time.sleep ( 5 )
        print 'Thread', self.getName(), 'ended.'

class ThreadTwo(threading.Thread):

    def run(self):
        print 'Thread', self.getName(), 'started.'
        thingOne.join()
        print 'Thread', self.getName(), 'ended.'

thingOne = ThreadOne()
thingOne.start()

thingTwo = ThreadTwo()
thingTwo.start()

Мы также можем использовать метод setDaemon. Если при вызове в него передаётся значение True и другие потоки завершили своё исполнение, то из основной программы будет произведён выход, а поток продолжит работу:

import threading
import time

class DaemonThread(threading.Thread):

    def run(self):
        self.setDaemon(True)
        time.sleep(10)

DaemonThread().start()
print 'Leaving.'

Python также содержит модуль thread, работающий на более низком уровне, чем threading. Хочу обратить ваше внимание на одну особенность: это содержащаяся в нём функция start_new_thread. Используя её мы можем превратить обычную функцию в поток:

import thread

def thread(stuff):
    print "I'm a real boy!"
    print stuff

thread.start_new_thread(thread, ('Argument'))

Заключение.

О многопоточности можно рассказать значительно больше, чем я сделал в этой статье, но я не буду пытаться объять необъятное. Кроме того, как упомянул Гвидо ван Россум, преимущества, которые даёт сложная многопоточность в Python могут быть сведены на нет последствиями. Однако, небольшая доза здравого смысла может устранить большинство проблем в простой многопоточности.

Многопоточность очень важна, когда дело касается компьютерных приложений и, как я упоминал раньше, Python её поддерживает. При условии правильного использования, эффект от применения потоков может быть очень благотворным и часто даже критическим, как я подчеркивал в этой статье.

Python PycURL HTTP запросы на получение контента

Получение какого-либо содержимого, находящегося по данному URL через PycURL.


import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'http://news.ycombinator.com')
c.perform()


Данный код выполнит GET-запрос по заданному URL и выведет полученные в ответе данные в стандартный поток вывода stdout.

Сначала создается новый объект Curl, затем устанавливаются опции для передачи URL внутрь объекта для дальнейшей обработки, после чего вызывается метод объект perform(), который выполняет HTTP-запрос по заданному URL.

Данный код является типичным примером работы PycURL: создание объекта, установка опций и выполнение запроса.

Далее мы рассмотрим как выводить полученный от сервера ответ в переменную вместо стандартного потока вывода stdout.


import pycurl
import cStringIO

buf = cStringIO.StringIO()

c = pycurl.Curl()
c.setopt(c.URL, 'http://news.ycombinator.com')
c.setopt(c.WRITEFUNCTION, buf.write)
c.perform()

print buf.getvalue()
buf.close()


Здесь мы используем строковый буфер, который позволяет cURL записывать полученный от сервера ответ в него. Устанавливая опцию WRITEFUNCTION и указывая ей записать ответ сервера в буфер, мы можем перехватить поток вывода как только будет вызван метод perform(). Вы так же можете использовать StringIO вместо cStringIO, но последний работает быстрее.
Обратите внимание, что опять поведение объекта задается через опции setopt().


Если вам надо, чтобы ваш запрос прошел через прокси, а также установить таймауты на соединение и чтение полученных данных, то используйте этот код:



import pycurl
import cStringIO

buf = cStringIO.StringIO()

c = pycurl.Curl()
c.setopt(c.URL, 'http://news.ycombinator.com')
c.setopt(c.WRITEFUNCTION, buf.write)
c.setopt(c.CONNECTTIMEOUT, 5)
c.setopt(c.TIMEOUT, 8)
c.setopt(c.PROXY, 'http://inthemiddle.com:8080')
c.perform()

print buf.getvalue()
buf.close()


Откуда мы знаем какую опцию мы можем использовать и что она делает? Легко! Перейдите по ссылке http://curl.haxx.se/libcurl/c/curl_easy_setopt.html и найдите опцию, которая вам нужна. Установите опцию в метод setopt(). Но не устанавливайте опцию "CURLOPT_" так как PycURL устанавливает ее за вас автоматически, так что вам не нужно вводить "CURLOPT_" каждый раз).

Далее попробуем выполнить POST-запрос.


import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'http://myfavpizzaplace.com/order')
c.setopt(c.POSTFIELDS, 'pizza=Quattro+Stagioni&extra=cheese')
c.perform()


Установкой опции POSTFIELDS запрос автоматически будет послан как POST-запрос. POST-запрос предназначен для передачи значений из форм. Значения должны быть закодированы через URL-encode (использую urllib.urlencode()).

Если что-то происходит не так как вы ожидали, то вы можете вывести сырой запрос, используя опцию VERBOSE.


import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'http://myfavpizzaplace.com/order')
c.setopt(c.POSTFIELDS, 'pizza=Quattro+Stagioni&extra=cheese')
c.setopt(c.VERBOSE, True)
c.perform()


Установка опции VERBOSE выведет информацию в стандартный поток вывода stdout, после чего вы сможете разобраться с тем, что происходит с соединением при создании HTTP-запроса и узнать какие заголовки и ответ возвращаются обратно от сервера.

Далее поработаем с cookie. Установить в запросе cookie очень просто.


import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'http://myappserver.com/ses1')
c.setopt(c.COOKIEFILE, '')
c.setopt(c.VERBOSE, True)
c.perform()

c.setopt(c.URL, 'http://myappserver.com/ses2')
c.perform()


Давайте представим, что myappserver.com/ses1 создает сессию, используя сессионную cookie, а так же устанавливает обычные cookie, которые необходимо передать myappserver.com/ses1. Так что cookie надо послать серверу во время второго запроса.
Здесь мы создаем 1 объект Curl и выполняем 2 запроса. В этом случае при повторном запросе используются те же опции, что и для первого до тех пор пока они не будут перезаписаны. В этом случае мы устанавливаем опцию COOKIEFILE которая обычно используется для установки пути для записи файла c cookie, посланного сервером как HTTP Cookie header. Однако мы устанавливаем значение пути в виде пустой строки, что делает cURL cookie-безопасным,  врезультате чего все присланные cookie будут пересланы подзапросам.
Поскольку мы установили опцию VERBOSE, то мы увидим что происходит во время выполнения запросов.

Вот более эффективная версия предыдущего кода.

import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'http://myappserver.com/ses1')
c.setopt(c.CONNECTTIMEOUT, 5)
c.setopt(c.TIMEOUT, 8)
c.setopt(c.COOKIEFILE, '')
c.setopt(c.FAILONERROR, True)
c.setopt(c.HTTPHEADER, ['Accept: text/html', 'Accept-Charset: UTF-8'])
try:
    c.perform()

    c.setopt(c.URL, 'http://myappserver.com/ses2')
    c.setopt(c.POSTFIELDS, 'foo=bar&bar=foo')
    c.perform()
except pycurl.error, error:
    errno, errstr = error
    print 'An error occurred: ', errstr



В данном примере мы просто прописали таймауты. Вы всегда должны так делать в реальных проектах. А также добавили HTTP заголовки и установили опцию FAILONERROR чтобы cURL в случае получения HTTP-кода больше или равного коду 400 завершал программу. В этом случае PycURL выбросит исключение, которое позволит разобраться в ситуации.

Для выполнения нескольких параллельных запросов одновременно вы можете использовать объект CurlMulti, о котором вы можете прочитать в документации по адресу http://pycurl.sourceforge.net/doc/curlmultiobject.html

Python Отправка GET запроса через urllib и urllib2

Пример берет перевод с translate.google.com


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

import re
import urllib
import urllib2

def translate(text, sl, tl):
    params = { 'ie': 'UTF-8', 'text': text, 'sl': sl, 'tl': tl }
    url = 'http://translate.google.com/translate_t?'
    req = urllib2.Request(url + urllib.urlencode(params), headers={ 'User-Agent': 'Mozilla/5.0', 'Accept-Charset': 'utf-8' })
    page = urllib2.urlopen(req).read()
   
    return re.findall(r'<textarea name=utrans[^<>]*">([^<>]*)</textarea>', page)[0].replace('&lt;br&gt;','\n')

print translate('Проверка перевода\nЕсть', 'ru', 'en')
print translate('Hello world', 'en', 'ru')


Результат вывода в консоль:

Checking the translation
There is
Привет мир