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 (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']
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

Comment article