Był pomysł, a obecnie jest już serwis, wersja *Beta* :) www.jobmaster.rk.edu.pl . Jest to wyszukiwarka ofert pracy z różnych serwisów. Na chwilę obecną nie jest ich wiele, ale mają serwis do pokazania łatwiej się rozmawia w kwestiach takich jak udostępnienie ładnego API do indeksowania ofert (teraz te co idą regexami nie zawsze dają poprawnie dobrane dane... a przez API idzie na chwilę obecną Hays i Goldenline). Zastosowałem Whoosha do pełnotekstowego wyszukiwania (na start na pewno starczy, a i tak z czasem źle być nie powinno gdy stare ofert będą ubywać), GeoIP do geolokalizacji IP i dobierania regionu (i ewentualnie miasta) na stronie głównej, blipy do powiadomień na blipie :) Sam serwis stoi na serwerze megiteam.pl .
› Read more
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 (Jest np. praca.money.pl , ale to element większego serwisu i nie promuje się ostro jako multikatalog ofert pracy). 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. W przypadku większych serwisów można by pewnie uzyskać dostęp do API umożliwiającego bezproblemowy import ofert.
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):
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? :)
› Read more
Comment article