recuperar links de página da web usando Python e BeautifulSoup
-
22-08-2019 - |
Pergunta
Como posso recuperar os elos de uma página e copie o endereço URL dos links usando Python?
Solução
Aqui está um pequeno trecho usando a classe SoupStrainer em BeautifulSoup:
import httplib2
from BeautifulSoup import BeautifulSoup, SoupStrainer
http = httplib2.Http()
status, response = http.request('http://www.nytimes.com')
for link in BeautifulSoup(response, parse_only=SoupStrainer('a')):
if link.has_attr('href'):
print(link['href'])
A documentação BeautifulSoup é realmente muito bom, e abrange uma série de cenários típicos:
http://www.crummy.com/software/BeautifulSoup/documentation.html
Edit: Note que eu usei a classe SoupStrainer porque é um pouco mais eficiente (memória e velocidade sábio), se você sabe o que está a análise de antecedência
.Outras dicas
Para sermos mais completos, a versão BeautifulSoup 4, fazendo uso da codificação fornecidos pelo servidor assim:
from bs4 import BeautifulSoup
import urllib2
resp = urllib2.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, from_encoding=resp.info().getparam('charset'))
for link in soup.find_all('a', href=True):
print link['href']
ou a versão Python 3:
from bs4 import BeautifulSoup
import urllib.request
resp = urllib.request.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, from_encoding=resp.info().get_param('charset'))
for link in soup.find_all('a', href=True):
print(link['href'])
e uma versão usando o biblioteca requests
, que como escrito irá funcionar tanto em Python 2 e 3:
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
import requests
resp = requests.get("http://www.gpsbasecamp.com/national-parks")
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
encoding = html_encoding or http_encoding
soup = BeautifulSoup(resp.content, from_encoding=encoding)
for link in soup.find_all('a', href=True):
print(link['href'])
A chamada soup.find_all('a', href=True)
encontra todos os elementos <a>
que têm um atributo href
; elementos sem o atributo são ignoradas.
BeautifulSoup 3 parou o desenvolvimento em março de 2012; novos projetos realmente deve usar BeautifulSoup 4, sempre.
Note que você deve deixar decodificar o código HTML bytes para BeautifulSoup . Você pode informar BeautifulSoup do characterset encontrado nos cabeçalhos de resposta HTTP para auxiliar na decodificação, mas esta pode estar errado e em conflito com a informações do cabeçalho <meta>
encontrada no próprio HTML, razão pela qual os usos acima dos BeautifulSoup EncodingDetector.find_declared_encoding()
método de classe interna para certificar-se de que tais sugestões que codificam incorporados conquistar um servidor mal configurado.
Com requests
, os padrões de atributos response.encoding
para Latin-1 se a resposta tem um mimetype text/*
, mesmo se nenhum characterset foi devolvido. Isto é consistente com o HTTP RFC, mas doloroso quando utilizado com HTML análise, para que você deve ignorar esse atributo quando nenhum charset
está definido no cabeçalho Content-Type.
Outros têm recomendado BeautifulSoup, mas é muito melhor usar lxml . Apesar do nome, é também para analisar e raspagem HTML. É muito, muito mais rápido do que BeautifulSoup, e ainda lida com "quebrado" HTML melhor do que BeautifulSoup (seu pedido de fama). Ele tem uma API de compatibilidade para BeautifulSoup também se você não quer aprender a API lxml.
Não há nenhuma razão para usar BeautifulSoup mais, a menos que você está no Google App Engine ou algo onde tudo não puramente Python não é permitido.
lxml.html também suporta seletores CSS3 assim que este tipo de coisa é trivial.
Um exemplo com lxml e xpath ficaria assim:
import urllib
import lxml.html
connection = urllib.urlopen('http://www.nytimes.com')
dom = lxml.html.fromstring(connection.read())
for link in dom.xpath('//a/@href'): # select the url in href for all a tags(links)
print link
import urllib2
import BeautifulSoup
request = urllib2.Request("http://www.gpsbasecamp.com/national-parks")
response = urllib2.urlopen(request)
soup = BeautifulSoup.BeautifulSoup(response)
for a in soup.findAll('a'):
if 'national-park' in a['href']:
print 'found a url with national-park in the link'
O código a seguir é recuperar todos os links disponíveis em uma página web usando urllib2
e BeautifulSoup4
:
import urllib2
from bs4 import BeautifulSoup
url = urllib2.urlopen("http://www.espncricinfo.com/").read()
soup = BeautifulSoup(url)
for line in soup.find_all('a'):
print(line.get('href'))
Sob o capô BeautifulSoup agora usa lxml. Os pedidos, lxml & compreensões lista faz uma combinação assassino.
import requests
import lxml.html
dom = lxml.html.fromstring(requests.get('http://www.nytimes.com').content)
[x for x in dom.xpath('//a/@href') if '//' in x and 'nytimes.com' not in x]
Na lista comp, o "se '//' e 'url.com' não em x" é um método simples para esfregar a lista url de urls de navegação os sites de 'internos', etc.
Para encontrar todos os links, vamos neste exemplo o uso do módulo urllib2 juntos com o re.module * Uma das funções mais poderoso do módulo é re "re.findall ()". Enquanto re.search () é usado para localizar a primeira correspondência de um padrão, re.findall () localiza todas os jogos e as retorna como uma lista de strings, com cada seqüência que representa uma partida *
import urllib2
import re
#connect to a URL
website = urllib2.urlopen(url)
#read html code
html = website.read()
#use re.findall to get all the links
links = re.findall('"((http|ftp)s?://.*?)"', html)
print links
apenas para obter os links, sem B.soup e regex:
import urllib2
url="http://www.somewhere.com"
page=urllib2.urlopen(url)
data=page.read().split("</a>")
tag="<a href=\""
endtag="\">"
for item in data:
if "<a href" in item:
try:
ind = item.index(tag)
item=item[ind+len(tag):]
end=item.index(endtag)
except: pass
else:
print item[:end]
para operações mais complexas, é claro BSoup é ainda o preferido.
Por que não usar expressões regulares:
import urllib2
import re
url = "http://www.somewhere.com"
page = urllib2.urlopen(url)
page = page.read()
links = re.findall(r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page)
for link in links:
print('href: %s, HTML text: %s' % (link[0], link[1]))
Este script faz o que você está procurando, mas também resolve os links relativos a links absolutos.
import urllib
import lxml.html
import urlparse
def get_dom(url):
connection = urllib.urlopen(url)
return lxml.html.fromstring(connection.read())
def get_links(url):
return resolve_links((link for link in get_dom(url).xpath('//a/@href')))
def guess_root(links):
for link in links:
if link.startswith('http'):
parsed_link = urlparse.urlparse(link)
scheme = parsed_link.scheme + '://'
netloc = parsed_link.netloc
return scheme + netloc
def resolve_links(links):
root = guess_root(links)
for link in links:
if not link.startswith('http'):
link = urlparse.urljoin(root, link)
yield link
for link in get_links('http://www.google.com'):
print link
Os links podem estar dentro de uma variedade de atributos para que você possa passar uma lista desses atributos para selecionar
Por exemplo, com src e href atributo (aqui eu estou usando os começos com ^ operador para especificar que qualquer um destes atributos valores começa com http. Você pode adaptar isso como necessário
from bs4 import BeautifulSoup as bs
import requests
r = requests.get('https://stackoverflow.com/')
soup = bs(r.content, 'lxml')
links = [item['href'] if item.get('href') is not None else item['src'] for item in soup.select('[href^="http"], [src^="http"]') ]
print(links)
[attr ^ = valor]
elementos representa com um nome de atributo de attr cujo valor é prefixado (precedida) por valor.
Aqui está um exemplo usando @ars aceito resposta e os BeautifulSoup4
, requests
e módulos wget
para lidar com os downloads.
import requests
import wget
import os
from bs4 import BeautifulSoup, SoupStrainer
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/eeg-mld/eeg_full/'
file_type = '.tar.gz'
response = requests.get(url)
for link in BeautifulSoup(response.content, 'html.parser', parse_only=SoupStrainer('a')):
if link.has_attr('href'):
if file_type in link['href']:
full_path = url + link['href']
wget.download(full_path)
Eu encontrei a resposta por @ Blairg23 de trabalho, após a seguinte correção (que abrange o cenário em que não funcionou corretamente):
for link in BeautifulSoup(response.content, 'html.parser', parse_only=SoupStrainer('a')):
if link.has_attr('href'):
if file_type in link['href']:
full_path =urlparse.urljoin(url , link['href']) #module urlparse need to be imported
wget.download(full_path)
Para Python 3:
urllib.parse.urljoin
tem que ser usado para obter a URL completa em seu lugar.
próprio analisador de BeatifulSoup pode ser lento. Pode ser mais viável para uso lxml que é capaz de analisar diretamente a partir de uma URL ( com algumas limitações mencionado abaixo).
import lxml.html
doc = lxml.html.parse(url)
links = doc.xpath('//a[@href]')
for link in links:
print link.attrib['href']
O código acima irá retornar as ligações como é, e na maioria dos casos eles seriam ligações relativo ou absoluto da raiz do site. Desde o meu caso de uso foi apenas para extrair um certo tipo de links, abaixo é uma versão que converte os links para URLs completos e que opcionalmente aceita um padrão glob como *.mp3
. Não vai lidar com pontos simples e duplas nos caminhos relativos, porém, mas até agora eu não tinha necessidade disso. Se você precisa de fragmentos de URL de análise contendo ../
ou ./
então urlparse.urljoin poder vir a calhar.
NOTA :. Lxml direto análise de URL não manipula o carregamento de https
e não faz redirecionamentos, por este motivo a versão abaixo está usando urllib2
+ lxml
#!/usr/bin/env python
import sys
import urllib2
import urlparse
import lxml.html
import fnmatch
try:
import urltools as urltools
except ImportError:
sys.stderr.write('To normalize URLs run: `pip install urltools --user`')
urltools = None
def get_host(url):
p = urlparse.urlparse(url)
return "{}://{}".format(p.scheme, p.netloc)
if __name__ == '__main__':
url = sys.argv[1]
host = get_host(url)
glob_patt = len(sys.argv) > 2 and sys.argv[2] or '*'
doc = lxml.html.parse(urllib2.urlopen(url))
links = doc.xpath('//a[@href]')
for link in links:
href = link.attrib['href']
if fnmatch.fnmatch(href, glob_patt):
if not href.startswith(('http://', 'https://' 'ftp://')):
if href.startswith('/'):
href = host + href
else:
parent_url = url.rsplit('/', 1)[0]
href = urlparse.urljoin(parent_url, href)
if urltools:
href = urltools.normalize(href)
print href
O uso é a seguinte:
getlinks.py http://stackoverflow.com/a/37758066/191246
getlinks.py http://stackoverflow.com/a/37758066/191246 "*users*"
getlinks.py http://fakedomain.mu/somepage.html "*.mp3"
import urllib2
from bs4 import BeautifulSoup
a=urllib2.urlopen('http://dir.yahoo.com')
code=a.read()
soup=BeautifulSoup(code)
links=soup.findAll("a")
#To get href part alone
print links[0].attrs['href']