recuperare collegamenti dalla pagina Web utilizzando Python e BeautifulSoup
-
22-08-2019 - |
Domanda
Come posso recuperare i collegamenti di una pagina Web e copiare l'indirizzo URL dei collegamenti utilizzando Python?
Soluzione
Ecco un breve frammento utilizzando la classe SoupStrainer in 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 documentazione BeautifulSoup è in realtà abbastanza buono, e copre una serie di scenari tipici:
http://www.crummy.com/software/BeautifulSoup/documentation.html
Modifica:. Si noti che ho usato la classe SoupStrainer perché è un po 'più efficiente (memoria e la velocità saggio), se si sa cosa si sta analizzando in anticipo
Altri suggerimenti
Per completezza amore, la versione BeautifulSoup 4, facendo uso della codifica fornito dal server così:
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']
o la versione di 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'])
e una versione con il requests
biblioteca, che come scritto funzionerà sia in Python 2 e 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'])
La chiamata soup.find_all('a', href=True)
trova tutti gli elementi <a>
che hanno un attributo href
; elementi senza l'attributo vengono saltati.
BeautifulSoup 3 smesso di sviluppo nel marzo 2012; nuovi progetti in realtà dovrebbe usare BeautifulSoup 4, sempre.
Si noti che si dovrebbe lasciare la decodifica il codice HTML dal byte per BeautifulSoup . È possibile informare BeautifulSoup del characterset trova nelle intestazioni di risposta HTTP per aiutare nella decodifica, ma questo possono essere sbagliato e in conflitto con informazioni di intestazione <meta>
trovato nel codice HTML stesso, che è il motivo per cui la usi al di sopra della BeautifulSoup interno metodo di classe EncodingDetector.find_declared_encoding()
per assicurarsi che tali suggerimenti di codifica incorporato conquistare un server configurato correttamente.
Con requests
, il response.encoding
attribuire default Latin-1 se la risposta ha un mimetype text/*
, anche se nessuno di caratteri è stato restituito. Ciò è coerente con RFC HTTP, ma doloroso quando viene utilizzato con parsing del codice HTML, così si dovrebbe ignorare che attributo quando non charset
è impostato nell'intestazione Content-Type.
Altri hanno raccomandato BeautifulSoup, ma è molto meglio usare lxml . Nonostante il suo nome, è anche per il parsing e raschiando HTML. E 'molto, molto più veloce di BeautifulSoup, e gestisce anche "rotto" HTML meglio di BeautifulSoup (la loro pretesa di fama). Ha un'API per la compatibilità BeautifulSoup anche se non si vuole imparare l'API lxml.
Non c'è motivo di usare BeautifulSoup più, a meno che non sei su Google App Engine o qualcosa in cui tutto ciò che non puramente Python non è consentito.
lxml.html supporta anche selettori CSS3 in modo da questo genere di cose è banale.
Un esempio con lxml e XPath sarebbe simile a questa:
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'
Il seguente codice è quello 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. Le richieste, lxml & list comprehension fa un killer combo.
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]
Nella lista comp, il "se '//' e 'url.com' non in x" è un metodo semplice per fregare l'elenco degli URL di URL di navigazione 'interne' dei siti, ecc.
Per trovare tutti i link, saremo in questo esempio utilizzare il modulo urllib2 insieme con la re.module * Uno dei più potenti funzione nel modulo re è "re.findall ()". Mentre re.search () viene utilizzato per trovare la prima partita per un motivo, re.findall () trova tutti le sue partite e li restituisce come una lista di stringhe, con ogni stringa che rappresenta una partita *
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
solo per ottenere i link, 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, naturalmente BSoup è ancora preferito.
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]))
Questo script fa quello che cercate, ma risolve anche i link 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
I collegamenti possono essere all'interno di una varietà di attributi così si potrebbe passare un elenco di questi attributi per selezionare
per esempio, con src e attributo href (qui sto usando inizia con ^ operatore per specificare che uno di questi valori attributi inizia con http. È possibile personalizzare questa 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)
[attr ^ = valore]
Rappresenta elementi con un nome attributo attr cui valore è prefissato (preceduto) in valore.
Ecco un esempio utilizzando @ars risposta accettata e le BeautifulSoup4
, requests
e moduli wget
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 da @ Blairg23 lavorando, dopo la seguente correzione (che copre lo scenario in cui non è riuscito a funzionare 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 al fine di ottenere l'URL completo, invece.
Il parser di BeatifulSoup può essere lento.Potrebbe essere più fattibile da 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 riportato sopra restituirà i collegamenti così come sono e nella maggior parte dei casi saranno collegamenti relativi o assoluti dalla radice del sito.Poiché il mio caso d'uso era quello di estrarre solo un certo tipo di collegamenti, di seguito è riportata 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 percorsi relativi, ma finora non ne ho avuto bisogno.Se è necessario analizzare frammenti di URL contenenti ../
O ./
Poi urlparse.urljoin potrebbe tornare utile.
NOTA:L'analisi diretta dell'URL lxml non gestisce il caricamento da https
e non esegue reindirizzamenti, quindi per questo motivo viene utilizzata la versione seguente 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"
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']