Categorías
Python

Detección de la cara en Python Usando una Webcam

 

Tabla de Contenidos

  • Biblioteca OverviewsFlask (Python) Sinatra (Rubí) Martini (Golang)
  • Frasco (Python)
  • Sinatra (Rubí)
  • Martini (Golang)
  • Servicio

  • DescriptionAdd Un POSTVIEW El HTMLView El JSON
  • Añadir configurar un mensaje
  • ver el código HTML
  • Ver El JSON
  • Aplicación StructureApplication SetupRequestResponseDatabaseApplication despliegue
  • Aplicación

  • Solicitud
  • Respuesta
  • base de datos de configuración
  • proyecto de implementación

  • Aplicación
  • Inicializar ejecutar una aplicación /
  • Definir una ruta (GET / POST)
  • RenderA Respuesta JSON
  • emitir una respuesta HTML (Plantillas)
  • base de datos de conexión
  • Insertar datos desde un puesto de
  • recuperar datos
  • implementación de aplicaciones (acoplable!)
  • ConclusionSimplicityDocumentationCommunity
  • La simplicidad
  • D Determinación OCUMENTACIÓN
  • Comunidad
  • final
  • Frasco (Python)
  • Sinatra (Rubí)
  • Martini (Golang)
  • Añadir aplicación
    • D Enviar
    • ver el código HTML
    • Ver El JSON

    configuración

  • solicitud
  • Respuesta
  • base de datos
  • aplicación

  • despliegue
  • Simplicidad
  • Documentación
  • Comunidad

Después de una reciente comparación de Python, Ruby, y Golang para una aplicación de línea de comandos que decidí utilizar el mismo patrón para comparar la construcción de un servicio web simple. He seleccionado Frasco (Python), Sinatra (Rubí) y Martini (Golang) para esta comparación. Sí, hay muchas otras opciones para las bibliotecas de aplicaciones web en cada idioma, pero me sentí estos tres se prestan bien a la comparación.

Biblioteca Panorama

Aquí está una comparación de alto nivel de las bibliotecas por Stackshare.

Flask (Python)

Flask es un micro-marco para Python basado en Werkzeug, Jinja2 y buenas intenciones.

para aplicaciones muy simples, como la mostrada en esta demostración, el frasco es una gran elección. La aplicación básica frasco está a sólo 7 líneas de código (LOC) en un solo archivo fuente de Python. El sorteo del matraz durante otras bibliotecas web de Python (como Django o pirámide) es que se puede empezar poco a poco y se acumulan a una aplicación más compleja, según sea necesario.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run()

Sinatra (Rubí)

Sinatra es un DSL para la creación rápida de aplicaciones web en Ruby con un mínimo esfuerzo.

Al igual que el frasco, Sinatra es ideal para aplicaciones sencillas. La aplicación básica Sinatra está a sólo 4 LOC en un solo archivo fuente Ruby. Sinatra se utiliza en lugar de las bibliotecas como Ruby on Rails para la misma razón que el frasco – se puede empezar poco a poco y ampliar la aplicación según sea necesario.

require 'sinatra'

get '/hi' do
"Hello World!"
end

Martini (Golang)

Martini es un paquete de gran alcance para escribir rápidamente aplicaciones web / servicios modulares en Golang.

Martini viene con unas cuantas baterías incluidas tanto de Sinatra y el frasco, pero sigue siendo muy ligero para empezar – sólo el 9 LOC para la aplicación básica. Martini ha sido objeto de algunas críticas por parte de la comunidad Golang pero todavía tiene uno de los proyectos Github de mayor audiencia de cualquier framework web Golang. El autor de Martini respondió directamente a las críticas aquí. Algunos otros marcos incluyen Revel, Gin, e incluso la biblioteca incorporada net / http.

package main

import "github.com/go-martini/martini"

func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}

Con los conceptos básicos fuera del camino, vamos a construir una aplicación!

Servicio

Descripción

El servicio creado proporciona una aplicación muy básica blog. Las siguientes rutas se construyen:

  • GET /: Volver al blog (usando una plantilla se mostrará).
  • GET / JSON: Volver al contenido del blog en formato JSON.
  • de POST / nueva: Añadir una nueva entrada (título, resumen, contenido) al blog.

La interfaz externa al servicio de blogs es exactamente el mismo para cada idioma. Por simplicidad MongoDB será utilizado como almacén de datos para este ejemplo, ya que es el más simple de configurar y no tiene que preocuparse acerca de los esquemas en absoluto. En una base de datos normal de un “blog-como” la aplicación relacional probablemente sería necesario.

