Pular para o conteúdo principal

Web scraping e parsing de dados em R | explorando dados de H‑1B, parte 1

Aprenda a coletar dados da web, pré-processá-los e realizar uma análise exploratória básica com R
Atualizado 17 de set. de 2026  · 15 min lido

Explorar com IA

ChatGPTClaudePerplexity

O objetivo deste tutorial é mostrar como você pode reunir dados sobre vistos H‑1B por meio de web scraping com R. Em seguida, você também vai aprender a fazer o parsing de objetos JSON e a armazenar e manipular os dados para realizar uma análise exploratória de dados (EDA) básica no grande conjunto de registros de solicitações H‑1B.

Quem sabe você descobre como se posicionar melhor como candidato — ou aprende um código novo em R!

Introdução

Na semana passada, o blog da DataCamp "Can Data Help Your H-1B Visa Application" apresentou alguns resultados de uma análise de dados de H‑1B ao longo dos anos. Agora é hora de colocar a mão na massa, analisar os dados por conta própria e ver o que mais você encontra! Ted Kwartler vai te guiar com uma série de tutoriais em R.

Tenho um amigo em um escritório de advocacia no Texas que protocola vistos H‑1B. O H‑1B é um visto de não imigrante nos Estados Unidos que permite a empregadores americanos contratarem temporariamente trabalhadores estrangeiros em ocupações especializadas. Pelo visto, ser aceito é bem difícil porque há poucas vagas de visto para milhares de candidatos. Apesar de isso ser anedótico, decidi explorar os dados por conta própria na esperança de ajudar candidatos qualificados a ver que os EUA são um lugar acolhedor!

Obtendo seus dados: web scraping e parsing

Um colega na DataCamp me indicou este site, que é um site simples com dados de H‑1B de 2012 a 2016. O site afirma ter 2 milhões de solicitações H‑1B organizadas em uma única tabela. Eu decidi coletar esses dados programaticamente (leia-se: fazer web scraping), porque eu não ia copiar e colar pelo resto da vida! Como você pode ver, a imagem abaixo mostra um trecho do site com dados de H‑1B de Boston:

\"H1B

As bibliotecas usadas neste tutorial incluem jsonlite para fazer parsing de objetos JSON, rvest, que “colhe” HTML, pbapply, meu favorito por adicionar barras de progresso às funções apply da base R, e data.table, que melhora o desempenho do R com data frames grandes.

library(jsonlite)
library(rvest)
library(pbapply)
library(data.table)

Explorando a estrutura da página

Ao explorar o site, você vai notar que o formulário de busca sugere opções de preenchimento automático. Por exemplo, ao digitar “B” no campo de cidade, aparece um modal com sugestões, como mostrado abaixo. A imagem a seguir mostra as opções quando eu digito “B”:

\"Salary

Isso significa que você pode usar o preenchimento automático como uma forma eficiente de consultar o site. No Chrome, recarregue e clique com o botão direito para “Inspecionar” a página, depois vá até “Network” no painel do desenvolvedor e, por fim, digite “B” na página para carregar o modal. Explorando os links do painel de rede, você vai encontrar uma consulta PHP que retorna um objeto JSON de cidades como esta. A ideia é primeiro coletar todas as cidades sugeridas e depois usar essa lista para extrair muitas páginas com dados de H‑1B. Ao explorar a URL anterior, você notará que ela termina com uma letra. Então você pode usar paste0() com a base da URL, http://h1bdata.info/cities.php?term=, e letters. A base é reciclada para cada valor em letters. O objeto letters é um vetor embutido do R, de “a” a “z”. O objeto json.cities é um vetor de URLs, de a a z, que contém todas as sugestões de preenchimento como JSON.

json.cities<-paste0('http://h1bdata.info/cities.php?term=', letters)
Fazendo parsing de objetos JSON O objeto json.cities é um vetor com 26 links que precisam ser lidos pelo R. Usando lapply() ou pblapply() junto com fromJSON, o R vai fazer o parsing de cada objeto JSON para criar all.cities. Você aninha o resultado em unlist para que a saída seja um vetor simples de strings. Com esse código, você tem todas as cidades sugeridas organizadas em um vetor que pode ser usado para construir as páginas reais com dados.
all.cities<-unlist(pblapply(json.cities,fromJSON))
Para reduzir o tempo de carregamento de cada página, você pode decidir passar dois parâmetros, cidade e ano, em cada consulta de página. Por exemplo, dados de H‑1B de Boston em 2012, depois Boston 2013 e assim por diante. Uma função ótima para criar combinações de fatores é expand.grid(). No código abaixo, você vê que a informação de cidade é passada, all.cities, e o ano usando seq() de 2012 a 2016. A função cria mais de 5000 combinações cidade‑ano. expand.grid() cria programaticamente Boston 2012, Boston 2013, Boston 2014 etc., pois cada cidade e cada ano representam uma combinação única de fatores.
city.year<-expand.grid(city=all.cities,yr=seq(2012,2016))
Algumas cidades, como Los Angeles, têm duas palavras e precisam ser codificadas para URLs. A função url_encode() transforma “Los Angeles” em Los%20Angeles para validar o endereço. Você passa o vetor inteiro e url_encode() atua linha a linha:
city.year$city<-urltools::url_encode(as.character(city.year$city))
Por fim, você usa novamente a função paste0() para concatenar a URL base às combinações de cidade e estado em city.year. Veja um link de exemplo aqui.
all.urls<-paste0('http://h1bdata.info/index.php?em=&job=&city=', city.year[,1],'&year=', city.year[,2])

