récupérer des liens de la page Web en utilisant python et BeautifulSoup
-
22-08-2019 - |
Question
Comment puis-je récupérer les liens d'une page Web et copiez l'adresse URL des liens en utilisant Python?
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.
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']