Выбрать главу
Пример проверки cookie-наборов. Для того чтобы проверить, какие именно cookie-наборы используются на веб-сайте, укажите его имя в командной строке. Так, если назвать эту программу CookieDemo, то по команде CookieDemo http://msn.com отобразятся cookie-наборы с веб-сайта по адресу www.msn.com. / using System; using System.Net; class CookieDemo { static void Main(string[] args) { if(args.Length != 1) { Console.WriteLine("Применение: CookieDemo "); return; } // Создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(args[0]); // Получить пустой контейнер. req.CookieContainer = new CookieContainer(); // Отправить сформированный запрос и получить на него ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); // Отобразить cookie-наборы. Console.WriteLine("Количество cookie-наборов: " + resp.Cookies.Count); Console.WriteLine("{0,-20}{1}", "Имя", "Значение"); for(int i=0; i < resp.Cookies.Count; i++) Console.WriteLine("{0, -20}{1}", resp.Cookies[i].Name, resp.Cookies[i].Value); // Закрыть ответный поток. resp.Close(); } } Применение свойства LastModified Иногда требуется знать, когда именно сетевой ресурс был обновлен в последний раз. Это нетрудно сделать, пользуясь сетевыми средствами класса HttpWebResponse, среди которых определено свойство LastModified, приведенное ниже. public DateTime LastModified { get; } С помощью свойства LastModified получается время обновления содержимого се тевого ресурса в последний раз. В приведенном ниже примере программы отображаются дата и время, когда был в последний раз обновлен ресурс, указываемый по URI в командной строке. / Использовать свойство LastModified. Для того чтобы проверить дату последнего обновления веб-сайта, введите его URI в командной строке. Так, если назвать эту программу LastModifiedDemo, то для проверки даты последней модификации веб-сайта по адресу www.HerbSchildt.com введите команду LastModifiedDemo http://HerbSchildt.com / using System; using System.Net; class LastModifiedDemo { static void Main(string[] args) { if(args.Length != 1) { Console.WriteLine("Применение: LastModifiedDemo "); return; } HttpWebRequest req = (HttpWebRequest) WebRequest.Create (args[0]); HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); Console.WriteLine("Последняя модификация: " + resp.LastModified); resp.Close(); } } Практический пример создания программы MiniCrawler Для того чтобы показать, насколько просто программировать для Интернета сред ствами классов WebRequest и WebReponse, обратимся к разработке скелетного ва рианта поискового робота под названием MiniCrawler. Поисковый робот представляет собой программу последовательного перехода от одной ссылки на сетевой ресурс к другой. Поисковые роботы применяются в поисковых механизмах для каталогизации содержимого. Разумеется, поисковый робот MiniCrawler не обладает такими развиты ми возможностями, как те, что применяются в поисковых механизмах. Эта программа начинается с ввода пользователем конкретного адреса URI, по которому затем читается содержимое и осуществляется поиск в нем ссылки. Если ссылка найдена, то программа запрашивает пользователя, желает ли он перейти по этой ссылке к обнаруженному се тевому ресурсу, найти другую ссылку на имеющейся странице или выйти из програм мы. Несмотря на всю простоту такого алгоритма поиска сетевых ресурсов, он служит интересным и наглядным примером доступа к Интернету средствами С#. Программе MiniCrawler присущ ряд ограничений. Во-первых, в ней обнаруживают ся только абсолютные ссылки, указываемые по гипертекстовой команде href="http. Относительные ссылки при этом не обнаруживаются. Во-вторых, возврат к предыду щей ссылке в программе не предусматривается. И в-третьих, в ней отображаются толь ко ссылки, но не окружающее их содержимое. Несмотря на все указанные ограничения данного скелетного варианта поискового робота, он вполне работоспособен и может быть без особых хлопот усовершенствован для решения других задач. На самом деле добавление новых возможностей в программу MiniCrawler — это удобный случай осво ить на практике сетевые классы и узнать больше о сетевом подключении к Интернету. Ниже приведен полностью исходный код программы MiniCrawler. / MiniCrawler: скелетный вариант поискового робота. Применение: для запуска поискового робота укажите URI в командной строке. Например, для того чтобы начать поиск с адреса www.McGraw-Hill.com, введите следующую команду: MiniCrawler http://McGraw-Hill.com / using System; using System.Net; using System.IO; class MiniCrawler { // Найти ссылку в строке содержимого. static string FindLink(string htmlstr, ref int startloc) { int i; int start, end; string uri = null; i = htmlstr.IndexOf("href=\"http", startloc, StringComparison.OrdinalIgnoreCase); if(i != -1) { start = htmlstr.IndexOf('"', i) + 1; end = htmlstr.IndexOf('"', start); uri = htmlstr.Substring(start, end-start); startloc = end; } return uri; } static void Main(string[] args) { string link = null; string str; string answer; int curloc; // содержит текущее положение в ответе if(args.Length != 1) { Console.WriteLine("Применение: MiniCrawler "); return; } string uristr = args[0]; // содержит текущий URI HttpWebResponse resp = null; try { do { Console.WriteLine("Переход по ссылке " + uristr); // Создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(uristr); uristr = null; // запретить дальнейшее использование этого URI // Отправить сформированный запрос и получить на него ответ. resp = (HttpWebResponse) req.GetResponse(); // Получить поток ввода из принятого ответа. Stream istrm = resp.GetResponseStream(); // Заключить поток ввода в оболочку класса StreamReader. StreamReader rdr = new StreamReader(istrm); // Прочитать всю страницу. str = rdr.ReadToEnd(); curloc = 0; do { // Найти следующий URI для перехода по ссылке. link = FindLink(str, ref curloc); if(link != null) { Console.WriteLine("Найдена ссылка: " + link); Console.Write("Перейти по ссылке, Искать дальше, Выйти?"); answer = Console.ReadLine(); if(string.Equals(answer, "П", StringComparison.OrdinalIgnoreCase)) { uristr = string.Copy(link); break; } else if(string.Equals(answer, "B", StringComparison.OrdinallgnoreCase)) { break; } else if(string.Equals(answer, "И", StringComparison.OrdinallgnoreCase)) ( Console.WriteLine("Поиск следующей ссылки."); } } else { Console.WriteLine("Больше ссылок не найдено."); break; } } while(link.Length > 0); // Закрыть ответный поток. if(resp != null) resp.Close(); } while(uristr != null); } catch(WebException exc) { Console.WriteLine("Сетевая ошибка: " + exc.Message + "\nКод состояния: " + exc.Status); } catch(ProtocolViolationException exc) { Console.WriteLine("Протокольная ошибка: " + exc.Message); } catch(UriFormatException exc) { Console.WriteLine("Ошибка формата URI: " + exc.Message); } catch(NotSupportedException exc) { Console.WriteLine("Неизвестный протокол: " + exc.Message); } catch(IOException exc) { Console.WriteLine("Ошибка ввода-вывода: " + exc.Message); } finally { if(resp != null) resp.Close(); } Console.WriteLine("Завершение программы MiniCrawler."); } } Ниже приведен пример сеанса поиска, начиная с адреса www.McGraw-Hill.com. Следует иметь в виду, что конкретный результат поиска зависит от состояния содер жимого на момент поиска. Переход по ссылке http://mcgraw-hill.com Найдена ссылка: http://sti.mcgraw-hill.com:9000/cgi-bin/query?mss=search&pg=aq Перейти по ссылке, Искать дальше, Выйти? И Поиск следующей ссылки. Найдена ссылка: http://investor.mcgraw-hill.com/phoenix.zhtml?c=96562&p=irol-irhome Перейти по ссылке, Искать дальше, Выйти? П Переход по ссылке http://investor.mcgraw-hill.com/phoenix.zhtml?c=96562&p=irol-irhome Найдена ссылка: http://www.mcgraw-hill.com/index.html Перейти по ссылке, Искать дальше, Выйти? П Переход по ссылке http://www.mcgraw-hill.com/index.html Найдена ссылка: http://sti.mcgraw-hill.com:9000/cgi-bin/query?mss=search&pg=aq Перейти по ссылке, Искать дальше. Выйти? В Завершение программы MiniCrawler. Рассмотрим подробнее работу программы MiniCrawler. Она начинается с ввода пользователем конкретного URI в командной строке. В методе Main() этот URI сохра няется в строковой переменной uristr. Затем по указанному URI формируется запрос, и переменной uristr присваивается пустое значение, указывающее на то, что данный URI уже использован. Далее отправляется запрос и получается ответ. После этого со держимое читается из потока ввода, возвращаемого методом GetResponseStream() и заключаемого в оболочку класса StreamReader. Для этой цели вызывается метод ReadToEnd(), возвращающий все содержимое в виде строки из потока ввода. Далее программа осуществляет поиск ссылки в полученном содержимом. Для этого вызывается статический метод FindLink(), определяемый в программе MiniCrawler. Этот метод вызывается со строкой содержимого и исходным положением, с которо го начинается поиск в полученном содержимом. Эти значения передаются методу FindLink() в виде параметров htmlstr и startloc соответственно. Обратите внима ние на то, что параметр startloc относится к типу ref. Сначала в методе FindLink() создается копия строки содержимого в нижнем регистре, а затем осуществляется по иск подстроки href="http, обозначающей ссылку. Если эта подстрока найдена, то URI копируется в строковую переменную uri, а значение параметра startloc об новляется и становится равным концу ссылки. Но поскольку параметр startloc от носится к типу ref, то это приводит к обновлению соответствующего аргумента ме тода Main(), активизируя поиск с того места, где он был прерван. В конечном итоге возвращается значение переменной uri. Эта переменная инициализирована пустым значением, и поэтому если ссылка не найдена, то возвращается пустая ссылка, обозна чающая неудачный исход поиска. Если ссылка, возвращаемая методом FindLink(), не является пустой, то она ото бражается в методе Main(), и далее программа запрашивает у пользователя очеред ные действия. Пользователю предоставляются одна из трех следующих возможностей: перейти по найденной ссылке, нажав клавишу <П>, искать следующую ссылку в имею щемся содержимом, нажав клавишу <И>, иди же выйти из программы, нажав клави шу <В>. Если пользователь нажмет клавишу <П>, то программа осуществит переход по найденной ссылке и получит новое содержимое по этой ссылке. После этого поиск очередной ссылки будет начат уже в новом содержимом. Этот процесс продолжается до тех пор, пока не будут исчерпаны все возможные ссылки. В качестве упражнения вы сами можете усовершенствовать программу MiniCrawler, дополнив ее, например, возможностью перехода по относительным ссылкам. Сделать это не так уж и трудно. Кроме того, вы можете полностью автоматизировать поиско вый робот, чтобы он сам переходил по найденной ссылке без вмешательства со стороны пользователя, начиная со ссылки, обнаруженной на самой первой странице получен ного содержимого, и продолжая переход по ссылкам на новых страницах. Как только будет достигнут тупик, поисковый робот должен вернуться на один уровень назад, най ти следующую ссылку и продолжить переход по ссылке. Для организации именно та кого алгоритма работы программы вам потребуется стек, в котором должны храниться идентификаторы URI и текущее состояние поиска в строке URI. С этой целью можно, в частности, воспользоваться коллекцией класса Stack. В качестве более сложной, но интересной задачи попробуйте организовать вывод ссылок в виде дерева. Применение класса WebClient В заключение этой главы уместно рассмотреть класс WebClient. Как упомина лось в самом ее начале, класс WebClient рекомендуется использовать вместо классов WebRequest и WebResponse в том случае, если в приложении требуется лишь выгру жать или загружать данные из Интернета. Преимущество класса WebClient заклю чается в том, что он автоматически выполняет многие операции, освобождая от их программирования вручную. В классе WebClient определяется единственный конструктор. public WebClient() Кроме того, в классе WebClient определяются свойства, сведенные в табл. 26.6, а также целый ряд методов, поддерживающих как синхронную, так и асинхронную пе редачу данных. Но поскольку рассмотрение асинхронной передачи данных выходит за рамки этой главы, то в табл. 26.7 приведены только те методы, которые поддерживают синхронную передачу данных. Все методы класса WebClient генерируют исключение WebException, если во время передачи данных возникает ошибка. Таблица 26.6. Свойства, определенные в классе WebClient Свойство Описание public string BaseAddress { get; set; } Получает или устанавливает базовый адрес требуемого URI. Если это свойство установлено, то адреса, задава емые в методах класса WebClient, должны опреде ляться относительно этого базового адреса public RequestCachePolicy CachePolicy { get; set; } Получает или устанавливает правила, определяющие, когда именно используется кэш public ICredentials Credentials { get; set; } Получает или устанавливает мандат, т.е. учетные дан ные пользователя. По умолчанию это Свойство имеет пустое значение public Encoding Encoding { get; set; } Получает или устанавливает схему кодирования симво лов при передаче строк Таблица 26.7. Методы синхронной передачи, определенные в классе WebClient Окончание табл. 26.6 Свойство Описание public WebHeaderCollection Headers{ get; set; } Получает или устанавливает коллекцию заголовков за проса public bool IsBusy{ get; } Принимает логическое значение true, если данные по-прежнему передаются по запросу, а иначе — логи ческое значение false public IWebProxy Proxy { get; set; } Получает или устанавливает прокси-сервер public NameValueCollection QueryString { get; set; } Получает или устанавливает строку запроса, состоя щую из пар “имя-значение”, которые могут быть при соединены к запросу. Строка запроса отделяется от URI символом ?. Если же таких пар несколько, то каждая из них отделяется символом @ public WebHeaderCollection ResponseHeaders{ get; } Получает коллекцию заголовков ответа public bool UseDefaultCredentials { get; set; } Получает или устанавливает значение, которое опреде ляет, используется ли для аутентификации устанавливае мый по умолчанию мандат. Если принимает логическое значение true, то используется мандат, устанавливае мый по умолчанию, т.е. учетные данные пользователя, в противном случае этот мандат не используется Метод Определение public byte[] DownloadData(string address) Загружает информацию по адресу URI, обозначае мому параметром address. Возвращает результат в виде массива байтов public byte[] DownloadData(Uri address) Загружает информацию по адресу URI, обозначае мому параметром address. Возвращает результат в виде массива байтов public void DownloadFile(string uri, string fileName) Загружает информацию по адресу URI, обозначае мому параметром fileName. Сохраняет результат в файле fileName public void DownloadFile(Uri address, string fileName) Загружает информацию по адресу URI, обозначае мому параметром address. Сохраняет результат в файле fileName public string DownloadString(string address) Загружает информацию по адресу URI, обозначае мому параметром address. Возвращает результат в виде символьной строки типа string public string DownloadString(Uri address) Загружает информацию по адресу URI, обозначае мому параметром address. Возвращает результат в виде символьной строки типа string public Stream OpenRead(string address) Возвращает поток ввода для чтения информации по адресу URI, обозначаемому параметром address. По окончании чтения информации этот поток необ ходимо закрыть Продолжение табл. 26.7 Метод Определение public Stream OpenRead(Uri address) Возвращает поток ввода для чтения информации по адресу URI, обозначаемому параметром address. По окончании чтения информации этот поток необ ходимо закрыть public Stream OpenWrite(string address) Возвращает поток вывода для записи информа ции по адресу URI, обозначаемому параметром address. По окончании записи информации этот поток необходимо закрыть public Stream OpenWrite(Uri address) Возвращает поток вывода для записи информа ции по адресу URI, обозначаемому параметром address. По окончании записи информации этот поток необходимо закрыть public Stream OpenWrite(string address, string method) Возвращает поток вывода для записи информа ции по адресу URI, обозначаемому параметром address. По окончании записи информации этот поток необходимо закрыть. В строке, передаваемой в качестве параметра method, указывается, как именно следует записывать информацию public Stream OpenWrite(Uri address, string method) Возвращает поток вывода для записи информа ции по адресу URI, обозначаемому параметром address. По окончании записи информации этот поток необходимо закрыть. В строке, передаваемой в качестве параметра method, указывается, как именно следует записывать информацию public byte[] UploadData(string address, byte[] data) Записывает информацию из массива data по адресу URI, обозначаемому параметром address. В итоге возвращается ответ public byte[] UploadData(Uri address, byte[] data) Записывает информацию из массива data по адресу URI, обозначаемому параметром address. В итоге возвращается ответ public byte[] UploadData(string address, string method, byte[] data) Записывает информацию из массива data по адресу URI, обозначаемому параметром address. В итоге возвращается ответ. В строке, передавае мой в качестве параметра method, указывается, как именно следует записывать информацию public byte[] UploadData(Uri address, string method, byte[] data) Записывает информацию из массива data по адресу URI, обозначаемому параметром address. В итоге возвращается ответ. В строке, передавае мой в качестве параметра method, указывается, как именно следует записывать информацию public byte[] UploadFile(string address, string fileName) Записывает информацию в файл fileName по адресу URI, обозначаемому параметром address. В итоге возвращается ответ public byte[] UploadFile(Uri address, string fileName) Записывает информацию в файл fileName по адресу URI, обозначаемому параметром address. В итоге возвращается ответ Окончание табл. 26.7 В приведенном ниже примере программы демонстрируется применение класса WebClient для загрузки данных в файл по указанному сетевому адресу. Метод Определение public byte[] UploadFile(string address, string method, string fileName) Записывает информацию в файл fileName по адресу URI, обозначаемому параметром address. В итоге возвращается ответ. В строке, передавае мой в качестве параметра method, указывается, как именно следует записывать информацию public byte[] UploadFile(Uri address, string method, string fileName) Записывает информацию в файл fileName по адресу URI, обозначаемому параметром address. В итоге возвращается ответ. В строке, передавае мо