Neste tutorial, vamos aprender a criar um Service que utiliza HttpClient para capturar a contagem de seguidores de um perfil público do Instagram.
1. Criando a Interface
Primeiro, criamos a interface do Service. Isso permite que o Service seja adicionado ao container de injeção de dependência.
public interface IInstagramScraper
{
Task<string> GetFollowerCountAsync(string instagramHandle);
}Essa interface define um método GetFollowerCountAsync, que recebe o instagramHandle (nome de usuário do Instagram) e retorna uma string contendo a quantidade de seguidores.
2. Implementando o Service
Seguindo o padrão da arquitetura limpa, implementamos serviços externos na camada de infraestrutura. Primeiro, criamos a classe InstagramScraper que implementa a interface IInstagramScraper.
public class InstagramScraper : IInstagramScraper
{
private readonly HttpClient _httpClient;
public InstagramScraper(HttpClient httpClient)
{
_httpClient = httpClient;
ConfigureHttpClient();
}
}Injeção de Dependência do HttpClient
A classe InstagramScraper tem uma variável readonly do tipo HttpClient para gerenciar as requisições. Recebemos a instância do HttpClient no construtor, facilitando a injeção de dependência.
3. Configurando o HttpClient
Configuramos o HttpClient para enviar cabeçalhos específicos que ajudam a imitar um navegador, garantindo que o Instagram não bloqueie a requisição.
private void ConfigureHttpClient()
{
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3");
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
_httpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.5");
}Essa configuração adiciona cabeçalhos User-Agent, Accept e Accept-Language, simulando um navegador comum para evitar bloqueios do site.
4. Implementando o Método de Scraping
Agora, implementamos o método GetFollowerCountAsync na classe InstagramScraper. Esse método é responsável por acessar a página do perfil, buscar o HTML e extrair a contagem de seguidores.
Passo 1: Construindo a URL do Perfil do Instagram
public async Task<string> GetFollowerCountAsync(string instagramHandle)
{
var url = $"https://www.instagram.com/{instagramHandle}/";Usamos interpolação de strings para construir a URL do perfil a partir do instagramHandle fornecido.
Passo 2: Fazendo a Requisição HTTP
var response = await _httpClient.GetStringAsync(url);Usando o HttpClient, enviamos uma requisição HTTP para a página do perfil e aguardamos o HTML da resposta. Esse método é assíncrono (await) para não bloquear a execução enquanto a resposta é carregada.
Passo 3: Verificando a Resposta
if (string.IsNullOrEmpty(response))
throw new Exception("The page response is empty or null.");Caso a response seja nula ou vazia, lançamos uma exceção indicando que a página não foi carregada corretamente. Isso ajuda a evitar erros ao tentar processar um HTML inexistente.
Passo 4: Capturando a Contagem de Seguidores com Expressão Regular
Para extrair a contagem de seguidores, usamos uma expressão regular que busca no HTML o valor esperado no meta tag og:description.
var followersRegex = new Regex(@"<meta property=""og:description"" content=""(\d+(?:[,.]\d+)?[KM]?) Followers");
var match = followersRegex.Match(response);A expressão regular captura um padrão específico, permitindo números formatados como 5K, 10.5M ou 1000. O grupo de captura (\d+(?:[,.]\d+)?[KM]?) lida com vírgulas, pontos e as letras K e M para representar milhares e milhões.
Passo 5: Verificando se a Contagem foi Encontrada
if (!match.Success)
throw new Exception("Unable to find the follower count.");Se não houver correspondência (match.Success é false), lançamos uma exceção informando que a contagem de seguidores não foi encontrada. Esse tratamento de erro é importante, pois o HTML pode variar, e a estrutura da página do Instagram pode mudar.
Passo 6: Retornando a Contagem de Seguidores
return match.Groups[1].Value;
}Finalmente, retornamos o valor do grupo de captura Groups[1].Value, que contém a contagem de seguidores no formato capturado do HTML.
5. Adicionando o Service ao Container de Dependência
Para tornar o Service disponível por injeção de dependência, adicionamos a configuração ao container de serviços.
private static void AddScraper(IServiceCollection services)
=> services.AddHttpClient<IInstagramScraper, InstagramScraper>();No método de configuração de serviços:
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
AddScraper(services);
// Outros métodos...
return services;
}6. Exemplo de Uso do Service
Aqui está um exemplo de como o Service pode ser utilizado em uma Handler para processar uma consulta que retorna informações sobre influenciadores:
public record GetInfluencersQuery : IRequest<IEnumerable<ResponseInfluencerProfileJson>>;
public class GetInfluencersQueryHandler(
IInfluencerReadOnlyRepository repository,
IInstagramScraper instagramScraper) : IRequestHandler<GetInfluencersQuery, IEnumerable<ResponseInfluencerProfileJson>>
{
public async Task<IEnumerable<ResponseInfluencerProfileJson>> Handle(GetInfluencersQuery request, CancellationToken cancellationToken)
{
var influencers = await repository.GetInfluencers();
var responseTasks = influencers.Select(async influencer =>
{
try
{
var instagram = await instagramScraper.GetFollowerCountAsync(influencer.Instagram);
return new ResponseInfluencerProfileJson(
influencer.Username.Value,
influencer.Instagram,
instagram);
}
catch (Exception ex)
{
return new ResponseInfluencerProfileJson(
influencer.Username.Value,
influencer.Instagram,
ex.Message);
}
});
var responses = await Task.WhenAll(responseTasks);
return responses;
}
}Esse exemplo demonstra como o InstagramScraper pode ser usado em um Handler para obter informações de seguidores e compilar um perfil de resposta, com tratamento de exceção para eventuais falhas na captura de dados.