una - recuperar enlaces desde la página web usando Python y BeautifulSoup
web scraping python beautifulsoup tutorial (14)
¿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"<a.*?/s*href=/"(.*?)/".*?>(.*?)</a>", page)
for link in links:
print(''href: %s, HTML text: %s'' % (link[0], link[1]))
¿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 ejemplo que usa 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)
Aquí hay un fragmento corto usando 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, parseOnlyThese=SoupStrainer(''a'')):
if link.has_attr(''href''):
print link[''href'']
La documentación de BeautifulSoup en realidad es bastante buena y cubre varios escenarios típicos:
http://www.crummy.com/software/BeautifulSoup/documentation.html
Editar: Tenga en cuenta que utilicé la clase SoupStrainer porque es un poco más eficiente (memoria y velocidad), si sabe lo que está analizando de antemano.
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 el comp de la lista, "if ''//'' y ''url.com'' no en x" es un método simple para borrar la lista de direcciones URL internas de navegación de los sitios, etc.
El analizador propio de BeatifulSoup puede ser lento. Podría ser más factible usar lxml que es capaz de analizar directamente desde una URL (con algunas limitaciones que se mencionan 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 como están, y en la mayoría de los casos serán enlaces relativos o absolutos desde la raíz del sitio. Como mi caso de uso consistía en extraer solo un cierto tipo de enlaces, a continuación se muestra una versión que convierte los enlaces a URL completas y que opcionalmente acepta un patrón global como *.mp3
. Sin embargo, no manejará puntos individuales y dobles en las rutas relativas, pero hasta ahora no tenía la necesidad de hacerlo. Si necesita analizar fragmentos de URL que contengan ../
o ./
entonces urlparse.urljoin podría ser útil.
NOTA : El análisis directo de lxml url no maneja la carga desde https
y no hace redirecciones, por lo que la siguiente versión usa 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://.com/a/37758066/191246
getlinks.py http://.com/a/37758066/191246 "*users*"
getlinks.py http://fakedomain.mu/somepage.html "*.mp3"
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''))
Encontré la respuesta de @ Blairg23 en funcionamiento, 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
tiene que ser utilizado para obtener la URL completa en su lugar.
Este script hace lo que usted busca, 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
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 HTML "roto" mejor que BeautifulSoup (su reclamo de fama). También tiene una API de compatibilidad para BeautifulSoup si no desea aprender la API lxml.
Ian Blicking está de acuerdo .
Ya no hay motivo para usar BeautifulSoup, a menos que esté en Google App Engine o algo en el que no se permita nada que no sea puramente Python.
lxml.html también es compatible con 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
Para completar, la versión BeautifulSoup 4, haciendo uso de la codificación proporcionada por el servidor también:
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 usa la biblioteca de requests
, que, como está escrito, funcionará en Python 2 y 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 <a>
que tienen un atributo href
; los elementos sin el atributo se omiten.
BeautifulSoup 3 detuvo el desarrollo en marzo de 2012; nuevos proyectos realmente deberían usar BeautifulSoup 4, siempre.
Tenga en cuenta que debe dejar de decodificar el HTML de bytes a BeautifulSoup . Puede informar a BeautifulSoup del juego de caracteres encontrado en los encabezados de respuesta HTTP para ayudar en la decodificación, pero puede ser incorrecto y conflictivo con una información de encabezado <meta>
encontrada en el HTML mismo, por lo que lo anterior usa el método de clase interna BeautifulSoup EncodingDetector.find_declared_encoding()
para asegurarse de que dichas sugerencias de codificación incrustadas ganen sobre 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 coherente con los RFC de HTTP, pero es doloroso cuando se utiliza con el análisis de HTML, por lo que debe ignorar ese atributo cuando no se establece un juego de charset
en el encabezado de tipo de contenido.
solo para obtener los enlaces, sin B.soup y 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 operaciones más complejas, por supuesto, aún se prefiere BSoup.
Para encontrar todos los enlaces, en este ejemplo utilizaremos el módulo urllib2 junto con el re.module * Una de las funciones más potentes del módulo re es "re.findall ()". Mientras que 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, con cada cadena representando 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
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'']
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''