Archive for March, 2009

Yandex & django sitemap

Сегодня столкнулся с тем, что yandex выдавал ошибку, когда я пытался скормить ему sitemap.xml через http://webmaster.yandex.ru/. Причина отказа, как было установлено, долгая генерация файла с картой сайта. Проблема решается просто: нужно кэшировать генерируемый sitemap: предварительно записать его в кэш и затем уже скармливать яндексу.

Если на сайте кэширования ранее не было вообще, то самый простой способ настроить кэш — это использовать файловый backend.

Пишем в settings.py:

CACHE_BACKEND = 'file:///tmp/domain.com'

В файле urls.py заворачиваем генерацию sitemap в cache_page декоратор

from django.views.decorators.cache import cache_page
from django.contrib.sitemaps.views import sitemap

from project.sitemaps import ProjectSitemap

sitemaps = {
    'site': ProjectSitemap,
}

urlpatterns = patterns('',
    (r'^sitemap.xml$', cache_page(sitemap, 60 * 30), {'sitemaps': sitemaps}),

linkfeed.ru & python

Захотелось мне пощупать linkfeed.ru биржу ссылок. Поэтому пришлось писать python-адаптер. Как оказалось биржа вполне себе прогрессивная — возможно забирать базу данных в виде XML. Даже какой-то SOAP API есть для оптимизаторских контор.

Если кому нужно, адаптер можно взять тут: hg.pydev.ru/linkfeed. Код пока очень сырой — я его минут 10 назад написал :-) Как пользоваться написано в README

Приручаем socks прокси

Бывает нужно наладить работу библиотеки через socks прокси, но сама библиотека не предоставляет такой возможности. Если внутри неё используется модуль socket, то проблему можно решить с помощью socksipy

Рассмотрим конкретный пример. Библиотека xmpppy. Она позволяет работать только c HTTP/HTTPS проксями. Попробуем это изменить. Исследовав исходный код, увидим, что подключение к сети осуществляется в методе xmpp.transports.TCPsocket.connect, где создаётся сокет. Этот метод и нужно манкипатчить.

Я сделал так:

import xmpp
import xmpp_hack

xmpp_hack.PROXY = '12.34.56.789'
xmpp_hack.PORT = '7890'
... далее работаем с библиотекой xmpp как обычно!...

Вся суть в xmpp_hack модуле:

import xmpp
import logging
import socket
# Импорируем библиотеку socksify
import socks

from xmpp import transports

PROXY = ''
PORT = 0

# Это метод, которым мы проманкипатчим xmpp либу
# Он в точности похож на оригинал за исключением пары строчек
def custom_connect(self,server=None):
    """ Try to connect. Returns non-empty string on success. """
    try:
        if not server: server=self._server

        # Комментируем стандартное создание сокета
        #self._sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        logging.debug('Using %s:%s SOCKS5 proxy' % (PROXY, PORT))
        # И создаём socks-обёртку, которая имитирует обычный сокет
        self._sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
        self._sock.setproxy(socks.PROXY_TYPE_SOCKS5, PROXY, PORT)

        # Далее продолжается код из оригинального метода
        self._sock.connect(server)
        self._send=self._sock.sendall
        self._recv=self._sock.recv
        self.DEBUG("Successfully connected to remote host %s"%`server`,'start')
        return 'ok'
    except socket.error, (errno, strerror): 
        self.DEBUG("Failed to connect to remote host %s: %s (%s)"%(`server`, strerror, errno),'error')
    except Exception, ex:
        raise
        #logging.error(ex)

# Собственно манкипатчинг!
transports.TCPsocket.connect = custom_connect

Удачного манкипатчинга :-)

Обёртка для pycurl

Уже пару лет я периодически пользуюсь пакетом pycurl для того, чтобы чего-нить распарсить из сети. Библиотека pycurl хороша тем, что она предоставляет в меру простой интерфейс к мощному функционалу и, что важно для меня, позволяет работать с любыми типами прокси: http, https, socks4, socks5. Стандартная библиотека urllib2 не предоставляет возможности работать с socks проксями. Я пробовал искать решения — не нашёл. Самому написать некую обёртку urllib2 & socks у меня мозгов не хватает. Вот тут pycurl и выручает :-)

Так вот, интерфейс у него простой… даже слишком. Поэтому я довольно быстро отказался от родного pycurl API и набросал свой велосипедик. Его код показывать людям нельзя т.к. писал я его на заре изучения питона и, соответственно, он страшненький. Недавно я начал переписывать обёртку с нуля. За проектом можно следить по адресу: http://bitbucket.org/lorien/grab/ Проект назван grab т.к. кроме очевидной задачи сделать более удобный API к pycurl, я преследую цель написать инструмент облегчающий парсинг, грабинг, скрэйпинг вебсайтов.

Рассмотрим пример работы с pycurl с официального сайта http://pycurl.sourceforge.net/doc/curlobject.html

import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://www.python.org/")
import StringIO
b = StringIO.StringIO()
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)
c.perform()
print b.getvalue()

C помощью библиотеки grab пример преобразится в:

from grab import Grab
grab = Grab()
grab.setup(url='http://python.org/')
print grab.request()['body']

На самом деле для одиночных запросов у меня есть shortcut, который позволяет:

from grab import request
print request('http://python.org/')['body']

Естественно, такие простые запросы можно и нужно делать через urllib/urllib2. Выгода использования pycurl проявляется, когда нужно реализовывать сложные запросы, типа работы с socks проксями.