
Для начала, конечно, стоило рассказать зачем едят этот самый «пользовательский агент». Ну или, вообще, начать с того что же это за агент такой. (Кстати, никто не знает какой-нибудь славянско-православный перевод этого термина?)Но рассчитывая, что хабра юзер либо уже знает и использует useragent либо ему это не нужно, я бы не хотел останавливаться на предисловиях. И так, мой совет — употребляйте useragent с регулярными выражениями!
Конечно, тебе свойственно регулярно употреблять выражения, %username%, но они другие и для души, а я о regex. Одной из основных задач в моей работе, является правильное определение возможностей устройства и браузера конечного пользователя. Так как основной упор мы делаем на мобильные устройства (сотовые телефоны), то их я и возьму в пример. В отличие от пользователей обычных компьютеров, пользователи мобильных устройств жёстко и жестоко ограничены в разрешении экрана, возможностях браузера и т.д. У нас имеется небольшая база данных собранная и автоматически обновляемая с помощью UAProf и Wurfl. Но заголовки агентов (useragent header) постоянно изменяются и количество различий постоянно растёт. О том чтобы делать поиск очередного устройства проверкой агента один к одному не может идти и речи, но как то же надо искать. Поэтому мы стали разбираться с устройством useragent и что из него можно выжать.
Ингредиенты
Стандарты и формат — как обычно никто их не соблюдает. Формат useragent изменяется от производителя к производителю и от серии к серии. К тому же большинство сотовых операторов любят переписывать заголовки.
Основные блоки должны быть такие:
устройство/версия браузер/версия (поддерживаемые стандарты и технологии).
.gif)
Первый же пример sonyericssonk530i/r6bc browser/netfront/3.3 profile/midp-2.0 configuration/cldc-1.1 как бы говорит нам, что скобки ожидать не приходится, а второй пример mozilla/5.0 (symbianos/9.4; u; series60/5.0 nokia5800d-1/21.0.025; profile/midp-2.1 configuration/cldc-1.1 ) applewebkit/413 (khtml, like gecko) safari/413 мягко намекает, что и порядок никто соблюдать не будет. Но всё же мне важно знать, что разные агенты появляются у одного и того же устройства, например nokia n95:
- mozilla/5.0 (symbianos/9.2; u; series60/3.1 nokian95/12.0.013; profile/midp-2.0 configuration/cldc-1.1 ) applewebkit/413 (khtml, like gecko) safari/413
- mozilla/5.0 (symbianos/9.2; u; series60/3.1 nokian95/31.0.017; profile/midp-2.0 configuration/cldc-1.1 ) applewebkit/413 (khtml, like gecko) safari/413 [en-us]
- mozilla/5.0 (symbianos/9.2; u; series60/3.1 nokian95/30.0.015; profile/midp-2.0 configuration/cldc-1.1 )
- mozilla/5.0 (symbianos/9.2; u; series60/3.1 nokian95_8gb/30.0.018; profile/midp-2.0 configuration/cldc-1.1 ) applewebkit/413
- mozilla/5.0 series60; nokian95;
Рецепт
Однако, как можно заметить кое какая логика есть. После слеша (/) идёт версия — динамическая часть, которая особой роли не играет. Обязательно присутствует указание на браузер. Разделение токенов с помощью пробела и/или точки с запятой. Покрутив логи мы обнаружили много мусора в заголовках агента, поэтому первым шагом стала стандартизация и выделение сегментов. Получились вот такие полезности:
- Выбираем то, что действительно useragent:
([[(]?[a-z0-9._+;]\s?[/\-;:\\,*\s]*[)\]]?\s?)*
- Определяем токен браузера:
((iemobile|kbrowser)\s[0-9.]+)|((up(\.link)?|netfront|obigo|opera\s?(mini|mobile)?|deckit|safari|(apple)?webkit|mozilla|openwave)/[0-9\.a-z\-]+\+?)|(browser/[a-z\-0-9]+/?[0-9\.a-z\-]+)|([a-z\.-]+browser[a-z\.-]*(/[0-9\.a-z\-]+)?)
- Определяем профиль и конфигурацию:
(((profile|configuration|java(platform)?)/[a-z]+-?)|((cldc|midp|wap)[\s\-]?))[0-9\.-a-z]+
- Язык:
((?<=[\s;\[\(])[a-z]{2}[\s-][a-z]{2}(?=[\s;\]\)]))|\[([a-z]{2,3}[\-_\s]?)+\]
- Версия:
[\s;/]+(v(er)?[\s.]*)?[0-9]+\.[0-9\.]+([a-z]{1,2}[0-9\.]*)?
- Иногда указывают размеры экрана в пикселях:
[0-9]{3}x[0-9]{3}
Естественно, что сто процентного результата не получилось, но прогонка по 30 000 useragent-ов показала, что правильные сегменты высветились в 97%. Так что результат вполне достойный. Но нам этого не хватило. Некоторые вещи надо проверять по базе данных и там всё тот же разброс и разнообразие моделей и агентов. Возникла простая и интуитивно понятная идея — поиск по модели. То есть несмотря на то, что существует более десятка разных useragent-ов для той же 95-ой Нокии, в каждом варианте присутствует nokian95. Задача была бы тривиальной, если бы нужно было определить/искать только одну и ту же модель (допустим узнать iPhone или нет). Но тогда вполне хватило бы if-else. В жизни всё сложней и никакого универсального стандарта для определения модели просто нет.
Десерт
Мы пошли от обратного — подчистим useragent от тех токенов, определять которые мы научились.
Используя те же выражения (с лёгкими изменениями) я стираю из useragent блоки один за другим (псевдокод
while useragent ismatch replace match with string.empty
). Получается остаток из неизвестных мне заранее кусков, часть которых является мусором, а какой то один — моделью. Простейшим решением стало разбиение остатка на отдельные токены — Split(' ', '/', ';')
и поиск токена с производителем. Ищем в какая часть содержит одну из следующих строк:"nokia", "motorola", "mot-", "moto-", "motorazr", "sonyericsson", "samsung", "sec-", "sgh-", "lg-", "lge", "lg", "sie-", "siemens","ipod", "iphone" ,"ipaq", "spv", "i-mate", "mobilephone", "htc", "vodafone", "palm", "rover", "gigabyte", "asus", "alcatel", "mitsu", "verizon", "apple".
Теперь из приведённых выше разных длинных useragent-ов n95 у меня остаются только nokian95 и nokian95_8gb соответственно. Вот ещё несколько примеров полных useragent-ов и результатов очистки:
- samsung-sgh-f480/f480jihh2 shp/vpp/r5 netfront/3.4 qtv5.3 smm-mms/1.2.0 profile/midp-2.0 configuration/cldc-1.1
=samsung-sgh-f480 - sonyericssonw705/r1ea browser/netfront/3.4 profile/midp-2.1 configuration/cldc-1.1 javaplatform/jp-8.4.2
=sonyericssonw705 - lg-kc910q browser/teleca-q7.1 mms/lg-mms-v1.0/1.2 mediaplayer/lgplayer/1.0 java/asvm/1.1 profile/midp-2.1 configuration/cldc-1.1
=lg-kc910q - mozilla/5.0 (symbianos/9.3; u; series60/3.2 nokia6210navigator/03.25; profile/midp-2.1 configuration/cldc-1.1 ) applewebkit/413 (khtml, like gecko) safari/413
=nokia6210navigator - sgh-z370/1.0 netfront/3.3 profile/midp-2.0 configuration/cldc-1.1
=sgh-z370 - vodafone/1.0/sex1i/r2aa mozilla/4.0 (compatible; msie 6.0; windows ce; iemobile 7.11) up.link/6.3.1.20.0 profile/midp-2.0 configuration/cldc-1.1
=vodafone sex1i
На посошок
Помимо браузера вас может интересовать токен WAP (кратко WAP 1.0 = WML, WAP 2.0 = XHTML). Версия mmp (multimedia mobile processor) должна указывать на поддержку аудио/видео кодеков — 1.0 только аудио mp3, а 2.0 поддерживает и видео 3gp. В большей части useragent-ов указанна операционная система и версия — актуально для iPhone:
ip(hone|od).*?os\s*(v(er(sion)?)?)?[\s.]*([0-9._]+|[a-z]+)
Приятного аппетита
Проверка на базе данных и подгонка (finetunning) привели к 99% результату. Это конечно явный overfitting, но это была одной из целей (максимальная точность в определённой аудитории и регионе). Кстати, вышеприведённые regex-ы более абстрактны и должны дать большую погрешность в силу своей универсальности.