Risposte:
Ecco un breve frammento che utilizza la classe SoupStrainer in 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 documentazione di BeautifulSoup è in realtà abbastanza buona e copre una serie di scenari tipici:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Modifica: nota che ho usato la classe SoupStrainer perché è un po 'più efficiente (in termini di memoria e velocità), se sai cosa stai analizzando in anticipo.
/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
. Invece vedo che c'è qualcosa chiamato has_key
e funziona.
Per completezza, la versione BeautifulSoup 4, utilizzando anche la codifica fornita dal server:
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'])
o la versione di 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']
e una versione che utilizza la requests
libreria , che come scritto funzionerà sia in Python 2 che in 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'])
La soup.find_all('a', href=True)
chiamata trova tutti gli <a>
elementi che hanno un href
attributo; gli elementi senza l'attributo vengono saltati.
BeautifulSoup 3 ha interrotto lo sviluppo a marzo 2012; i nuovi progetti dovrebbero davvero usare BeautifulSoup 4, sempre.
Nota che dovresti lasciare la decodifica dell'HTML da byte a BeautifulSoup . È possibile informare BeautifulSoup del set di caratteri trovato nelle intestazioni della risposta HTTP per facilitare la decodifica, ma ciò può essere errato e in conflitto con le <meta>
informazioni di intestazione trovate nell'HTML stesso, motivo per cui quanto sopra utilizza il metodo di classe interno BeautifulSoup EncodingDetector.find_declared_encoding()
per assicurarsi che tali suggerimenti di codifica incorporati vincono su un server configurato in modo errato.
Con requests
, l' response.encoding
attributo viene impostato automaticamente su Latin-1 se la risposta ha un text/*
mimetipo, anche se non è stato restituito alcun set di caratteri. Ciò è coerente con gli RFC HTTP ma è doloroso se utilizzato con l'analisi HTML, quindi è necessario ignorare tale attributo quando non charset
è impostato alcun valore nell'intestazione Content-Type.
SoupStrainer
vuoi dire? Non è andato da nessuna parte, fa ancora parte del progetto .
Altri hanno consigliato BeautifulSoup, ma è molto meglio usare lxml . Nonostante il suo nome, è anche per l'analisi e lo scraping dell'HTML. È molto, molto più veloce di BeautifulSoup e gestisce anche l'HTML "rotto" meglio di BeautifulSoup (la loro pretesa di fama). Ha un'API di compatibilità anche per BeautifulSoup se non vuoi imparare l'API lxml.
Non c'è più motivo di utilizzare BeautifulSoup, a meno che tu non sia su Google App Engine o qualcosa in cui non sia consentito nulla che non sia puramente Python.
lxml.html supporta anche i selettori CSS3, quindi questo genere di cose è banale.
Un esempio con lxml e xpath sarebbe simile al seguente:
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
come parser predefinito se installato.
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'
Il codice seguente consente di recuperare tutti i collegamenti disponibili in una pagina Web utilizzando urllib2
e 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'))
Sotto il cofano BeautifulSoup ora utilizza lxml. Richieste, lxml e comprensione delle liste rendono una combo killer.
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]
Nell'elenco comp, "if '//' e 'url.com' non in x" è un metodo semplice per cancellare l'elenco di URL degli URL di navigazione "interni" dei siti, ecc.
solo per ottenere i collegamenti, senza B.soup e 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]
per operazioni più complesse, ovviamente BSoup è ancora preferito.
<a
e href
? Dire rel="nofollow"
o onclick="..."
anche solo una nuova linea? stackoverflow.com/questions/1732348/...
Questo script fa quello che stai cercando, ma risolve anche i collegamenti relativi ai collegamenti assoluti.
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
Per trovare tutti i collegamenti, in questo esempio useremo il modulo urllib2 insieme a re.module * Una delle funzioni più potenti nel modulo re è "re.findall ()". Mentre re.search () viene utilizzato per trovare la prima corrispondenza per un modello, re.findall () trova tutte le corrispondenze e le restituisce come un elenco di stringhe, con ciascuna stringa che rappresenta una corrispondenza *
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
Perché non usare le espressioni regolari:
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)
significa? Grazie!
I collegamenti possono essere all'interno di una varietà di attributi in modo da poter passare un elenco di quegli attributi da selezionare
ad esempio, con l'attributo src e href (qui sto usando l'operatore inizia con ^ per specificare che uno di questi valori di attributi inizia con http. È possibile personalizzare questo come richiesto
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)
Attributo = selettori di valore
[Attr ^ = value]
Rappresenta elementi con un nome di attributo di attr il cui valore è preceduto (preceduto) da valore.
Ecco un esempio utilizzando @ars risposta accettata e le BeautifulSoup4
, requests
e wget
moduli per gestire i download.
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)
Ho trovato la risposta di @ Blairg23 funzionante, dopo la seguente correzione (che copre lo scenario in cui non ha funzionato correttamente):
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)
Per Python 3:
urllib.parse.urljoin
deve essere utilizzato per ottenere invece l'URL completo.
Lo stesso parser di BeatifulSoup può essere lento. Potrebbe essere più fattibile usare lxml che è in grado di analizzare direttamente da un URL (con alcune limitazioni menzionate di seguito).
import lxml.html
doc = lxml.html.parse(url)
links = doc.xpath('//a[@href]')
for link in links:
print link.attrib['href']
Il codice sopra restituirà i collegamenti così come sono, e nella maggior parte dei casi sarebbero collegamenti relativi o assoluti dalla radice del sito. Dal momento che il mio caso d'uso è stato quello di estrarre solo un certo tipo di collegamenti, di seguito è una versione che converte i collegamenti in URL completi e che facoltativamente accetta un modello glob come *.mp3
. Tuttavia, non gestirà punti singoli e doppi nei relativi percorsi, ma finora non ne ho avuto bisogno. Se devi analizzare frammenti di URL contenenti ../
o ./
quindi urlparse.urljoin potrebbe tornare utile.
NOTA : l'analisi dell'URL lxml diretto non gestisce il caricamento da https
e non esegue reindirizzamenti, quindi per questa ragione la versione seguente utilizza 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'utilizzo è il seguente:
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
può gestire solo input validi, come può sostituirli BeautifulSoup
?
lxml.html
sia un po 'più indulgente del lxml.etree
. Se il tuo input non è ben formato, puoi impostare esplicitamente il parser BeautifulSoup: lxml.de/elementsoup.html . E se vai con BeatifulSoup, BS3 è la scelta migliore.
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']
Possono esserci molti collegamenti duplicati insieme a collegamenti sia esterni che interni. Per distinguere tra i due e ottenere solo collegamenti univoci utilizzando i set:
# 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)