<jupyter_start><jupyter_text>Minando datos de 8ch.net con python====================[![Anaconda-Server Badge](https://anaconda.org/bc-privsec-devel/chanscrape/badges/license.svg)](https://anaconda.org/bc-privsec-devel/chanscrape)La idea detras de esta libreta, es brindar total transparencia al procedimiento que estamos utilizandom de manera que pueda ser auditable por cualquier persona con minimos conocimientos del lenguaje **Python 3** Al proporcionar esta transparencia, estamos asegurando que no hay ninguna clase de modificacion o manipulacion en los datos, excepto en lo que esta publicado en el sitio del que extraeremos la evidencia forenseLa libreta explicara los pasos que desarrolla el programa, siguiendo el estilo de programacion literaria. El *kernel* configurado es Python 3, y el ambiente de desarrolloes la ultima version disponible del proyecto Jupyter.El codigo fuente, asi como instrucciones para instalar y ejecutar esta libreta por su propia cuenta, estan disponibles en su [repositorio oficial de Github](https://github.com/BC-PRIVSEC/ChanScrape). Los autores y colaboradores en el desarrollo de este software hemos decidido **dedicar este trabajo al Dominio Publico** renunciando a cualquier derecho de autor _en detrimento propio y de nuestros sucesores_; y para beneficio de el publico general. Consulte los [detalles legales en el Repositorio oficial](https://github.com/BC-PRIVSEC/ChanScrape/LICENCIA-ES).Queremos demostrar de esta manera que **no tenemos interes alguno en lucrar** con las victimas, por el contrario el objetivo es proporcionarles herramientas informaticas y conocimiento en mejores practicas para **defender su seguridad, integridad e identidad en Linea.**  Use esta libreta y la herramienta que representa para los fines legales que a usted convenga.------------   Configuracion del programa y librerias adicionalesEn esta seccion, se definen y cargan las librerias adicionales a la librera estandar del lenguaje que se utilizaran en este programa. De particular interes es la libreria (py8chan)[1] que proporciona una interfaz al API de datos del sitio (8chan)[2]. `BeautifulSoup`[3] es una libreria muy comun para la extraccion de datos pues es muy sencilla de usar y provee de poderosas caracteristicas para la manipulacion de **HTML**. En nuestro caso usaremos `re` y `BeautifulSoup` con el fin de identificar direcciones de correo electronico y extraerlas al limpiar el HTML que se encuentre alrededor de las mismas, tal vez ofuscandolas.Todas las librerias requeridas se distribuyen atraves de Anaconda Cloud en el mismo paquete<jupyter_code>import py8chan
import re
import json

from datetime import datetime
from bs4 import BeautifulSoup
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all" # Con esto, la libreta dara formato legible a las varables que
                                                # calculemos, haciendo mas sencillo de entender el programa<jupyter_output><empty_output><jupyter_text>Mas adelante, el procedimiento de extraccion de datos requerira una rutina para de-duplicar los correos electronicos extraidos. La convencion en python es definir las rutinas antes de la seccion principal del programa. Entonces aqui definimos funciones auxiliares:<jupyter_code>def alt_unique(input_list):
    ''' implementacion eficiente de elementos uniicos usando
    conversion intermedia a set() que no permite duplicados. de
    esta manera evitamos caminar por cada elemento de la lista en 
    un bucle convencional'''
    output = set(input_list)
    return list(output)<jupyter_output><empty_output><jupyter_text>Enlace al API e instancias de objetos de datos<jupyter_code>timestamp = datetime.isoformat( datetime.now()) 
#Hora exacta como sello para identificar la corrida

tablero = py8chan.Board('ensenada')        
conversaciones = tablero.get_all_threads()<jupyter_output><empty_output><jupyter_text>Creamos ahora una instancia de `py8chan.board`, Objeto de enlace al API del sitio 8chan. es a traves de sus metodos obtendremos los datos requeridos. Uno de estos metodos, `get_all_threads` produce una lista cuyos miembros representan todas las conversaciones que pertenecen al tableroEn este punto, el programa y sus librerias internas han realizado conecciones y consultas al API del servidor del sitio 8chan. como si se tratara de un navegador. Ademas, hemos creado una instancia de datos que representa todo el conjunto de informacion en el tablero identificado al crear la instancia. Esta instancia contiene varios metodos que nos permitiran acceder a los datos de mas bajo nivel. Lo primero que hemos hecho es obtener un arreglo de objetos que contiene en cada elemento una conversacion perteneciente a dicho tablero.Podremos usar esta lista para obtener mensajes individuales de cada conversacion<jupyter_code>emailpattern = re.compile(r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}')
emails = []
mensajes = 0<jupyter_output><empty_output><jupyter_text>Antes de proseguir, hemos definido algunas variables auxiliares. el primer caso se trata de una expresion regular muy popular para extraer direcciones de correo electronico. En resumen, esta expresion dispara una coincidencia cada vez que encuentra un texto que corresponde al formato indicado. En el caso de las ultimas dos variables, su uso es obvio: en la primera almacenaremos una lista de todos los correos que encontremos usando la expresion regular. La segunda es un contador para tener idea de la cantidad de mensajes individuales que hemos procesado<jupyter_code>for conversacion in conversaciones:             # 1
    mensajes += len(conversacion.all_posts)     # 2
    for msg in conversacion.all_posts:          # 3
        em = emailpattern.findall(msg.comment)  # 4
        if em:                                  # 5
            for m in em:                        # 6
                emails.append(m)                # 6
                                                # 7

unique_emails = alt_unique(emails)              # 8
unique_emails.sort()                            # 9<jupyter_output><empty_output><jupyter_text>1. camnaremos por cada conversacon en la lista de conversaciones2. sumamos el numero de mensajes de la lista al total de procesados3. caminaremos por cada mensaje que forma parte de cada conversacion4. examinamos la propiedad 'comment' que contiene el texto de cada mensaje5. si hay una o mas coincidencias con el patron que representa un correo electronico6. entonces caminamos la lista de coincidencias, en caso de que sean 2 o mas7. y agregamos cada elemento al arreglo que definimos anteriormente para almacenar el dato.8. Aqui limpiamos el arreglo al eliminar los correos electronicos duplicados9. y ordenamos alfabeticamente el arreglo de correos unicos.------Ahora podemos imprimr un reporte de las direcciones de correo que fueron minadas del tablero:<jupyter_code>print('Hemos capturado ', len(conversaciones))
print('Contienen ', mensajes)
print('y se extrajeron ', len(unique_emails), 'emails')<jupyter_output>Hemos capturado  375
Contienen  3203
y se extrajeron  216 emails<jupyter_text>Los datos que nos interesan estan en la variable `unique_emails` Para poder utilizarla de manera interoperable asi como legible al ser humano, serializaremos la variable a formato JSON (Notacion de objetos para JavaScript) y podremos grabar el archivo en disco; de manera que podemos mantener un registro historico de los correos elecronicos pertenecientes a las personas que participan en esos foros.<jupyter_code>filename = "data/" + timestamp.split(".")[0] + ".json"

with open(filename,'w+') as storage:
    storage.write(json.dumps(unique_emails))

print("Si no hay ningum error,los corrreos se han grabado en el archivo ", filename )<jupyter_output><empty_output><jupyter_text>Esto es facilmente comprobable:<jupyter_code>%%bash
echo && echo ">>> Que hay en el directorio data/ ?"
ls -l data/
echo && echo ">>> Que tipo de archivos son?"
file data/*
echo && echo ">>> Cuantas lineas contienen?"
wc -l data/*
echo && echo ">>> Pretty print? (requiere jq)"
cat data/** | jq . | head -20

#TODO: Implementar un JSON Pretty Printer en python.<jupyter_output>>>> Que hay en el directorio data/ ?
total 16
-rw-rw-r-- 1 hector hector 5701 Feb  8 10:35 2018-02-08T10:35:02.json
-rw-rw-r-- 1 hector hector 5701 Feb  8 10:41 2018-02-08T10:39:46.json

>>> Que tipo de archivos son?
data/2018-02-08T10:35:02.json: ASCII text, with very long lines, with no line terminators
data/2018-02-08T10:39:46.json: ASCII text, with very long lines, with no line terminators

>>> Cuantas lineas contienen?
    0 data/2018-02-08T10:35:02.json
    0 data/2018-02-08T10:39:46.json
    0 total

>>> Pretty print? (requiere jq)
[
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "Anofisjimenez<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>",
  "<EMAIL>.[...]
{'fraction_non_alphanumeric': 0.0732033426183844, 'fraction_numerical': 0.018161559888579388, 'mean_word_length': 4.327002967359051, 'pattern_counts': {'":': 0, '<': 48, '<?xml version=': 0, '>': 72, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '387912', 'license': 'WTFPL', 'n_tokens_mistral': 2891, 'n_tokens_neox': 2821, 'n_words': 1079, 'path': 'Minando_datos_de_8chan_1.ipynb', 'repo_name': 'BC-PRIVSEC/ChanScrape', 'size': '9195'}