На Хабре уже была заметка, как вытащить и распарсить список вакансий с hh.ru.
Однако там использовался C# — язык сложных предложений с хитрыми интерфейсами. Что же касается обработки больших «простыней» однотипных данных, то для мелких задач в этой области испокон веку применяют скриптовые языки.
Вот и написался небольшой скрипт на python, который утаскивает с hh.ru список вакансий по некоторому запросу (пишется в переменную searchParam) и выводит их в формате CSV. Можно выводить на экран и удивляться, можно перенаправить в output.csv и открыть в каком-нибудь табличном редакторе.
Всё написано около полугода назад на Python 3.x и тестировалось из-под Windows. До сих пор с удовольствием использую как болванку для скрипта, который должен откуда-то что-то скачать.
Странный способ выхода из цикла связан с может быть багой, а может быть фичей hh.ru: если ты попытаешься открыть несуществующую страницу списка результатов, разбитого paging-ом, то вместо 404 он покажет стандартную страницу, но уже без вакансий.
import urllib.parse
import re
def LoadPageText(url):
import urllib.request
from urllib.error import HTTPError,URLError
opener = urllib.request.build_opener()
try: pageSource = opener.open(url)
except HTTPError as e: return None
except URLError as e: return None
return pageSource.read().decode('utf-8')
searchUrl = "http://hh.ru/applicant/searchvacancyresult.xml?text={0}&page={1}&professionalAreaId=0&desireableCompensation=&compensationCurrencyCode=RUR"
searchParam = "C++"
regExpElemSource = r'<div class="searchresult__name"><span class="b-marker"><a.*?href="(?P<vacancy_url>http://(hh|career).ru/vacancy/(?P<vacancy_id>d+)(?query=.*?)?|http://[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}(/S*)?)".*?>(?P<title>[ ()а-яw/,.#+-]*)</a></span>(<.*?>)*?</div>(<div class="b-vacancy-list-nosalary">[s/а-я.]*</div>|<div class="b-vacancy-list-salary">s*(отs*(?P<from>[d]*(s*[d]+){0,2}))?s*(доs*(?P<to>[d]*(s*[d]+){0,2})s*)?(?P<currency>[wа-я.]+)s*</div>)<div class="searchresult__placetime"><a href="(?P<company_url>/employer/(?P<company_id>d*))">(?P<company>[ ()а-яw/,.#+-]+)</a>s*<span class="searchresult__address">s*((?P<city>[ а-я-]*)(,<br/><span style="color:#[a-fd]*">s*м.s*(?P<underground_station>[ а-я-]*)</span>)?),s*</span>s*<span class="b-vacancy-list-date">s*(?P<date_day>d+)s+(?P<date_month>[а-я]+)'
regExpElem = re.compile(regExpElemSource, re.IGNORECASE)
isFirst = True
i = 0
while True :
i += 1
searchUrlPrep = searchUrl.format(urllib.parse.quote_plus(searchParam), i)
pageText = LoadPageText(searchUrlPrep)
if pageText != None:
hasElements = False
for elemVacancy in re.finditer(regExpElem, pageText):
hasElements = True
if isFirst:
[print('"%s"' % x, end=";") for x in elemVacancy.groupdict().keys()]
isFirst = False
print()
[print((elemVacancy.group(key) if key == "from" and key == "to" else str(elemVacancy.group(key)).replace('u00A0', ' ')) if elemVacancy.group(key) != None else "0", end=";") for iterate, key in enumerate(elemVacancy.groupdict())]
if not hasElements: break
Любопытствующим будет интересно посетить официальную страницу скрипта, а неугомонным — сделать форк на GitHub и переделать его во что-нибудь совершенно невообразимое.
Автор: RikkiMongoose