http://www.python.rk.edu.pl/ Serwis poświęcony językowi programistycznemu o nazwie Python, w tym także frameworkowi Django i PyQt4. pl http://www.python.rk.edu.pl/site_media/layout/tux.png http://www.python.rk.edu.pl/ 132 124 http://www.python.rk.edu.pl/w/p/problem-z-ofertami-pracy-w-sieci/ http://www.python.rk.edu.pl/w/p/problem-z-ofertami-pracy-w-sieci/ <![CDATA[Problem z ofertami pracy w sieci]]> Bazując m.in. na swoich doświadczeniach - jest masa serwisów oferujących oferty pracy. Jedne bardziej renomowane, inne "darmowe" i zawierające wiele słabych ofert. Są też "franczyzy" - oferty pracy z jednego serwisu udostępniane w drugim, czy też dodawanie tej samej oferty przez firmę na wielu stronach. Nie ma też chyba konkretnego użytecznego serwisu (poza Google) z prawdziwego zdarzenia będącego wyszukiwarką ofert z większości/wszystkich liczących się serwisów ofertowych. Generalnie zamieszczanie ofert jest płatne, więc jeżeli szukamy dobrych ofert - musimy przejrzeć wszystkie fajne serwisy żeby mieć pewność że sprawdziliśmy wszystkie fajne oferty.

Od strony Django jakby taki serwis, zbieracz ofert pracy mógł wyglądać? Najprostsze rozwiązanie to pełnotekstowa wyszukiwarka indeksująca oferty pracy. Konkurencja dla Google, lecz jeżeli zaoferuje trafniejsze wyszukiwanie (np. z filtrowaniem po branży, województwie czy mieście) to będzie poręczniejszy. Xapian, Whoosh, czy może na całego i Solr? Druga opcja to pobieranie (indeksowanie) ofert pracy, wyciąganie ich treści, dodatkowych danych i zapis we własnej bazie danych. W efekcie otrzymamy serwis podobny do innych ofertowych serwisów, lecz oferujący przegląd ofert z wielu źródeł. Kwestia to napisanie parserów dla każdego serwisu - listę najnowszych ofert zazwyczaj otrzymamy z RSS, do tego trochę regexów do wyciągnięcia treści ogłoszenia z gotowej strony, jak i dla każdego źródła będzie pewnie konieczne trochę poprawek różnego typu (CSS, w parserze), żeby treść importowała się w miarę poprawnie - chyba że i treść ogłoszenia rozbijemy na poszczególne składowe pozbywając się formatowania HTML źródła.

Dla przykładu dość prosty parser:

# -*- coding: utf-8 -*-
from datetime import datetime
import sys
from os import environ
import urllib2
from re import findall

environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import feedparser

from settings import *
from django.contrib.sessions.models import *
# proof-of-concept app
from diamandas.jobmaster.models import *

############
## Jobexpress
############
source = JobSource.objects.get(title='Jobexpress')
## IT

data = feedparser.parse('http://www.jobexpress.pl/recent,it,84.html')
if data and 'entries' in data:
	for elem in data['entries']:
		sum = elem['summary_detail']
		published = sum['value'].split(',')[0].strip().split('-')
		published = datetime(int(published[2]), int(published[1]), int(published[0]))
		company = sum['value'].split(',')[1].strip()
		
		opener = urllib2.build_opener()
		opener.addheaders = [('user-agent', 'Opera/9.64 (X11; Linux x86_64; U; en) Presto/2.1.1'), ('referer', 'http://www.jobexpress.pl/recent,it,84.html')]
		o = opener.open(elem['link'])
		of = o.read()
		text = findall(r'(?xs)<ul\s*class="oferta_pracy">(.*?)<!--\s*eof:\s*oferta''', str(of))
		if text and len(text) > 0:
			text = text[0]
			# cleaning
			text = '<ul>' + text
			text = text.replace('href="', 'href="http://www.jobexpress.pl/')
			text = text.replace('src="', 'src="http://www.jobexpress.pl/')
			
			city = findall(r'(?xs)<span\s*class="cv_label">Miejsce\s*pracy:\s*</span>(.*?)</li>''', text)
			try:
				city = city[0].strip()
			except:
				city = '???'
			
			# TODO
			region = 'Na bazie miasta'
			
			branch = 'Informatyka/Programowanie'
			try:
				j = JobOffer.objects.get(position=elem['title'], company=company, city=city)
			except:
				j = JobOffer(position=elem['title'], company=company, source=source, url=elem['link'], indexed_at=datetime.now(), updated_at=datetime.now(),
						published_on_source=published, city=city, region=region, branch=branch, offer=text)
				j.save()
			
				print u'ZAPISANO: %s' % elem['title']
			else:
				j.updated_at = datetime.now()
				j.offer=text
				j.save()
				print u'ZAKTUALIZOWANO: %s' % elem['title']
			
