Domanda

Come posso recuperare i collegamenti di una pagina Web e copiare l'indirizzo URL dei collegamenti utilizzando Python?

È stato utile?

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.

Ian Blicking concorda .

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)

attribute = value selettori

  

[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']
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top