JavaScript: перехват всех http/https запросов страницы
Прилетела задача перехватить URL всех загрузок тайлов на странице с размещенной Яндекс картой. В принципе если бы был простой случай, то всё решилось бы созданием прототипа для функции XMLHttpRequest , что-то в духе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, uri, async, user, pass) { this.addEventListener("readystatechange", function(event) { if(this.readyState == 4){ var self = this; var response = { method: method, uri: uri, responseText: self.responseText }; console.log(response); } else { console.log(this.readyState); } }, false); open.call(this, method, uri, async, user, pass); }; |
Но возник один нюанс, карта располагается в iframe, а прототипы «вниз» не распространяются. Единственным способом осталось написать сервис Service Worker, который будет отлавливать все запросы..
На странице добавляем функцию загрузки сервиса:
1 2 3 4 5 6 7 8 9 10 11 12 |
<script> console.log("пробуем зарегистрировать Service Worker"); if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js',{}).then(function(registration) { console.log('ServiceWorker registration successful with scope: ', registration.scope); }, function(err) { console.log('ServiceWorker registration failed: ', err); }); }); } </script> |
Далее в sw.js добавим слушательсобытия fetch, и реализацию отправки перехваченых url на сервер:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
self.addEventListener('install', (event) => { console.log('Установлен'); }); self.addEventListener('activate', (event) => { console.log('Активирован'); self.clients.claim(); }); self.addEventListener('fetch', (event) => { console.log('Запрашиваем URL: '+event.request.url); postData("woodpecker.php", { "url": event.request.url}).then((data) => { console.log(data); }); }); async function postData(url = "", data = {}) { console.log("-отправляем в "+url); // Default options are marked with * const response = await fetch(url, { method: "POST", headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', }, body: "data="+JSON.stringify(data), // body data type must match "Content-Type" header }); return await response.json(); // parses JSON response into native JavaScript objects } |
Серверная часть woodpecker.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php $body= json_decode($_POST["data"]); if ($body->url!=""){ file_put_contents("urls.txt",$body->url."\n",FILE_APPEND); } $answer=new stdClass(); $answer->error=false; $answer->result="ok"; echo json_encode($answer); die(); |
В результате в файл url.txt на сервере пишутся все запрошенные url.
Отладка сервиса в FireFox возможна на вкладке about:debugging#/runtime/this-firefox, ищем там свой сервис, нажимаем «исследовать»
PHP: интересное поведение сложения чисел типа float
Был немножно удивлен сегодня, когда пытался сложить два числа типа float обычным оператором +. Например сложение чисел вида 59.86601 + 0,01 успешно выполнялось, а 59.86601+0,001 уже нет. Т.е. результирующее число оставалось прежним. Оказывается для точного сложения чисел, в PHP нужно использовать специальные операторы:
1 2 3 4 5 6 7 8 9 10 |
bcadd — Сложить 2 числа произвольной точности bccomp — Сравнение двух чисел произвольной точности bcdiv — Операция деления для чисел произвольной точности bcmod — Получает остаток от деления чисел с произвольной точностью bcmul — Умножение двух чисел с произвольной точностью bcpow — Возведение в степень чисел с произвольной точностью bcpowmod — Возводит одно число в степень другого и возвращает остаток от деления результата на третье число bcscale — Задает количество чисел после десятичной точки по умолчанию для всех bc math функций. bcsqrt — Извлекает квадратный корень из числа с произвольной точностью bcsub — Вычитает одно число с произвольной точностью из другого |
Например:
1 |
$point2["lat"]=Bcadd($point2["lat"],$cnt,8); |
1С: Автоматическое сохранение данных на форме
Часто бывает ситуация, что необходимо во внешней обработке какие-то реквизиты позволять пользователю сохранять. Классический вариант — сохранять данные в пользовательских настройках, или придумывать иное место хранения. Но для этого нужны телодвижения со стороны программиста. С недавних пор (ну как с недавних, лет 5 уже 😉 ) , есть уже штатный способ, встроенный в платформу:
Автоматическое сохранение данных»В свойствах формы выбираем «Автоматическое сохранение данных» = использовать:
И далее проставляем в графе «Сохранение» галочки у тех реквизитов, которые необходимо сохранять:
Rbot: парсер данных с госуслуг
На днях довелось попробовать в работе специализированную платформу для написания «роботов» — RPA Bot. По сути это хорошо документированная надстройка на Selenium. В базе позволяет писать «роботов» на Python, Node.js и PHP. На сайте ОЧЕНЬ хорошая документация по всему функционалу с примерами, а потому роботов писать гораздо приятнее чем при использовании «чистого» Selenium. Это плюс. Из минусов — отсутствие версии под Linux и конский ценник. Ну если первое разработчики усиленно пилят, то второе лично мне фиолетово (босс платит 😉 )
В ходе реализации очень понравилась функция save_url_to_file, аналога которой в Selenium я не нашел в своё время, а потому приходилось изобретать велосипед. Ну скорее всего конечно это то-же «велосипед» (ну логично, проект то на основе Selenium), но сделанный «штатно». К сожалению функция не отрабатывает, если закачька происходит с использованием Redirect 301. потому в случае необходимости можно воспользоваться таким вариантом закачки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$browser->set_default_download($mySettings['work_path']); $browser->enable_download_file_dialog(false); $browser->navigate("https://www.gosuslugi.ru/api/lk/geps/file/download/$a_aid"); wait_on_file($a_name, 120); function wait_on_file($path, $wait=120, $pause=1) { global $file_os; $a=0; while(!$file_os->is_exist($path)) { sleep($pause); if($a>$wait) { debug_mess("ОШИБКА: не дождались появление файла по заданному пути $path!"); return false; } $a++; } return true; } |