A modele mogą wyglądać np. tak:
class JobSource(models.Model):
	"""
	Keeps a "list" of sites with job offers that are parsed by the application.
	Just for ease of data manipulation
	"""
	title = models.CharField(verbose_name='Tytuł', max_length=255)
	url = models.CharField(verbose_name='URL Strony głównej', max_length=255)
	css = models.TextField(verbose_name='Style CSS', blank=True)
	class Meta:
		verbose_name = 'Serwis z ofertami'
		verbose_name_plural = 'Serwisy z ofertami'
	def __str__(self):
		return self.title
	def __unicode__(self):
		return self.title

class JobOffer(models.Model):
	"""
	Indexed job offers from various sites
	"""
	position = models.CharField(verbose_name='Stanowisko', max_length=255)
	company = models.CharField(verbose_name='Firma', max_length=255)
	source = models.ForeignKey(JobSource, verbose_name='Źródło')
	url = models.CharField(verbose_name='URL oferty', max_length=255)
	indexed_at = models.DateTimeField(verbose_name='Data zaindeksowania oferty')
	updated_at = models.DateTimeField(blank=True, null=True, verbose_name='Data ostatniej aktualizacji')
	published_on_source = models.DateTimeField(blank=True, null=True, verbose_name='Data publikacji na stronie-źródle')
	is_inactive = models.BooleanField(blank=True, default=False, verbose_name='Oferta nieaktywna')
	city = models.CharField(verbose_name='Miasto', max_length=255)
	region = models.CharField(verbose_name='Województwo', max_length=255)
	branch = models.CharField(verbose_name='Branża', max_length=255)
	offer = models.TextField(verbose_name='Treść oferty')
	class Meta:
		verbose_name = 'Oferta'
		verbose_name_plural = 'Oferty'
	def __str__(self):
		return self.position
	def __unicode__(self):
		return self.position
