Para muchos proyectos he buscado algo que me extraiga el contenido de una web, y si de paso me da información útil como su idioma mejor.
Estos proyectos con cosas como:
- Un bot
xmpp
que le mandes una url y te devuelva el texto - Un recolector de enlaces que luego con ellos forme un rss con contenido completo
- Un agregador que necesita saber el idioma de las páginas que esta agregando
- etc
Pues bien, parece que graby
es lo que estaba buscando.
Ver https://github.com/j0k3r/graby
Mi interés no es usarla en php
, así que haré una instalación y un pequeño
python
para usar como comando externo para otras aplicaciones.
1- Instalo graby
La manera habitual sería usar compose
, pero como no estoy interesado
en un proyecto php
y además en la raspberry
el intento de instalar
con compose
se ha llevado toda la memoria hasta petar... usare php-download.com
Y para obtener automáticamente la última versión usare este script:
#!/bin/bash
LAST=$(curl -sS "https://php-download.com/package/j0k3r/graby" | grep "selected=\"selected\"" | cut -d'"' -f2)
URL="https://php-download.com/downloads/j0k3r/graby/${LAST}/j0k3r_graby_${LAST}_require.zip"
wget -O graby.zip "$URL"
unzip -o -q graby.zip 'vendor/*'
rm graby.zip
y lo ejecuto:
pi@bot ~/wks/my-graby $ ./get-graby.sh
--2018-09-26 19:06:12-- https://php-download.com/downloads/j0k3r/graby/1.13.6.0/j0k3r_graby_1.13.6.0_require.zip
Resolviendo php-download.com (php-download.com)... 104.18.41.212, 104.18.40.212, 2606:4700:30::6812:28d4, ...
Conectando con php-download.com (php-download.com)[104.18.41.212]:443... conectado.
Petición HTTP enviada, esperando respuesta... 200 OK
Longitud: 37589435 (36M) [application/zip]
Grabando a: “graby.zip”
graby.zip 100%[========================================================================================>] 35,85M 1011KB/s en 41s
2018-09-26 19:06:55 (906 KB/s) - “graby.zip” guardado [37589435/37589435]
pi@bot ~/wks/my-graby $ ls
get-graby.sh vendor
1- Creo el comando
#!/usr/bin/env php
<?php
require_once("vendor/autoload.php");
use Graby\Graby;
$article = $argv[1];
$graby = new Graby();
$result = $graby->fetchContent($article);
echo json_encode($result);
?>
Ejemplo de ejecución:
pi@bot ~/wks/my-graby $ ./graby.php https://s-nt-s.github.io/extension_markdown_pelican/
{"status":200,"html":"<p>Seg\u00fan la documentaci\u00f3n de <code>pelican<\/code> la manera de incluir un plugin <code>markdown<\/code>\nes poniendo en <code>pelicanconf.py<\/code> lo siguiente:<\/p><div class=\"highlight\"><pre>MARKDOWN = {\n 'extensions': [ <<aqu\u00ed van las extensiones>> ],\n 'extension_configs': {\n 'markdown.extensions.codehilite': {'css_class': 'highlight'},\n 'markdown.extensions.extra': {},\n 'markdown.extensions.meta': {},\n },\n 'output_format': 'html5',\n}\n<\/pre><\/div><p>donde la parte relevante es el array <code>extensions<\/code> ya que lo dem\u00e1s solo se pone\npara preservar el valor por defecto de la variable <code>MARKDOWN<\/code><\/p><p>Si estamos incluyendo extensiones de terceros probablemente esto sea lo mejor,\npero si la extensi\u00f3n la estamos haciendo nosotros puede que queramos evitarnos\ntener que escribir este bloque y as\u00ed mantener m\u00e1s limpio nuestro fichero\n<code>pelicanconf.py<\/code> y de este modo solo pensar en <code>plugins<\/code> en general, sin hacer\ndistinci\u00f3n si son <code>plugins de pelican<\/code> o <code>extensiones de markdown<\/code>.<\/p><p>En definitiva, la idea es es crear la extensi\u00f3n dentro de un plugin.\nEsto tambi\u00e9n es \u00fatil cuando queremos configurar la extensi\u00f3n en base a\nvariables de <code>pelicanconf.py<\/code> evitando tener que pasarlas por par\u00e1metro una a una.<\/p><p>Este ser\u00eda un ejemplo para poder usar variables en <code>markdown<\/code><\/p><p>En <code>pelicanconf.py<\/code>:<\/p><div class=\"highlight\"><pre>import os\nsys.path.append('.') #### 1\nfrom plugins import replacements #### 2\nabspath = os.path.abspath(__file__)\ncur_dir = os.path.dirname(abspath)\nREPLACEMENTS_CONFIG = cur_dir+\"\/config\/replacements.yml\" #### 3\nPLUGINS=[replacements] #### 4\nGITHUB_URL = 'https:\/\/github.com\/s-nt-s\/s-nt-s.github.io'\nDEFAULT_PAGINATION = 10\n<\/pre><\/div><ul><li>Con <code>1<\/code> y <code>2<\/code> incluimos el <code>plugin<\/code> que esta en <code>plugins\/replacements.py<\/code><\/li>\n<li>Con <code>3<\/code> definimos la variable que se usara para obtener el fichero de configuraci\u00f3n<\/li>\n<li>Con <code>4<\/code> incluimos el <code>plugin<\/code><\/li>\n<\/ul><p>En <code>plugins\/replacements.py<\/code> tenemos:<\/p><div class=\"highlight\"><pre>from markdown.preprocessors import Preprocessor\nfrom markdown.extensions import Extension\nfrom pelican import signals\nimport yaml\nclass MkReplace(Preprocessor):\n def __init__(self, delimiter, replacements):\n self.replacements = replacements\n self.delimiter = delimiter\n def run(self, lines):\n txt = \"\\n\".join(lines)\n for key, value in self.replacements.items():\n txt = txt.replace(self.delimiter+key+self.delimiter, value)\n return txt.split(\"\\n\")\nclass ExReplace(Extension):\n def __init__(self, delimiter, replacements, **config):\n self.replacements = replacements\n self.delimiter = delimiter\n super(ExReplace, self).__init__(**config)\n def extendMarkdown(self, md, md_globals):\n md.preprocessors.add('replacements', MkReplace(self.delimiter, self.replacements), \">html_block\")\ndef process_settings(pelican_object):\n config_file = pelican_object.settings['REPLACEMENTS_CONFIG'] #### 1\n with open(config_file, 'r') as stream:\n replacements = yaml.load(stream)\n if 'PELICAN_SETTINGS' in replacements:\n pSettings = replacements['PELICAN_SETTINGS']\n del replacements['PELICAN_SETTINGS']\n if isinstance(pSettings, str):\n pSettings = pSettings.strip().split()\n if isinstance(pSettings, list):\n for s in pSettings:\n if s not in replacements and s in pelican_object.settings:\n replacements[s]=str(pelican_object.settings[s]) #### 2\n delimiter = replacements.pop('DELIMITER', '::')\n return delimiter, replacements\ndef replacements_markdown_extension(pelicanobj, delimiter, replacements):\n \"\"\"Instantiates a customized Markdown extension\"\"\"\n pelicanobj.settings['MARKDOWN'].setdefault('extensions', []).append(ExReplace(delimiter, replacements))\ndef replacements_init(pelicanobj):\n \"\"\"Loads settings and instantiates the Python Markdown extension\"\"\"\n # Process settings\n delimiter, replacements = process_settings(pelicanobj)\n # Configure Markdown Extension\n replacements_markdown_extension(pelicanobj, delimiter, replacements)\ndef register():\n \"\"\"Plugin registration\"\"\"\n signals.initialized.connect(replacements_init)\n<\/pre><\/div><ul><li><code>register<\/code> registra el <code>plugin<\/code><\/li>\n<li><code>replacements_init<\/code> usa <code>pelicanobj<\/code> para preparar las opciones a trav\u00e9s de <code>process_settings<\/code> y pasarselas a <code>replacements_markdown_extension<\/code><\/li>\n<li><code>replacements_markdown_extension<\/code> include la extensi\u00f3n de <code>markdown<\/code> en el <code>pelican<\/code> sin machacar los valores por defecto de la variable <code>MARKDOWN<\/code><\/li>\n<li>El resto es el contenido habitual de una extensi\u00f3n <code>markdown<\/code><\/li>\n<\/ul><p>En <code>process_settings<\/code> vemos como nos es de ayuda tener el objeto <code>pelican<\/code> disponible,\nde manera que tenemos disponible <code>REPLACEMENTS_CONFIG<\/code> para encontrar el <code>yaml<\/code> de configuraci\u00f3n (en <code>1<\/code>)\ny a su vez podemos usar otras variables (en <code>2<\/code>) de manera que si el <code>yaml<\/code> es:<\/p><div class=\"highlight\"><pre>DELIMITER: '::'\nPELICAN_SETTINGS: GITHUB_URL DEFAULT_PAGINATION\nLOREM: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...\n<\/pre><\/div><p>y tenemos un <code>markdown<\/code> tal que as\u00ed:<\/p><div class=\"highlight\"><pre>Este blog usa una imaginaci\u00f3n de **::DEFAULT_PAGINATION::** items por hoja y\npuedes encontrar el c\u00f3digo en [github](::GITHUB_URL::)\n::LOREN::\n<\/pre><\/div><p>se transformara justo antes de ser pasado a <code>html<\/code> en:<\/p><div class=\"highlight\"><pre>Este blog usa una imaginaci\u00f3n de **10** items por hoja y\npuedes encontrar el c\u00f3digo en [github](https:\/\/github.com\/s-nt-s\/s-nt-s.github.io)\nNeque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...\n<\/pre><\/div><p>y finalmente en el <code>html<\/code><\/p><div class=\"highlight\"><pre><p>Este blog usa una imaginaci\u00f3n de <b>10<\/b> items por hoja y\npuedes encontrar el c\u00f3digo en <a href='https:\/\/github.com\/s-nt-s\/s-nt-s.github.io'>github<\/a><\/p>\n<p>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...<\/p>\n<\/pre><\/div><p>Resumo las ventajas:<\/p><ul><li>menos lineas en <code>pelicanconf.py<\/code><\/li>\n<li>no hay peligro de machacar valores por defecto de <code>MARKDOWN<\/code> necesarios<\/li>\n<li>la extensi\u00f3n tiene acceso al objeto <code>pelican<\/code> y por lo tanto a sus variables de configuraci\u00f3n<\/li>\n<li>la extensi\u00f3n es m\u00e1s f\u00e1cil de configurar<\/li>\n<li>no hace falta diferencia entre <code>extensiones markdown<\/code> y <code>plugins pelican<\/code><\/li>\n<\/ul><p>Fuentes:\n<a href=\"http:\/\/docs.getpelican.com\/en\/stable\/settings.html#settings\" target=\"_blank\">docs.getpelican.com - settings<\/a>,\n<a href=\"http:\/\/docs.getpelican.com\/en\/stable\/plugins.html#how-to-create-plugins\" target=\"_blank\">docs.getpelican.com - plugins<\/a>,\n<a href=\"https:\/\/github.com\/s-nt-s\/s-nt-s.github.io\" target=\"_blank\">mi github :P<\/a><\/p>","title":"Crear extensi\u00f3n markdown para usar en pelican","language":"es","date":null,"authors":[],"url":"https:\/\/s-nt-s.github.io\/extension_markdown_pelican\/","content_type":"text\/html","open_graph":[],"native_ad":false,"all_headers":{"server":"GitHub.com","content-type":"text\/html; charset=utf-8","last-modified":"Wed, 26 Sep 2018 08:59:31 GMT","etag":"W\/\"5bab4a73-538d\"","access-control-allow-origin":"*","expires":"Wed, 26 Sep 2018 17:01:49 GMT","cache-control":"max-age=600","content-encoding":"gzip","x-github-request-id":"4272:358F:3836627:4B61346:5BABB923","content-length":"4138","accept-ranges":"bytes","date":"Wed, 26 Sep 2018 17:13:51 GMT","via":"1.1 varnish","age":"0","connection":"keep-alive","x-served-by":"cache-mad9442-MAD","x-cache":"HIT","x-cache-hits":"1","x-timer":"S1537982031.486043,VS0,VE110","vary":"Accept-Encoding","x-fastly-request-id":"15e8bf8ca5b6dd90d6f63a1d0d5c5b8c50e349d7"},"summary":"Seg\u00fan la documentaci\u00f3n de pelican la manera de incluir un plugin markdown es poniendo en pelicanconf.py lo siguiente: MARKDOWN = { 'extensions': [ <<aqu\u00ed van las extensiones>> ], 'extension_configs': { 'markdown.extensions.codehilite': {'css_class': …"}
es decir, devuelve el json
:
{
"all_headers": {
"accept-ranges": "bytes",
"access-control-allow-origin": "*",
"age": "0",
"cache-control": "max-age=600",
"connection": "keep-alive",
"content-encoding": "gzip",
"content-length": "4138",
"content-type": "text/html; charset=utf-8",
"date": "Wed, 26 Sep 2018 17:27:48 GMT",
"etag": "W/\"5bab4a73-538d\"",
"expires": "Wed, 26 Sep 2018 17:01:49 GMT",
"last-modified": "Wed, 26 Sep 2018 08:59:31 GMT",
"server": "GitHub.com",
"vary": "Accept-Encoding",
"via": "1.1 varnish",
"x-cache": "HIT",
"x-cache-hits": "1",
"x-fastly-request-id": "e3088257cb9a3cd8805bc44ee055908f3c01be54",
"x-github-request-id": "4272:358F:3836627:4B61346:5BABB923",
"x-served-by": "cache-mad9442-MAD",
"x-timer": "S1537982868.240513,VS0,VE107"
},
"authors": [],
"content_type": "text/html",
"date": null,
"html": "<p>Seg\u00fan la documentaci\u00f3n de <code>pelican</code> la manera de incluir un plugin <code>markdown</code>\nes poniendo en <code>pelicanconf.py</code> lo siguiente:</p><div class=\"highlight\"><pre>MARKDOWN = {\n 'extensions': [ <<aqu\u00ed van las extensiones>> ],\n 'extension_configs': {\n 'markdown.extensions.codehilite': {'css_class': 'highlight'},\n 'markdown.extensions.extra': {},\n 'markdown.extensions.meta': {},\n },\n 'output_format': 'html5',\n}\n</pre></div><p>donde la parte relevante es el array <code>extensions</code> ya que lo dem\u00e1s solo se pone\npara preservar el valor por defecto de la variable <code>MARKDOWN</code></p><p>Si estamos incluyendo extensiones de terceros probablemente esto sea lo mejor,\npero si la extensi\u00f3n la estamos haciendo nosotros puede que queramos evitarnos\ntener que escribir este bloque y as\u00ed mantener m\u00e1s limpio nuestro fichero\n<code>pelicanconf.py</code> y de este modo solo pensar en <code>plugins</code> en general, sin hacer\ndistinci\u00f3n si son <code>plugins de pelican</code> o <code>extensiones de markdown</code>.</p><p>En definitiva, la idea es es crear la extensi\u00f3n dentro de un plugin.\nEsto tambi\u00e9n es \u00fatil cuando queremos configurar la extensi\u00f3n en base a\nvariables de <code>pelicanconf.py</code> evitando tener que pasarlas por par\u00e1metro una a una.</p><p>Este ser\u00eda un ejemplo para poder usar variables en <code>markdown</code></p><p>En <code>pelicanconf.py</code>:</p><div class=\"highlight\"><pre>import os\nsys.path.append('.') #### 1\nfrom plugins import replacements #### 2\nabspath = os.path.abspath(__file__)\ncur_dir = os.path.dirname(abspath)\nREPLACEMENTS_CONFIG = cur_dir+\"/config/replacements.yml\" #### 3\nPLUGINS=[replacements] #### 4\nGITHUB_URL = 'https://github.com/s-nt-s/s-nt-s.github.io'\nDEFAULT_PAGINATION = 10\n</pre></div><ul><li>Con <code>1</code> y <code>2</code> incluimos el <code>plugin</code> que esta en <code>plugins/replacements.py</code></li>\n<li>Con <code>3</code> definimos la variable que se usara para obtener el fichero de configuraci\u00f3n</li>\n<li>Con <code>4</code> incluimos el <code>plugin</code></li>\n</ul><p>En <code>plugins/replacements.py</code> tenemos:</p><div class=\"highlight\"><pre>from markdown.preprocessors import Preprocessor\nfrom markdown.extensions import Extension\nfrom pelican import signals\nimport yaml\nclass MkReplace(Preprocessor):\n def __init__(self, delimiter, replacements):\n self.replacements = replacements\n self.delimiter = delimiter\n def run(self, lines):\n txt = \"\\n\".join(lines)\n for key, value in self.replacements.items():\n txt = txt.replace(self.delimiter+key+self.delimiter, value)\n return txt.split(\"\\n\")\nclass ExReplace(Extension):\n def __init__(self, delimiter, replacements, **config):\n self.replacements = replacements\n self.delimiter = delimiter\n super(ExReplace, self).__init__(**config)\n def extendMarkdown(self, md, md_globals):\n md.preprocessors.add('replacements', MkReplace(self.delimiter, self.replacements), \">html_block\")\ndef process_settings(pelican_object):\n config_file = pelican_object.settings['REPLACEMENTS_CONFIG'] #### 1\n with open(config_file, 'r') as stream:\n replacements = yaml.load(stream)\n if 'PELICAN_SETTINGS' in replacements:\n pSettings = replacements['PELICAN_SETTINGS']\n del replacements['PELICAN_SETTINGS']\n if isinstance(pSettings, str):\n pSettings = pSettings.strip().split()\n if isinstance(pSettings, list):\n for s in pSettings:\n if s not in replacements and s in pelican_object.settings:\n replacements[s]=str(pelican_object.settings[s]) #### 2\n delimiter = replacements.pop('DELIMITER', '::')\n return delimiter, replacements\ndef replacements_markdown_extension(pelicanobj, delimiter, replacements):\n \"\"\"Instantiates a customized Markdown extension\"\"\"\n pelicanobj.settings['MARKDOWN'].setdefault('extensions', []).append(ExReplace(delimiter, replacements))\ndef replacements_init(pelicanobj):\n \"\"\"Loads settings and instantiates the Python Markdown extension\"\"\"\n # Process settings\n delimiter, replacements = process_settings(pelicanobj)\n # Configure Markdown Extension\n replacements_markdown_extension(pelicanobj, delimiter, replacements)\ndef register():\n \"\"\"Plugin registration\"\"\"\n signals.initialized.connect(replacements_init)\n</pre></div><ul><li><code>register</code> registra el <code>plugin</code></li>\n<li><code>replacements_init</code> usa <code>pelicanobj</code> para preparar las opciones a trav\u00e9s de <code>process_settings</code> y pasarselas a <code>replacements_markdown_extension</code></li>\n<li><code>replacements_markdown_extension</code> include la extensi\u00f3n de <code>markdown</code> en el <code>pelican</code> sin machacar los valores por defecto de la variable <code>MARKDOWN</code></li>\n<li>El resto es el contenido habitual de una extensi\u00f3n <code>markdown</code></li>\n</ul><p>En <code>process_settings</code> vemos como nos es de ayuda tener el objeto <code>pelican</code> disponible,\nde manera que tenemos disponible <code>REPLACEMENTS_CONFIG</code> para encontrar el <code>yaml</code> de configuraci\u00f3n (en <code>1</code>)\ny a su vez podemos usar otras variables (en <code>2</code>) de manera que si el <code>yaml</code> es:</p><div class=\"highlight\"><pre>DELIMITER: '::'\nPELICAN_SETTINGS: GITHUB_URL DEFAULT_PAGINATION\nLOREM: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...\n</pre></div><p>y tenemos un <code>markdown</code> tal que as\u00ed:</p><div class=\"highlight\"><pre>Este blog usa una imaginaci\u00f3n de **::DEFAULT_PAGINATION::** items por hoja y\npuedes encontrar el c\u00f3digo en [github](::GITHUB_URL::)\n::LOREN::\n</pre></div><p>se transformara justo antes de ser pasado a <code>html</code> en:</p><div class=\"highlight\"><pre>Este blog usa una imaginaci\u00f3n de **10** items por hoja y\npuedes encontrar el c\u00f3digo en [github](https://github.com/s-nt-s/s-nt-s.github.io)\nNeque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...\n</pre></div><p>y finalmente en el <code>html</code></p><div class=\"highlight\"><pre><p>Este blog usa una imaginaci\u00f3n de <b>10</b> items por hoja y\npuedes encontrar el c\u00f3digo en <a href='https://github.com/s-nt-s/s-nt-s.github.io'>github</a></p>\n<p>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...</p>\n</pre></div><p>Resumo las ventajas:</p><ul><li>menos lineas en <code>pelicanconf.py</code></li>\n<li>no hay peligro de machacar valores por defecto de <code>MARKDOWN</code> necesarios</li>\n<li>la extensi\u00f3n tiene acceso al objeto <code>pelican</code> y por lo tanto a sus variables de configuraci\u00f3n</li>\n<li>la extensi\u00f3n es m\u00e1s f\u00e1cil de configurar</li>\n<li>no hace falta diferencia entre <code>extensiones markdown</code> y <code>plugins pelican</code></li>\n</ul><p>Fuentes:\n<a href=\"http://docs.getpelican.com/en/stable/settings.html#settings\" target=\"_blank\">docs.getpelican.com - settings</a>,\n<a href=\"http://docs.getpelican.com/en/stable/plugins.html#how-to-create-plugins\" target=\"_blank\">docs.getpelican.com - plugins</a>,\n<a href=\"https://github.com/s-nt-s/s-nt-s.github.io\" target=\"_blank\">mi github :P</a></p>",
"language": "es",
"native_ad": false,
"open_graph": [],
"status": 200,
"summary": "Seg\u00fan la documentaci\u00f3n de pelican la manera de incluir un plugin markdown es poniendo en pelicanconf.py lo siguiente: MARKDOWN = { 'extensions': [ <<aqu\u00ed van las extensiones>> ], 'extension_configs': { 'markdown.extensions.codehilite': {'css_class': …",
"title": "Crear extensi\u00f3n markdown para usar en pelican",
"url": "https://s-nt-s.github.io/extension_markdown_pelican/"
}
3- Lo uso desde python
#!/usr/bin/python3
import json
import sys
from subprocess import check_output, STDOUT
url = sys.argv[1]
js = check_output(['./graby.php', url], stderr=STDOUT).decode('UTF-8')
js = json.loads(js)
js = json.dumps(js, indent=4, sort_keys=True)
print(js)
Fuentes: github.com - j0k3r/graby, php-download.com - j0k3r/graby