Extraindo informações das páginas

Depois de concluir as etapas anteriores, você pode criar uma função customizada chamada main para coletar os dados de cada página. É um fluxo simples usando funções do rvest. Primeiro, uma URL é recebida e read_html() faz o parsing do conteúdo. Em seguida, a única html_table da página é selecionada dentre as demais informações HTML. A função main converte o objeto x em um data.table para armazená-lo de forma eficiente na memória. Por fim, antes de encerrar a main, você pode adicionar um Sys.sleep para não ser considerado um ataque DDoS.

main<-function(url.x){
  x<-read_html(url.x)
  x<-html_table(x)
  x<-data.table(x[[1]])
  return(x)
  Sys.sleep(5)
}

Vamos buscar esses dados! Eu gosto da barra de progresso do pblapply() para acompanhar o scraping.
Basta passar all.urls e a função main dentro de pblapply(). Imediatamente, o R começa a carregar uma página, coletar a tabela e manter um data.table em memória para aquela página. Cada URL é coletada em sequência e mantida em memória.

all.h1b<-pblapply(all.urls, main)

Combinando os dados em um data table

Ufa! Isso levou horas! Neste ponto, all.h1b é uma lista de data tables, um por página. Para unificar tudo em um único data table, você pode usar rbindlist. É similar a do.call(rbind, all.h1b), mas bem mais rápido.

all.h1b<-rbindlist(all.h1b)

Por fim, salve os dados para não precisar repetir o processo. Por sorte, eu deixei uma cópia salva aqui.

write.csv(all.h1b,'h1b_data.csv', row.names=F)

Limpando seus dados

Mesmo tendo feito o scraping, ainda são necessários alguns passos para deixar tudo em um formato mais gerenciável.

Você vai usar lubridate para organizar datas. Também vai usar stringr, que oferece funções práticas para manipulação de strings.

library(lubridate)
library(stringr)

Por preferência pessoal, eu gosto de usar scipen=999. Não é obrigatório, mas elimina a notação científica.

options(scipen=999)

Descobrimos que o scraping capturou 1,8 milhão dos 2 milhões de registros H‑1B. Na minha opinião, 1,8 M já é bom o suficiente. Então vamos carregar os dados usando fread(): essa função é como o read.csv, mas é o “fast & friendly file finagler” mais eficiente.

h1b.data<-fread('h1b_data.csv')

Os nomes das colunas raspadas estão em maiúsculas e contêm espaços.

Referenciá-las pelo nome é chato, então a primeira coisa a fazer é renomeá-las.

Renomear colunas exige funções nos dois lados do operador de atribuição (<-). À esquerda, use colnames() e passe o data frame. À direita, você pode passar um vetor de strings.

Neste exemplo, primeiro você pega os nomes originais e os coloca em minúsculas com tolower(). Na segunda linha, aplica gsub(), uma função de substituição global.

Quando gsub() reconhece um padrão — neste caso, espaço — ele substitui todas as ocorrências pelo segundo parâmetro, o sublinhado. Por fim, você indica que gsub() deve fazer as substituições em names(h1b.data), que representam as colunas agora em minúsculas.

colnames(h1b.data)<-tolower(names(h1b.data))
colnames(h1b.data)<-gsub(' ', '_', names(h1b.data))  

Uma das primeiras funções que uso ao explorar dados é tail(). Ela retorna as últimas linhas. Aqui, tail() vai retornar as últimas 8.

Isso ajuda a ver rapidamente o formato dos dados e como estão os vetores.

tail(h1b.data, 8)

Depois, eu sempre verifico a classe dos vetores. Em dados de scraping, valores numéricos ou fatores podem virar texto. Corrigir classes agora evita dor de cabeça depois!

Usando a função apply(), você passa h1b.data, depois 2 e a função class. Como você escolheu 2, o R vai checar a classe de cada coluna e retornar no console. Você pode usar apply() com 1 para aplicar por linha, mas não ajudaria neste caso.

apply(h1b.data,2,class)

Opa!

