Expressões regulares com Python

Esse é um tópico que sempre patinei bastante, ainda tenho alguma dificuldade pra ler e interpretar expressões regulares no python, então estou escrevendo esse artigo principalmente para me ajudar 🙂

O python tem uma biblioteca bem poderosa para expressões regulares:

import re

A função match serve para casar uma string em um texto, por exemplo:

In [2]: re.match("The", "The book is on the table")
Out[2]: <_sre.SRE_Match object; span=(0, 3), match='The'>

Note que a saída é um objeto que tem duas propriedades interessantes, span que devolve onde a string foi encontrada, no caso na posição 0 até 3, e a propriedade match que mostra o que foi encontrado. Mas a função match tem uma limitação, ela só funciona se a string buscada estiver no começo do texto.

Para encontrar strings ao longo do texto devemos usar a função search:

In [8]: re.search("book", "The book is on the table")
Out[8]: <_sre.SRE_Match object; span=(4, 8), match='book'>

Mas o search se limita a retornar apenas uma ocorrência, para encontrar todas as ocorrências da string no texto usamos a função findall:

In [11]: re.findall("book", "The book is on the book table")
Out[11]: ['book', 'book']

Nesse caso ele retornou uma lista com as strings encontradas.

Em expressão regular existem os metacaracteres, são caracteres especiais que usamos para dar match em determinados padrões, por exemplo  o ‘. ‘ (ponto ) , que da match em qualquer caracter, exceto quebra de linha, por exemplo:

In [12]: re.match(".", "The book is on the book table")
Out[12]: <_sre.SRE_Match object; span=(0, 1), match='T'>

In [13]: re.match(".", "12341234")
Out[13]: <_sre.SRE_Match object; span=(0, 1), match='1'>

In [14]: re.match(".", "\t\t\t")
Out[14]: <_sre.SRE_Match object; span=(0, 1), match='\t'>

In [18]: print(re.match(".", "\n\t\t"))
None

Com o search o funcionamento é similar:

In [33]: re.search('.', '\n\t\t')
Out[33]: <_sre.SRE_Match object; span=(1, 2), match='\t'>

Note que nesse caso ele deu match no \t mesmo com o texto começando com \n, o search ignorou o \n e partiu para processar o próximo caractere.

O “.” (ponto) quando usado com  findall tem um comportamento interessante, ele retorna uma lista de cada caractere da sequencia:

In [35]: re.findall(".", "The book")
Out[35]: ['T', 'h', 'e', ' ', 'b', 'o', 'o', 'k']

Outros dois metacaracteres  importantes são os do tipo âncora como o ^ e o $ , eles delimitam o início e o fim da string, por exemplo:

In [39]: re.findall("^.", "The book\n is on the\n book table")
Out[39]: ['T']

Mas é possível pegar os caracteres de todas as linhas do texto:

In [41]: re.findall("^.", "The book\nis on the\nbook table", re.MULTILINE)
Out[41]: ['T', 'i', 'b']

A âncora de fim de linha ( $ ) funciona de forma similar a ^:

In [47]: re.findall(".$", "The book\nis on the\nbook table", re.MULTILINE)
Out[47]: ['k', 'e', 'e']

Podemos combinar as duas âncoras:

In [48]: re.match("^.$", "The")

In [49]: re.match("^.$", "T")
Out[49]: <_sre.SRE_Match object; span=(0, 1), match='T'>

In [50]: re.match("^.$", "")

O “.” é muito útil porém muito abrangente , podemos limitar os caracters buscados, os colchetes “[ ]” funcionam como conjunto, por exemplo:

In [52]: re.findall("[abcdef]", "The book\nis on the\nbook table", re.MULTILINE)
Out[52]: ['e', 'b', 'e', 'b', 'a', 'b', 'e']

Ele entrou dentro do conjunto de caracteres e fez uma busca para cada ocorrência bem sucedida.

Podemos também buscar pelos caracteres que não estão dentro da sequencia declarada dentro dos colchetes:

In [56]: re.findall("[^abcdef]", "The book\n", re.MULTILINE)
Out[56]: ['T', 'h', ' ', 'o', 'o', 'k', '\n']

