wiki2html
Nombre: wiki2html
Autor: point_to_null
Origen: original
Lenguaje: python
Licencia: gpl
Propósito: Crea un compendio en html de un árbol de enlaces recursivos de un articulo Wiki, de todas las paginas a las que enlaza, y de estas a su vez, hasta que el limite de recursion es alcanzado, se llena el espacio disponible en memoria o todos los artículos abiertos son completamente duplicados.
Dependencias: python-creoleparser
Código:
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
# Bugs conocidos:
# * Se elimina todo el código en la misma linea donde se recursa
import re
import urllib2
import sys
def debug(mensaje):
sys.stderr.write(str(mensaje) + "\n")
try:
from creoleparser import text2html
## Uso text2html en lugar de creole2html porque no me importa usar caracteristicas
## no incluidas en Creoles 1.0
except ImportError:
debug("""Este programa necesita de Creoleparser para funcionar.
Si estás usando Debian debes instalar el paquete python-creoleparser:
# apt-get install python-creoleparser
Si estás utilizando cualquier otro sistema operativo debería poder instalar Creoleparser usando las setup tools de python (http://peak.telecommunity.com/DevCenter/setuptools):
# easy_install Creoleparser
Si no tienes apt-get ni easyinstall puedes descargar manualmente Geshi de aquí (http://genshi.edgewall.org/wiki/Download) y Creoleparser de aquí (http://pypi.python.org/pypi/Creoleparser), luego de descomprimir los paquetes los instalas ejecutando setup.py con el argumento instalar (usando permisos de administrador, e.g.:
# ./setup.py install
Si nada de lo anterior sirvio no te preocupes, aún puedes pasarte al lado debian de la vida...""")
exit(15)
PRE_ORDEN = "/index.php?title="
POS_ORDEN = "&action="
RECURS = " "
MAX_RECURS = 4
def convertir_direccion(direccion, recursiones=0):
debug(RECURS * recursiones + "Convirtiendo: " + direccion)
if direccion.startswith(WIKI):
if direccion.count("/go/"):
direccion = direccion.replace("/go/", PRE_ORDEN, 1) + POS_ORDEN + "edit"
else:
raise ReferenceError("No debe referenciar a la pagina raiz")
# direccion = direccion[:-1] + PRE_ORDEN + "Dbnews" + POS_ORDEN + "edit"
elif direccion.startswith("[[") and direccion.endswith("]]"):
expresion_regular = r"""^\[\[(?P<pagina>.*?)(\|.*?)?\]\]"""
objeto = re.search(expresion_regular, direccion, re.UNICODE)
direccion = "[[" + objeto.group('pagina') + "]]"
direccion = WIKI + PRE_ORDEN + direccion[2:-2].replace(" ", "_") \
+ POS_ORDEN + "edit"
debug(RECURS * recursiones + "Nueva direccion: " + direccion)
return direccion
def obtener_autores(articulo, recursiones=0):
direccion = WIKI + PRE_ORDEN + articulo + POS_ORDEN + "history"
debug()
# pagina = urllib2.urlopen(direccion)
# r'(target=|title="User:)(?P<usuario>.*?)"'
def obtener_codigo(direccion, recursiones=0):
recursiones += 1
debug(RECURS * recursiones + "Intentando obtener el codigo en " + direccion)
if not direccion.endswith("&action=edit"):
direccion = convertir_direccion(direccion, recursiones + 1)
try:
pagina = urllib2.urlopen(direccion)
html = "".join(pagina.readlines())
except urllib2.URLError:
raise OSError("No se pudo descargar la web: %s" % direccion)
expresion_regular = r"""<textarea(.|\s)*?>(?P<codigo>(.|\s)*?)</textarea>"""
procesador = re.compile(expresion_regular, re.MULTILINE)
codigo = procesador.search(html)
return codigo.group("codigo")
def procesar(codigo, recursiones=0):
debug(RECURS * recursiones + "Procesando el codigo obtenido")
expresion_regular = "|".join([
"^.*?(?P<recursar>\[\[(.|\s)*?\]\]).*?$",
"^(?P<resto>.*?)$",
])
procesador = re.compile(expresion_regular, re.MULTILINE)
iterador = procesador.finditer(codigo)
salida = u""
for elemento in iterador:
grupos = elemento.groupdict()
if grupos["recursar"]:
if recursiones < MAX_RECURS:
codigo = obtener_codigo(grupos["recursar"], recursiones)
salida += procesar(codigo, recursiones + 1)
else:
debug(RECURS * recursiones \
+ "Enlace ignorado; maximo nivel de recursion alcanzado" )
else:
for clase in grupos:
if grupos[clase]:
try:
salida += unicode(grupos[clase],'utf-8', 'replace')
except UnicodeDecodeError:
raise
salida += "\n"
return salida
if __name__ == "__main__":
debug("Fun time!")
if len(sys.argv) == 2:
if sys.argv[1].startswith("http://"):
re_raiz = "^\s*?(?P<raiz>http://.*?\..*?)/(?P<resto>.*?)$"
procesador = re.compile(re_raiz)
if procesador.findall(sys.argv[1]):
WIKI = procesador.findall(sys.argv[1])[0][0]
PAGINA = sys.argv[1]
else:
raise ReferenceError("Debe especificar un articulo dentro del wiki")
codigo_wiki = procesar(obtener_codigo(PAGINA, -1))
# *Corregimos errores de formato ya que la comprobacion de cierre de marcador
# que hace coreoleparser es más estricta que la de la mayoria de las wikis
#
# **Aplico las modificaciones de formato que coreole no aplica
# **Separamos el texto en parrafos
parrafos = codigo_wiki.split("\n\n")
# **Chequeamos la paridad de los marcadores de formato por parrafo
for i in xrange(len(parrafos)):
# ***negritas (''')
if parrafos[i].count("""'''""") % 2:
parrafos[i] = parrafos[i] + """'''"""
# ***cursivas ('')
if parrafos[i].count("""''""") % 2:
parrafos[i] = parrafos[i] + """''"""
# ***h3 (===)
if parrafos[i].count("""===""") % 2:
parrafos[i] = parrafos[i] + """==="""
# ***h2 (==)
if parrafos[i].count("""==""") % 2:
parrafos[i] = parrafos[i] + """=="""
# ***h1 (=)
# if parrafos[i].count("""=""") % 2:
# parrafos[i] = parrafos[i] + """="""
codigo_wiki = u"\n\n".join(parrafos)
#*Modificaciones de código que no realiza coreoleparser
#**que no identa los parrafos que comienzan con ":"
p = re.compile(r"""^:::""", re.MULTILINE|re.UNICODE)
codigo_wiki = p.sub("\n" + " " * 12, codigo_wiki)
p = re.compile(r"""^::""", re.MULTILINE|re.UNICODE)
codigo_wiki = p.sub("\n" + " " * 8, codigo_wiki)
p = re.compile(r"""^:""", re.MULTILINE|re.UNICODE)
codigo_wiki = p.sub("\n" + " " * 4, codigo_wiki)
#*Aplicacion de guia de estilo
#**Resalto en rojo las comillas '"'
codigo_wiki = codigo_wiki.replace('"', '<font size=8 color=red>"</font>')
#**Delatores del uso de traductor "auto>> "
codigo_wiki = codigo_wiki.replace('auto>> ', '<font size=8 color=red>auto>> </font>')
#**Aplico cursiva al texto entre cosos "«" y "»"
codigo_wiki = codigo_wiki.replace(u'«', u"""<em>«""")
codigo_wiki = codigo_wiki.replace(u'»', u"""»</em>""")
debug("Convirtiendo wiki > html")
html = text2html(codigo_wiki)
#*Esto no debería ser necesario, es paleativo para errores en creoleparser
#
#**que escapa "&" en el código generado... lo que no es muy logíco.
html = html.replace("""&""", """&""")
#**que escapa "<" en el código generado
html = html.replace("""<""", """<""")
#**que escapa ">" en el código generado
html = html.replace(""">""", """>""")
#**que escapa "&" en el código generado... lo que no es muy logíco (reloaded).
html = html.replace("""&""", """&""")
#**que no formatea el texto en negritas ("'''")
for i in xrange(html.count("""'''""") / 2):
html = html.replace("""'''""", "<strong>", 1)
html = html.replace("""'''""", "</strong>", 1)
#**que no formatea el texto en cursivas ("''")
for i in xrange(html.count("""''""") / 2):
html = html.replace("""''""", "<em>", 1)
html = html.replace("""''""", "</em>", 1)
#**que no crea etiquetas de imagenes para direcciones de imagenes
#<a href="http://fqdn/ruta/nombre.ext">http://fqdn/ruta/nombre.ext</a>
#<img src="http://fqd/ruta/nombre.ext" alt="nombre" title="nombre">
p = re.compile(r"""<a href="(?P<protocol>http|ftp|ftps|https)://(?P<fqdn>.*?)/(?P<ruta>.*)/(?P<nombre>.*)\.(?P<ext>png|gif|jpg|jpeg)">(?P=protocol)://(?P=fqdn).(?P=ruta)/(?P=nombre).(?P=ext)</a>""", re.MULTILINE|re.UNICODE|re.IGNORECASE)
html = p.sub(r"""<img src="http://\g<fqdn>/\g<ruta>/\g<nombre>.\g<ext>" alt="\g<nombre>" title="\g<nombre>">""", html)
#**que crea de forma incorrecta enlaces de referencia (WTF!)
#[<a href="ruta%5D">ruta]</a>
#[<a href="ruta">ruta</a>]
p = re.compile(r"""\[<a href="(?P<ruta>.*)\]">(?P=ruta)\]</a>""", re.MULTILINE|re.UNICODE)
html = p.sub(r"""[<a href="\g<ruta>">\g<ruta></a>]""", html)
#**que no respeta los nombres en los enlaces de referencia
#[<a href="url">url</a> nombre]
#<a href="ruta">nombre</a>
p = re.compile(r"""\[<a href="(?P<ruta>.*?)">(?P=ruta)</a> (?P<nombre>.*?)\]""", re.MULTILINE|re.UNICODE)
html = p.sub(r"""<a href="\g<ruta>">\g<nombre></a>""", html)
#*Aqui aplico el parche esDebianizador
#**Creo espacios previos a h2 para la prolijidad en el foro
html = html.replace("""<h2>""", """<br><h2>""")
html = html.replace("""</h2>""", """<br></h2><img src="http://arnet.no-ip.org/iconos/barra_495x2.png">""")
# html = html.replace("""</h2>""", """<br></h2><hr size=2 width="50%" align=left tabindex=-1>""")
#**"<h1>" -> "cabecera" porque en el foro no podemos usar el estilo h1
html = html.replace("""<h1>""", """<br><h2><img src="http://arnet.no-ip.org/iconos/pre-titular.png">""")
html = html.replace("""</h1>""", """<img src="http://arnet.no-ip.org/iconos/post-titular.png"></h2><img src="http://arnet.no-ip.org/iconos/barra_495x4.png"><br>""")
# html = html.replace("""</h1>""", """<img src="http://arnet.no-ip.org/iconos/post-titular.png"></h2><hr size=4 width="50%" align=left tabindex=-1><br>""")
print html
else:
raise ValueError("La direccion debe comenzar con http://")
else:
raise ReferenceError("Se debe especificar solo un argumento, la pagina Wiki a replicar")
Ejemplos de uso:
Solo se debe especificar la dirección del articulo a replicar. e.g.:
wiki2html http://misitio.miwiki/go/mi_articulo > pagina.htmlTened en cuenta que si la página pasada como argumento enlaza a varias otras la respuesta tardará, naturalmente, un poco en ser elaborada.
Comentarios:
Ya que el script fue escrito con un caso particular en mente no es compatible con las urls de las wikipedias que han modificado el comportamiento estándar de wikimedia o que nunca lo han usado (son la mayoría, pues las clearurl se pusieron de moda). Pero si alguien necesita trabajar con una de estas Wikis y necesita ayuda para extender el script no dude en avisarme. De no.. ya lo haré un fin de semana de estos.