Todas as colunas são “character” e precisam ser corrigidas. Vou mostrar como mudar uma das colunas de data e deixar o restante com você. Com tail(), você examina as últimas 6 linhas das datas mal classificadas.

Para corrigir as datas, as barras / precisam virar traços -. Mais uma vez, use gsub() para buscar / e substituir por -.

tail(h1b.data$submit_date)
h1b.data$submit_date<-gsub('/', '-', h1b.data$submit_date)

Com os traços no lugar, aplique mdy(), que significa “month, day, year”. Isso porque as datas estão nesse formato. Se a ordem fosse diferente, você ajustaria as letras de mdy conforme necessário.

Para garantir que a coluna foi alterada corretamente, reexamine o tail e cheque a classe. O tail() deve imprimir datas como “2016-03-11 UTC” e a classe do vetor deve ser “POSIXct” em vez de “character”.

h1b.data$submit_date<-mdy(h1b.data$submit_date)
tail(h1b.data$submit_date)
class(h1b.data$submit_date)

Para este tipo de análise, é uma boa ideia extrair apenas o mês e o ano das datas em novas colunas. No código abaixo, você vê que duas novas colunas, $submit_month e $submit_yr, são criadas.

No lubridate, a função month() pode ser aplicada a uma coluna inteira para extrair os valores de mês de uma data. A função year() faz o mesmo para criar h1b.data$submit_yr. Ao usar head(), você deve ver as duas novas colunas criadas.

h1b.data$submit_month<-month(h1b.data$submit_date, label=T)
h1b.data$submit_yr<-year(h1b.data$submit_date)
head(h1b.data)

Agora, vamos examinar a coluna $base_salary. Ela tem vírgula na casa dos milhares e o R a trata como texto, então precisa ser convertida. Mais uma vez, gsub() salva o dia, removendo a vírgula e substituindo por vazio. Depois, as.numeric() é aplicado a h1b.data$base_salary para transformar oficialmente os valores em números.

Você pode examinar uma parte do novo vetor com head() na terceira linha.

h1b.data$base_salary<-gsub(',','',h1b.data$base_salary)
h1b.data$base_salary<-as.numeric(h1b.data$base_salary)
head(h1b.data$base_salary)

Outra forma de fatiar esses dados é por estado. Ao examinar a coluna h1b.data$location, você vê que cidade e estado são separados por vírgula. O código abaixo usa str_split_fixed() para separar a informação de localização na primeira vírgula. Basta passar a coluna, o caractere separador e o número de colunas a retornar. O objeto resultante state é uma matriz grande com o mesmo número de linhas de h1b.data e 2 colunas.

state<-str_split_fixed(h1b.data$location,', ', 2)

As duas linhas seguintes fazem o bind das colunas individuais como $city e $state em h1b.data. Os vetores não ficam perfeitos porque pode haver variações de grafia, como “Winston Salem” versus “Winston-Salem”. No geral, esse método é bom o suficiente para uma EDA simples, mas tenha em mente que pode ser necessário agregar termos em outras análises.

h1b.data$city<-state[,1]
h1b.data$state<-state[,2] 

Explorando seus dados: primeiros passos

Se você estivesse se candidatando a um visto H‑1B, iria querer saber em quais estados há mais oportunidades para aumentar suas chances de aceitação. A função table() é usada para contar variáveis categóricas e pode ser aplicada facilmente a h1b.data$state. Na segunda linha, você cria um pequeno data frame para capturar os nomes dos estados e as contagens de H‑1B.

state.tally<-table(h1b.data$state)
state.tally<-data.frame(state=names(state.tally), h1b=as.vector(state.tally))

Usando state.tally e barplot(), você cria um gráfico de barras básico com os valores de H‑1B por estado. O segundo parâmetro, names.arg, define os rótulos das barras e las=3 instrui o R a posicionar os rótulos verticalmente. Você vai notar algumas localidades “bagunçadas” por causa da divisão na vírgula, mas o ponto fica claro… um candidato H‑1B provavelmente estará em CA, NJ, NY ou TX.

barplot(state.tally$h1b,
        names.arg = names(table(h1b.data$state)),
        las=3)

\"H1B

Você vê a contagem de vistos H‑1B por estado de 2012 a 2016.

Agora, vamos tentar entender a correlação entre vistos H‑1B e algum fato externo. Para simplificar, o R tem um conjunto de dados embutido chamado state.x77. É uma matriz com 50 linhas, uma por estado, e fatos como população e expectativa de vida no Censo dos EUA de 1977.

Dica: use uma fonte mais recente na sua própria análise.

Por ora, usar o state.x77 é um bom exemplo para aprender. Examine esse dataset com head().

head(state.x77)

