Часто требуется считать однотипные данные из веб-
страниц, отформатировать и сохранить в файл.
Выбор как всегда прост, найти готовое приложение или разрабртать свое. В учебных целях создаем свое консольное
приложение на C#.
В конфигурационный файл будем помещать управляющие команды.
Потребуются команды:
@SAVE - файл, куда сохранять,
@LINK -
адреса веб-страниц и заголовки (через разделитель | ),
@START, @END - для указния, где искать данные,
@INPUT - шаблон (регулярное-выражение) для
входных данных,
@OUTPUT - шаблон (регулярное-выражение) для выходных данных.
Пример конфигурационного
файла.
@SAVE C:\temp\weather.csv
@INPUT <tr[^>]*>\s*<th[^>]*>([^<]*?)</th>
s*<td[^>]*>(.*?)</td>\s*<td[^>]*>(.*?)</td>\s*<td[^>]*>(.*?)</td>\s*<td[^>]*>(.*?)</td>\s*<td[^>]*>(.*?)</td>\s*</tr>
@OUTPUT {0};{1};{2};{3};{4};{5}
@START <table cellSpacing=1 cellPadding=2 border=0>
@END </table>
@LINK
http://www.pogodaiklimat.ru/monitor.php?id=27612|Москва
http://www.pogodaiklimat.ru/monitor.php?id=26063|Санкт-Петербург
Код
программы.
namespace Weather
{
class Program
{
static string fconfig =
"weather.config.txt";
static string freport = @"C:\temp\weather.csv";
static string start, end, input, output;
static
List<string> lstLink = new List<string>();
static List<string> lstTitle = new List<string>();
static bool bRus = true;
static void Main(string[] args)
{
string s = "";
try
{
//парсим конфиг-файл
Parse();
//создаем регулярное выражение для выборки данных
Regex _regexInp = new Regex(input,
RegexOptions.Singleline | RegexOptions.IgnoreCase);
//объект для создания результирующего текста
System.Text.StringBuilder
sb = new System.Text.StringBuilder();
for (int i = 0; i < lstLink.Count; i++)
{
Console.WriteLine(lstLink[i]);
//получить данные из веб-страницы
s = GetUrl(lstLink[i], Encoding.UTF8);
s = GetBody(s).Replace("\n", "").Replace("\r", "");
//добавить заголовок
sb.Append(lstTitle[i] +
"\n");
//найти все совпадения с шаблоном
Match match = _regexInp.Match(s);
while
(match.Success)
{
//временная строка указывает на выходной шаблон
string sCode =
output;
for (int j = 1; j < match.Groups.Count; j++)
{
//провести замену во
временной строке всех подстрок, которые соответствуют результатам поиска по шаблону
sCode = sCode.Replace("{" + (j-1) + "}",
match.Groups[j].Value.Trim());
}
//если нужен русский формат чисел
if( bRus )
sCode = sCode.Replace(".", ",");
//добавить к результату
sb.Append(sCode + "\n
");
match = match.NextMatch();
}
}
//записать в выходной файл
File.WriteAllText(freport, sb.ToString(), Encoding.GetEncoding(1251));
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
finally
{
}
Console.WriteLine("Press
any key!");
Console.ReadKey();
}
//считать данные из URL
static string GetUrl(string url, Encoding enc)
{
string s = "";
try
{
WebRequest request = WebRequest.Create(url);
request.Proxy.Credentials = CredentialCache.DefaultCredentials;
WebResponse response = request.GetResponse();
Stream data
= response.GetResponseStream();
using (StreamReader sr = new StreamReader(data, enc))
{
s =
sr.ReadToEnd();
}
}
catch (Exception ex)
{
Console.WriteLine(url + ", " +
ex.ToString());
s = "";
}
return s;
}
//вычленить область с данными
static string GetBody(string s)
{
int ip = s.IndexOf(start);
if (ip < 0) return "";
int ip2 =
s.IndexOf(end, ip);
if (ip2 < 0) return "";
return s.Substring(ip, ip2 - ip);
}
//парсим конфиг-
файл
static void Parse()
{
string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly
().Location);
string[] lines = File.ReadAllLines(dir + "\\" + fconfig, Encoding.UTF8);
for (int i = 0; i < lines.Length; i+
+)
{
string s = lines[i].Trim();
if (s == "" || s.Substring(0, 1) == "#")
continue;
if (s.Contains("@SAVE"))
{
freport = s.Replace("@SAVE", "").Trim();
continue;
}
if (s.Contains("@INPUT"))
{
input = s.Replace("@INPUT",
"").Trim();
continue;
}
if (s.Contains("@OUTPUT"))
{
output = s.Replace("@OUTPUT", "").Trim();
continue;
}
if (s.Contains("@START"))
{
start = s.Replace("@START", "").Trim();
continue;
}
if
(s.Contains("@END"))
{
end = s.Replace("@END", "").Trim();
continue;
}
//для отладки - выход из парсинга
if (s.Contains("@BREAK"))
{
break;
}
string [] arr = s.Split('|');
if (arr.Length != 2)
continue;
lstLink.Add(arr[0]);
lstTitle.Add(arr[1]);
}
}
}
}
Пояснения к коду программы.
Делаем парсинг конфиг-файла, создаем регулярное выражение для выборки данных и объект для создания результирующего текста.
В цикле по всем ссылкам получаем
данные из веб-страницы, добавить заголовок, потом применяем регулярное выражение.
Для каждого совпадения с входным шаблоном провести замену в выходном шаблоне
всех подстрок, которые соответствуют результатам поиска по входному шаблону,
добавить к результату. При выходе из цикла результат записать в выходной файл.
Пример текста, который будет найден по шаблону @INPUT из приведенного выше конфигурационного файла.
<tr align="middle" bgcolor="#ffffff">
<th class="black">2</th>
<td class="blue1">-3.3</td>
<td class="green
">0.1</td>
<td class="red1">2.2</td><td class="blue1">-2.1</td><td class="blue3">0.0</td></tr>
Ему будет
соответствовать выходная строка - 2;-3.3;0.1;2.2;-2;0.0