recuperar enlaces de la página web usando python y BeautifulSoup

¿Cómo puedo recuperar los enlaces de una página web y copiar la dirección url de los enlaces usando Python?

Aquí hay un breve fragmento de código que utiliza la clase SoupStrainer en 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']) 

La documentación de BeautifulSoup es bastante buena y cubre varios escenarios típicos:

http://www.crummy.com/software/BeautifulSoup/documentation.html

Edición: Tenga en cuenta que utilicé la clase SoupStrainer porque es un poco más eficiente (memoria y velocidad), si sabe de antemano lo que está analizando.

Para completar, la versión de BeautifulSoup 4, que también utiliza la encoding suministrada por el servidor:

 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'] 

o la versión de 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']) 

y una versión que utiliza la biblioteca de requests , que, como está escrito, funcionará tanto en Python 2 como en 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']) 

La soup.find_all('a', href=True) encuentra todos los elementos que tienen un atributo href ; Se omiten los elementos sin el atributo.

BeautifulSoup 3 dejó de desarrollarse en marzo de 2012; Los nuevos proyectos realmente deberían usar BeautifulSoup 4, siempre.

Tenga en cuenta que debe dejar la deencoding del HTML de bytes a BeautifulSoup . Puede informar a BeautifulSoup del conjunto de caracteres que se encuentra en los encabezados de respuesta HTTP para ayudar en la deencoding, pero esto puede ser incorrecto y en conflicto con la información del encabezado encuentra en el propio HTML, por lo que lo anterior utiliza el método de clase interno BeautifulSoup EncodingDetector.find_declared_encoding() para asegurarse de que tales sugerencias de encoding incrustada ganen a un servidor mal configurado.

Con las requests , el atributo response.encoding predeterminada en Latin-1 si la respuesta tiene un tipo de text/* mimetype, incluso si no se devolvió ningún conjunto de caracteres. Esto es consistente con las RFC de HTTP, pero es doloroso cuando se usa con el análisis HTML, por lo que debe ignorar ese atributo cuando no se establece un conjunto de charset en el encabezado Content-Type.

Otros han recomendado BeautifulSoup, pero es mucho mejor usar lxml . A pesar de su nombre, también es para analizar y raspar HTML. Es mucho, mucho más rápido que BeautifulSoup, e incluso maneja el HTML “roto” mejor que BeautifulSoup (su afirmación de fama). También tiene una API de compatibilidad para BeautifulSoup si no quieres aprender la API lxml.

Ian Blicking está de acuerdo .

Ya no hay razón para usar BeautifulSoup, a menos que estés en el motor de aplicaciones de Google o algo donde no se permite nada que no sea puramente Python.

lxml.html también admite los selectores de CSS3, por lo que este tipo de cosas es trivial.

Un ejemplo con lxml y xpath se vería así:

 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' 

Bajo el capó, BeautifulSoup ahora usa lxml. Solicitudes, lxml y listas de comprensión hacen un combo asesino.

 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] 

En la lista de comp, el “if ‘//’ y ‘url.com’ no en x” es un método simple para limpiar la lista de url de las urls de navegación ‘internas’ de los sitios, etc.

El siguiente código es para recuperar todos los enlaces disponibles en una página web usando urllib2 y 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')) 

Para encontrar todos los enlaces, en este ejemplo usaremos el módulo urllib2 junto con el módulo de re * Una de las funciones más poderosas en el módulo re es “re.findall ()”. Mientras re.search () se usa para encontrar la primera coincidencia para un patrón, re.findall () encuentra todas las coincidencias y las devuelve como una lista de cadenas, donde cada cadena representa una coincidencia *

 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 

Solo por obtener los enlaces, sin B.soup y regex:

 import urllib2 url="http://www.somewhere.com" page=urllib2.urlopen(url) data=page.read().split("") tag="" for item in data: if " 

para operaciones más complejas, por supuesto, todavía se prefiere BSoup.

¿Por qué no usar expresiones regulares?

 import urllib2 import re url = "http://www.somewhere.com" page = urllib2.urlopen(url) page = page.read() links = re.findall(r"(.*?)", page) for link in links: print('href: %s, HTML text: %s' % (link[0], link[1])) 

Este script hace lo que está buscando, pero también resuelve los enlaces relativos a los enlaces 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 

Aquí hay un ejemplo que utiliza la respuesta aceptada de @ars y los módulos BeautifulSoup4 , requests y wget para manejar las descargas.

 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) 

Encontré que la respuesta de @ Blairg23 funcionaba, después de la siguiente corrección (que cubre el escenario donde no funcionó correctamente):

 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 debe utilizarse para obtener la URL completa.

El propio analizador de BeatifulSoup puede ser lento. Podría ser más factible usar lxml que sea capaz de analizar directamente desde una URL (con algunas limitaciones mencionadas a continuación).

 import lxml.html doc = lxml.html.parse(url) links = doc.xpath('//a[@href]') for link in links: print link.attrib['href'] 

El código anterior devolverá los enlaces tal como están, y en la mayoría de los casos serían enlaces relativos o absolutos de la raíz del sitio. Como mi caso de uso fue solo para extraer un cierto tipo de enlaces, a continuación se muestra una versión que convierte los enlaces en direcciones URL completas y que opcionalmente acepta un patrón global como *.mp3 . Sin embargo, no manejará puntos simples y dobles en las rutas relativas, pero hasta ahora no tenía la necesidad de hacerlo. Si necesita analizar los fragmentos de URL que contienen ../ o ./ , urlparse.urljoin puede ser útil.

NOTA : El análisis directo de URL de lxml no admite la carga desde https y no realiza redirecciones, por lo que, por este motivo, la versión que aparece a continuación utiliza 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 

El uso es el siguiente:

 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'] 

Los enlaces pueden estar dentro de una variedad de atributos para que pueda pasar una lista de esos atributos para seleccionar

por ejemplo, con los atributos src y href (aquí estoy usando el operador comience con ^ para especificar que cualquiera de los valores de estos atributos comience con http. Puede personalizar esto según sea necesario

 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) 

Atributo = selectores de valor

[attr ^ = valor]

Representa elementos con un nombre de atributo de attr cuyo valor es prefijado (precedido) por valor.