Añadir una publicación

de POST / GET nueva

$ curl --form title='Test Post 1' \
--form summary='The First Test Post' \
--form content='Lorem ipsum dolor sit amet, consectetur ...' \
http://[IP]:[PORT]
ew

ver el código HTML

/

Ver El JSON

GET / JSON Estructura

[
{
content:"Lorem ipsum dolor sit amet, consectetur ...",
title:"Test Post 1",
_id:{
$oid:"558329927315660001550970"
},
summary:"The First Test Post"
}
]
Aplicación

Cada aplicación puede dividirse en los siguientes componentes : Configuración

aplicación

  • Inicializar una aplicación
  • Ejecutar la aplicación

solicitud

  • Definir rutas en las que un usuario puede solicitar datos (GET)
  • Definir rutas en las que un usuario puede enviar datos (POST)

Respuesta

  • Render JSON (GET / JSON)
  • reproducir una plantilla (GET /) Base de datos

  • Inicializar una conexión
  • Insertar datos
  • Recuperar datos
  • implementación de aplicaciones

  • acoplable!

El resto de este artículo comparará cada uno de estos componentes para cada biblioteca. El propósito no es sugerir que una de estas bibliotecas es mejor que el otro – es proporcionar una comparación específica entre las tres herramientas:

  • Frasco (Python)
  • Sinatra (Rubí)
  • Martini (Golang)
  • Configuración

Proyecto

Todos los proyectos están utilizando bootstrapped cargador de muelle y de composición del cargador de muelle. Antes de entrar en cómo cada aplicación se bootstrap bajo el capó sólo podemos usar cargador de muelle para conseguir cada uno en marcha y funcionando exactamente de la misma manera – ventana acoplable-componer hasta

En serio, eso es todo! Ahora, para cada aplicación hay una ventana acoplable un archivo-compose.yml que especifican lo que sucede cuando se ejecuta el comando anterior y Dockerfile.

Python (frasco) – Dockerfile

FROM python:3.4

ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

Este Dockerfile dice que estamos iniciando una imagen de base con Python 3.4 instalado desde, añadiendo nuestra aplicación para el directorio / app y el uso de la pipa para instalar nuestros requisitos de aplicación especificados en requirements.txt .

Ruby (Sinatra)

FROM ruby:2.2

ADD . /app
WORKDIR /app

RUN bundle install

Este Dockerfile dice que estamos iniciando una imagen de base con Ruby 2.2 instalado desde, añadiendo nuestra aplicación para el directorio / app y utilizando bundler para instalar nuestros requisitos de aplicación especificados en el Gemfile.

Golang (martini)

FROM golang:1.3

ADD . /go/src/github.com/kpurdon/go-blog
WORKDIR /go/src/github.com/kpurdon/go-blog

RUN go get github.com/go-martini/martini && \
go get github.com/martini-contrib/render && \
go get gopkg.in/mgo.v2 && \
go get github.com/martini-contrib/binding

Este Dockerfile dice que estamos iniciando una imagen base con Golang 1.3 de instalado, añadiendo nuestra aplicación al directorio /go/src/github.com/kpurdon/go-blog y conseguir todo nuestro dependencias necesarias utilizando el comando de ir a buscar.

Inicializar / ejecutar una aplicación

Python (Frasco) – app.py

# initialize application
from flask import Flask
app = Flask(__name__)

# run application
if __name__ == '__main__':
app.run(host='0.0.0.0')
$ python app.py

Ruby (Sinatra) – app.rb

# initialize application
require 'sinatra'
$ ruby app.rb

Golang (Martini) – app.go

// initialize application
package main
import "github.com/go-martini/martini"
import "github.com/martini-contrib/render"

func main() {
app := martini.Classic()
app.Use(render.Renderer())

// run application
app.Run()
}
$ go run app.go

definir una ruta (GET / POST)

Python (Frasco)

# get
@app.route('/') # the default is GET only
def blog():
# ...

