Парсинг сайта metanit

303
05 мая 2018, 14:33

Хочу спарсить сайт metanit.com

Стандартными средствами(пример кода https://github.com/extremecodetv/Html-Parser-Tutorial) не выходит. Просто отдает одну пустую страницу.

Пытался использовать Selenium, но тоже работает через раз, да и способ этот мне не нравится: ради парсинга целый движок браузера загружать.

Может вы знаете способ лучше? И почему не работает обычный System.Net.Http.HttpClient ?

Answer 1

Проверил я данный сайт. Получаете вы не пустую страницу как таковую, а ошибку 403, которая гласит:

Access forbidden!
You don't have permission to access the requested object.
It is either read-protected or not readable by the server.

Из за чего это может быть?
Хм, ну по сути это происходит по двум причинам:

  1. Вам заблокировали доступ на сервер (или часть сервера)
  2. Сервер имеет некую защиту от автоматизации, которая обычно бывает:
    • Банальная проверка User-Agent заголовка у запроса.
    • Проверка Cookie.
    • Комплексные защиты (CloudFlare например, где используют и то и то сразу (а то и больше...)).

Для начала нам стоит отловить запрос, который мы посылаем при заходе на сервер и по очереди добавлять в наш код все, что необходимо.

Лично я всегда использую подобный код для отправки запросов:

public async Task<string> SendRequest(string url)
{
    string data;
    var baseAddress = new Uri("https://metanit.com");
    var cookieContainer = new CookieContainer();
    using (var handler = new HttpClientHandler { CookieContainer = cookieContainer })
    using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
    {
        var result = await client.GetAsync(url);
        data = await result.Content.ReadAsStringAsync();
    }
    return data;
}

И так, пробуем по порядку, попробуем задать другой User-Agent. Для этого нам достаточно добавить подобную строку до получения result переменной:

client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17120");

И о чудо, сервер открыл нам доступ!

Если бы сервер требовал от нас Cookie, то стоило бы добавить что то вроде этого до result:

cookieContainer.Add(baseAddress, new Cookie("key", "value"));

В общем, экспериментируйте и смотрите что именно отправляет ваш браузер на сервер, тогда будет в разы легче сделать то, что вы хотите.

Answer 2

Приведу пример с HttpWebRequest код получен, парсить можно чем угодно дальше.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Forms;
namespace TestParse
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            txtLog.Text = Parser("https://metanit.com");
        }
        public static string Parser(string url)
        {
            HttpWebRequest request =
            (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.Accept = "application/json";
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream());
            StringBuilder output = new StringBuilder();
            output.Append(reader.ReadToEnd());
            response.Close();
            return output.ToString();
        }
    }
}

Раз вы упомянули Selenium приведу пример с использованием WebDriver с возможностью обхода Cloudflare

public  string GetHtml(string url)
{
    string html = null;
    try
    {
        var driverService = PhantomJSDriverService.CreateDefaultService();
        driverService.HideCommandPromptWindow = true;
        var Driver = new PhantomJSDriver(driverService);
        Driver.Navigate().GoToUrl(url);
        while (Driver.PageSource.Contains("setTimeout(function()"))
            Thread.Sleep(1000);
        html = Driver.PageSource;
        Driver.Close();
        Driver.Quit();
        return html;
    }
    catch { }
      return null;
}
READ ALSO
Использование статических библиотек (C++) в С# и Python

Использование статических библиотек (C++) в С# и Python

Можете ли вы пояснить мне, возможно ли использование статических библиотек, написанных на C++, в приложениях на C# и PythonВо время компиляции...

238
Сохранение состояния radiobutton c#

Сохранение состояния radiobutton c#

У меня в программе есть Combobox с четырьмя значениями и четыре radiobutton'aМне нужно выбрать первый элемент в combobox, нажать на нужные radiobutton'ы, выбрать...

305
XML сериализация сложного класса

XML сериализация сложного класса

Имеется довольно сложная структура следующего вида:

223