{"id":2516,"date":"2025-10-01T02:40:22","date_gmt":"2025-10-01T02:40:22","guid":{"rendered":"https:\/\/paolozada.com\/info\/?p=2516"},"modified":"2025-10-05T16:15:46","modified_gmt":"2025-10-05T16:15:46","slug":"convertir-un-pequeno-bot-en-un-servicio-rest","status":"publish","type":"post","link":"https:\/\/paolozada.com\/info\/convertir-un-pequeno-bot-en-un-servicio-rest\/","title":{"rendered":"\u00bfC\u00f3mo convertir un peque\u00f1o bot en un servicio REST?"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\"><\/h1>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-a07b618b790c249694f00ca158679b0c\">\u00bfAlguna vez te has preguntado qu\u00e9 pasar\u00eda si en lugar de limitarte a crear un bot que navega p\u00e1ginas y hace clics, pudieras transformarlo en un servicio que cualquiera puede consumir desde la web? \ud83e\udd14<\/p>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-a4e2020b7b90741f6d538f1385bb404e\">En el mundo de la automatizaci\u00f3n, muchas veces empezamos creando un bot sencillo en Python para hacer clics, leer precios o extraer datos de una p\u00e1gina. \ud83d\ude80<br>Pero \u00bfqu\u00e9 pasa cuando queremos que ese bot no viva solo en nuestro computador, sino que cualquiera pueda consumirlo como un servicio REST desde otra aplicaci\u00f3n?<\/p>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-67318b97b8ac74e9b14847f5e4004e39\">En este art\u00edculo te muestro c\u00f3mo lo hice paso a paso, para este proyecto utilic\u00e9 <strong>Python, Selenium y FastAPI<\/strong> porque son herramientas sencillas de implementar y ayudan a explicar de manera clara la idea de transformar un bot en un servicio REST capaz de devolver datos en formato JSON, listo para integrarse en cualquier aplicaci\u00f3n \ud83d\ude80. Adem\u00e1s, te comparto el c\u00f3digo en GitHub y ejemplos en vivo para que puedas probarlos.<\/p>\n\n\n\n<div class=\"wp-block-uagb-icon-list uagb-block-c88b0694\"><div class=\"uagb-icon-list__wrap\">\n<div data-aos= \"fade-down\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-8e20f79c\"><a target=\"_blank\" aria-label=\"Ver bots en acci\u00f3n en mi Portafolio\" rel=\"noopener noreferrer\" href=\"https:\/\/paolozada.com\/#projects\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 640 512\"><path d=\"M9.375 233.4C3.375 239.4 0 247.5 0 256v128c0 8.5 3.375 16.62 9.375 22.62S23.5 416 32 416h32V224H32C23.5 224 15.38 227.4 9.375 233.4zM464 96H352V32c0-17.62-14.38-32-32-32S288 14.38 288 32v64H176C131.8 96 96 131.8 96 176V448c0 35.38 28.62 64 64 64h320c35.38 0 64-28.62 64-64V176C544 131.8 508.3 96 464 96zM256 416H192v-32h64V416zM224 296C201.9 296 184 278.1 184 256S201.9 216 224 216S264 233.9 264 256S246.1 296 224 296zM352 416H288v-32h64V416zM448 416h-64v-32h64V416zM416 296c-22.12 0-40-17.88-40-40S393.9 216 416 216S456 233.9 456 256S438.1 296 416 296zM630.6 233.4C624.6 227.4 616.5 224 608 224h-32v192h32c8.5 0 16.62-3.375 22.62-9.375S640 392.5 640 384V256C640 247.5 636.6 239.4 630.6 233.4z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Ver bots en acci\u00f3n en mi Portafolio<\/strong><\/span><\/div>\n\n\n\n<div data-aos= \"fade-left\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-cf680e97\"><a target=\"_blank\" aria-label=\"Repositorio en GitHub\" rel=\"noopener noreferrer\" href=\"https:\/\/github.com\/PaoLozada\/Botsapp.git\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 496 512\"><path d=\"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Repositorio en GitHub<\/strong><\/span><\/div>\n\n\n\n<div data-aos= \"fade-left\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-673e1ac0\"><a target=\"_blank\" aria-label=\"Demo del endpoint en Railway \ud83d\ude80 (ejecuci\u00f3n en tiempo real: 30 seg. aprox. para generar el resultado JSON).\" rel=\"noopener noreferrer\" href=\"https:\/\/botpaolozada.up.railway.app\/botSearchOffer\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 320 512\"><path d=\"M318.4 304.5c-3.531 9.344-12.47 15.52-22.45 15.52h-105l45.15 94.82c9.496 19.94 1.031 43.8-18.91 53.31c-19.95 9.504-43.82 1.035-53.32-18.91L117.3 351.3l-75 88.25c-4.641 5.469-11.37 8.453-18.28 8.453c-2.781 0-5.578-.4844-8.281-1.469C6.281 443.1 0 434.1 0 423.1V56.02c0-9.438 5.531-18.03 14.12-21.91C22.75 30.26 32.83 31.77 39.87 37.99l271.1 240C319.4 284.6 321.1 295.1 318.4 304.5z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Demo del endpoint en Railway<\/strong> \ud83d\ude80<strong> <em>(<\/em><\/strong><em>ejecuci\u00f3n en tiempo real: 30 seg. aprox. para generar el resultado JSON<\/em><strong><em>).<\/em><\/strong><\/span><\/div>\n<\/div><\/div>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-c30a6ee2b9f850fd324aa105dd77d655\">Sin embargo, no son las \u00fanicas alternativas: existen muchas otras como <strong>Playwright<\/strong>, <strong>Puppeteer<\/strong>, <strong>Scrapy<\/strong> u otros frameworks para exponer APIs. Cada una tiene sus ventajas y se adapta mejor a distintos escenarios o preferencias.<\/p>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-740b614de297b17b2629a55184c704ea\">Lo valioso aqu\u00ed es entender el funcionamiento: c\u00f3mo un bot puede automatizar tareas repetitivas y luego convertirse en un servicio que entrega datos en formato JSON. Una vez comprendido, ya puedes apoyarte en la IA para construir soluciones m\u00e1s avanzadas, eligiendo las herramientas que mejor se ajusten a tus necesidades. <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-media-text alignwide is-stacked-on-mobile is-vertically-aligned-center\" style=\"grid-template-columns:42% auto\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/results-1024x683.webp\" alt=\"results\" class=\"wp-image-2974 size-full\" srcset=\"https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/results-1024x683.webp 1024w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/results-300x200.webp 300w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/results-768x512.webp 768w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/results.webp 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><div class=\"wp-block-media-text__content\">\n<h2 class=\"wp-block-heading has-palette-color-1-color has-text-color has-link-color wp-elements-cdaae66173db5d449ae6f3ba84076719\">\ud83c\udf0d \u00bfPor qu\u00e9 crear bots web?<\/h2>\n\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\" style=\"padding-top:2em;padding-right:2em;padding-bottom:2em;padding-left:2em\">\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-b38fbf720176fbe279781a234e1519b0\" style=\"font-style:normal;font-weight:600\">Porque un bot aislado solo sirve en tu m\u00e1quina. Pero al exponerlo como servicio:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-ac2f57ee2f089e148c2d259ce18f2a88\">\ud83d\udd04 Puedes <strong>reutilizarlo en distintas aplicaciones<\/strong>.<\/li>\n\n\n\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-5fb591c2ec86e8f38efb5b8079338606\">\ud83d\udcf1 Lo consumes desde tu web, m\u00f3vil o cualquier cliente.<\/li>\n\n\n\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-7927ac14ecd44b0a60ccaf944ae83df1\">\ud83e\udd1d Lo compartes con otros equipos sin darles acceso al c\u00f3digo.<\/li>\n\n\n\n<li class=\"has-palette-color-7-color has-text-color has-link-color wp-elements-fa525302ff2d824c6f4a557f03b63669\">\u26a1 Escalas f\u00e1cilmente con infraestructura en la nube.<\/li>\n<\/ul>\n<\/div>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-separator uagb-block-493d6091 wp-block-uagb-separator--icon\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M260.4 254.9 131.5 33.1a2.208 2.208 0 0 0 -3.829 .009L.3 254.9A2.234 2.234 0 0 0 .3 257.1L129.1 478.9a2.208 2.208 0 0 0 3.83-.009L260.4 257.1A2.239 2.239 0 0 0 260.4 254.9zm39.08-25.71a2.19 2.19 0 0 0 1.9 1.111h66.51a2.226 2.226 0 0 0 1.9-3.341L259.1 33.11a2.187 2.187 0 0 0 -1.9-1.111H190.7a2.226 2.226 0 0 0 -1.9 3.341zM511.7 254.9 384.9 33.11A2.2 2.2 0 0 0 382.1 32h-66.6a2.226 2.226 0 0 0 -1.906 3.34L440.7 256 314.5 476.7a2.226 2.226 0 0 0 1.906 3.34h66.6a2.2 2.2 0 0 0 1.906-1.112L511.7 257.1A2.243 2.243 0 0 0 511.7 254.9zM366 284.9H299.5a2.187 2.187 0 0 0 -1.9 1.111l-108.8 190.6a2.226 2.226 0 0 0 1.9 3.341h66.51a2.187 2.187 0 0 0 1.9-1.111l108.8-190.6A2.226 2.226 0 0 0 366 284.9z\"><\/path><\/svg><\/div><\/div><\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading has-palette-color-1-color has-text-color\"><strong>\ud83e\udde9<\/strong> Parte 1 \u2013 Construyendo el bot con Selenium y Python<\/h2>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-236e67748a39e49a608acff3262b2dc9\">Para este ejemplo, us\u00e9 Selenium en Python. La idea era simular la navegaci\u00f3n en una tienda online, capturar productos y precios y devolver esa informaci\u00f3n.<\/p>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-fad27f17d5cd1b4c519000925e919614\">Lo m\u00e1s importante es la organizaci\u00f3n del proyecto y tratar un c\u00f3digo lo m\u00e1s limpio posible y f\u00e1cil de mantener, en este caso implementamos el patr\u00f3n  Page Object Model (POM), que separa los elementos, las acciones y la ejecuci\u00f3n en diferentes archivos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-palette-color-7-color has-text-color has-link-color wp-elements-5489c6e46e39783ec3293f456a3fa881\"><strong>Archivo de elementos<\/strong> \u2192 guarda todos los selectores de la p\u00e1gina. page_katro.py \u2192 donde defino los elementos de la p\u00e1gina (selectores XPATH). <\/li>\n\n\n\n<li class=\"has-palette-color-7-color has-text-color has-link-color wp-elements-a36b818423a12c2b41620690718b3bc1\"><strong>Archivo de acciones<\/strong> \u2192 define qu\u00e9 hace el bot con esos elementos. actions.py \u2192 donde centralizo todas las acciones repetitivas (clics, scroll, obtener texto, etc.).<\/li>\n\n\n\n<li class=\"has-palette-color-7-color has-text-color has-link-color wp-elements-658e21dd60cc6fb4813672eb1eac8b14\"><strong>Archivo ejecutor<\/strong> \u2192 orquesta todo y devuelve el resultado. appExecution.py \u2192 donde orquesto el bot usando los elementos y acciones.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" style=\"margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)\"\/>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-container-core-quote-is-layout-1 wp-block-quote-is-layout-flow\" style=\"margin-right:var(--wp--preset--spacing--20);margin-left:var(--wp--preset--spacing--20);padding-right:var(--wp--preset--spacing--40);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-18ce10f2a9d4d9597069f38dcd51b37b\">\ud83d\udccc <em>Nota:<\/em> Los siguientes archivos son <strong>fragmentos de un bot de ejemplo<\/strong>, dise\u00f1ados \u00fanicamente para mostrar c\u00f3mo organizar un proyecto con POM. No es el bot completo, pero s\u00ed una peque\u00f1a gu\u00eda clara de c\u00f3mo separar elementos, acciones y ejecuci\u00f3n.<\/p>\n<\/blockquote>\n\n\n\n<div class=\"wp-block-stackable-icon-label aligncenter stk-block-icon-label stk-block stk-3c96cdd\" data-block-id=\"3c96cdd\"><style>.stk-3c96cdd .stk-inner-blocks{gap:13px !important;}<\/style><div class=\"stk-row stk-inner-blocks stk-block-content\">\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-1da688a\" id=\"a-href-https-github-com-pao-lozada-botsapp-git-data-type-link-data-id-https-github-com-pao-lozada-botsapp-git-target-blank-rel-noreferrer-noopener-codigo-completo-a\" data-block-id=\"1da688a\"><style>.stk-1da688a {box-shadow:none !important;}.stk-1da688a .stk-block-heading__text{text-shadow:2px 4px 5px #00000066 !important;background-image:linear-gradient(to top, #0c3483 0%, #a2b6df 100%, #6b8cce 100%, #a2b6df 100%) !important;}<\/style><h5 class=\"stk-block-heading__text stk--is-gradient has-text-color has-text-align-right\"><a href=\"https:\/\/github.com\/PaoLozada\/Botsapp.git\" data-type=\"link\" data-id=\"https:\/\/github.com\/PaoLozada\/Botsapp.git\" target=\"_blank\" rel=\"noreferrer noopener\">C\u00f3digo completo<\/a><\/h5><\/div>\n\n\n\n<div class=\"wp-block-stackable-icon stk-block-icon has-text-align-left stk-block stk-3ef69da\" data-block-id=\"3ef69da\"><style>.stk-3ef69da .stk--svg-wrapper .stk--inner-svg svg:last-child, .stk-3ef69da .stk--svg-wrapper .stk--inner-svg svg:last-child :is(g, path, rect, polygon, ellipse){fill:var(--theme-palette-color-1, #77406A) !important;}.stk-3ef69da .stk--svg-wrapper .stk--inner-svg{border-radius:50% !important;}<\/style><a class=\"stk-link\" href=\"https:\/\/github.com\/PaoLozada\/Botsapp.git\" target=\"_blank\" rel=\"noreferrer noopener\"><span class=\"stk--svg-wrapper\"><div class=\"stk--inner-svg\"><svg style=\"height:0;width:0\"><defs><linearGradient id=\"linear-gradient-3ef69da\" x1=\"0\" x2=\"100%\" y1=\"0\" y2=\"0\"><stop offset=\"0%\" style=\"stop-opacity:1;stop-color:var(--linear-gradient-3-ef-69-da-color-1)\"><\/stop><stop offset=\"100%\" style=\"stop-opacity:1;stop-color:var(--linear-gradient-3-ef-69-da-color-2)\"><\/stop><\/linearGradient><\/defs><\/svg><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 496 512\" aria-hidden=\"true\" width=\"32\" height=\"32\"><path d=\"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z\"><\/path><\/svg><\/div><\/span><\/a><\/div>\n<\/div><\/div>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-83a3b4de wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">page_katro.py \u2013 Definici\u00f3n de elementos de la p\u00e1gina Ktronix<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-25246088 alignwide uagb-is-root-container\">\n<div class=\"code-container\">\n  <pre><code>\nfrom selenium.webdriver.common.by import By\n\nclass KatroHome:\n    cel = (By.XPATH,\"(\/\/a[@title='Celulares'])[1]\")\n    celu = (By.XPATH,\"\/\/a[@data-subcategory='Celulares']\")\n    notifi = (By.XPATH,\"\/\/button[@class='button-primary js-cookie-notification-accept']\")\n\nclass KatroCell:\n    order = (By.XPATH,\"\/\/div[@class='float-select js-float-select js-float-group full-width active-click']\")\n    menor = (By.XPATH,\"\/\/li[contains(.,'Precio: menor a mayor')]\")\n\n    @staticmethod\n    def val_element(val):\n        return (By.XPATH, f\"(\/\/span[contains(@class,'price')])[{val}]\")\n\n  <\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-47bdf2aa wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">actions.py \u2013 M\u00e9todos reutilizables para acciones sobre la p\u00e1gina<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-016e552b alignwide uagb-is-root-container\">\n<div class=\"code-container\">\n  <pre><code>\nimport time\nfrom selenium.webdriver.common.action_chains import ActionChains\nfrom selenium.webdriver.common.keys import Keys\n\nclass Actions:\n\n    @staticmethod\n    def open_url(driver, url):\n        driver.get(url)\n\n    @staticmethod\n    def move_to_element(driver, selector):\n        acciones = ActionChains(driver)\n        elementMove = driver.find_element(*selector)\n        acciones.move_to_element(elementMove).perform()\n  <\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-2ee31be2 wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">appExecution.py \u2013 Script principal que integra p\u00e1ginas y acciones<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-5e0d63c3 alignwide uagb-is-root-container\">\n<div class=\"code-container\">\n  <pre><code>\nfrom selenium import webdriver\nfrom selenium.webdriver.support import expected_conditions as EC\nfrom unidecode import unidecode\nfrom page_katro import *\nfrom actions import *\n\n\ndef createDriver() -> webdriver.Chrome:\n    chrome_options = webdriver.ChromeOptions()\n    chrome_options.add_argument(\"--headless\")\n    chrome_options.add_argument(\"--no-sandbox\")\n    chrome_options.add_argument(\"--disable-dev-shm-usage\")    \n    chrome_options.add_argument(\"user-agent=Chrome\/100.0.1000.0\")\n    prefs = {\"profile.managed_default_content_settings.images\":2}\n    chrome_options.headless = True\n    chrome_options.add_experimental_option(\"prefs\", prefs)\n    myDriver = webdriver.Chrome(options=chrome_options)\n    # Alternativa con webdriver_manager si no tienes chromedriver instalado:\n    # from webdriver_manager.chrome import ChromeDriverManager\n    # from selenium.webdriver.chrome.service import Service \n    #myDriver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)\n    return myDriver\n  \ndef getBotSearchOffer(driver: webdriver.Chrome) -> str:\n    Actions.set_window_position(driver, 0, 0)\n    Actions.set_window_size(driver, 1496, 1024)\n    data ={}\n    referencia = []\n    valor =[]\n    Actions.open_url(driver, \"https:\/\/www.ktronix.com\/\")\n    Actions.wait(5)\n    Actions.move_to_element(driver, KatroHome.cel)\n    Actions.wait(5)\n    Actions.click_element(driver, KatroHome.celu)  \n    Actions.wait(3)  \n    Actions.if_click_element(driver, KatroHome.notifi) \n    Actions.scroll_to(driver,751,631)\n    Actions.wait(3) \n    Actions.click_element(driver, KatroCell.order) \n    Actions.click_element(driver, KatroCell.menor)\n    Actions.wait(5)  \n    valPrice=1\n    for i in range(3) :\n        valRef=str(i+1)\n        ref = Actions.get_text(driver, KatroCell.ref_element(valRef))\n        val = Actions.get_text(driver, KatroCell.val_element(str(valPrice)))\n        referencia.append(ref)\n        valor.append(val)\n        valPrice+=2\n    data['Ktronix']=[referencia, valor]\n    return(data)\n  <\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-uagb-separator uagb-block-e8bc27a6 wp-block-uagb-separator--icon\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M260.4 254.9 131.5 33.1a2.208 2.208 0 0 0 -3.829 .009L.3 254.9A2.234 2.234 0 0 0 .3 257.1L129.1 478.9a2.208 2.208 0 0 0 3.83-.009L260.4 257.1A2.239 2.239 0 0 0 260.4 254.9zm39.08-25.71a2.19 2.19 0 0 0 1.9 1.111h66.51a2.226 2.226 0 0 0 1.9-3.341L259.1 33.11a2.187 2.187 0 0 0 -1.9-1.111H190.7a2.226 2.226 0 0 0 -1.9 3.341zM511.7 254.9 384.9 33.11A2.2 2.2 0 0 0 382.1 32h-66.6a2.226 2.226 0 0 0 -1.906 3.34L440.7 256 314.5 476.7a2.226 2.226 0 0 0 1.906 3.34h66.6a2.2 2.2 0 0 0 1.906-1.112L511.7 257.1A2.243 2.243 0 0 0 511.7 254.9zM366 284.9H299.5a2.187 2.187 0 0 0 -1.9 1.111l-108.8 190.6a2.226 2.226 0 0 0 1.9 3.341h66.51a2.187 2.187 0 0 0 1.9-1.111l108.8-190.6A2.226 2.226 0 0 0 366 284.9z\"><\/path><\/svg><\/div><\/div><\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading has-palette-color-1-color has-text-color\"><strong>\ud83e\udde9<\/strong> Parte 2 \u2013 Llevando el bot a un servicio REST con FastAPI<\/h2>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-f0339c1f64b8c07e67678a860d8fb6f7\">En este archivo convertimos nuestro bot en un servicio accesible mediante <strong>FastAPI<\/strong>.<br>Aqu\u00ed se definen las rutas, la configuraci\u00f3n de CORS, la carga de la p\u00e1gina principal y un endpoint que ejecuta el bot y devuelve resultados en formato JSON.<\/p>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-27fdf1b2 wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">appExecution.py \u2013 Script principal que integra p\u00e1ginas y acciones<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-9c0fe8f5 alignwide uagb-is-root-container\">\n<div class=\"code-container\">\n  <pre><code>\nfrom fastapi import FastAPI, HTTPException\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom pydantic import BaseModel\nfrom appExecution import *\nfrom fastapi.responses import HTMLResponse, JSONResponse\n\napp = FastAPI()\n\n# Or\u00edgenes permitidos (ajusta seg\u00fan tu dominio)\norigins = [\n    \"https:\/\/tu-dominio.com\"  # Cambia este dominio por el tuyo\n]\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=[\"*\"],  # Si quieres restringir, reemplaza con: origins\n    allow_credentials=True,\n    allow_methods=[\"*\"],\n    allow_headers=[\"*\"],\n)\n\nclass Msg(BaseModel):\n    msg: str\n    secret: str\n\n@app.get(\"\/\")\nasync def root():\n    with open(\"index.html\", \"r\") as file:\n        html_content = file.read()\n    return HTMLResponse(content=html_content, status_code=200)\n\n@app.get(\"\/botSearchOffer\")\nasync def demo_get():\n    driver = createDriver()\n    listOffers = getBotSearchOffer(driver)\n    driver.quit()\n    return JSONResponse(content={\"listOffer\": listOffers})\n  <\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-86a31efffc63d855b770deb6486b226b\">Ahora, al llamar la URL del endpoint, el bot se ejecuta en segundo plano, navega por la web y devuelve un JSON con los resultados.<\/p>\n\n\n\n<div class=\"wp-block-uagb-separator uagb-block-74f3124d wp-block-uagb-separator--icon\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M260.4 254.9 131.5 33.1a2.208 2.208 0 0 0 -3.829 .009L.3 254.9A2.234 2.234 0 0 0 .3 257.1L129.1 478.9a2.208 2.208 0 0 0 3.83-.009L260.4 257.1A2.239 2.239 0 0 0 260.4 254.9zm39.08-25.71a2.19 2.19 0 0 0 1.9 1.111h66.51a2.226 2.226 0 0 0 1.9-3.341L259.1 33.11a2.187 2.187 0 0 0 -1.9-1.111H190.7a2.226 2.226 0 0 0 -1.9 3.341zM511.7 254.9 384.9 33.11A2.2 2.2 0 0 0 382.1 32h-66.6a2.226 2.226 0 0 0 -1.906 3.34L440.7 256 314.5 476.7a2.226 2.226 0 0 0 1.906 3.34h66.6a2.2 2.2 0 0 0 1.906-1.112L511.7 257.1A2.243 2.243 0 0 0 511.7 254.9zM366 284.9H299.5a2.187 2.187 0 0 0 -1.9 1.111l-108.8 190.6a2.226 2.226 0 0 0 1.9 3.341h66.51a2.187 2.187 0 0 0 1.9-1.111l108.8-190.6A2.226 2.226 0 0 0 366 284.9z\"><\/path><\/svg><\/div><\/div><\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading has-palette-color-1-color has-text-color\"><strong>\ud83e\udde9<\/strong> Parte 3 \u2013 Desplegando en Railway &#8211; Llevando el bot a producci\u00f3n<\/h2>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-9794ecb298c5144596a6fda60ea60c1a\">En lugar de dejarlo en local, opt\u00e9 por <strong>Railway<\/strong>, una plataforma muy pr\u00e1ctica y sencilla para proyectos peque\u00f1os con <strong>Python + FastAPI<\/strong>, lo que permite:<\/p>\n\n\n\n<ul class=\"wp-block-list has-palette-color-4-color has-text-color has-link-color wp-elements-8e79d14a2b3a7684b473bda1b4177967\">\n<li class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-0b77d8a7b4299faf9c4114bf53411922\">Exponerlo p\u00fablicamente.<\/li>\n\n\n\n<li class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-4023e7b825d82db8108bbeac27ab14b4\">Integrarlo en un portafolio web.<\/li>\n\n\n\n<li class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-6549234e41d1f607d616fdea0ef255ea\">Tener endpoints listos para consumir desde cualquier app o frontend.<\/li>\n<\/ul>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-fb4bd76a1f287adff74d26d974a288dd\">No es la \u00fanica herramienta de despliegue (existen alternativas como <strong>Render, Heroku, Vercel<\/strong> o incluso servidores propios), pero, en mi consideraci\u00f3n, <strong>Railway<\/strong> ofrece un flujo muy directo y r\u00e1pido, ideal para este tipo de proyectos personales o demostrativos.<\/p>\n\n\n\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-c645d6c559e2861389838ff0f50ea8b3\">Con este paso, mi bot pas\u00f3 de ser un simple script local que solo corr\u00eda en mi m\u00e1quina, a convertirse en un <strong>servicio REST accesible desde cualquier lugar<\/strong>, listo para integrarse con aplicaciones externas o probarse desde herramientas como Postman o el navegador.<\/p>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-06d17e25 wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">Ejemplo de Salida en JSON<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-94552b42 alignwide uagb-is-root-container\">\n<div class=\"code-container\">\n  <pre><code>\n    {\"listOffer\":\n      \"Ktronix\":         \n         [\n            [\"KALLEY\"],\n            [\"KALLEY\"],\n            [\"ZTE\"]\n         ],\n         [\n            [\"$239.900\"],\n            [\"$239.900\"],\n            [\"$269.900\"]\n         ]    \n    }\n  <\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<div data-aos= \"fade-up\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-separator uagb-block-3a7244fe wp-block-uagb-separator--text\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><h4 class=\"uagb-html-tag\">\ud83d\udcca Ejemplo de resultados<\/h4><\/div><\/div><\/div><\/div>\n\n\n\n<div class=\"wp-block-media-text alignwide has-media-on-the-right is-stacked-on-mobile is-vertically-aligned-center has-small-font-size\" style=\"border-style:none;border-width:0px;border-radius:22px;grid-template-columns:auto 61%\"><div class=\"wp-block-media-text__content\">\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\" style=\"padding-top:2em;padding-right:2em;padding-bottom:2em;padding-left:2em\">\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-8d1fd57e303a4416ba0e985163ac4a66\" style=\"font-size:clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.313), 18px);font-style:normal;font-weight:700\">Imagina que buscas celulares ordenados por precio de menor a mayor. El bot podr\u00eda devolver algo como esto.<\/p>\n<\/div>\n<\/div><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/result_search-1024x683.webp\" alt=\"results search\" class=\"wp-image-2976 size-full\" srcset=\"https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/result_search-1024x683.webp 1024w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/result_search-300x200.webp 300w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/result_search-768x512.webp 768w, https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/result_search.webp 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<div class=\"wp-block-uagb-separator uagb-block-9901c712 wp-block-uagb-separator--icon\"><div class=\"uagb-separator-spacing-wrapper\"><div class=\"wp-block-uagb-separator__inner\" style=\"--my-background-image:\"><div class=\"wp-block-uagb-separator-element\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M260.4 254.9 131.5 33.1a2.208 2.208 0 0 0 -3.829 .009L.3 254.9A2.234 2.234 0 0 0 .3 257.1L129.1 478.9a2.208 2.208 0 0 0 3.83-.009L260.4 257.1A2.239 2.239 0 0 0 260.4 254.9zm39.08-25.71a2.19 2.19 0 0 0 1.9 1.111h66.51a2.226 2.226 0 0 0 1.9-3.341L259.1 33.11a2.187 2.187 0 0 0 -1.9-1.111H190.7a2.226 2.226 0 0 0 -1.9 3.341zM511.7 254.9 384.9 33.11A2.2 2.2 0 0 0 382.1 32h-66.6a2.226 2.226 0 0 0 -1.906 3.34L440.7 256 314.5 476.7a2.226 2.226 0 0 0 1.906 3.34h66.6a2.2 2.2 0 0 0 1.906-1.112L511.7 257.1A2.243 2.243 0 0 0 511.7 254.9zM366 284.9H299.5a2.187 2.187 0 0 0 -1.9 1.111l-108.8 190.6a2.226 2.226 0 0 0 1.9 3.341h66.51a2.187 2.187 0 0 0 1.9-1.111l108.8-190.6A2.226 2.226 0 0 0 366 284.9z\"><\/path><\/svg><\/div><\/div><\/div><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading has-palette-color-1-color has-text-color has-link-color wp-elements-602c4ecd08de7d4eaceb0ba91937aa5a\">\ud83d\udca1 La IA impulsa, el entendimiento sostiene<\/h2>\n\n\n\n<p class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-7209a949ba7a1b6bdfd87cc52ffd442d\">Claro que puedes pedirle a la IA que genere un bot por ti (yo misma lo hago muchas veces \ud83d\ude05). Pero entender c\u00f3mo funciona un bot, c\u00f3mo organizarlo y c\u00f3mo exponerlo como servicio REST te da un s\u00faper poder extra:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-320e98954a43edecd5a964044b0ee6f3\">Te permite personalizar y adaptar el bot a cualquier necesidad.<\/li>\n\n\n\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-54c0f42bbb51688c35eb88d0297a1eda\">Te da la capacidad de mantener y escalar tu soluci\u00f3n.<\/li>\n\n\n\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-0085058122cd3c5482b63c1d9cfb131f\">Abrir la puerta a proyectos m\u00e1s grandes (integraciones, an\u00e1lisis de datos, automatizaci\u00f3n de procesos).<\/li>\n\n\n\n<li class=\"has-palette-color-2-color has-text-color has-link-color wp-elements-7a803bbb70cb8240b8a60808cea3f2b4\">Y lo m\u00e1s importante: entiendes lo que pasa detr\u00e1s del \u201cc\u00f3digo m\u00e1gico\u201d que genera la IA.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" style=\"margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)\"\/>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-container-core-quote-is-layout-2 wp-block-quote-is-layout-flow\" style=\"margin-right:var(--wp--preset--spacing--20);margin-left:var(--wp--preset--spacing--20);padding-right:var(--wp--preset--spacing--40);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-palette-color-4-color has-text-color has-link-color wp-elements-38fd1ab05d3accfbdd6df8e601da0051\">\ud83d\udc49 Y lo mejor es que t\u00fa tambi\u00e9n puedes hacerlo. La pr\u00f3xima vez que pienses en pedirle a la IA que \u201cte haga un bot\u201d, recuerda que entender la l\u00f3gica detr\u00e1s es lo que realmente te da poder para construir soluciones incre\u00edbles.<\/p>\n\n\n\n<div class=\"wp-block-uagb-icon-list uagb-block-97c5b732\"><div class=\"uagb-icon-list__wrap\">\n<div data-aos= \"fade-down\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-2094c09d\"><a target=\"_blank\" aria-label=\"Bots en mi Portafolio\" rel=\"noopener noreferrer\" href=\"https:\/\/paolozada.com\/#projects\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 640 512\"><path d=\"M9.375 233.4C3.375 239.4 0 247.5 0 256v128c0 8.5 3.375 16.62 9.375 22.62S23.5 416 32 416h32V224H32C23.5 224 15.38 227.4 9.375 233.4zM464 96H352V32c0-17.62-14.38-32-32-32S288 14.38 288 32v64H176C131.8 96 96 131.8 96 176V448c0 35.38 28.62 64 64 64h320c35.38 0 64-28.62 64-64V176C544 131.8 508.3 96 464 96zM256 416H192v-32h64V416zM224 296C201.9 296 184 278.1 184 256S201.9 216 224 216S264 233.9 264 256S246.1 296 224 296zM352 416H288v-32h64V416zM448 416h-64v-32h64V416zM416 296c-22.12 0-40-17.88-40-40S393.9 216 416 216S456 233.9 456 256S438.1 296 416 296zM630.6 233.4C624.6 227.4 616.5 224 608 224h-32v192h32c8.5 0 16.62-3.375 22.62-9.375S640 392.5 640 384V256C640 247.5 636.6 239.4 630.6 233.4z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Bots en mi Portafolio<\/strong><\/span><\/div>\n\n\n\n<div data-aos= \"fade-left\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-f2b6bba8\"><a target=\"_blank\" aria-label=\"Repositorio\" rel=\"noopener noreferrer\" href=\"https:\/\/github.com\/PaoLozada\/Botsapp.git\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 496 512\"><path d=\"M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Repositorio<\/strong><\/span><\/div>\n\n\n\n<div data-aos= \"fade-left\" data-aos-duration=\"400\" data-aos-delay=\"0\" data-aos-easing=\"ease\" data-aos-once=\"true\" class=\"wp-block-uagb-icon-list-child uagb-block-66007bfc\"><a target=\"_blank\" aria-label=\"Demo del endpoint (30 segundos aprox.)\" rel=\"noopener noreferrer\" href=\"https:\/\/botpaolozada.up.railway.app\/botSearchOffer\"> <\/a><span class=\"uagb-icon-list__source-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 320 512\"><path d=\"M318.4 304.5c-3.531 9.344-12.47 15.52-22.45 15.52h-105l45.15 94.82c9.496 19.94 1.031 43.8-18.91 53.31c-19.95 9.504-43.82 1.035-53.32-18.91L117.3 351.3l-75 88.25c-4.641 5.469-11.37 8.453-18.28 8.453c-2.781 0-5.578-.4844-8.281-1.469C6.281 443.1 0 434.1 0 423.1V56.02c0-9.438 5.531-18.03 14.12-21.91C22.75 30.26 32.83 31.77 39.87 37.99l271.1 240C319.4 284.6 321.1 295.1 318.4 304.5z\"><\/path><\/svg><\/span><span class=\"uagb-icon-list__label\"><strong>Demo del endpoint (30 segundos aprox.)<\/strong><\/span><\/div>\n<\/div><\/div>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\u00bfTienes un bot en Python y quieres llevarlo al siguiente nivel? \ud83d\ude80 Descubre c\u00f3mo exponerlo como servicio REST con FastAPI y desplegarlo f\u00e1cilmente en Railway.<\/p>\n","protected":false},"author":2,"featured_media":2973,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-2516","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"blocksy_meta":[],"uagb_featured_image_src":{"full":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job.webp",1024,1024,false],"thumbnail":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job-150x150.webp",150,150,true],"medium":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job-300x300.webp",300,300,true],"medium_large":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job-768x768.webp",768,768,true],"large":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job.webp",1024,1024,false],"1536x1536":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job.webp",1024,1024,false],"2048x2048":["https:\/\/paolozada.com\/info\/wp-content\/uploads\/2025\/10\/job.webp",1024,1024,false]},"uagb_author_info":{"display_name":"Paola Lozada","author_link":"https:\/\/paolozada.com\/info\/author\/pao\/"},"uagb_comment_info":6,"uagb_excerpt":"\u00bfTienes un bot en Python y quieres llevarlo al siguiente nivel? \ud83d\ude80 Descubre c\u00f3mo exponerlo como servicio REST con FastAPI y desplegarlo f\u00e1cilmente en Railway.","_links":{"self":[{"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/posts\/2516","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/comments?post=2516"}],"version-history":[{"count":145,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/posts\/2516\/revisions"}],"predecessor-version":[{"id":2977,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/posts\/2516\/revisions\/2977"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/media\/2973"}],"wp:attachment":[{"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/media?parent=2516"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/categories?post=2516"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/paolozada.com\/info\/wp-json\/wp\/v2\/tags?post=2516"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}