Existem também os ranges, declaramos eles com o ‘-‘ hifen, por exemplo:

In [57]: re.findall("[a-c]", "The book\n", re.MULTILINE)
Out[57]: ['b']

In [58]: re.findall("[a-h]", "The book\n", re.MULTILINE)
Out[58]: ['h', 'e', 'b']

In [60]: re.findall("[a-zA-Z]", "The book\n", re.MULTILINE)
Out[60]: ['T', 'h', 'e', 'b', 'o', 'o', 'k']

Podemos usar ranges para números e caracteres, segue um exemplo de um range que abrange todos os caracteresque formam palavras:

In [61]: re.findall("[a-zA-Z0-9_]", "The book\n", re.MULTILINE)
Out[61]: ['T', 'h', 'e', 'b', 'o', 'o', 'k']

Esse range é bem útil para filtrar campos de formulário em sites, e por isso usamos bastante esse padrão, por isso existe atalho para abreviar a digitação, o \w :

In [63]: re.findall("\w", "The book @\n", re.MULTILINE)
Out[63]: ['T', 'h', 'e', 'b', 'o', 'o', 'k']

Existem outras abreviaturas para ranges, são elas:

  • \d == [0-9]
  • \D == [^0-9]
  • \s == [\t\n\r\f\v]
  • \S == [^\t\n\r\f\v]
  • \w == [a-zA-Z0-9_]
  • \W == [^a-zA-Z0-9_]

Todas sequências especiais começam com “\”.  Isso gera problemas já que nosso texto pode vir com \ espalhadas no seu conteúdo, para isso temos que escapar a \, por exemplo:

Antes:

olá \n

Correto:

olá \\n

Para evitar dores de cabeça e tornar o código mais legível podemos usar as Raw strings, alertando o python de que aquela string não contem caracteres especiais, por exemplo:

In [88]: re.match(r'\\www', r'\www.otimo.com.gr')
Out[88]: <_sre.SRE_Match object; span=(0, 4), match='\\www'>

Podemos usar o metacaractere  | para expressões do tipo OU:

In [91]: re.match('ww|ss', r'www.otimo.com.gr')
Out[91]: <_sre.SRE_Match object; span=(0, 2), match='ww'>

Expressões regulares também suportam repetições:

In [96]: re.match(r'\w{5}', 'abcdef')
Out[96]: <_sre.SRE_Match object; span=(0, 5), match='abcde'>

In [97]: re.match(r'\w{5}', 'abcdefg')
Out[97]: <_sre.SRE_Match object; span=(0, 5), match='abcde'>

In [98]: re.match(r'\w{5}', 'abcdefg df')
Out[98]: <_sre.SRE_Match object; span=(0, 5), match='abcde'>

In [99]: re.match(r'\w{5}', 'abcd')

In [100]:

Note que ele pegou apenas textos com mais de 4 caracteres, os textos que excederam esse número foram processado mas retornaram apenas o limite de 5 caracteres.

Essa repetição pode ser configurada para retornar os caracteres excedentes, bastando colocar uma vírgula:

In [100]: re.match(r'\w{5,}', 'abcdefg df')
Out[100]: <_sre.SRE_Match object; span=(0, 7), match='abcdefg'>

Note que ele não pegou o “df”
Como você já suspeita, é possível configurar o mínimo e o máximo nas expressões:

In [108]: re.match(r'\w{2,6}', 'abcdefg df')
Out[108]: <_sre.SRE_Match object; span=(0, 6), match='abcdef'>

O metacaractere “?” é usado de duas formas, para limitar o mínimo de repetições (equivalente a {,1}) e para transformar expressões greed em expressões  lazy, por exemplo:

# Transformando a expressão greed em lazy
In [111]: re.match(r'\w{2,}', 'abcdefg df')
Out[111]: <_sre.SRE_Match object; span=(0, 7), match='abcdefg'>

In [112]: re.match(r'\w{2,}?', 'abcdefg df')
Out[112]: <_sre.SRE_Match object; span=(0, 2), match='ab'>

Já o metacaractere “*” é usado para pegar zero ou mais ocorrências ( equivalente a {,} ) :