Vamos juntar essa informação com os dados de state.tally em um data frame maior para entender relações. Para isso, crie um data frame state.data contendo abreviações dos estados, state.abb, e os dados antigos do censo de 1977. Depois chame merge passando state.tally e state.data.

Você pode declarar explicitamente a coluna state como o vetor de junção. Examine um trecho do data frame na terceira linha, indexando as linhas da 15ª à 20ª.

state.data<-data.frame(state=state.abb,state.x77)
state.data<-merge(state.tally,
                  state.data,
                  by='state')
state.data[15:20,]

Uma função básica de EDA é cor: ela imprime a correlação entre duas variáveis.

Lembre que a correlação varia de -1 a 1: 0 significa que as variáveis não têm correlação e provavelmente não estão relacionadas. Um número se aproximando de 1 indica correlação positiva (tomara!) como programação em R e renda. Um número negativo indica relação inversa, como programação em R e vida social!

Este código aplica cor à população dos estados em 1977 e às contagens atuais de H‑1B. Lembre-se: o objetivo é ilustrar o método da análise, apesar do desencontro temporal. Você pode mudar $Population para outra variável do data frame.

cor(state.data$Population, state.data$h1b)

Outra forma de investigar relações entre variáveis é com um diagrama de dispersão, especialmente uma matriz de dispersão. Use pairs() para criar rapidamente. O código abaixo usa uma fórmula para definir os relacionamentos. Cada coluna é declarada individualmente, com um sinal de mais entre elas. O parâmetro data recebe o data frame e main define o título.

pairs(~ h1b + Population + Income,
      data = state.data,
      main='h1b relationships')

\"H1B

Esta matriz de dispersão visualiza as contagens de H‑1B versus Population e Income dos estados.

Você pode observar uma relação entre Population e h1b. Isso faz sentido: estados mais populosos tendem a ter mais oportunidades que demandam visto H‑1B.

Para “dar zoom” em um único gráfico da matriz, basta chamar plot() e passar as duas variáveis.

plot(state.data$Income,state.data$h1b,
     main = 'Income to H1B')

Próximos passos com seus dados de H‑1B

Você só começou nossa exploração de vistos H‑1B! Este conjunto é riquíssimo e, no próximo post, você vai explorar os salários, remover outliers e criar visuais mais impactantes com ggplot2.

Você também vai avançar nesses conceitos de EDA analisando o status de H‑1B ao longo do tempo e os principais empregadores. Um dos visuais legais que já vou te mostrar é o boxplot que apresenta as distribuições de salário por status do visto H‑1B, usando este código:

ggplot(h1b.data) +
geom_boxplot(aes(factor(case_status),base_salary,fill=as.factor(case_status))) +
  ylim(0,100000) +
  theme_gdocs() +
  scale_fill_gdocs() +
  theme(axis.text.x=element_blank())

\"H1B

Fique de olho para a próxima parte da série de tutoriais "Explorando dados de H‑1B com R"! Enquanto isso, vale conferir nosso tutorial sobre data frames em R, o curso Importing Data in R ou o curso Data Manipulation in R with dplyr

Tópicos
R
Ciência de dados
Python
Data Analysis

Cursos de R

Curso

Web Scraping em R

4 h
15.2K
Aprenda a coletar e baixar dados de qualquer site de forma eficiente usando o R.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

blog

O que é data wrangling? Um guia prático com exemplos

Aprenda os conceitos e fundamentos do data wrangling com exemplos práticos. Use essas habilidades no dia a dia para gerar dados limpos e úteis para seus modelos.
Tim Lu's photo

Tim Lu

12 min

Tutorial

21 ferramentas essenciais do Python

Aprenda sobre as ferramentas Python essenciais para o desenvolvimento de software, raspagem e desenvolvimento da Web, análise e visualização de dados e aprendizado de máquina.
Abid Ali Awan's photo

Abid Ali Awan

6 min

Tutorial

Tutorial de como executar consultas SQL em Python e R

Aprenda maneiras fáceis e eficazes de executar consultas SQL em Python e R para análise de dados e gerenciamento de bancos de dados.
Abid Ali Awan's photo

Abid Ali Awan

13 min

Tutorial

Tutorial de Pipes em R para iniciantes

Saiba mais sobre o famoso operador de pipe %>% e outros pipes no R, por que e como você deve usá-los e quais alternativas você pode considerar!
Karlijn Willems's photo

Karlijn Willems

15 min

Tutorial

Criação de uma lista no R

Pratique Listas em R usando o material do curso Intro to R do DataCamp.
Ryan Sheehy's photo

Ryan Sheehy

3 min

Tutorial

Leitura e importação de arquivos do Excel para o R com o readxl

Neste artigo, saiba como importar arquivos do Excel para o R com a biblioteca readxl.
Vidhi Chugh's photo

Vidhi Chugh

5 min

Ver MaisVer Mais