После мая 2012 года, когда Одеск перестал публиковать "окономику" (www.odesk.com/oconomy), оставив только след на Веб-архиве (en), не стало единого источника информации о текущем состоянии и трендах рынка труда на этой электронной бирже труда.
Косвенную информация о числе контракторов, средней стоимости часа работ по умениям и их востребованности теперь можно получить только из тестов на Одеске. Данная информация размазана по описаниям тестов и для ее обобщения необходимо загрузить и обработать все страницы описаний. Это довольно трудоемкая и местами нудная работа, даже для питониста с requests и html5lib, называемая web scraping (en).
Извлекаем данные тестов с Scrapy
Scrapy (en) - это, написанный на Питоне, фреймворк для реализации программных роботов-пауков, извлекающих данные из веб-страниц. Реализация извлечения данных тестов Одесков со "Скрэйпи" уместилась в 3 небольших модуля:
- items.py - содержит декларативное описание извлекаемых данных и во многом напоминает модель данных в Джанго. В нашем случае - это данные подробного описания теста на Одеске
- tests_spider.py - реализует самого паука и содержит:
- правила извлечения адресов страниц, которые необходимо загрузить и назначает страницам обработчиков
- реализацию обработчиков, которые извлекают данные с помощью XPath и присваивают их объекту класса из items.py
- settings.py - содержит настройки проекта, например, задержку между загрузкой страниц DOWNLOAD_DELAY, число одновременных запросов CONCURRENT_REQUESTS_PER_DOMAIN и другие.
Ниже приведен пример запуска процесса извлечения данных и сохранения их в CSV файл:
$ scrapy crawl -o tests_apr7.csv -t csv tests
2013-04-07 23:48:13+0400 [scrapy] INFO: Scrapy 0.16.4 started (bot: otests)
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Enabled extensions: FeedExporter, LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, RedirectMiddleware, HttpCompressionMiddleware, ChunkedTransferMiddleware, DownloaderStats
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Enabled item pipelines:
2013-04-07 23:48:13+0400 [tests] INFO: Spider opened
2013-04-07 23:48:13+0400 [tests] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:6023
2013-04-07 23:48:13+0400 [scrapy] DEBUG: Web service listening on 0.0.0.0:6080
...
2013-04-08 00:20:47+0400 [tests] INFO: Closing spider (finished)
2013-04-08 00:20:47+0400 [tests] INFO: Stored csv feed (440 items) in: tests_apr7.csv
2013-04-08 00:20:47+0400 [tests] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 259496,
'downloader/request_count': 888,
'downloader/request_method_count/GET': 888,
'downloader/response_bytes': 1672291,
'downloader/response_count': 888,
'downloader/response_status_count/200': 446,
'downloader/response_status_count/301': 1,
'downloader/response_status_count/302': 441,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2013, 4, 7, 20, 20, 47, 939438),
'item_scraped_count': 440,
'log_count/DEBUG': 1334,
'log_count/INFO': 37,
'request_depth_max': 3,
'response_received_count': 446,
'scheduler/dequeued': 888,
'scheduler/dequeued/memory': 888,
'scheduler/enqueued': 888,
'scheduler/enqueued/memory': 888,
'start_time': datetime.datetime(2013, 4, 7, 19, 48, 13, 512624)}
2013-04-08 00:20:47+0400 [tests] INFO: Spider closed (finished)
Уже из этого отрывка лога видно, что на Одеске всего 440 тестов. Попробуем проанализировать полученную информацию.
Анализируем данные с Pandas
Pandas (en) - это большая библиотека для анализа данных, основанная на Numpy (en). Если Numpy называют "Матлабом на Питоне", то Pandas - это "R-язык на Питоне".
Запустим интерактивный интерпретатор Питона и загрузим сначала данные из CSV файла:
>>> import pandas as pd
>>> import numpy as np
>>> def default_value(typ, default, val):
... try:
... return typ(val)
... except ValueError:
... return default
>>> def maybe_int(val):
... return default_value(np.int64, None, val.replace(',', ''))
>>> def maybe_float(val):
... return default_value(np.float64, None, val)
>>> tests = pd.read_csv('tests_apr7.csv', thousands=',', converters={
... 'hourly_rate_max': maybe_float,
... 'hourly_rate_avg': maybe_float,
... 'percent_independent': maybe_float,
... 'average_qualificatinos': maybe_float,
... 'taken_test': maybe_int,
... 'passed_test': maybe_int,
... 'tests_taken': maybe_int,
... })
>>> tests
<class 'pandas.core.frame.DataFrame'>
Int64Index: 440 entries, 0 to 439
Data columns:
hourly_rate_max 432 non-null values
hourly_rate_avg 432 non-null values
percent_independent 440 non-null values
title 440 non-null values
average_qualifications 440 non-null values
taken_test 440 non-null values
average_hours 435 non-null values
passed_test 440 non-null values
test_id 440 non-null values
tests_taken 440 non-null values
dtypes: float64(5), int64(4), object(1)
Посмотрим подробнее на сами тесты и выделим некоторые интересные статистики.
10 тестов в порядке убывания числа контракторов
Отгадайте какой тест самый популярный.
>>> tests.sort_index(
... by=['passed_test'], ascending=False
... ).ix[
... :, ['test_id', 'title', 'passed_test']
... ][:10]
test_id title passed_test
0 752 oDesk Readiness Test for Independent Contracto... 743081
439 511 U.S. English Basic Skills Test 345213
438 688 English Spelling Test (U.S. Version) 269360
435 545 Office Skills Test 114577
436 584 Windows XP Test 104314
434 693 English Vocabulary Test (U.S. Version) 88943
437 753 oDesk Readiness Test for Agency Contractors 84282
433 506 Email Etiquette Certification 60019
429 571 Telephone Etiquette Certification 48861
428 484 Call Center Skills Test 44063
10 тестов в порядке убывания средней стоимости часа
Интересная корреляция средней стоимости часа и числа контракторов прошедших тест.
>>> tests.sort_index(
... by=['hourly_rate_avg'], ascending=False
... ).ix[
... :, ['title', 'hourly_rate_avg', 'passed_test']
... ][:10]
title hourly_rate_avg passed_test
14 VB.NET Programming Skills Test (Hands-on progr... 49.50 5
253 Adobe FrameMaker 8 Test 47.75 36
131 Design Considerations for Mobile Web Applicati... 36.19 58
166 VLSI Test 34.00 48
143 Checkpoint Security Test 29.00 68
248 RDF Test 28.50 26
240 Knowledge of ColdFusion 9 Skills Test 28.49 50
29 PostgreSQL Test 28.10 199
266 Web Services Test 27.95 301
53 Cocoa programming for Mac OS X 10.5 Test 27.55 567
10 тестов в порядке убывания суммарно отработанных часов
Показывает нижнюю оценку суммарно отработанных часов и заработанных денег на Одеске всеми контракторами на 8ое апреля, 2013 года.
>>> tests['total_hours'] = tests['passed_test'] * tests['average_hours']
>>> tests['total_earnings'] = tests['total_hours'] * tests['hourly_rate_avg']
>>> tests[tests['total_hours'] > 0].sort_index(
... by=['total_hours'], ascending=False
... ).ix[
... :, ['title', 'total_hours', 'total_earnings']
... ][:10]
title total_hours total_earnings
0 oDesk Readiness Test for Independent Contracto... 308378615 2.692145e+09
439 U.S. English Basic Skills Test 196080984 1.815710e+09
438 English Spelling Test (U.S. Version) 144646320 9.922738e+08
435 Office Skills Test 70808586 4.935358e+08
436 Windows XP Test 61023690 5.339573e+08
434 English Vocabulary Test (U.S. Version) 52120598 3.841288e+08
437 oDesk Readiness Test for Agency Contractors 49642098 2.765065e+08
433 Email Etiquette Certification 44474079 3.740270e+08
429 Telephone Etiquette Certification 36499167 2.901684e+08
428 Call Center Skills Test 33884447 2.232985e+08