2009-07-03 42 views

Respuesta

139

Aquí está un breve fragmento utilizando 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 BeautifulSoup es en realidad bastante bueno, y abarca una serie de 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 por adelantado.

+10

+1, usar el filtro de sopa es una gran idea, ya que le permite eludir una gran cantidad de análisis innecesarios cuando lo único que busca son los enlaces. –

+0

Lo edité para agregar una explicación similar antes de ver el comentario de Evan. ¡Gracias por notar eso, sin embargo! – ars

+0

gracias, esto resuelve mi problema, con esto termino mi proyecto muchas gracias – NepUS

25
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' 
4

sólo para conseguir los enlaces, sin B.soup y expresiones regulares:

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, de BSoup supuesto, todavía se prefiere.

+5

Y si, por ejemplo, hay algo entre ' dimo414

+0

¿Hay alguna manera de filtrar solo algunos enlaces con esto? como decir que solo quiero enlaces que tengan "Episodio" en el enlace? – wiak

44

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 agrees.

No hay ninguna razón para usar BeautifulSoup, a menos que esté en Google App Engine o algo en que no esté permitido 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 
+16

BeautifulSoup 4 usará 'lxml' como el analizador predeterminado si está instalado. –

2

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])) 
+0

Me encantaría poder entender esto, ¿dónde puedo averiguar de manera eficiente qué '(r" (. *?) ", página)' significa? ¡Gracias! – user1063287

+7

Realmente una mala idea. HTML roto en todas partes. – Ufoguy

+2

¿Por qué no utilizar expresiones regulares para analizar html: http: // stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags? page = 1 & tab = votes # 1732454 – allcaps

6

Bajo el capó BeautifulSoup ahora utiliza lxml. Solicitudes, lxml & lista de comprensiones hace 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 comp, el "si '//' y 'url.com' no en x" es un método simple para fregar la lista de direcciones URL url de navegación 'internos' los sitios, etc.

+1

Si se trata de un repositorio, ¿por qué la publicación original no incluye: 1. solicita 2.list comp 3. logic para eliminar los enlaces internos y basura del sitio? Intenta comparar los resultados de las dos publicaciones, mi comp de lista hace un trabajo sorprendentemente bueno fregando los enlaces basura. – cheekybastard

+0

El OP no solicitó esas características y la parte que solicitó ya se ha publicado y resuelto utilizando el mismo método exacto que usted publica. Sin embargo, eliminaré la votación negativa ya que la comprensión de la lista agrega valor para las personas que sí desean esas funciones y las menciona explícitamente en el cuerpo de la publicación. Además, podría usar el representante :) – dotancohen

8

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

Para completarlo, la versión BeautifulSoup 4, haciendo uso de la codificación proporcionada por el servidor, así:

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 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 usando el requests library, que como escrito funcionará en tanto 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 llamada soup.find_all('a', href=True) encuentra todos <a> elementos 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 los bytes a BeautifulSoup. Puede informar BeautifulSoup del juego de caracteres encontrado en los encabezados de respuesta HTTP para ayudar en la decodificación, pero puede estar equivocado y en conflicto con una información de encabezado <meta> que se encuentra en el HTML, por lo que lo anterior usa el método de clase interna BeautifulSoup EncodingDetector.find_declared_encoding() para asegurarse de que tales sugerencias de codificación incrustadas ganen sobre un servidor mal configurado.

Con requests, el atributo response.encoding tiene como valor predeterminado Latin-1 si la respuesta tiene un tipo mimet text/*, 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 HTML, por lo que debe ignorar ese atributo cuando no se establece charset en el encabezado Content-Type.

+0

¿Hay algo así como StrainedSoup for bs4? (No lo necesito ahora, solo me pregunto, si hay algo que quiera agregar) –

+0

@AnttiHaapala: ¿'SoupStrainer' te refieres? No [fue a ninguna parte, todavía es parte del proyecto] (https://www.crummy.com/software/BeautifulSoup/bs4/doc/#soupstrainer). –

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

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 
5

permite encontrar todos los enlaces, vamos a utilizar en este ejemplo el módulo urllib2 junto con el re.module * Una de las funciones más poderoso del módulo re es "re.findall() ". Mientras re.search() se utiliza para encontrar el primer partido para un patrón, re.findall() busca todos los los partidos y los devuelve como una lista de cadenas, donde cada cadena que representa un partido *

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 
1

El analizador propio de BeatifulSoup puede ser lento. Puede 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 devuelve los enlaces como es, y en la mayoría de los casos sería enlaces relativos o absolutos de la raíz del sitio. Como mi caso de uso consistía en extraer solo cierto tipo de enlaces, a continuación se muestra una versión que convierte los enlaces a URL completas y que acepta un patrón glob como *.mp3. Sin embargo, no manejará puntos individuales y dobles en las rutas relativas, pero hasta ahora no tuve la necesidad de hacerlo. Si necesita analizar fragmentos de URL que contengan ../ o ./, entonces urlparse.urljoin puede ser útil.

NOTA: lxml directa análisis de URL no se ocupa de la carga de https y no hace redirecciones, por lo que por esta razón la versión más adelante está utilizando 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" 
+0

'lxml' solo puede manejar entradas válidas, ¿cómo puede reemplazar' BeautifulSoup'? – alexis

+0

@alexis: Creo que 'lxml.html' es un poco más indulgente que' lxml.etree'. Si su entrada no está bien formada, puede establecer explícitamente el analizador BeautifulSoup: http://lxml.de/elementsoup.html. Y si vas con BeatifulSoup entonces BS3 es una mejor opción. – ccpizza

0

Aquí hay un ejemplo usando @ars aceptados respuesta y la BeautifulSoup4, requests y wget módulos 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) 
0

he encontrado la respuesta por @ Blairg23 de trabajo, después de la siguiente corrección (que cubre el escenario en el que 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.

Cuestiones relacionadas