In [120]: re.match(r'\w', 'abcdefg df')
 Out[120]: <_sre.SRE_Match object; span=(0, 1), match='a'>

In [121]: re.match(r'\w*', 'abcdefg df')
 Out[121]: <_sre.SRE_Match object; span=(0, 7), match='abcdefg'>

O metacaractere “+” é usado para pegar 1 ou mais ocorrências  ( equivale a { 1 , } ):

In [128]: re.match(r'\w+', '')

In [129]: re.match(r'\w+', 'abcdefg df')
Out[129]: <_sre.SRE_Match object; span=(0, 7), match='abcdefg'>

Um exemplo prático do uso do caractere “?” em uma  expressão:

In [132]: re.findall(r'".+"?', 'src="blablabla" alt="altaltalt"')
Out[132]: ['"blablabla" alt="altaltalt"']

Note que queriamos apenas os valores dos atributos de forma separada, mas como o “+” é greedy ele trouxe o resto da expressão, para resolver:

In [133]: re.findall(r'".+?"', 'src="bla bla bla" alt="altaltalt"')
Out[133]: ['"bla bla bla"', '"altaltalt"']

Para buscar valores vazios nos atributos temos que substituir o + por * :

In [134]: re.findall(r'".+?"', 'src="" alt=""')
Out[134]: ['"" alt="']

In [135]: re.findall(r'".*?"', 'src="" alt=""')
Out[135]: ['""', '""']

Um exemplo fazendo parsing de uma tag HTML:

In [136]: html = '<input type="email" id="id_email" name="user mail">'

In [137]: padrao = r'<(.+?) type="(.+?)" id="(.+?)" name="(.+?)"'

In [138]: re.match(padrao, html).groups()
Out[138]: ('input', 'email', 'id_email', 'user mail')

É possível criar um dicionário com os resultados da expressão regular:

In [143]: padrao = r'<(?P<tag>.+?) (?:(?:type="(?P<type>.+?)"|id="(?P<id>.+?)"|name="(P<name>.+?)") ?)*'

In [144]: re.match(padrao, html).groups()
Out[144]: ('input', 'email', 'id_email', None)

In [145]: re.match(padrao, html).groupdict()
Out[145]: {'id': 'id_email', 'tag': 'input', 'type': 'email'}

Espero que seja útil 😀

Inspiração:  http://henriquebastos.net/

HP, não esconda DRM em uma atualização de segurança

Nos últimos dias mais de 10,000 pessoas  se juntaram a EFF na mobilização para que a HP repare suas  impressoras que se auto-destroem (proteção DRM para quando usados cartuchos não oficiais).

Parece que conseguiram a atenção da empresa:  Hoje a HP respondeu em seu blog. Reconhecendo que seus consumidores são mais propensos a ver uma atualização que limita a interoperabilidade como um bug do que como um recurso, a HP diz que vai disponibilizar uma atualização de firmware opcional para reverter as mudanças feitas. Estamos muito contentes de ver HP dando esse passo.

Mas ainda existem questões em aberto.

Primeiro, gostariamos de saber o que a HP planeja informar os usuários sobre o upgrade opcional de firmware. No momento, a vasta maioria das pessoas que usam as impressoras afetadas não sabem por que suas impressoras perderam funcionalidades, muito menos sabem que é possível restaurar essas funcionalidades. Todos esses consumidores deveriam conseguir usar suas impressoras livres de restrições artificiais e não apenas os mais antenados que estão acompanhando o processo.

Segundo,  estamos pedindo para a  HP para prometer que nunca voltará a usar uma atualização de segurança para retirar características do produto. Os clientes devem ser capazes de comprar uma impressora HP sem medo de que a limites de uso artificiais serão impostos no futuro por meio de atualizações. Seria um pesadelo para os clientes  evitar a instalação de atualizações de segurança por medo de alterações de recursos indesejados e sem aviso prévio. Mesmo as pessoas que não usam impressoras Officejet deveriam temer usar impressoras sem atualizações de segurança instaladas.

