Réponses:
Voici un court extrait utilisant la classe SoupStrainer dans BeautifulSoup:
import httplib2
from bs4 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:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Edit: Notez que j'ai utilisé la classe SoupStrainer car elle est un peu plus efficace (mémoire et vitesse), si vous savez ce que vous analysez à l'avance.
/usr/local/lib/python2.7/site-packages/bs4/__init__.py:128: UserWarning: The "parseOnlyThese" argument to the BeautifulSoup constructor has been renamed to "parse_only."
has_attr
. Au lieu de cela, je vois qu'il y a quelque chose qui s'appelle has_key
et cela fonctionne.
Par souci d'exhaustivité, la version BeautifulSoup 4, utilisant également l'encodage fourni par le serveur:
from bs4 import BeautifulSoup
import urllib.request
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib.request.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().get_param('charset'))
for link in soup.find_all('a', href=True):
print(link['href'])
ou la version Python 2:
from bs4 import BeautifulSoup
import urllib2
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib2.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().getparam('charset'))
for link in soup.find_all('a', href=True):
print link['href']
et une version utilisant la requests
bibliothèque , qui, telle qu'elle est écrite, fonctionnera à la fois en Python 2 et 3:
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
import requests
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
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, parser, from_encoding=encoding)
for link in soup.find_all('a', href=True):
print(link['href'])
le soup.find_all('a', href=True)
appel trouve tous les <a>
éléments qui ont un href
attribut; les éléments sans l'attribut sont ignorés.
BeautifulSoup 3 a arrêté le développement en mars 2012; les nouveaux projets devraient vraiment utiliser BeautifulSoup 4, toujours.
Notez que vous devez laisser le décodage du 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 aider au décodage, mais cela peut être erroné et en conflit avec une <meta>
information d'en-tête trouvée dans le HTML lui-même, c'est pourquoi ce qui précède utilise la méthode de classe interne de BeautifulSoupEncodingDetector.find_declared_encoding()
pour vous assurer que ces astuces d'encodage intégrées l'emportent sur un serveur mal configuré.
Avec requests
, l' response.encoding
attribut prend par défaut Latin-1 si la réponse a un text/*
type MIME, même si aucun jeu de caractères n'a été renvoyé. Ceci est cohérent avec les RFC HTTP mais pénible lorsqu'il est utilisé avec l'analyse HTML, vous devez donc ignorer cet attribut lorsque no charset
est défini dans l'en-tête Content-Type.
SoupStrainer
tu veux dire? Il n'est allé nulle part, il fait toujours partie du projet .
D'autres ont recommandé BeautifulSoup, mais il est préférable d'utiliser lxml . Malgré son nom, il sert également à analyser et à gratter du HTML. C'est beaucoup, beaucoup plus rapide que BeautifulSoup, et il gère même mieux le HTML "cassé" que BeautifulSoup (leur prétention à la renommée). Il dispose également d'une API de compatibilité pour BeautifulSoup si vous ne souhaitez pas apprendre l'API lxml.
Il n'y a plus de raison d'utiliser BeautifulSoup, sauf si vous êtes sur Google App Engine ou quelque chose où tout ce qui n'est pas purement Python n'est pas autorisé.
lxml.html supporte également 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
lxml
comme analyseur par défaut s'il est installé.
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 désormais lxml. Requests, lxml & list comprehensions 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 liste comp, le "if '//' et 'url.com' not in x" est une méthode simple pour parcourir la liste des URL de navigation des sites 'internes', etc.
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, BSoup est bien entendu toujours préféré.
<a
et href
? Dites rel="nofollow"
ou onclick="..."
ou même juste une nouvelle ligne? stackoverflow.com/questions/1732348/…
Ce script fait ce que vous recherchez, mais résout également les liens relatifs vers 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
Pour trouver tous les liens, nous allons dans cet exemple utiliser le module urllib2 avec le re.module * Une des fonctions les plus puissantes du module re est "re.findall ()". Alors que re.search () est utilisé pour trouver la première correspondance d'un modèle, 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
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]))
(r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page)
signifie? Merci!
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 (ici, j'utilise l'opérateur commence par ^ pour spécifier que l'une ou l'autre de ces valeurs d'attribut commence par http. Vous pouvez personnaliser cela si 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 les éléments avec un nom d'attribut attr dont la valeur est précédée (précédée) de value.
Voici un exemple d' utilisation @ars réponse acceptée et les 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)
J'ai trouvé la réponse de @ Blairg23 fonctionnant, 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.
Le propre analyseur de BeatifulSoup peut être lent. Il pourrait être plus possible d'utiliser lxml qui est 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 de la racine du site. Comme mon cas d'utilisation consistait à extraire uniquement un certain type de liens, voici une version qui convertit les liens en URL complètes et qui accepte éventuellement un modèle global 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 avez besoin d'analyser des fragments d'URL contenant ../
ou ./
alors urlparse.urljoin peut être utile.
REMARQUE : L'analyse directe d'url lxml ne gère pas le chargement à partir de https
et ne fait pas de redirections, c'est pourquoi 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"
lxml
ne peut gérer qu'une entrée valide, comment peut-il remplacer BeautifulSoup
?
lxml.html
c'est un peu plus indulgent que le lxml.etree
. Si votre entrée n'est pas bien formée, vous pouvez définir explicitement l'analyseur BeautifulSoup: lxml.de/elementsoup.html . Et si vous optez pour BeatifulSoup, BS3 est un meilleur choix.
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']
Il peut y avoir de nombreux liens en double avec des liens externes et internes. Pour différencier les deux et obtenir simplement des liens uniques à l'aide d'ensembles:
# Python 3.
import urllib
from bs4 import BeautifulSoup
url = "http://www.espncricinfo.com/"
resp = urllib.request.urlopen(url)
# Get server encoding per recommendation of Martijn Pieters.
soup = BeautifulSoup(resp, from_encoding=resp.info().get_param('charset'))
external_links = set()
internal_links = set()
for line in soup.find_all('a'):
link = line.get('href')
if not link:
continue
if link.startswith('http'):
external_links.add(link)
else:
internal_links.add(link)
# Depending on usage, full internal links may be preferred.
full_internal_links = {
urllib.parse.urljoin(url, internal_link)
for internal_link in internal_links
}
# Print all unique external and full internal links.
for link in external_links.union(full_internal_links):
print(link)