После мая 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