В последнее время меня привлекла техника парсинга веб-страниц. Это отличный метод сбора данных из первых рук, которые недоступны через API или предварительно обработаны. Хорошо, что, учитывая, что ваш набор данных собран из первых рук, ваш анализ может быть уникальным и спровоцировать интересные выводы для вас и других. Компромисс — вам придется потратить дополнительное время на сбор и очистку данных, а также тщательно спроектировать архитектуру для запуска парсинг-кодов. В целом веб-скрапинг обеспечит большую гибкость при анализе данных, упростит как предварительный анализ, так и крупномасштабный сбор данных.

В качестве игрушечной задачи я попытался сбросить ставки на футбольные матчи на Hong Kong Jockey Club. У меня нет привычки смотреть футбол, но я просто обнаружил, что данные довольно просты, и все же недоступны через какой-либо систематический API.

В итоге я сделал именно то, что хотел, менее чем в 30 строк кода, намного меньше, чем я ожидал. Процедура достаточно интуитивна и при некоторой настройке применима и к другим случаям. В основном я использовал Cron и Splinter.

Cron — это встроенная в Mac утилита, которая позволяет выполнять запланированные задачи. Идея состоит в том, чтобы запланировать запуск сканера с каждым интервалом для сбора данных через браузер. На практике очистка должна выполняться на сервере, чтобы избежать прерывания. Но запуск программы на локальной машине все же был бы хорошим способом начать.

Во-вторых, Splinter — это Python-слой Selenium. Это позволяет просматривать и извлекать информацию на основе кода. Он имеет высокоуровневый API для написания автоматизированных тестов и очистки веб-страниц. Для тех, у кого есть некоторый опыт работы с Python, это не составит труда. Конечно, есть и другие библиотеки просмотра веб-страниц на других языках, например, RSelenium/rvest в R и ExpressJS в Javascript.

Для тех, кто хочет установить Splinter, вам нужно сначала установить драйвер, пожалуйста, введите:

$ brew install chromedriver/geckodriver 

chromdriver для chrome и geckodrive для firefox соответственно.

$ pip install Splinter

Тогда вот!

Поскольку я никогда не использовал Cron, я решил поиграть с этим, чтобы понять, как это работает:

$ crontab -e 

Чтобы попасть в файл crontab, по умолчанию используйте редактор Vim. Вы можете указать свой пользовательский редактор:

$ export EDITOR=youreditor

В целях тестирования я создаю файл с именем 123.sh, который просто печатает текущее время в новый файл. Внутри Crontab я набрал
* * * * * /bin/sh /path/to/my/file/123.sh.
* * * * * — это пространство таймера, каждое из которых соответствует минуте, часу, дню, месяцу и дню недели. Пять звезд в основном означают исполнение каждую минуту. Итак, Cron выполнял мою 123.sh каждую минуту, и это срабатывало, здорово!

Как только скрипт окажется внутри Crontab. Сценарий будет запускаться автоматически каждую минуту.
Кроме того, вы можете указать MAILTO=@someemail, чтобы отправить письмо на вашу системную почту в var/mail/users для журнала ошибок.

Чтобы выполнить мой скрипт python, в crontab мой скрипт:
* * * * * /Library/Frameworks/Python.framework/Versions/3.6/bin/python3 /Users/pathtomyfile
Первый путь указывает на python, который я хочу вызвать, а второй путь указывает на мой скрипт python.

Наконец, введите
$ which python
, чтобы проверить, где находится ваш питон, для меня это предыдущий путь, поэтому вы можете заменить его своим собственным путем.

Как только Crontab запущен и запущен, мы можем обратиться к самому сканеру.
Перед сканированием я сначала просмотрел веб-сайт.

Как видите, сайт уже представляет данные в виде чистой таблицы, включая номер матча, дату матча, названия команд и коэффициенты.

Помимо этих данных, я решил включить время обновления и текущую дату для индексации данных. Теперь давайте откроем редактор и вставим несколько строк:

from splinter import Browser
browser = Browser(‘chrome’)
browser.visit(‘http://bet.hkjc.com/football/default.aspx')

Эти три строки откроют браузер Chrome и посетят указанный веб-сайт.

Теперь я перехожу прямо к проверке идентификаторов элементов, которые мне нужны.
 – Щелкните правой кнопкой мыши в браузере и выберите "Проверить".
- Проверен каждый элемент в HTML, включая их класс, идентификатор, css и xpath.

Я подошел к таблице и обнаружил, что идентификатор каждой строки отформатирован как rmid110876 и rmid110877 и так далее, поэтому я решил использовать регулярное выражение для извлечения их идентификаторов и перебора каждого из его идентификаторов.

import re
pattern = ‘(rmid11[0–9]{4})’
ids = re.findall(pattern, browser.html)

Этот блок должен возвращать список идентификаторов на веб-сайте. К сожалению, он вернул None. После некоторого осмотра я обнаружил, что вся таблица встроена во фрейм, который фактически связывает веб-сайт с другим URL-адресом. Может быть, они не хотят, чтобы другие сканировали коэффициенты ставок, я думаю? В любом случае я заменил предыдущий URL-адрес новым.

browser.visit(‘http://bet.hkjc.com/football/index.aspx?lang=en')

Теперь `ids` действительно содержит список идентификаторов, полученных с веб-сайта.
Далее мне нужно просканировать текст каждого элемента, красиво его отформатировать:
Поскольку элемент содержит текстовый атрибут, который является такая строка: ’THU 3 Orebro vs AFC Eskilstuna 22/09 00:00\n1.48 4.00 5.10'

поэтому я могу просто разделить нужную мне информацию на группы.

matchtime, home, away = [], [] , []
odd_home, odd_draw, odd_away = [], [], []
for match in ids:
 entry = browser.find_by_id(match).text
 regex = re.search(r’[\d]\s(.+)\svs\s(\D+)\s(.+)\n(\S+)\s(\S+)\s(\S+)’, entry)
 home.append(regex.group(1))
 away.append(regex.group(2))
 matchtime.append(regex.group(3))
 odd_home.append(regex.group(4))
 odd_draw.append(regex.group(5))
 odd_away.append(regex.group(6))
 
#get the latest refresh time 
refresh = [browser.find_by_id(‘sRefreshTime’).value] * len(ids)

Код может выглядеть повторяющимся, но его логика проста. Мы можем записать информацию в файл csv несколькими строками:

import csv
import time
#so to append the current date on each row for sorting later
date = [time.strftime(“%Y/%m/%d”)] * len(ids)
#zip the eight columns into rows
rows = zip(matchtime,date,refresh,home,away,odd_home,odd_draw,odd_away)
with open(‘football.odds.csv’,’a’) as f:
 writer = csv.writer(f)
 for row in rows:
 writer.writerow(row)
 f.close()
browser.quit()

Ну вот. Поместите код в скрипт и вызовите его из Crontab, как указано выше. Мой CSV-файл выглядит так:

Чтобы остановить Crontab, вы можете contab file.txt скопировать содержимое в файл резервной копии, а затем стереть содержимое contab -r.

Это всего лишь эксперимент, сканирование большего масштаба будет учитывать задержку, настройку удаленного сервера и потенциальный отказ клиента из-за частого доступа.

В качестве следующего шага я, вероятно, протестировал бы код на удаленном сервере или виртуальной машине, просто чтобы посмотреть, не возникнут ли какие-либо новые проблемы.