Terceiro, a HP deve prometer que nunca vai usar Seção 1201 do Digital Millennium Copyright Act de processar ou ameaçar pesquisadores de segurança para contornar suas travas digitais durante seu trabalho. Nós já vimos como departamentos legais de empresas que defendem DRM tem dissuadido pesquisadores  de divulgação de vulnerabilidades. Por uma questão de segurança dos seus clientes, a HP deve comprometer-se a imunização os pesquisadores de segurança contra ameaças legais sob DMCA 1201.
Essas medidas podem ajudar a corrigir o erro cometido pela HP. Mas continuamos preocupados com a tendência de empresas que utilizam travas digitais para quebrar a funcionalidade de seus próprios produtos, e, em seguida apresentam esses bloqueios como recursos de segurança. Estes anti-características colocam em perigo a segurança da Internet ao fazer nossos produtos menos úteis. Esperamos que outras empresas aprendam com os erros da HP.

 

Diga para HP:  Não ao DRM!

Fonte: https://www.eff.org/deeplinks/2016/09/dont-hide-drm-security-update

Problema com PyEnv no OSX Sierra

Pyenv é um gerenciador de instalações do Python, permite a instalação de várias versões do interpretador em paralelo, incluindo pypy, jython stackless etc.

Após instalar o OSX Sierra tive um problema que me impedia de instalar o python 3.5.2:

zipimport.ZipImportError: can't decompress data; zlib not available

Por alguma razão, no Sierra a biblioteca zlib não vem instalado por padrão para uso no stack unix padrão, para resolver isso fiz o seguinte:

xcode-select --install

Com isso uma caixa de diálogo vai se oferecer para instalar o XCode inteiro ou apenas as ferramentas de console. Instalei as ferramentas de console e o problema foi resolvido 🙂

10 coisas que você deve fazer ao instalar o WordPress

Seguem algumas dicas do que se deve fazer ao instalar o WordPress.

1.  Altere o título, tagline, fuso horário e favicon. Essas são pequenas coisas que personalizam seu site ainda mais e ajuda no agendamento de posts.

2.  A estrutura permalink do WordPress padrão não é muito amigável para SEO . Selecione Permalink a partir do menu de Configurações. Lá você vai encontrar algumas opções diferentes para a criação de uma nova estrutura. Os melhores para escolher é Post Name que é mais útil para os visitantes.

3. Configurar as definições de leitura. Seja qual for o tema que você tem você pode optar por ter seus posts na primeira página ou ter página para mostrar os posts. Então você pode decidir quantos posts para mostrar na página aqui.

4. Instale o plugin SEO WordPress by Yoast que checa seu conteúdo antes de atualizar seu site. É fácil de usar e pode até mesmo gerar um sitemap XML  com um clique do mouse.

5. Evite temas gratuitos eles são propensos a codificação base64, e podem ser usados para inserir  spam links em seu site, ou outro código malicioso que pode causar todos os tipos de transtornos.

6. Instalar um plugin de cache comoW3 total cache ou WP super
cache
. Esses plug-ins geram arquivos HTML estáticos a partir do seu blog WordPress dinâmico que são servidos por seu servidor em vez de processar os scripts PHP.

NOTA: Leitores indicaram o uso de MEMCACHE e REDIS.

7. Para manter o seu WordPress seguro use plugins como succuri-scanner,
exploit-scanner
que busca em seu site por malware e varre seu banco de dados caçando por qualquer código suspeito.

NOTA: Leitores indicaram o Wordfence plugin.

8. Evite que comentários spam prejudiquem seu SEO instalando plugins anti-spam . Você pode usar Akismet que vem WordPress ou o
Antispam Bee.

9. Otimize seu site para compartilhamentos em redes sociais através de plug-ins. Ter um plug-in de compartilhamento social ajuda o seu site ser compartilhado com outros o que aumenta seu tráfego e visitantes.

10. Agende backups regulares que podem te salvar em situações catastróficas como site hackeado ou coisa pior. Você pode usar alguns plugins de backup como BackUpBuddy e BackupWordPress.

fonte: http://onlineshouter.com/

PostgreSQL para usuários Mysql

Eu sei Mysql, então PostgreSQL não vai ser problema certo ? Errado.

Segue uma lista de soluções que enfrentei , que sirva de guia para quem já enfrentou os mesmos desafios.

Criando o primeiro Banco de Dados

