Para coletar dados de páginas da Web, a biblioteca mechanize automatiza a
análise e a interação com Web sites. O mechanize permite preencher
formulários e configurar e salvar cookies e oferece outras ferramentas
variadas para fazer um script Python se parecer com um navegador da Web
genuíno para um Web site interativo. Uma ferramenta associada
frequentemente usada chamada Beautiful Soup ajuda um programa Python a
entender o confuso “quase HTML” que Web sites tendem a conter.
Escrever scripts para interagir com Web sites é possível com os módulos básicos do Python, mas prefira não fazer isso se não for preciso. Os módulos urllib e urllib2 no Python 2.x, junto com os subpacotes urllib.*
em Python 3.0, fazem uma tarefa transitável de busca de recursos nas
extremidades de URLs. Porém, quando você deseja fazer qualquer tipo de
interação moderadamente sofisticada com o conteúdo que localiza em uma
página da Web, você realmente precisa da biblioteca mechanize.
Uma das grandes dificuldades com a automação de análise
da Web ou outras simulações de interação do usuário com Web sites é o
uso do servidor de cookies para controlar o progresso da sessão.
Obviamente, os cookies fazem parte de cabeçalhos HTTP e são
inerentemente visíveis quando urllib abre recursos.
Além disso, os módulos padrão Cookie (http.cookie
em Python 3) e cookielib
(http.cookiejar em Python 3) ajudam na
manipulação desses cabeçalhos em um nível mais alto do que o
processamento de texto bruto. Mesmo assim, fazer esta manipulação neste
nível é mais incômodo do que necessário. A biblioteca mechanize
considera esta manipulação em um nível mais alto de abstração e permite
que o seu scriptou seu shell Python interativoaja de maneira muito
parecida com um navegador da Web real.
O mechanize do Python é inspirado pelo WWW:Mechanize
do Perl, que tem uma gama de recursos parecida. É claro que, como um
Pythonista de longa data, eu acho o mechanize mais robusto, o que parece
seguir o padrão geral das duas linguagens.
Um amigo íntimo do mechanize é a igualmente excelente biblioteca Beautiful Soup. Ela é um ótimo “parser medíocre” para
códigos HTML não estritamente válidos que você frequentemente encontra
em páginas da Web reais. Você não precisa usar o Beautiful Soup
com o mechanize, nem vice-versa, mas, mais frequentemente do que o
contrário, você desejará usar as duas ferramentas juntas à medida que
interagir com a “Web existente de verdade”.
Um exemplo da vida real
Eu uso o mechanize em vários projetos de programação. O mais recente foi
um projeto para reunir uma lista de nomes correspondentes a alguns
critérios de um Web site popular. Este site vem com alguns recursos de
busca, mas não vem com nenhuma API oficial para executar tais buscas.
Ainda que os leitores possam conseguir imaginar mais especificamente o
que eu estava fazendo, eu mudarei aspectos específicos do código que vou
apresentar para evitar fornecer informações em excesso sobre o site
analisado ou sobre meu cliente. De forma geral, código muito parecido
com o que eu apresento será comum para tarefas similares.
Ferramentas para iniciar
No processo de desenvolvimento real de código de análise da Web, eu
acho inestimável poder efetuar peek, poke e prod no conteúdo das
páginas da Web de uma maneira interativa para descobrir o que realmente
ocorre em páginas da Web relacionadas. Geralmente, esses são conjuntos
de páginas dentro de um site, que são gerados dinamicamente a partir de
consultas (mas, por isso, tendo padrões consistentes) ou são pré-gerados
seguindo modelos razoavelmente rígidos.
Uma maneira valioso de fazer esta experimentação
interativa é usar o próprio mechanize dentro de um shell Python,
especificamente dentro de um shell aprimorado como o IPython (consulte Recursos
ao final do artigo para obter um link). Ao fazer a exploração desta maneira, é possível
solicitar vários recursos vinculados, enviar formulários, manter ou
manipular cookies de site e assim por diante, antes de gravar seu script
final que executa a interação que você deseja em produção.
Porém, eu acredito que grande parte da minha interação
experimental com Web sites é mais bem executada dentro de um real
navegador da Web moderno. Visualizar uma página convenientemente
renderizada fornece um gestalt muito mais rápido de o que está
acontecendo com uma determinada página ou formulário.
O problema é que
renderizar uma página sozinha somente fornece metade da história, talvez
menos que a metade. Ter a “origem de página” o leva um pouco mais
adiante. Para realmente entender o que está por trás de uma determinada
página da Web ou de uma sequência de interações com um servidor da Web,
acho que é necessário mais.
Para chegar tão fundo, geralmente eu uso o Firebug ou plug-ins Web Developer para Firefox (ou o menu integrado opcional Develop
nas versões recentes do Safari, mas isso é para um público diferente).
Todas essas ferramentas permitem fazer coisas como revelar campos de
formulário, mostrar senhas, examinar o DOM de uma página, efetuar peek
em Javascript ou executar Javascript, observar tráfego Ajax e mais.
Comparar os benefícios e peculiaridades dessas ferramentas é um outro
artigo completo, mas familiarize-se com eles se você faz qualquer
programação orientada para Web.
Seja qual for a ferramenta específica que você
experimente com um Web site com o qual deseja automatizar a interação,
provavelmente você gastará mais horas descobrindo o que, de fato, um
site está fazendo do que escrevendo o código mechanize incrivelmente
compacto necessário para executar sua tarefa.
A análise de resultado da procura
Para os propósitos do projeto que eu mencionei acima, eu dividi meu script de centenas de linhas em duas funções:
- Recuperar todos os resultados que me interessam
- Extrair as informações que me interessam a partir dessas páginas recuperadas
Eu organizei o script desta maneira como uma conveniência
de desenvolvimento; quando eu iniciei a tarefa, sabia que precisaria
descobrir como fazer cada uma dessas duas coisas. Eu tinha noção de que
as informações que eu queria estavam em uma coleta geral de páginas, mas
eu ainda não havia examinado o layout específico dessas páginas.
Ao recuperar primeiro um lote de páginas e somente
salvá-las em disco, eu poderia voltar à tarefa de extrair as informações
do meu interesse desses arquivos salvos. É claro que, se a sua tarefa
envolver o uso dessas informações recuperadas para formular novas
interações dentro da mesma sessão, você precisará usar uma sequência um
pouco diferente de etapas de desenvolvimento.
Desta forma, primeiro, vamos dar uma olhada em minha função fetch():
Lista 1. Buscando o conteúdo da página
import sys, time, os from mechanize
import Browser
LOGIN_URL = 'http://www.example.com/login'
USERNAME = 'DavidMertz'
PASSWORD = 'TheSpanishInquisition'
SEARCH_URL = 'http://www.example.com/search?'
FIXED_QUERY = 'food=spam&' 'utensil=spork&' 'date=the_future&'
VARIABLE_QUERY = ['actor=%s' % actor for actor in
('Graham Chapman',
'John Cleese',
'Terry Gilliam',
'Eric Idle',
'Terry Jones',
'Michael Palin')]
def fetch():
result_no = 0 # Number the output files
br = Browser() # Create a browser
br.open(LOGIN_URL) # Open the login page
br.select_form(name="login") # Find the login form
br['username'] = USERNAME # Set the form values
br['password'] = PASSWORD
resp = br.submit() # Submit the form
# Automatic redirect sometimes fails, follow manually when needed
if 'Redirecting' in br.title():
resp = br.follow_link(text_regex='click here')
# Loop through the searches, keeping fixed query parameters
for actor in in VARIABLE_QUERY:
# I like to watch what's happening in the console
print >> sys.stderr, '***', actor
# Lets do the actual query now
br.open(SEARCH_URL + FIXED_QUERY + actor)
# The query actually gives us links to the content pages we like,
# but there are some other links on the page that we ignore
nice_links = [l for l in br.links()
if 'good_path' in l.url
and 'credential' in l.url]
if not nice_links: # Maybe the relevant results are empty
break
for link in nice_links:
try:
response = br.follow_link(link)
# More console reporting on title of followed link page
print >> sys.stderr, br.title()
# Increment output filenames, open and write the file
result_no += 1
out = open(result_%04d' % result_no, 'w')
print >> out, response.read()
out.close()
# Nothing ever goes perfectly, ignore if we do not get page
except mechanize._response.httperror_seek_wrapper:
print >> sys.stderr, "Response error (probably 404)"
# Let's not hammer the site too much between fetches
time.sleep(1)
Tendo feito minha exploração interativa deste site de interesse, eu
descubro que as consultas que eu desejo executar possuem alguns
elementos fixos e alguns elementos variáveis. Eu simplesmente as
concateno juntas em um grande pedido GET e dou uma olhada
na página “resultados”. Em troca, essa lista de resultados contém links
para os recursos que eu realmente desejo. Assim, eu os sigo (com alguns
blocos try/except lançados para o caso de
alguma coisa não funcionar ao longo do caminho) e salvo qualquer coisa
que localizar nessas páginas de conteúdo.
Bem simples, não? O mechanize pode fazer mais que isso, mas este pequeno exemplo mostra uma ampla gama de seus recursos.
Processando os resultados
Neste ponto, terminamos com o mechanize; tudo o que falta é compreender esse monte de arquivos HTML que salvamos durante o loop
fetch() . A natureza do processo em lote nos permite separá-los de forma limpa, mas, obviamente, em um programa diferente, fetch() e process() pode interagir mais intimamente.
O Beautiful Soup torna o pós-processamento ainda mais fácil que a busca inicial.
Para esta tarefa em lote, desejamos produzir dados
tabulares de valores separados por vírgula (CSV) a partir de algumas
pequenas coisas que encontramos nessas várias páginas da Web que
buscamos.
Lista 2. Criando dados ordenados a partir de um conjunto de dados randômicos com a Beautiful Soup
from glob import glob
from BeautifulSoup import BeautifulSoup
def process():
print "!MOVIE,DIRECTOR,KEY_GRIP,THE_MOOSE"
for fname in glob('result_*'):
# Put that sloppy HTML into the soup
soup = BeautifulSoup(open(fname))
# Try to find the fields we want, but default to unknown values
try:
movie = soup.findAll('span', {'class':'movie_title'})[1].contents[0]
except IndexError:
fname = "UNKNOWN"
try:
director = soup.findAll('div', {'class':'director'})[1].contents[0]
except IndexError:
lname = "UNKNOWN"
try:
# Maybe multiple grips listed, key one should be in there
grips = soup.findAll('p', {'id':'grip'})[0]
grips = " ".join(grips.split()) # Normalize extra spaces
except IndexError:
title = "UNKNOWN"
try:
# Hide some stuff in the HTML <meta> tags
moose = soup.findAll('meta', {'name':'shibboleth'})[0]['content']
except IndexError:
moose = "UNKNOWN"
print '"%s","%s","%s","%s"' % (movie, director, grips, moose)
O código aqui em process() é uma primeira visualização
impressionista do Beautiful Soup. Os leitores devem ler sua documentação
para saber mais sobre detalhes do módulo, mas a sensação geral é bem
representada neste fragmento. A maioria do código soup consiste em
algumas chamadas .findAll() em uma página que pode ser apenas HTML aproximadamente bem formatado. Lançados aqui, estão alguns atributos .parent, nextSibling e
previousSibling parecidos com DOM. Eles são semelhantes ao modo “quirks” de navegadores da Web. O que localizamos no soup não é bem uma árvore de análise; se parece mais com um saco cheio de vegetais que podem entrar na sopa (para forçar uma metáfora).
Conclusão
Pessoas antiquadas como eu, e mesmo alguns leitores mais jovens, se
lembrarão do grande deleite do script com TCL Expect (ou com seus
semelhantes escritos em Python e muitas outras linguagens). Automatizar a
interação com shells, incluindo os remotos como telnet, ftp, ssh e
parecidos, é relativamente direto desde que tudo seja exibido na
sessão. A interação da Web é um pouco mais sutil quanto às informações
que são divididas entre cabeçalhos e corpos, e vários recursos
dependentes são frequentemente compactados juntos com links href , quadros, Ajax, e assim por diante. Em princípio, porém, você poderia somente usar uma ferramenta como wget
para recuperar cada byte que um servidor da Web poderia fornecer e,
então, executar o mesmo estilo dos scripts Expect como com outros
protocolos de conexão.
Na prática, poucos programadores são tão comprometidos com abordagens antigas como a minha abordagem sugerida de wget
+ Expect.
O mechanize ainda tem muito da mesma sensação familiar e confortante
daqueles bons scripts Expect e é tão fácil de escrever quanto eles, se
não for mais fácil. Os comando do objeto Browser()
como .select_form(),
.submit() e .follow_link()
são, realmente, a maneira mais simples e mais óbvia de dizer “procure
por isso e envie aquilo” enquanto compacta toda a beleza do estado
sofisticado e da manipulação de sessão que gostaríamos em uma estrutura
de automação da Web.
Recursos
Aprender
-
“Criar um Web spider no Linux” discute os Web spiders e
analisadores e mostra como construir vários scrapers simples usando o
Ruby. -
“Debug and tune applications on the fly with Firebug” mostra como usar o Firebug para ir muito
além da visualização da origem da página para aplicativos da Web e do
Ajax. -
“Using Net-SNMP and IPython” detalha como o IPython e o Net-SNMP
podem se combinar para fornecer gerenciamento de rede interativo baseado
em Python. - Na zona Linux do developerWorks, encontre mais recursos para desenvolvedores Linux e confira nossos mais populares artigos e tutoriais.
-
Consulte todas as
dicas de Linux e
os tutoriais do Linux no developerWorks. - Mantenha-se atualizado com os eventos técnicos e Webcasts do developerWorks.
Obter produtos e tecnologias
- Faça o download do mechanize e de sua documentação.
- Faça o download do Beautiful Soup e de sua documentação.
-
IPython é
uma versão maravilhosamente aprimorada do shell interativo nativo do
Python que pode fazer coisas bem singulares como auxiliar em cálculos de
paralelização; eu o uso principalmente por seus auxílios de
interatividade como colorização de código, rechamada de linha de
comandos melhorada, conclusão de guia, recursos de macro e ajuda
interativa melhorada. -
É possível instalar o Firebugque
oferece uma abundância de ferramentas de edição, depuração e
monitoramento de desenvolvimento na Web ao alcance de suas mãos enquanto
você navega diretamente do menu Tools/Add-ons do Firefox 3.0+. É
possível incluir a Web Developer extensionque inclui um menu e uma barra de ferramentas no navegador com várias ferramentas de desenvolvedor da Web da mesma forma. - Com o software de avaliação IBM, disponível para download diretamente do developerWorks, construa seu próximo projeto de desenvolvimento no Linux.
Discutir
- Participar do fórum de discussão.
-
Participe da
comunidade do My developerWorks; com seu perfil pessoal e a página
inicial customizada, é possível padronizar o developerWorks para seus
interesses e interagir com outros usuários do developerWorks.
artigo publicado originalmente no developerWorks Brasil, por David Mertz
Para David Mertz, o mundo todo é um teste; ele devota sua carreira a
fornecer instruções de testes marginais. Para saber mais sobre sua
carreira, consulte sua página pessoal na Web. Ele escreve as colunas Charming Python e XML Matters do developerWorks desde 2000. Consulte seu livro, Text Processing in Python. É possível entrar em contato com David em mertz@gnosis.cx.