Python и SEO

Парсер Grab:Spider.

Вот ссылка о нём. Парсер на пару строк, парсит 10к популярных фильмов с кинопоиска:

from grab.spider import Spider, Task, Data
from grab.tools.logs import default_logging
from grab import Grab
import re

class KinoSpider(Spider):
    def task_initial(self, grab, task):
        for xpath in grab.tree.xpath('//*/div[@class="stat"]/div/a'):
            name = xpath.text_content().encode('utf8')
            name = re.sub("\(.*?\)",'',name)
            self.out.write(name+"\n")
            self.out.flush()

def main():
    initial_urls = ["http://www.kinopoisk.ru/level/56/day/2012-02-22/page/{0}/".format(x) for x in xrange(1,51)]
    threads = 50
    default_logging(grab_log="log/log.txt")
    fl = open("out.txt","w")

    bot = KinoSpider(thread_number=threads,network_try_limit=2)
    bot.initial_urls = initial_urls
    bot.out = fl

    try: bot.run()
    except KeyboardInterrupt: pass
    fl.close()

    print bot.render_stats()

if __name__ == '__main__':
    main()

Процесс парсинга занимает 3-5 секунд!

 

Автор: kalombo

Февраль 23, 2012 в 6:29 пп

Категория: Разное

Свой дистрибутив Python для Windows.

Создал свой дистрибутив python с блекджеком и шлюхами (grab,lxml,pycurl). Сделал это с помощью программы installshield, просто закинул необходимые модули в site-packages. Давно это надо было сделать, а то запарило уже всем подряд объяснять, скачай тут, запусти то, пропиши setup.py install…

 

Автор: kalombo

Февраль 11, 2012 в 5:40 пп

Категория: Python,Клиентам

Метки: ,

sqlalchemy

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

import sqlalchemy
from sqlalchemy.orm import sessionmaker, scoped_session

connection = "mysql://root:@localhost/mydatabase"
# pool_size - указывает сколько запросов хранить в очереди, если будет больше, вылетит exception
engine = sqlalchemy.create_engine(connection, pool_size=30)
# Создаем объект Session и дальше во всех потоках используем только его.
Session = scoped_session(sessionmaker(bind=engine))

#Accounts - моя таблица, к которой мне нужно делать запросы, код по настройке отображения я опустил.
accounts = Session.query(Accounts)
account = accounts[0]

Один интересный момент, account, созданный в одном потоке и переданный в другой, изменить не получится.
Если мы поменяем атрибуты account в новом потоке и выполним Session.commit() — таблица не обновится.

 

Автор: kalombo

Январь 30, 2012 в 1:43 пп

Категория: Разное

Threading и KeyboardInterrupt.

Как организовать пул потоков? Допустим у меня есть 100 данных (пусть они находятся в каком-либо списке) и все их нужно переработать в 10 потоков. Стартуем 10 потоков, в каждый передаем по 1 данному, следим за потоками, как только один отработал, стартуем новый и так пока не закончатся данные… В общем, это не то, чтобы бред, но есть способ поудобней.

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

# -*- coding: utf-8 -*-
import threading, Queue, 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(), то скрипт наши Ctr+C банально «не услышит». Я начал искать, что делать в таком случае, нашел вот такое решение, но у меня оно работало абы как. А потом, поразмыслив, я сам придумал решение, которое оказалось ужасно простым.

# -*- coding: utf-8 -*-
import threading, Queue, 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()
 

Автор: kalombo

Январь 15, 2012 в 12:37 дп

Категория: Python

Метки: , ,

Multiprocessing

Узнал про модуль spynner, это такой управляемый браузер. Если на страничке много трудноподдающегося JavaScript-а, то вместо urllib2 или curl можно попробовать натравить на него spynner. Основы работы с ним можно почитать здесь. В общем был он нужен мне, но проблема в том, что он достаточно медленный, а работать в несколько потоков с ним не представляется возможным, так он берет своё начало от GUI-класса библиотеки Qt, которые в свою очередь можно использовать только в main-потоке. Поэтому вместо многопоточности пришлось думать о многопроцессовости. Ну это тоже самое если сделать однопоточный скрипт и
запустить одновременно N его копий. Про многопоточность я уже написал один пост, который полностью состоит из быдлокода=) С тех пор, я чутка поумнел и для организации потоков наследуюсь от класса Thread. С многопроцессовостью всё аналогично, только классы другие, а методы и структура вся та же.

# coding: utf8
import spynner
from spynner import browser
import multiprocessing
from multiprocessing import Process

class Serfer(multiprocessing.Process):
    def __init__(self,queue):
        multiprocessing.Process.__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

            error = True
            try: error = self.serfing(item)
            except: traceback.print_exc()

            time.sleep(0.1)
            self.__queue.task_done() # задача завершена
            if error: self.__queue.put(item) # Если была ошибка, то еще раз с этими данными
        return
    def serfing(self,url):

        br = browser.Browser() # запускаем браузер
        br.create_webview()
        br.show() # отображаем
        br.load(url) # загружаем страницу
        br.wait_a_little(75)

        br.destroy_webview()
        br.close() # закрываем браузер

def main():
    queue = multiprocessing.JoinableQueue() # создаем очередь заданий
    processes = 5
    # Урлы по которым надо будет перейти в браузере
    urls = ["http://ya.ru","http://google.ru","http://yahoo.com",
"http://bing.com","http://rambler.ru"]

    for url in urls:
        queue.put(url) # заносим данные в очередь
    for i in xrange(processes):
        t = Serfer(queue) # создаем процесс
        t.start() # стартуем
        time.sleep(0.1)
    queue.join() # приостанавливаем дальнейшее выполнение кода, пока очередь не опустошится
    print "Done"
if __name__ == '__main__':
    main()
 

Автор: kalombo

Сентябрь 29, 2011 в 10:56 пп

Категория: GUI,Python

Метки: , , ,