Em nosso tempo dinâmico, o programador precisa manter-se a par e aprender constantemente novas habilidades para permanecer um especialista procurado.Faço programação em Python há cerca de dois anos e agora chegou a hora de abordar conscientemente o desenvolvimento de novas habilidades. Para isso, decidi analisar as vagas e apresentar as habilidades necessárias na forma de gráfico. Eu esperava ver que as habilidades formariam grupos correspondentes a diferentes especialidades: desenvolvimento de back-end, ciência de dados etc. Mas e a realidade? Primeiras coisas primeiro.Coleção de dados
Primeiro você tinha que decidir sobre a fonte de dados. Eu considerei várias opções: Habr Career , Yandex Work , HeadHunter e outras. O HeadHunter parecia o mais conveniente, porque aqui nas vagas há uma lista de habilidades-chave e uma API aberta conveniente .Tendo estudado a API do HeadHunter, decidi analisar primeiro a lista de IDs de trabalhos para uma determinada palavra-chave (neste caso, “python”) e, em seguida, analisar a lista de tags correspondentes para cada trabalho.Ao procurar vagas, as vagas são retornadas página por página, o número máximo de vagas por página é 100. No início, salvei os resultados completos como uma lista de respostas da página.Para isso, foi utilizado o módulo de solicitações. No campo "user-agent", de acordo com a API, o nome do navegador virtual foi inserido para que HH entendesse que o script estava acessando. Ele fez um pequeno atraso entre as solicitações para não sobrecarregar o servidor.ses = requests.Session()
ses.headers = {'HH-User-Agent': "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0"}
phrase_to_search = 'python'
url = f'https://api.hh.ru/vacancies?text={phrase_to_search}&per_page=100'
res = ses.get(url)
res_all = []
for p in range(res.json()['pages']):
print(f'scraping page {p}')
url_p = url + f'&page={p}'
res = ses.get(url_p)
res_all.append(res.json())
time.sleep(0.2)
Como resultado, recebi uma lista de dicionários de respostas, em que cada dicionário correspondia a uma página de resultados de pesquisa.Como se viu, a API hh.ru limita o número máximo de vagas a dois mil, ou seja, com 100 vagas por página, o número máximo de páginas pode ser 20. Para a palavra-chave Python, foram retornadas 20 páginas de vaga, o que significa que as vagas reais em Python são mais prováveis tudo o mais.Para obter uma lista de tags, fiz o seguinte:- Iterado sobre cada página dos resultados de pesquisa,
- Iterou cada trabalho na página e obteve o ID do trabalho,
- solicitou detalhes da vaga por meio da API,
- se pelo menos uma tag foi especificada na vaga, a lista de tags foi adicionada à lista.
tags_list = []
for page_res_json in res_all:
for item in page_res_json['items']:
vac_id = item['id']
vac_res = ses.get(f'https://api.hh.ru/vacancies/{vac_id}')
if len(vac_res.json()["key_skills"]) > 0:
print(vac_id)
tags = [v for v_dict in vac_res.json()["key_skills"] for _, v in v_dict.items()]
print(' '.join(tags))
tags_list.append(tags)
print()
time.sleep(0.1)
As listas de tags foram salvas como um dicionáriores = {'phrase': phrase_to_search, 'items_number': len(tags_list), 'items': tags_list}
with open(f'./data/raw-tags_{phrase_to_search}.json', 'w') as fp:
json.dump(res, fp)
Curiosamente, das 2000 vagas visualizadas, apenas 1579 vagas possuíam tags.Formatação de dados
Agora você precisa processar as tags e traduzi-las para um formato conveniente para exibição em gráfico, a saber:- traga todas as tags para um único registro, para que “aprendizado de máquina”, “aprendizado de máquina” e “aprendizado de máquina” signifiquem a mesma coisa
- calcular o valor do nó como a frequência de ocorrência de cada tag,
- calcule o valor da conexão como a frequência da reunião conjunta de tags.
Reduzindo para um único registro, calculando a frequência de ocorrência de cada tag, a filtragem pelo tamanho do nó foi realizada da seguinte maneira.tags_list['items'] = [[i.lower() for i in line] for line in tags_list['items']]
flattened_list = [i for line in tags_list for i in line]
nodes_dict_all = {i: flattened_list.count(i) for i in set(flattened_list)}
nodes_dict = {k:v for k, v in nodes_dict_all.items() if v > del_nodes_count}
Ocorrência em pares calculada da seguinte forma. Primeiro, criei um dicionário no qual as chaves eram todos possíveis pares de tags na forma de tupla e os valores eram zero. Depois, percorreu a lista de tags e aumentou os contadores para cada par encontrado. Então eu apaguei todos os elementos cujos valores eram zero.
formatted_tags = {(tag1, tag2): 0 for tag1, tag2 in itertools.permutations(set(nodes_dict.keys()), 2)}
for line in tags_list:
for tag1, tag2 in itertools.permutations(line, 2):
if (tag1, tag2) in formatted_tags.keys():
formatted_tags[(tag1, tag2)] += 1
for k, v in formatted_tags.copy().items():
if v == 0:
del formatted_tags[k]
Na saída, formei um dicionário do formulário{
'phrase': phrase searched,
'items_number': number of vacancies parced,
'items': {
"nodes": [
{
"id": tag name,
"group": group id,
"popularity": tag count
},
…
]
"links": [
{
"source": pair[0],
"target": pair[1],
"value": pair count
},
…
]
}
}
nodes = []
links = []
for pair, count in formatted_tags.items():
links.append({"source": pair[0], "target": pair[1], "value": count})
max_count = max(list(nodes_dict.values()))
count_step = max_count // 7
for node, count in nodes_dict.items():
nodes.append({"id": node, "group": count // count_step, "popularity": count})
data_to_dump = in_json.copy()
data_to_dump['items'] = {"nodes": nodes, "links": links}
Visualização Python
Para visualizar o gráfico, usei o módulo networkx. Foi o que aconteceu pela primeira vez sem filtrar os nós.
Essa visualização é mais uma bola de fios emaranhados do que um gráfico de habilidades. As conexões são confusas e penetram no gráfico com tanta densidade que é impossível distinguir nós. Além disso, existem muitos nós no gráfico, alguns tão pequenos que eles não têm significância estatística.Portanto, filtramos os menores nós com menos de 5 e também fiz links em cinza. Nesta figura, eu ainda não trouxe as palavras para um único registro, enquanto tentava excluir o maior nó Python para descarregar a conexão.
Tornou-se muito melhor. Agora os nós estão separados e os links não obstruem a visualização. Tornou-se possível ver as habilidades básicas, elas estão localizadas em bolas grandes no centro do gráfico e em nós pequenos. Mas este gráfico ainda tem muito a melhorar.Visualização JavaScript
Eu provavelmente continuaria escolhendo esse código se naquele momento não tivesse ajuda na forma de um irmão. Ele se envolveu ativamente no trabalho e fez uma bela exibição dinâmica baseada no módulo JavaScript D3 .Acabou assim.
A visualização dinâmica está disponível aqui. Observe que os nós podem ser puxados.Análise de Resultados
Como podemos ver, o gráfico mostrou-se muito interligado, e clusters claramente definidos não podem ser detectados à primeira vista. Você pode notar imediatamente vários nós grandes que são os mais procurados: linux, sql, git, postgresql e django. Também existem habilidades de popularidade média e habilidades raramente encontradas.Além disso, você pode prestar atenção ao fato de que as habilidades ainda formam grupos por profissão, localizadas em lados opostos do centro:- canto inferior esquerdo - análise de dados,
- no fundo estão os bancos de dados,
- canto inferior direito - desenvolvimento front-end,
- à direita está testando,
- canto superior direito - desenvolvimento web,
- canto superior esquerdo - aprendizado de máquina.
Essa descrição dos clusters é baseada no meu conhecimento e pode conter erros, mas espero que a ideia em si seja clara.Com base nos resultados obtidos, as seguintes conclusões podem ser tiradas:- você precisa dominar habilidades que correspondem a nós grandes, elas sempre serão úteis,
- você precisa dominar as habilidades do cluster que atendem aos seus interesses.
Espero que tenham gostado e esta análise seja útil para você.Você pode dar uma olhada no código ou participar de seu desenvolvimento usando os links: projeto GitHub , laptop observável com visualizaçãoSucesso em dominar novos horizontes!