Habilidades clave del programador de Python

En nuestro tiempo dinámico, el programador necesita mantenerse al día y aprender constantemente nuevas habilidades para seguir siendo un especialista solicitado.

He estado programando en Python durante aproximadamente dos años, y ahora ha llegado el momento de abordar conscientemente el desarrollo de nuevas habilidades. Para hacer esto, decidí analizar las vacantes y presentar las habilidades requeridas en forma de gráfico. Esperaba ver que las habilidades formarían grupos que corresponden a diferentes especialidades: desarrollo de backend, ciencia de datos, etc. Pero, ¿qué pasa con la realidad? Lo primero es lo primero.

Recopilación de datos


Primero tenía que decidir sobre la fuente de datos. Consideré varias opciones: Habr Career , Yandex Work , HeadHunter y otras. HeadHunter parecía el más conveniente, porque aquí en las vacantes hay una lista de habilidades clave y hay una API abierta conveniente .

Después de estudiar la API HeadHunter, decidí analizar primero la lista de ID de trabajo para la palabra clave dada (en este caso, "python"), y luego analizar la lista de etiquetas correspondientes para cada trabajo.

Al buscar vacantes, las vacantes se devuelven página por página, el número máximo de vacantes por página es 100. Al principio guardé los resultados completos como una lista de respuestas de página.

Para esto, se utilizó el módulo de solicitudes. En el campo "agente de usuario", de acuerdo con la API, se ingresó el nombre del navegador virtual para que HH entendiera que el script estaba accediendo a él. Hizo un ligero retraso entre las solicitudes para no sobrecargar el 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)

# getting a list of all pesponses
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, obtuve una lista de diccionarios de respuestas, donde cada diccionario correspondía a una página de resultados de búsqueda.

Al final resultó que, la API hh.ru limita el número máximo de vacantes a dos mil, es decir, con 100 vacantes por página, el número máximo de páginas puede ser 20. Para la palabra clave Python, se devolvieron 20 páginas vacantes, lo que significa que es más probable que haya vacantes reales en Python. cuanto más.

Para obtener una lista de etiquetas, hice lo siguiente:
  • Iterado sobre cada página de resultados de búsqueda,
  • Iterado sobre cada trabajo en la página y obtuve la identificación del trabajo,
  • detalles solicitados de la vacante a través de la API,
  • si se especificó al menos una etiqueta en la vacante, entonces la lista de etiquetas se agregó a la lista.

# parcing vacancies ids, getting vacancy page and scraping tags from each vacancy
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:  # at least one skill present
            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)

Las listas de etiquetas se guardaron como un diccionario

res = {'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:  # Serializing
    json.dump(res, fp)

Curiosamente, de las 2000 vacantes vistas, solo 1579 vacantes tenían etiquetas.

Formateo de datos


Ahora necesita procesar las etiquetas y traducirlas a un formato conveniente para mostrar como un gráfico, a saber:
  • traiga todas las etiquetas a un solo registro, por lo que "aprendizaje automático", "aprendizaje automático" y "aprendizaje automático" significan lo mismo
  • calcular el valor del nodo como la frecuencia de aparición de cada etiqueta,
  • calcule el valor de la conexión como la frecuencia de la reunión conjunta de etiquetas entre sí.

Reduciendo a un solo registro, calculando la frecuencia de aparición de cada etiqueta, el filtrado por el tamaño del nodo se realizó de la siguiente manera.

tags_list['items'] = [[i.lower() for i in line] for line in tags_list['items']]

# counting words occurrences
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}

Ocurrencia en parejas calculada de la siguiente manera. Primero creé un diccionario en el que las claves eran todos los pares de etiquetas posibles en forma de tupla, y los valores eran cero. Luego revisó la lista de etiquetas y aumentó los contadores para cada par encontrado. Luego eliminé todos aquellos elementos cuyos valores eran cero.

# tags connection dict initialization
formatted_tags = {(tag1, tag2): 0 for tag1, tag2 in itertools.permutations(set(nodes_dict.keys()), 2)}

# count tags connection
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

# filtering pairs with zero count
for k, v in formatted_tags.copy().items():
    if v == 0:
        del formatted_tags[k]

En la salida, formé un diccionario de la forma

{
'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}

Visualización de Python


Para visualizar el gráfico, utilicé el módulo networkx. Esto es lo que sucedió la primera vez sin filtrar los nodos.



Esta visualización se parece más a una bola de hilos enredados que a un gráfico de habilidades. Las conexiones son confusas y penetran en el gráfico tan densamente que es imposible distinguir los nodos. Además, hay demasiados nodos en el gráfico, algunos tan pequeños que no tienen significación estadística.

Por lo tanto, filtré los nodos más pequeños de menos de 5 de tamaño, y también hice enlaces grises. En esta imagen, todavía no he traído las palabras a un solo registro, mientras trataba de eliminar el nodo Python más grande para descargar la conexión.



Se ha vuelto mucho mejor. Ahora los nodos están separados y los enlaces no obstruyen la visualización. Se hizo posible ver las habilidades básicas, se encuentran en grandes bolas en el centro del gráfico y pequeños nodos. Pero este gráfico todavía tiene mucho que mejorar.

Visualización de JavaScript


Probablemente continuaría eligiendo este código si en ese momento no tuviera ayuda en forma de hermano. Participó activamente en el trabajo e hizo una hermosa pantalla dinámica basada en el módulo D3 de JavaScript .

Resultó así.


La visualización dinámica está disponible aquí. Tenga en cuenta que los nodos se pueden extraer.

Análisis de resultados


Como podemos ver, el gráfico resultó estar muy entrelazado, y los grupos claramente definidos no se pueden detectar a primera vista. Puede notar de inmediato varios nodos grandes que son más demandados: linux, sql, git, postgresql y django. También hay habilidades de popularidad media y habilidades raramente encontradas.

Además, puede prestar atención al hecho de que las habilidades todavía forman grupos por profesión, ubicadas en lados opuestos del centro:

  • abajo a la izquierda - análisis de datos,
  • en la parte inferior están las bases de datos,
  • abajo a la derecha - desarrollo front-end,
  • a la derecha está probando,
  • arriba a la derecha - desarrollo web,
  • arriba a la izquierda: aprendizaje automático.

Esta descripción de los grupos se basa en mi conocimiento y puede contener errores, pero la idea en sí misma, espero, es clara.

En base a los resultados obtenidos, se pueden extraer las siguientes conclusiones:
  • necesita dominar habilidades que corresponden a nodos grandes, siempre serán útiles,
  • debes dominar las habilidades del grupo que se adapte a tus intereses.

Espero que lo hayan disfrutado y este análisis les sea útil.

Puede echar un vistazo al código o participar en su desarrollo utilizando los enlaces: proyecto GitHub , portátil observable con visualización ¡

Éxito en dominar nuevos horizontes!

All Articles