Versão MySQL:

$ mysql -u root -p
 mysql> CREATE DATABASE dbTest;
 mysql> USE dbTest;

No Linux,  você deve logar com o usuário postgres  (após instalar o postgresql ).

$ su - postgres
 postgres@x220:~$ psql # Inicia o console
 postgres=# \h # Lista os comandos SQL disponíveis.
 postgres=# \h [comando] # Mostra o uso do [comando]

Cadê o CREATE DATABASE ?  Não tem, o comando é dado fora do console, deslogue do console com Ctrl-D ou \q e digite:

postgres@x220:~$ createdb dbTest

Legal, vamos entrar no console e usar o USE dbTest… Oops, não tem USE.

postgres=# \? # Lista os comandos internos
postgres=# \c dbTest

Agora você está conectado ao banco de dados “dbTest” como usuário “postgres”.

dbTest=#
OS X

No OS X o usuário postgres não é criado, então você deve rodar o psql no terminal.

CrIANDO UM NOVO USUÁRIO

No Mysql os comandos são parecidos com esses aqui:

mysql> CREATE USER milestone IDENTIFIED BY 'milestone';
mysql> GRANT ALL ON dbTest.* TO milestone;

No postgres a sintaxe é diferente, o comando \h CREATE USER explica.

dbTest=# CREATE USER milestone WITH PASSWORD "milestone";
ERROR: syntax error at or near ""milestone""

dbTest=# CREATE USER milestone WITH PASSWORD 'milestone';
postgres=# GRANT ALL ON DATABASE dbTest TO milestone;
ERROR: database "dbtest" does not exist

Oops, deu erro !!! O problem é que dbtaste realmente não existe, o nome do banco de dados foi convertido para lowercase no comando ( e foi executado dessa forma). A solução é usar aspas duplas como no exemplo abaixo:

postgres=# GRANT ALL ON DATABASE "dbTest" TO milestone;
GRANT

Legal! Agora vamos logar com esse usuário.

postgres=# \c - milestone
FATAL: Peer authentication failed for user "milestone"

hum…. Você deve configurar o  PostgreSQL  para permitir  que o usuario milestone tenha permissão de logar via password.  No arquivo /etc/postgresql/9.<x>/main/pg_hba.conf . Mude as linhas:

local all all peer

para

local all all md5

ou a linha abaixo para permitir apenas para o usuário milestone:

local all milestone md5

Obviamente essa linha deve ser colocada antes da regra válida para todos usuário ou não vai funcionar. Mais ou menos assim:

local all milestone md5
 local all all md5

PostgreSQL deve ser reiniciado:

# service postgresql restart

ou

# /etc/init.d/postgresql restart
PERMISSões Nas TABeLAS

Infelizmente todas as tentativas de ler ou escrever nas tabelas do banco vão falhar.

A especificação define uma hierarquia fixa de catalog.schema.table . No PG “catalog” é o banco de dados.

dbTest=# \dn
List of schemas
Name | Owner
--------+----------
 public | postgres
(1 row)

Então nosso usuário milestone precisa de permissão para acessar dbTest.public.* , tanto para todas as tabelas como as sequencias. A sequencia é usada (e alterada nos inserts) para alguns tipos de dados.

dbTest=# GRANT ALL ON ALL TABLES IN SCHEMA public TO milestone;
dbTest=# GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO milestone;
AUTO_INCREMENT

Obviamente não existe. Mas existe um tipo de dados para isso.

dbTest=# CREATE TABLE foo (id serial PRIMARY KEY, bar text);

O equivalente MySQL é:

mysql> CREATE TABLE foo (id int PRIMARY KEY AUTO_INCREMENT, bar text);
SHOW TABLES, DATABASES etc.
 postgres=# \l # SHOW DATABASES
 postgres=# \dt # SHOW TABLES
 postgres=# \d myTable # DESCRIBE myTable
Arquivos de configuração e testes

Uma instância PostgreSQL possui (e mantém) sua própria área de dados. Esse diretório (também conhecido como datadir) contém arquivos de configuração e a coleção de bancos de dados. Isso permite ter multiplas instâncias do PostgreSQL aceitando conexões em multiplas portas, mas isso também significa ao iniciar o postgres você sempre deverá informar o path dessa área de dados.

