Comment puis-je récupérer les liens d'une page Web et copier l'adresse URL des liens en utilisant Python?
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.
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.
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.
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
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 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'))
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.
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
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é.
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
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]))
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.
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)
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"
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.
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']