Привет всем.
Не так давно прочел статью Что нам стоит сайт распарсить. Основы webdriver API и вспомнил, что давно собирался довести хотя бы до относительно рабочего состояния одну забавную задумку. Руки все-таки дошли, а значит пора поведать, что же получилось.
Есть такая замечательная программа — Fiddler, позволяющая перехватывать и модифицировать http/https запросы. Есть замечательная штука под названием NW.js, она же node-webkit, позволяющая тыр… парсить разнообразные сайты в том числе. Вы красивы, я прекрасен — почему бы нам не подружиться?
Собственно, затея вот в чем: можно было бы, конечно, отдельно поднять Fiddler, написать в нем логику и гонять через него трафик с node-webkit-но это не так интересно. А значит, будем совмещать все под одной крышей, благо у Fiddler есть библиотека на C# — FiddlerCore.
Под ноду есть отличный модуль — Edge.js. Это такая хитрая штука, которая позволяет исполнять код C# (и не только). Есть под ноду? Замечательно, можно завести и под nw.js, благо даже мануал есть — да вот же он!
Итак, пропустим пару часов секаса по сборке этой замечательной библиотеки(сколько раз говорить себе, читай мануалы внимательно! Нужна 13 студия, не 12 и не 15!) и приступим к написанию кода. Останавливаться на подключении и загрузке модулей тоже не буду, предположим, что те, кого заинтересует эта статья, мануалы читать умеют(да понял я, понял, что мануалы нужно читать внимательно!).
//#r "System.Windows.Forms.dll" //#r "fiddler/FiddlerCore.dll" using Fiddler; using System; using System.Windows.Forms; using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; public class Startup { Func<object, Task<object>> _console; Func<object, Task<object>> _html; public async void _print(object text){ if(_console!=null) await _console(text); } public async void _getHtml(object html) { if (_html != null) await _html(html); } public async Task<object> Invoke(dynamic data) { _console = (Func<object, Task<object>>)data.console; _html = (Func<object, Task<object>>)data._html; _print("Started"); FiddlerApplication.Shutdown(); new FiddlerLogic { _beforeRequest = (oS) => { var proxy = oS.oRequest.headers["POverride"]; if (proxy != null) { oS["X-OverrideGateway"] = proxy; } }, _beforeResponse = (oS) => { var response = oS.GetResponseBodyAsString(); _html(response); } }._start(5000); return Task.FromResult("Done"); } } class FiddlerLogic { public Action<Session> _beforeRequest; public Action<Session> _beforeResponse; public void _start(int port=5555) { FiddlerApplication.BeforeRequest += (oS) => _beforeRequest(oS); FiddlerApplication.BeforeResponse += (oS) => _beforeResponse(oS); FiddlerApplication.Startup(port, false, true); } }
Итак, что же здесь происходит?
Эти вот 2 строки
//#r «System.Windows.Forms.dll»
//#r «fiddler/FiddlerCore.dll»
являются маркером для edge.js, нужны для подключения библиотек.
Класс FiddlerLogic — всего лишь небольшой враппер над Fiddler’ом, в котором был код для подключения сертификатов, но потом при помощи какой-то уличной магии(скорее всего, нашел в загашнике старую версию, которая этот самый код не требовала) оно заработало и так. Теперь этот класс, по факту, ничего особого не делает, но куда ж нынче без legacy-кода? Собственно, в конструкторе объекта мы указываем 2 callback’а, которые будут вызываться перед/после отправки запроса, порт(при необходимости) и все.
Ах да, в FiddlerApplication.Startup 2 и 3 аргументы отвечают за использование системного прокси/перехват https-запросов соответственно. Так как хотелось бы перехватывать https-запросы и нафиг не нужно использование системного прокси, значения — false и true.
Теперь про Startup. Это такой забавный класс, нужный для работы с edge.js(об этом модуле позже). Собственно, Invoke — это точка входа, console/_html — функции из js. FiddlerApplication.Shutdown() отвечает за завершение всех предыдущих инстансов фиддлера. В _beforeRequest происходит подмена прокси при наличии заголовка POverride в запросе. В _beforeResponse ничего шибко полезного не происходит, оставил для примера. Функции _print и _getHtml — просто обертки с проверкой на наличие переданных из js функций.
Теперь рассмотрим js-часть.
var edge = require('edge'); var gui = require('nw.gui'); var async = require('async'); var request = require('request'); var start=Date.now(); var arr=[]; var count=50; var prev=Date.now(); var proxyList = ['160.92.56.41:80']; _init(); for(var i=0; i<count; i++){ arr.push('http://myip.ru/index_small.php') } var i=0; var _node=function(url, c){ console.log(url); var options = { url: url, headers: { 'POverride': proxyList[0] } }; request(options, function(err, response, html){ if(err) console.log(err); var j=_html(html); console.log('Container:', j.find('.network-info tbody>:nth-child(2) td').text()); c(); }); }; async.map(arr, _node, function(){ var ms=Date.now()-start; console.info('Node parse got %f seconds. Mid time: %f. Mid page per second: %f', ms/1000, ms/count, count*1000/ms); } ); /***DEFINITIONS***/ function _html(html){ return $('<div></div>').html(html); }; function _init(){ try{ request=request.defaults({'proxy':'http://localhost:5000'}); gui.App.setProxyConfig("http://localhost:5000"); func = edge.func("fiddler/Main.cs"); func({ console: function(data, callback){ console.log(data); }, _html: function(html, callback){ //var container=_html(html); //console.log('Container:', container, container.find('.network-info tbody>:nth-child(2) td')); } }); } catch(ex){ console.log(ex); } };
В первую очередь стоит обратить внимание на 2 функции снизу — _html и _init. Первая занимается непотребством в виде построения DOM из строки, 2 — базовые настройки и подключение C# кода.
Edge.func подгружает содержимое Main.cs(см. код выше), и передает в качестве аргументов ссылки на нужные функции. Собственно, аргумент функции func и есть data из public async Task
ссылка на оригинал статьи http://habrahabr.ru/post/273311/
Добавить комментарий