Question

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

Était-ce utile?

La solution

Voici un court extrait en utilisant 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 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 je la classe SoupStrainer car il est un peu plus efficace (mémoire et vitesse sage), si vous savez ce que vous l'analyse syntaxique à l'avance

Autres conseils

Pour être complet, la version BeautifulSoup 4, en utilisant l'encodage fourni par le serveur ainsi:

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 version 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 fonctionnera comme écrit dans les deux 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) trouve tous les éléments <a> qui ont un attribut href; éléments sans l'attribut sont ignorées.

BeautifulSoup 3 arrêté le développement en Mars 2012; les nouveaux projets devraient vraiment utiliser BeautifulSoup 4, toujours.

Notez que vous devez laisser décoder le code HTML octets à BeautifulSoup . Vous pouvez informer BeautifulSoup du characterset trouvés dans les en-têtes de réponse HTTP pour aider à décoder, mais peut faux et contradictoires avec les informations d'en-tête de <meta> trouvé dans le code HTML lui-même, ce qui explique pourquoi l'utilise-dessus de la BeautifulSoup méthode de classe interne EncodingDetector.find_declared_encoding() pour vous assurer que ces conseils de codage embarqués victoire sur un serveur mal configuré.

Avec requests, l'response.encoding attribut par défaut Latin-1 si la réponse a une mimetype de text/*, même si aucune characterset a été retourné. Ceci est cohérent avec les RFCs HTTP mais douloureux quand il est utilisé avec l'analyse syntaxique HTML, vous devez donc ignorer cet attribut lorsqu'aucun charset est défini dans l'en-tête Content-Type.

D'autres ont recommandé BeautifulSoup, mais il est préférable d'utiliser lxml. Malgré son nom, il est aussi pour l'analyse syntaxique et le grattage HTML. Il est beaucoup, beaucoup plus rapide que BeautifulSoup, et il gère même « cassé » HTML mieux que BeautifulSoup (leur prétention à la célébrité). Il dispose d'une API de compatibilité pour BeautifulSoup aussi si vous ne voulez pas apprendre l'API lxml.

Ian Blicking accepte .

Il n'y a aucune raison d'utiliser BeautifulSoup plus, sauf si vous êtes sur Google App Engine ou quelque chose où tout pas purement Python n'est pas autorisé.

lxml.html prend également en charge les sélecteurs CSS3 donc ce genre de chose est trivial.

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

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

Sous le capot BeautifulSoup utilise maintenant lxml. Les demandes, lxml et liste compréhensions fait un combo 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 maquette de liste, le « si « // » et « url.com » pas x » est une méthode simple pour nettoyer la liste des URL de urls de navigation internes »de sites, etc.

Pour tous les liens, nous allons dans cet exemple utiliser le module urllib2 ensemble avec le re.module * Une des fonctions les plus puissantes dans le module re est « re.findall () ». Alors que re.search () est utilisé pour trouver le premier match pour un motif, re.findall () trouve tous les allumettes et les retourne comme une liste de chaînes, chaque chaîne représentant un match *

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

juste pour obtenir 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]

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

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

Ce script fait ce que vous cherchez, mais résout également les liens relatifs à des 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

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

par exemple, et src attribut href (ici je me sers du commence par l'opérateur ^ de préciser que l'une de ces valeurs d'attributs commence par http. Vous pouvez adapter cela comme nécessaire

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 des éléments avec un nom d'attribut de attr dont la valeur est préfixée (précédée) par la valeur.

Voici un exemple en utilisant @ars réponse acceptée et les BeautifulSoup4, requests et modules wget 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)

J'ai trouvé la réponse par @ Blairg23 de travail, après la correction suivante (couvrant le scénario où il n'a pas fonctionné 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é afin d'obtenir à la place l'URL complète.

propre analyseur de BeatifulSoup peut être lent. Il serait peut-être plus possible d'utiliser lxml qui est capable de l'analyse syntaxique directement à partir d'une URL ( avec quelques 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 retourne les liens comme il est, et dans la plupart des cas, ils seraient des liens relatifs ou absolus de la racine du site. Depuis mon cas d'utilisation était d'extraire seulement un certain type de liens, ci-dessous est une version qui convertit les liens vers des URL complètes et qui accepte éventuellement un motif glob comme *.mp3. Il ne traitera pas simples et doubles points dans les chemins relatifs bien, mais jusqu'à présent je n'avais pas le besoin. Si vous avez besoin pour analyser des fragments d'URL contenant ../ ou ./ puis urlparse.urljoin pourrait utile.

NOTE :. Parsing direct url lxml ne gère pas le chargement de https et ne fait pas les redirections, donc pour cette raison que 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 __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

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"
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']
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top