Powyższy przykład jest dość prosty i sporo można rozbudować ten pomysł (jak i wyciągać rozbite dane, a nie zbitą treść oferty). Ale w efekcie można wyświetlić ogłoszenie u siebie (choć aplikowanie zazwyczaj wymaga przejścia na źródłowy serwis):
jobmaster.png
Normalny serwis z dobrą marką zarabia na publikacji ogłoszeń, a tego typu serwis mógłby zarabiać np. poprzez dodatkowe usługi sprzedawane indeksowanym serwisom (np. własny szablon, wyróżnianie ofert), jak i też standardowo na reklamach kontekstowych. Ważne żeby indeks był kompletny i łatwo dostępny. Czy jest miejsce dla dobrze napisanego serwisu tego typu? Myślę że tak... jakieś komentarze? :)

]]>
2009-07-02 23:08:18.015765
http://www.python.rk.edu.pl/w/p/profilowanie-kodu-pythona-za-pomoca-pympler/ http://www.python.rk.edu.pl/w/p/profilowanie-kodu-pythona-za-pomoca-pympler/ <![CDATA[Profilowanie kodu Pythona za pomocą Pympler]]> Pympler to zestaw narzędzi dla Pythona pozwalający mierzyć, monitorować i analizować wykorzystanie pamięci przez kod Pythona. Narzędzia te są dość dobrze udokumentowane i działają na wszystkich bieżących wydaniach Pythona.]]> 2009-06-30 21:12:59.017521 http://www.python.rk.edu.pl/w/p/pybonjour/ http://www.python.rk.edu.pl/w/p/pybonjour/ <![CDATA[PyBonjour]]> 2009-06-30 20:25:10.494033 http://www.python.rk.edu.pl/w/p/tresc-w-plone3/ http://www.python.rk.edu.pl/w/p/tresc-w-plone3/ <![CDATA[Treść w Plone3]]> 2009-06-30 01:34:00.602760 http://www.python.rk.edu.pl/w/p/instalacja-nowych-skorek-w-plone/ http://www.python.rk.edu.pl/w/p/instalacja-nowych-skorek-w-plone/ <![CDATA[Instalacja nowych skórek w Plone]]> 2009-06-28 19:16:53.283125 http://www.python.rk.edu.pl/w/p/wprowadzenie-do-plone/ http://www.python.rk.edu.pl/w/p/wprowadzenie-do-plone/ <![CDATA[Wprowadzenie do Plone]]> 2009-06-28 17:44:10.511201 http://www.python.rk.edu.pl/w/p/wyspa-mrozu-gra-crpg-w-django-zrzut-kodu-dostepny/ http://www.python.rk.edu.pl/w/p/wyspa-mrozu-gra-crpg-w-django-zrzut-kodu-dostepny/ <![CDATA[Wyspa Mrozu, gra cRPG w Django - zrzut kodu dostępny]]> Postanowiłem wydać paczkę z obecnym kodem bardzo-rozwojowym Wyspy Mrozu, przeglądarkowej gry cRPG napisanej w Django. Paczka jest w wersji anglojęzycznej (częściowo wprowadzone i18n), ale spokojnie można się tym pobawić, przejrzeć kod i podzielić się wrażeniami. Gra jest daleka od ukończenia, jeszcze wiele szczegółów trzeba zaimplementować itd. Paczka dostępna jest na licencji Creative Commons BY NC SA i dalszy publiczny rozwój uzależniony jest od odzewu innych programistów :) Polskie demon dostępne jest w Bibliotece cRPG - wystarczy się zalogować i gotowe. Pakiet zawiera ikony Legendora, grafiki z dreamweavedworlds.com, a także z Baldurs Gate, Icewind Dale (używane tymczasowo w obecnej fazie rozwoju), które mają własne licencje.

ice1.png
ice2.jpg
ice3.jpg
ice4.jpg

Wyspa Mrozu ZIP]]>
2009-06-25 22:29:35.373980
http://www.python.rk.edu.pl/w/p/nowe-tutoriale-dla-pylonsa/ http://www.python.rk.edu.pl/w/p/nowe-tutoriale-dla-pylonsa/ <![CDATA[Nowe tutoriale dla Pylonsa]]> Pylons, PostgreSQL, and Simpycity in 60 Minutes or Less dość szczegółowo opisuje wykorzystanie Simpycity przy tworzeniu serwisu www. Simpycity to biblioteka Pythona mapująca zapytania i procedury PostgreSQL do prostych wywołań w Pythonie. Druga publikacja, a właściwie kod źródłowy to prosty "blog" w MongoDB :) Jest to nierelacyjna baza danych dokumentów podobna do CouchDB, lecz napisana w C++.]]> 2009-06-21 18:50:20.921232 http://www.python.rk.edu.pl/w/p/google-app-engine-python-sdk-123-z-obsluga-django-10/ http://www.python.rk.edu.pl/w/p/google-app-engine-python-sdk-123-z-obsluga-django-10/ <![CDATA[Google App Engine Python SDK 1.2.3 z obsługą Django 1.0]]> Task Queue). Także zgodnie z planem jutro powinny zostać zmniejszone limity dla "darmowych" kont do 6,5 godziny czasu procesora dziennie i 1 GB danych wysłanych i odebranych przez aplikację. ]]> 2009-06-21 18:23:26.753631 http://www.python.rk.edu.pl/w/p/jak-pownce-skalowalo-aplikacje-django/ http://www.python.rk.edu.pl/w/p/jak-pownce-skalowalo-aplikacje-django/ <![CDATA[Jak Pownce skalowało aplikacje Django]]> metody skalowania aplikacji Django, jakie były stosowane w pownce.com, webowym komunikatorze zbudowanym z wykorzystaniem technologii Adobe AIR jak i Django.]]> 2009-06-21 18:09:34.153991