#post
@app.route('
ew', methods=['POST'])
def new():
# ...

Ruby (Sinatra)

# get
get '/' do
# ...
end

# post
post '
ew' do
# ...
end

Golang (Martini)

// define data struct
type Post struct {
Title string `form:"title" json:"title"`
Summary string `form:"summary" json:"summary"`
Content string `form:"content" json:"content"`
}

// get
app.Get("/", func(r render.Render) {
// ...
}

// post
import "github.com/martini-contrib/binding"
app.Post("
ew", binding.Bind(Post{}), func(r render.Render, post Post) {
// ...
}

hacer una Respuesta JSON

Python (Frasco)

Flask proporciona un jsonify () se utiliza el método pero ya que el servicio está utilizando MongoDB la utilidad BSON mongodb.

from bson.json_util import dumps
return dumps(posts) # posts is a list of dicts [{}, {}]

Ruby (Sinatra)

require 'json'
content_type :json
posts.to_json # posts is an array (from mongodb)

Golang (Martini)

r.JSON(200, posts) // posts is an array of Post{} structs

emitir una respuesta HTML (Plantillas)

Python (Frasco)

return render_template('blog.html', posts=posts)



Python Flask Example<itle><br /> </head><br /> <body><br /> {% for post in posts %}</p> <h1> {{ post.title }} </h1> <h3> {{ post.summary }} </h3> <p> {{ post.content }} </p> <hr> <p> {% endfor %}<br /> </body><br /> </html><br /> </code> </p> <p> Ruby (Sinatra) </p> <p> <code>erb :blog<br /> </code> <code><!doctype HTML><br /> <html><br /> <head><br /> <title>Ruby Sinatra Example<itle><br /> </head><br /> <body><br /> <% @posts.each do |post| %></p> <h1><%= post['title'] %></h1> <h3><%= post['summary'] %></h3> <p><%= post['content'] %></p> <hr> <p> <% end %><br /> </body><br /> </html><br /> </code> </p> <p> Golang (Martini) </p> <p> <code>r.HTML(200, "blog", posts)<br /> </code> <code><!doctype HTML><br /> <html><br /> <head><br /> <title>Golang Martini Example<itle><br /> </head><br /> <body><br /> {{range . }}</p> <h1>{{.Title}}</h1> <h3>{{.Summary}}</h3> <p>{{.Content}}</p> <hr> <p> {{ end }}<br /> </body><br /> </html><br /> </code> </p> <h2> Conexión de la base de datos </h2> <p> Todas las aplicaciones están utilizando el controlador específico mongodb a la lengua. La variable de entorno DB_PORT_27017_TCP_ADDR es la IP de un recipiente ventana acoplable enlazado (la ip base de datos). </p> <p> Python (Frasco) </p> <p> <code>from pymongo import MongoClient<br /> client = MongoClient(os.environ['DB_PORT_27017_TCP_ADDR'], 27017)<br /> db = client.blog<br /> </code> </p> <p> Ruby (Sinatra) </p> <p> <code>require 'mongo'<br /> db_ip = [ENV['DB_PORT_27017_TCP_ADDR']]<br /> client = Mongo::Client.new(db_ip, database: 'blog')<br /> </code> </p> <p> Golang (Martini) </p> <p> <code>import "gopkg.in/mgo.v2"<br /> session, _ := mgo.Dial(os.Getenv("DB_PORT_27017_TCP_ADDR"))<br /> db := session.DB("blog")<br /> defer session.Close()<br /> </code> </p> <h2> Insertar datos desde un puesto de </h2> <p> Python (Frasco) </p> <p> <code>from flask import request<br /> post = {<br /> 'title': request.form['title'],<br /> 'summary': request.form['summary'],<br /> 'content': request.form['content']<br /> }<br /> db.blog.insert_one(post)<br /> </code> </p> <p> Ruby (Sinatra) </p> <p> <code>client[:posts].insert_one(params) # params is a hash generated by sinatra<br /> </code> </p> <p> Golang (Martini) </p> <p> <code>db.C("posts").Insert(post) // post is an instance of the Post{} struct<br /> </code> </p> <h2> recuperar datos </h2> <p> Python (Frasco) </p> <p> <code>posts = db.blog.find()<br /> </code> </p> <p> Ruby (Sinatra) </p> <p> <code>@posts = client[:posts].find.to_a<br /> </code> </p> <p> Golang (Martini) </p> <p> <code>var posts []Post<br /> db.C("posts").Find(nil).All(&posts)<br /> </code> </p> <h2> de despliegue de aplicaciones (acoplable!) </h2> <p> Una gran solución para la implementación de todas estas aplicaciones es el uso de estibador y docker- componer. </p> <p> Python (Frasco) </p> <p> <strong> Dockerfile </strong> </p> <p> <code>FROM python:3.4</p> <p>ADD . /app<br /> WORKDIR /app</p> <p>RUN pip install -r requirements.txt<br /> </code> </p> <p> <strong> ventana acoplable-compose.yml </strong> </p> <p> <code>web:<br /> build: .<br /> command: python -u app.py<br /> ports:<br /> - "5000:5000"<br /> volumes:<br /> - .:/app<br /> links:<br /> - db<br /> db:<br /> image: mongo:3.0.4<br /> command: mongod --quiet --logpath=/dev<br />ull<br /> </code> </p> <p> Ruby (Sinatra) </p> <p> <strong> Dockerfile </strong> </p> <p> <code>FROM ruby:2.2</p> <p>ADD . /app<br /> WORKDIR /app</p> <p>RUN bundle install<br /> </code> </p> <p> <strong> ventana acoplable-compose.yml </strong> </p> <p> <code>web:<br /> build: .<br /> command: bundle exec ruby app.rb<br /> ports:<br /> - "4567:4567"<br /> volumes:<br /> - .:/app<br /> links:<br /> - db<br /> db:<br /> image: mongo:3.0.4<br /> command: mongod --quiet --logpath=/dev<br />ull<br /> </code> </p> <p> Golang (Martini) </p> <p> <strong> Dockerfile </strong> </p> <p> <code>FROM golang:1.3</p> <p>ADD . /go/src/github.com/kpurdon/go-todo<br /> WORKDIR /go/src/github.com/kpurdon/go-todo</p> <p>RUN go get github.com/go-martini/martini && go get github.com/martini-contrib/render && go get gopkg.in/mgo.v2 && go get github.com/martini-contrib/binding<br /> </code> </p> <p> <strong> ventana acoplable-compose.yml </strong> </p> <p> <code>web:<br /> build: .<br /> command: go run app.go<br /> ports:<br /> - "3000:3000"<br /> volumes: # look into volumes v. "ADD"<br /> - .:/go/src/github.com/kpurdon/go-todo<br /> links:<br /> - db<br /> db:<br /> image: mongo:3.0.4<br /> command: mongod --quiet --logpath=/dev<br />ull<br /> </code> </p> <h2> Conclusión </h2> <p> Para concluir vamos a echar un vistazo a lo que creo que hay algunas categorías donde las bibliotecas presentados separan unos de otros. </p> <h3> Simplicidad </h3> <p> Si bien el frasco es muy ligero y lee claramente, la aplicación Sinatra es el más simple de los tres a los 23 LOC (comparado con el 46 por Frasco y 42 para Martini). Por estas razones Sinatra es el ganador en esta categoría. Cabe señalar sin embargo que la simplicidad de Sinatra es debido a la mayor defecto “mágica” – por ejemplo, el trabajo implícito de que sucede detrás de las escenas. Para los nuevos usuarios a menudo esto puede dar lugar a confusión. </p> <p> Aquí está un ejemplo específico de la “magia” de Sinatra: </p> <p> <code>params # the "request.form" logic in python is done "magically" behind the scenes in Sinatra.<br /> </code> </p> <p> y el equivalente de código Frasco: </p> <p> <code>from flask import request<br /> params = {<br /> 'title': request.form['title'],<br /> 'summary': request.form['summary'],<br /> 'content': request.form['content']<br /> }<br /> </code> </p> <p> Para los principiantes a la programación Frasco y Sinatra son ciertamente más simple, pero para un programador experimentado con el tiempo pasado en otras lenguas tipos estáticos Martini proporciona una interfaz bastante simplista. documentación </p> <h3> Documentación </h3> <p> El frasco fue la más sencilla de buscar y lo más accesible. Mientras Sinatra y Martini están bien documentados tanto, la documentación en sí no era tan accesible. Por esta razón, el frasco es el ganador en esta categoría. </p> <h3> Comunidad </h3> <p> matraz se las manos ganadoras en esta categoría. La comunidad Ruby es más a menudo que no dogmática sobre rieles siendo la única buena opción si necesitas algo más que un servicio básico (aunque Padrino ofrece esta en la parte superior de Sinatra). La comunidad Golang es todavía muy lejos de un consenso sobre una (o incluso unos pocos) frameworks web, lo cual es de esperar que el lenguaje mismo es tan joven. Python sin embargo, ha adoptado una serie de enfoques para el desarrollo web, incluyendo Django por fuera de la caja de aplicaciones web con todas las funciones y el frasco, botella, CheryPy y del tornado por un enfoque micro-marco. Determinación </p> <h2> final </h2> <p> Tenga en cuenta que el punto de este artículo no era promover una única herramienta, en lugar de proporcionar una comparación imparcial de Frasco, Sinatra y Martini. Dicho esto, me gustaría seleccionar Frasco (Python) o Sinatra (rubí). Si usted viene de un lenguaje como C o Java tal vez la naturaleza de tipo estático de Golang puede apelar a usted. Si usted es un principiante, Frasco podría ser la mejor opción ya que es muy fácil de poner en marcha y hay muy poco por defecto “mágica”. Mi recomendación es que sea flexible en sus decisiones a la hora de seleccionar una biblioteca para su proyecto. </p> <p> Preguntas? ¿Realimentación? Por favor, comentar a continuación. ¡Gracias! </p> <p> <em> También, háganos saber si usted estaría interesado en ver algunos puntos de referencia. </em> </p> </div><!-- .entry-content --> </div><!-- .post-inner --> <div class="section-inner"> </div><!-- .section-inner --> <nav class="pagination-single section-inner" aria-label="Entrada" role="navigation"> <hr class="styled-separator is-style-wide" aria-hidden="true" /> <div class="pagination-single-inner"> <a class="previous-post" href="https://eltecnofilo.es/su-guia-para-la-funcion-de-impresion-del-piton/"> <span class="arrow" aria-hidden="true">←</span> <span class="title"><span class="title-inner">Su guía para la función de impresión del pitón</span></span> </a> <a class="next-post" href="https://eltecnofilo.es/y-los-saltos-de-linea-se-tendra-en-cuenta-printa/"> <span class="arrow" aria-hidden="true">→</span> <span class="title"><span class="title-inner">y los saltos de línea se tendrá en cuenta: </p> <code>>>> print('a</span></span> </a> </div><!-- .pagination-single-inner --> <hr class="styled-separator is-style-wide" aria-hidden="true" /> </nav><!-- .pagination-single --> <div class="comments-wrapper section-inner"> <div id="respond" class="comment-respond"> <h2 id="reply-title" class="comment-reply-title">Deja un comentario <small><a rel="nofollow" id="cancel-comment-reply-link" href="/deteccion-de-la-cara-en-python-usando-una-webcam/#respond" style="display:none;">Cancelar la respuesta</a></small></h2><form action="https://eltecnofilo.es/wp-comments-post.php" method="post" id="commentform" class="section-inner thin max-percentage" novalidate><p class="comment-notes"><span id="email-notes">Tu dirección de correo electrónico no será publicada.</span> Los campos obligatorios están marcados con <span class="required">*</span></p><p class="comment-form-comment"><label for="comment">Comentario</label> <textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required="required"></textarea></p><p class="comment-form-author"><label for="author">Nombre <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" required='required' /></p> <p class="comment-form-email"><label for="email">Correo electrónico <span class="required">*</span></label> <input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" required='required' /></p> <p class="comment-form-url"><label for="url">Web</label> <input id="url" name="url" type="url" value="" size="30" maxlength="200" /></p> <p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Guardar mi nombre, correo electrónico y sitio web en este navegador para la próxima vez que haga un comentario.</label></p> <p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Publicar el comentario" /> <input type='hidden' name='comment_post_ID' value='6991' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p></form> </div><!-- #respond --> </div><!-- .comments-wrapper --> </article><!-- .post --> </main><!-- #site-content --> <div class="footer-nav-widgets-wrapper header-footer-group"> <div class="footer-inner section-inner"> <aside class="footer-widgets-outer-wrapper" role="complementary"> <div class="footer-widgets-wrapper"> <div class="footer-widgets column-one grid-item"> <div class="widget widget_search"><div class="widget-content"><form role="search" method="get" class="search-form" action="https://eltecnofilo.es/"> <label for="search-form-2"> <span class="screen-reader-text">Buscar:</span> <input type="search" id="search-form-2" class="search-field" placeholder="Buscar …" value="" name="s" /> </label> <input type="submit" class="search-submit" value="Buscar" /> </form> </div></div> <div class="widget widget_recent_entries"><div class="widget-content"> <h2 class="widget-title subheading heading-size-3">Entradas recientes</h2> <ul> <li> <a href="https://eltecnofilo.es/vps-cloud-hosting/">VPS cloud hosting</a> </li> <li> <a href="https://eltecnofilo.es/versiones-de-ejecucion-de-python-en-acoplable-como-probar-la-ultima-release-python/">Versiones de ejecución de Python en acoplable: Cómo probar la última Release Python</a> </li> <li> <a href="https://eltecnofilo.es/leer-y-escribir-archivos-csv/">Leer y escribir archivos CSV</a> </li> <li> <a href="https://eltecnofilo.es/python-puro-vs-vs-numpy-tensorflow-comparacion-de-rendimiento/">Python puro vs vs NumPy TensorFlow Comparación de Rendimiento</a> </li> <li> <a href="https://eltecnofilo.es/estructura-python-programa-lexico/">Estructura Python Programa léxico</a> </li> </ul> </div></div><div class="widget widget_recent_comments"><div class="widget-content"><h2 class="widget-title subheading heading-size-3">Comentarios recientes</h2><ul id="recentcomments"><li class="recentcomments"><span class="comment-author-link"><a href="https://wordpress.org/" rel="external nofollow ugc" class="url">A WordPress Commenter</a></span> en <a href="https://eltecnofilo.es/hello-world/#comment-1">Hello world!</a></li></ul></div></div> </div> <div class="footer-widgets column-two grid-item"> <div class="widget widget_archive"><div class="widget-content"><h2 class="widget-title subheading heading-size-3">Archivos</h2> <ul> <li><a href='https://eltecnofilo.es/2020/04/'>abril 2020</a></li> <li><a href='https://eltecnofilo.es/2020/03/'>marzo 2020</a></li> <li><a href='https://eltecnofilo.es/2019/12/'>diciembre 2019</a></li> </ul> </div></div><div class="widget widget_categories"><div class="widget-content"><h2 class="widget-title subheading heading-size-3">Categorías</h2> <ul> <li class="cat-item cat-item-22"><a href="https://eltecnofilo.es/category/python/">Python</a> </li> <li class="cat-item cat-item-1"><a href="https://eltecnofilo.es/category/uncategorized/">Uncategorized</a> </li> </ul> </div></div><div class="widget widget_meta"><div class="widget-content"><h2 class="widget-title subheading heading-size-3">Meta</h2> <ul> <li><a rel="nofollow" href="https://eltecnofilo.es/wp-login.php">Acceder</a></li> <li><a href="https://eltecnofilo.es/feed/">Feed de entradas</a></li> <li><a href="https://eltecnofilo.es/comments/feed/">Feed de comentarios</a></li> <li><a href="https://es.wordpress.org/">WordPress.org</a></li> </ul> </div></div> </div> </div><!-- .footer-widgets-wrapper --> </aside><!-- .footer-widgets-outer-wrapper --> </div><!-- .footer-inner --> </div><!-- .footer-nav-widgets-wrapper --> <footer id="site-footer" role="contentinfo" class="header-footer-group"> <div class="section-inner"> <div class="footer-credits"> <p class="footer-copyright">© 2020 <a href="https://eltecnofilo.es/">My Blog</a> </p><!-- .footer-copyright --> <p class="powered-by-wordpress"> <a href="https://es.wordpress.org/"> Funciona gracias a WordPress </a> </p><!-- .powered-by-wordpress --> </div><!-- .footer-credits --> <a class="to-the-top" href="#site-header"> <span class="to-the-top-long"> Ir arriba <span class="arrow" aria-hidden="true">↑</span> </span><!-- .to-the-top-long --> <span class="to-the-top-short"> Subir <span class="arrow" aria-hidden="true">↑</span> </span><!-- .to-the-top-short --> </a><!-- .to-the-top --> </div><!-- .section-inner --> </footer><!-- #site-footer --> <script src='https://eltecnofilo.es/wp-includes/js/comment-reply.min.js?ver=5.3.3'></script> <script src='https://eltecnofilo.es/wp-includes/js/wp-embed.min.js?ver=5.3.3'></script> <script> /(trident|msie)/i.test(navigator.userAgent)&&document.getElementById&&window.addEventListener&&window.addEventListener("hashchange",function(){var t,e=location.hash.substring(1);/^[A-z0-9_-]+$/.test(e)&&(t=document.getElementById(e))&&(/^(?:a|select|input|button|textarea)$/i.test(t.tagName)||(t.tabIndex=-1),t.focus())},!1); </script> </body> </html>