web-dev-qa-db-fra.com

récupérer des liens depuis une page Web en utilisant python et BeautifulSoup

Comment puis-je récupérer les liens d'une page Web et copier l'adresse URL des liens en utilisant Python?

126
NepUS

Voici un court extrait de la classe SoupStrainer dans 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 documentation de BeautifulSoup est en fait assez bonne et couvre un certain nombre de scénarios typiques:

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

Edit: Notez que j’ai utilisé la classe SoupStrainer car elle est un peu plus efficace (en termes de mémoire et de vitesse), si vous savez ce que vous analysez à l’avance.

173
ars

Par souci d’exhaustivité, la version de BeautifulSoup 4, qui utilise également le codage fourni par le serveur:

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

et une version utilisant la bibliothèque requests , qui, telle qu’elle a été écrite, fonctionnera à la fois Python 2 et 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'])

L'appel soup.find_all('a', href=True) recherche tous les éléments <a> Dotés d'un attribut href. les éléments sans l'attribut sont ignorés.

BeautifulSoup 3 a arrêté son développement en mars 2012; les nouveaux projets doivent vraiment utiliser BeautifulSoup 4, toujours.

Notez que vous devez laisser le décodage du code HTML d'octets vers BeautifulSoup . Vous pouvez informer BeautifulSoup du jeu de caractères trouvé dans les en-têtes de réponse HTTP pour faciliter le décodage, mais ceci peut être incorrect et entrer en conflit avec un <meta> informations d’en-tête trouvées dans le code HTML lui-même, raison pour laquelle la procédure ci-dessus utilise la méthode de classe interne BeautifulSoup EncodingDetector.find_declared_encoding() pour s’assurer que de tels conseils de codage incorporés l'emportent sur un serveur mal configuré.

Avec requests, l'attribut response.encoding Est défini par défaut sur Latin-1 si la réponse a un type mime text/*, Même si aucun jeu de caractères n'a été renvoyé. Ceci est cohérent avec les RFC HTTP mais douloureux lorsqu'il est utilisé avec l'analyse HTML. Vous devez donc ignorer cet attribut lorsque aucun charset n'est défini dans l'en-tête Content-Type.

61
Martijn Pieters

D'autres ont recommandé BeautifulSoup, mais il est bien préférable d'utiliser lxml . En dépit de son nom, il est également destiné à l’analyse et au grattage de HTML. C'est beaucoup, beaucoup plus rapide que BeautifulSoup, et il gère même le code HTML "cassé" mieux que BeautifulSoup (leur prétention à la renommée). Il a aussi une API de compatibilité pour BeautifulSoup si vous ne voulez pas apprendre l’API lxml.

Ian Blicking accepte .

Il n’ya plus de raison d’utiliser BeautifulSoup, à moins d’être sur Google App Engine ou d’autre chose qui ne soit pas purement Python n’est pas autorisé.

lxml.html supporte également les sélecteurs CSS3, ce genre de chose est donc triviale.

Un exemple avec lxml et xpath ressemblerait à ceci:

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
48
aehlke
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'
27
Andrew Johnson

Le code suivant permet de récupérer tous les liens disponibles dans une page Web à l'aide de urllib2 et 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'))
10
Sentient07

Sous le capot, BeautifulSoup utilise maintenant lxml. La compréhension des demandes, lxml et listes fait un combo de tueur.

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]

Dans la liste comp, "if '//" et "url.com" ne figurant pas dans x "est une méthode simple pour effacer la liste des URL des URL de navigation" internes "des sites, etc.

8
cheekybastard

Pour trouver tous les liens, nous allons dans cet exemple utiliser le module urllib2 avec le re.module * Un des La fonction la plus puissante du module re est "re.findall ()". Alors que re.search () est utilisé pour trouver la première correspondance d'un motif, re.findall () trouve toutes les correspondances et les renvoie sous forme de liste. de chaînes, chaque chaîne représentant une correspondance *

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
4
Mayur Ingle

juste pour avoir les liens, sans B.soup et 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]

bien sûr, pour les opérations plus complexes, BSoup est toujours préféré.

3
ghostdog74

Ce script fait ce que vous cherchez, mais résout également les liens relatifs en liens absolus.

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
3
Ricky Wilson

Pourquoi ne pas utiliser des expressions régulières:

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]))
3
ahmadh

Les liens peuvent être dans une variété d'attributs afin que vous puissiez passer une liste de ces attributs à sélectionner

par exemple, avec l'attribut src et href (j'utilise ici l'opérateur opérateur commence avec ^ pour spécifier que l'une ou l'autre de ces valeurs d'attribut commence par http. Vous pouvez l'adapter à votre guise.

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)

Attribut = sélecteurs de valeur

[attr ^ = valeur]

Représente les éléments avec un nom d'attribut attr dont la valeur est préfixée (précédée) par valeur.

2
QHarr

Voici un exemple utilisant la réponse acceptée par @ars et le BeautifulSoup4, requests et wget modules pour gérer les téléchargements.

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)
1
Blairg23

Le propre analyseur de BeatifulSoup peut être lent. Il serait peut-être plus pratique d'utiliser lxml capable d'analyser directement à partir d'une URL (avec certaines limitations mentionnées ci-dessous).

import lxml.html

doc = lxml.html.parse(url)

links = doc.xpath('//a[@href]')

for link in links:
    print link.attrib['href']

Le code ci-dessus renverra les liens tels quels et, dans la plupart des cas, il s'agirait de liens relatifs ou absolus à la racine du site. Étant donné que mon cas d'utilisation consistait uniquement à extraire un certain type de liens, voici une version qui convertit les liens en URL complètes et qui accepte éventuellement un modèle glob comme *.mp3. Cependant, il ne gérera pas les points simples et doubles dans les chemins relatifs, mais jusqu'à présent, je n'en avais pas besoin. Si vous devez analyser des fragments d’URL contenant ../ ou ./ _ alors rlparse.urljoin pourrait s'avérer utile.

[~ # ~] note [~ # ~] : l'analyse directe de l'URL lxml ne gère pas le chargement à partir de https et ne le fait pas. faire des redirections, donc pour cette raison la version ci-dessous utilise 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 __== '__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

L'utilisation est la suivante:

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"
1
ccpizza

J'ai trouvé la réponse de @ Blairg23 qui fonctionnait, après la correction suivante (couvrant le scénario où cela ne fonctionnait pas correctement):

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)

Pour Python 3:

urllib.parse.urljoin doit être utilisé pour obtenir l’URL complète à la place.

1
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']
0
Tilak Patidar