Odpowiedzi:
Oto krótki fragment wykorzystujący klasę SoupStrainer w 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'])
Dokumentacja BeautifulSoup jest w rzeczywistości całkiem dobra i obejmuje kilka typowych scenariuszy:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Edycja: Zwróć uwagę, że użyłem klasy SoupStrainer, ponieważ jest nieco bardziej wydajna (pod względem pamięci i szybkości), jeśli wiesz, co analizujesz z wyprzedzeniem.
/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
. Zamiast tego widzę, że coś się nazywa has_key
i działa.
Ze względu na kompletność wersja BeautifulSoup 4, również wykorzystująca kodowanie dostarczone przez serwer:
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'])
lub wersja 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']
oraz wersja korzystająca z requests
biblioteki , która jak napisano będzie działać zarówno w Pythonie 2, jak i 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'])
soup.find_all('a', href=True)
Wywołanie znajdzie wszystkie <a>
elementy, które mają href
atrybut; elementy bez atrybutu są pomijane.
BeautifulSoup 3 zatrzymał rozwój w marcu 2012; nowe projekty naprawdę powinny używać BeautifulSoup 4, zawsze.
Zauważ, że powinieneś pozostawić dekodowanie HTML z bajtów do BeautifulSoup . Możesz poinformować BeautifulSoup o zestawie znaków znajdującym się w nagłówkach odpowiedzi HTTP, aby pomóc w dekodowaniu, ale może to być błędne i sprzeczne z <meta>
informacjami nagłówka znalezionymi w samym kodzie HTML, dlatego powyższe używa metody wewnętrznej klasy BeautifulSoup, EncodingDetector.find_declared_encoding()
aby upewnić się, że takie wbudowane wskazówki dotyczące kodowania wygrywają z błędnie skonfigurowanym serwerem.
W requests
przypadku response.encoding
atrybutu wartość domyślna to Latin-1, jeśli odpowiedź ma text/*
typ MIME, nawet jeśli nie został zwrócony żaden zestaw znaków. Jest to zgodne ze specyfikacjami HTTP RFC, ale jest bolesne, gdy jest używane z analizą HTML, więc należy zignorować ten atrybut, jeśli charset
w nagłówku Content-Type ustawiono no .
SoupStrainer
masz na myśli? To nie idź nigdzie, nadal jest częścią projektu .
Inni polecili BeautifulSoup, ale znacznie lepiej jest użyć lxml . Pomimo swojej nazwy służy również do analizowania i zgrywania kodu HTML. Jest dużo, dużo szybszy niż BeautifulSoup, a nawet obsługuje „zepsuty” HTML lepiej niż BeautifulSoup (ich roszczenia do sławy). Ma również interfejs API zgodności dla BeautifulSoup, jeśli nie chcesz uczyć się lxml API.
Nie ma już powodu, aby używać BeautifulSoup, chyba że korzystasz z Google App Engine lub czegoś, w czym coś, co nie jest czysto Python, jest niedozwolone.
lxml.html obsługuje również selektory CSS3, więc tego rodzaju rzeczy są trywialne.
Przykład z lxml i xpath wyglądałby tak:
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
jako domyślnego parsera, jeśli zostanie zainstalowany.
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'
Poniższy kod służy do pobierania wszystkich linków dostępnych na stronie internetowej przy użyciu urllib2
i 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'))
Pod maską BeautifulSoup używa teraz lxml. Żądania, lxml i listy składają się na zabójczą kombinację.
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]
W zestawieniu list „if” // ”i„ url.com ”not in x” to prosta metoda przeglądania listy adresów URL „wewnętrznych” adresów URL nawigacji witryn itp.
tylko po to, aby uzyskać linki, bez B.soup i 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]
dla bardziej złożonych operacji, oczywiście BSoup jest nadal preferowana.
<a
a href
? Powiedz rel="nofollow"
lub onclick="..."
po prostu nową linijkę? stackoverflow.com/questions/1732348/…
Ten skrypt robi to, czego szukasz, ale także rozwiązuje względne linki do bezwzględnych linków.
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
Aby znaleźć wszystkie linki, w tym przykładzie użyjemy modułu urllib2 razem z re.module * Jedną z najpotężniejszych funkcji w module re jest „re.findall ()”. Podczas gdy re.search () służy do znalezienia pierwszego dopasowania dla wzorca, re.findall () znajduje wszystkie dopasowania i zwraca je jako listę ciągów, z których każdy reprezentuje jedno dopasowanie *
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
Dlaczego nie użyć wyrażeń regularnych:
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)
to znaczy? dzięki!
Łącza mogą mieć różne atrybuty, więc możesz przekazać listę tych atrybutów do wyboru
na przykład z atrybutem src i href (tutaj używam operatora rozpoczyna się od ^, aby określić, że każda z tych wartości atrybutów zaczyna się od http.
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)
[atr ^ = wartość]
Reprezentuje elementy z nazwą atrybutu attr, którego wartość jest poprzedzona (poprzedzona) wartością.
Oto przykład z użyciem @ars Zaakceptowanych odpowiedź i BeautifulSoup4
, requests
oraz wget
moduły do obsługi plików do pobrania.
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)
Znalazłem odpowiedź, którą @ Blairg23 działała, po następującej poprawce (obejmującej scenariusz, w którym nie działał poprawnie):
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)
W przypadku Pythona 3:
urllib.parse.urljoin
należy użyć zamiast tego w celu uzyskania pełnego adresu URL.
Parser BeatifulSoup może działać wolno. Bardziej realne może być użycie lxml, który jest w stanie analizować bezpośrednio z adresu URL (z pewnymi ograniczeniami wymienionymi poniżej).
import lxml.html
doc = lxml.html.parse(url)
links = doc.xpath('//a[@href]')
for link in links:
print link.attrib['href']
Powyższy kod zwróci linki bez zmian, aw większości przypadków będą to linki względne lub bezwzględne względem katalogu głównego witryny. Ponieważ moim przypadkiem użycia było wyodrębnianie tylko określonego typu linków, poniżej znajduje się wersja, która konwertuje linki na pełne adresy URL i która opcjonalnie akceptuje wzorzec glob, taki jak *.mp3
. Nie obsługuje jednak pojedynczych i podwójnych kropek na ścieżkach względnych, ale do tej pory nie było takiej potrzeby. Jeśli chcesz przeanalizować fragmenty adresów URL zawierające urlparse.urljoin../
lub ./
wtedy, może się przydać.
UWAGA : Bezpośrednie parsowanie adresu URL lxml nie obsługuje ładowania z https
i nie wykonuje przekierowań, więc z tego powodu poniższa wersja używa 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
Sposób użycia jest następujący:
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
obsługuje tylko prawidłowe dane wejściowe, w jaki sposób można je zastąpić BeautifulSoup
?
lxml.html
jest nieco bardziej wyrozumiały niż lxml.etree
. Jeśli dane wejściowe nie są poprawnie sformułowane, możesz jawnie ustawić parser BeautifulSoup: lxml.de/elementsoup.html . A jeśli zdecydujesz się na BeatifulSoup, lepszym wyborem będzie BS3.
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']
Może istnieć wiele zduplikowanych linków razem z linkami zewnętrznymi i wewnętrznymi. Aby rozróżnić te dwa elementy i po prostu uzyskać unikalne linki za pomocą zestawów:
# 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)