postgres@x220:~$ mkdir db13 # Cria nova area de dados
postgres@x220:~$ initdb -D db13 # Inicializa area de dados
postgres@x220:~$ postgres -D db13 & # Roda o PostgreSQL em background
postgres@x220:~$ pg_ctl status -D db13 # Checa se esta rodando
postgres@x220:~$ createdb dbTest # Cria novo banco
postgres@x220:~$ psql # Conecta ao PostgreSQL rodando na porta default (5432)
postgres@x220:~$ pg_ctl stop -D db13 # Para a instancia rodando em db13
postgres@x220:~$ postgres -D db12 -p 6543 & # Roda outro servidor, mas em uma area diferente e em outra porta (6543)
postgres@x220:~$ createdb dbTest -p 6543 # Cria novo DB (mantido pela segunda instancia na porta 6543)
postgres@x220:~$ psql -p 6543 # Conecta em db12

Para encontrar em qual diretório sua instancia do postgres está rodando, você pode usar algo como ps aux | grep postgres .

Socket file errors

Erros como:  /var/run/postgresql/.s.PGSQL.5432.lock not found podem acontecer em sistemas  Debian onde o PostgreSQL não está configurado para subir na inicialização ( rcconf –off postgresql por exemplo)
Existem duas soluções para esse problema, ou você cria /var/run/postgresql :

# mkdir /var/run/postgresql 
# chmod a+w /var/run/postgresql # adiciona permissão de escrita para o diretório

Ou você muda o arquivo postgresql.conf para escrever o socket em outro diretório, como o /tmp

unix_socket_directory = '/tmp' # (reinicie o postgres)

Por outro lado, você pode indicar ao postgreSQL o novo diretório de socket direto da linha de comando:

postgres@x220:~$ psql -h /tmp # Our socket directory is now /tmp

fonte: http://granjow.net/postgresql.html

netcat

Netcat é uma das ferramentas mais legais que existe no mundo *nix, se bem usado pode salvar sua vida quando não se tem outras ferramentas de rede disponíveis. É uma versão mais sofisticada do telnet. Com ele você pode transferir arquivos, testar portas ( como o nmap ), criar um web server etc.

Truques básicos:
Conectando na porta 80 do localhost (tente com outras URLs e servidores):

$ nc localhost 80

Servidor socket na porta 5000:

nc -l 5000

Se combinados os dois exemplos anteriores vão se comportar como um Chat 🙂
Um webserver muito útil para testar portas ou até mesmo subir uma página de manutenção em um servidor.

$ while true; do; sudo nc -l 80 < index.html; done

O Loop while permite renderizar a página para mais de uma conexão, é possível servir formatos binários também.

Mais um exemplo legal, um backdoor shell 🙂 Precisa acessar um computador remoto ? Sem problemas:

Abrindo a porta 2222 com o bash rodando:

$ ncat -v -l 2222 -e /bin/bash

Para acessar sua backdor basta:

$ ncat localhost 2222

Lembrando que pode ser usado em servidores remotos, basta trocar o localhost pelo IP ou dominio.

É possível usar o netcat como portscanner, por exemplo para escanear da porta 1 até a 100:

 

$ nc -v -z localhost 1-100

Transferência de arquivos também não é um problema para netcat:

Para disponibilizar um arquivo na porta 5000:

$ nc -l 5000 < /etc/passwd

Para copiar o arquivo:

 $ nc localhost 5000 > passwd

Clonar um HD/SSD de um servidor direto no de outro também é possível:

No servidor original:

$ dd if=/dev/vda | nc -l 5000

No servidor que vai receber a cópia:

$ nc -n localhost 5000 | dd of=/dev/vda

Sempre lembrando que os comandos valem para servidores e máquinas remotas, é só trocar o localhost dos exemplos pelo domínio.

Backup remoto também não é um limitador para o netcat:

No servidor

$ nc -l 5000 | tar -xv

No cliente:

$ tar -cv /path/ | nc localhost 5000

fonte http://aarvik.dk/netcat-basics-and-a-few-smart-examples/