Respostas:
Aqui está um pequeno trecho usando a classe SoupStrainer em BeautifulSoup:
import httplib2
from bs4 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 do BeautifulSoup é realmente muito boa e abrange vários cenários típicos:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Edit: Note que eu usei a classe SoupStrainer porque é um pouco mais eficiente (memória e velocidade), se você souber o que está analisando com antecedência.
/usr/local/lib/python2.7/site-packages/bs4/__init__.py:128: UserWarning: The "parseOnlyThese" argument to the BeautifulSoup constructor has been renamed to "parse_only."
has_attr
. Em vez disso, vejo que há algo chamado has_key
e funciona.
Para completar, a versão BeautifulSoup 4, usando também a codificação fornecida pelo servidor:
from bs4 import BeautifulSoup
import urllib.request
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib.request.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().get_param('charset'))
for link in soup.find_all('a', href=True):
print(link['href'])
ou a versão do Python 2:
from bs4 import BeautifulSoup
import urllib2
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib2.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().getparam('charset'))
for link in soup.find_all('a', href=True):
print link['href']
e uma versão usando a requests
biblioteca , que, conforme escrita, funcionará no Python 2 e 3:
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
import requests
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
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, parser, from_encoding=encoding)
for link in soup.find_all('a', href=True):
print(link['href'])
A soup.find_all('a', href=True)
chamada localiza todos os <a>
elementos que possuem um href
atributo; elementos sem o atributo são ignorados.
O BeautifulSoup 3 parou o desenvolvimento em março de 2012; novos projetos realmente devem usar o BeautifulSoup 4, sempre.
Observe que você deve deixar a decodificação do HTML dos bytes para BeautifulSoup . Você pode informar o BeautifulSoup do conjunto de caracteres encontrado nos cabeçalhos de resposta HTTP para ajudar na decodificação, mas isso pode estar errado e entrar em conflito com as <meta>
informações do cabeçalho encontradas no próprio HTML, e é por isso que o acima usa o método da classe interna BeautifulSoup EncodingDetector.find_declared_encoding()
para garantir que essas dicas de codificação incorporadas conquistam um servidor mal configurado.
Com requests
, o response.encoding
atributo padrão é Latin-1 se a resposta tiver um text/*
tipo de mimet, mesmo que nenhum conjunto de caracteres tenha sido retornado. Isso é consistente com os RFCs HTTP, mas é doloroso quando usado com análise de HTML, portanto, você deve ignorar esse atributo quando nenhum charset
estiver definido no cabeçalho Tipo de Conteúdo.
SoupStrainer
você quer dizer? Não foi a lugar nenhum, ainda faz parte do projeto .
Outros recomendaram o BeautifulSoup, mas é muito melhor usar o lxml . Apesar do nome, é também para analisar e copiar HTML. É muito, muito mais rápido que o BeautifulSoup, e até lida com HTML "quebrado" melhor que o BeautifulSoup (sua reivindicação à fama). Também possui uma API de compatibilidade para BeautifulSoup, se você não quiser aprender a API lxml.
Não há mais motivo para usar o BeautifulSoup, a menos que você esteja no Google App Engine ou algo em que algo que não seja puramente Python não seja permitido.
O lxml.html também suporta seletores CSS3, portanto esse 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
lxml
como o analisador padrão, se instalado.
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 da 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ô, o BeautifulSoup agora usa lxml. Pedidos, lxml e compreensão de lista fazem uma combinação incrível.
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 estiver em x" é um método simples para limpar a lista de URLs dos URLs de navegação 'internos' dos sites, etc.
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 que o BSoup ainda é o preferido.
<a
e href
? Diga rel="nofollow"
ou onclick="..."
mesmo apenas uma nova linha? stackoverflow.com/questions/1732348/…
Esse script faz o que você procura, mas também resolve os links relativos aos 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
Para encontrar todos os links, neste exemplo, usaremos o módulo urllib2 junto com o re.module * Uma das funções mais poderosas no módulo re é "re.findall ()". Enquanto re.search () é usado para encontrar a primeira correspondência para um padrão, re.findall () localiza todas as correspondências e as retorna como uma lista de cadeias, com cada cadeia representando uma correspondência *
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
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]))
(r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page)
significa? obrigado!
Os links podem estar em uma variedade de atributos para que você possa passar uma lista desses atributos para selecionar
por exemplo, com o atributo src e href (aqui estou usando o operador begin com ^ para especificar que um desses valores de atributos começa com http. Você pode adaptá-lo conforme 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]
Representa elementos com um nome de atributo de attr cujo valor é prefixado (precedido) por valor.
Aqui está um exemplo usando @ars resposta aceita e os BeautifulSoup4
, requests
e wget
módulos 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)
Encontrei a resposta do @ Blairg23 funcionando, após a seguinte correção (cobrindo 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
deve ser usado para obter o URL completo.
O analisador do BeatifulSoup pode ser lento. Pode ser mais viável usar o lxml, que é capaz de analisar diretamente de uma URL (com algumas limitações mencionadas 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 retornará os links como estão e, na maioria dos casos, seriam links relativos ou absolutos da raiz do site. Como meu caso de uso era apenas para extrair um certo tipo de links, abaixo está uma versão que converte os links em URLs completos e que opcionalmente aceita um padrão global como *.mp3
. Porém, ele não suporta pontos únicos e duplos nos caminhos relativos, mas até agora eu não precisava disso. Se você precisar analisar fragmentos de URL que contenham ../
ou ./
então urlparse.urljoin, pode ser útil.
OBSERVAÇÃO : A análise direta de URL lxml não processa o carregamento https
e não redireciona, portanto, por esse 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 é o 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"
lxml
só pode lidar com entrada válida, como ele pode substituir BeautifulSoup
?
lxml.html
é um pouco mais branda do que o lxml.etree
. Se sua entrada não for bem formada, você poderá definir explicitamente o analisador BeautifulSoup: lxml.de/elementsoup.html . E se você optar pelo BeatifulSoup, o BS3 é uma escolha melhor.
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']
Pode haver muitos links duplicados junto com links externos e internos. Para diferenciar os dois e obter links exclusivos usando conjuntos:
# Python 3.
import urllib
from bs4 import BeautifulSoup
url = "http://www.espncricinfo.com/"
resp = urllib.request.urlopen(url)
# Get server encoding per recommendation of Martijn Pieters.
soup = BeautifulSoup(resp, from_encoding=resp.info().get_param('charset'))
external_links = set()
internal_links = set()
for line in soup.find_all('a'):
link = line.get('href')
if not link:
continue
if link.startswith('http'):
external_links.add(link)
else:
internal_links.add(link)
# Depending on usage, full internal links may be preferred.
full_internal_links = {
urllib.parse.urljoin(url, internal_link)
for internal_link in internal_links
}
# Print all unique external and full internal links.
for link in external_links.union(full_internal_links):
print(link)