Jawaban:
Berikut cuplikan singkat menggunakan kelas SoupStrainer di 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'])
Dokumentasi BeautifulSoup sebenarnya cukup bagus, dan mencakup sejumlah skenario khas:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Sunting: Perhatikan bahwa saya menggunakan kelas SoupStrainer karena ini sedikit lebih efisien (memori dan kecepatan bijaksana), jika Anda tahu apa yang Anda parsing sebelumnya.
/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
. Sebaliknya saya melihat ada sesuatu yang disebut has_key
dan berfungsi.
Demi kelengkapannya, versi BeautifulSoup 4, menggunakan pengkodean yang disediakan oleh server juga:
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'])
atau versi 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']
dan versi menggunakan requests
pustaka , yang seperti yang ditulis akan bekerja di Python 2 dan 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'])
The soup.find_all('a', href=True)
panggilan menemukan semua <a>
elemen yang memiliki href
atribut; elemen tanpa atribut dilewati.
BeautifulSoup 3 menghentikan pengembangan pada Maret 2012; proyek baru benar-benar harus menggunakan BeautifulSoup 4, selalu.
Perhatikan bahwa Anda harus membiarkan decoding HTML dari byte ke BeautifulSoup . Anda dapat memberi tahu BeautifulSoup tentang karakter yang ditemukan di header respons HTTP untuk membantu dalam decoding, tetapi ini bisa salah dan bertentangan dengan <meta>
info header yang ditemukan dalam HTML itu sendiri, itulah sebabnya mengapa di atas menggunakan metode kelas internal BeautifulSoup EncodingDetector.find_declared_encoding()
untuk memastikan bahwa petunjuk enkode tertanam seperti itu menang atas server yang tidak terkonfigurasi.
Dengan requests
, response.encoding
atribut default ke Latin-1 jika respons memiliki text/*
mimetype, bahkan jika tidak ada karakter yang dikembalikan. Ini konsisten dengan HTTP RFCs tetapi menyakitkan ketika digunakan dengan parsing HTML, jadi Anda harus mengabaikan atribut itu ketika tidak charset
diatur dalam header Tipe-Konten.
SoupStrainer
maksud Anda? Itu tidak pergi ke mana pun, itu masih bagian dari proyek .
Orang lain merekomendasikan BeautifulSoup, tetapi jauh lebih baik menggunakan lxml . Meskipun namanya, itu juga untuk parsing dan memo HTML. Ini jauh, jauh lebih cepat daripada BeautifulSoup, dan bahkan menangani "rusak" HTML lebih baik daripada BeautifulSoup (klaim mereka untuk ketenaran). Ini memiliki API kompatibilitas untuk BeautifulSoup juga jika Anda tidak ingin mempelajari API lxml.
Tidak ada alasan untuk menggunakan BeautifulSoup lagi, kecuali Anda berada di Google App Engine atau sesuatu di mana segala sesuatu yang tidak murni Python tidak diizinkan.
lxml.html juga mendukung pemilih CSS3 sehingga hal semacam ini sepele.
Contoh dengan lxml dan xpath akan terlihat seperti ini:
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
sebagai parser default jika diinstal.
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'
Kode berikut adalah untuk mengambil semua tautan yang tersedia di halaman web menggunakan urllib2
dan 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'))
Di bawah tenda BeautifulSoup sekarang menggunakan lxml. Permintaan, lxml, & daftar pemahaman menjadikan kombo pembunuh.
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]
Dalam daftar comp, "jika '//' dan 'url.com' tidak dalam x" adalah metode sederhana untuk menggosok daftar url dari url navigasi 'internal' situs, dll.
hanya untuk mendapatkan tautan, tanpa B.soup dan 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]
untuk operasi yang lebih kompleks, tentu saja BSoup masih lebih disukai.
<a
dan href
? Katakan rel="nofollow"
atau onclick="..."
atau bahkan hanya baris baru? stackoverflow.com/questions/1732348/…
Script ini melakukan apa yang Anda cari, tetapi juga menyelesaikan tautan relatif ke tautan absolut.
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
Untuk menemukan semua tautan, dalam contoh ini kita akan menggunakan modul urllib2 bersama dengan re.module * Salah satu fungsi paling kuat dalam modul re adalah "re.findall ()". Sementara re.search () digunakan untuk menemukan kecocokan pertama untuk suatu pola, re.findall () menemukan semua kecocokan dan mengembalikannya sebagai daftar string, dengan setiap string mewakili satu kecocokan *
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
Mengapa tidak menggunakan ekspresi reguler:
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)
artinya Terima kasih!
Tautan dapat berada dalam beragam atribut sehingga Anda dapat melewati daftar atribut tersebut untuk dipilih
misalnya, dengan atribut src dan href (di sini saya menggunakan operator begin dengan ^ untuk menentukan bahwa salah satu dari nilai atribut ini dimulai dengan http. Anda dapat menyesuaikan ini sesuai kebutuhan
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 ^ = nilai]
Merupakan elemen dengan nama atribut attr yang nilainya diawali (didahului) oleh nilai.
Berikut ini adalah contoh menggunakan @ars jawabannya diterima dan BeautifulSoup4
, requests
, dan wget
modul untuk menangani 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)
Saya menemukan jawaban oleh @ Blairg23 berfungsi, setelah koreksi berikut (mencakup skenario yang gagal berfungsi dengan benar):
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)
Untuk Python 3:
urllib.parse.urljoin
harus digunakan untuk mendapatkan URL lengkap.
Parser BeatifulSoup sendiri bisa lambat. Mungkin lebih layak menggunakan lxml yang mampu melakukan parsing langsung dari URL (dengan beberapa batasan yang disebutkan di bawah).
import lxml.html
doc = lxml.html.parse(url)
links = doc.xpath('//a[@href]')
for link in links:
print link.attrib['href']
Kode di atas akan mengembalikan tautan apa adanya, dan dalam kebanyakan kasus mereka akan berupa tautan relatif atau absolut dari root situs. Karena use case saya hanya mengekstraksi jenis tautan tertentu, di bawah ini adalah versi yang mengubah tautan ke URL lengkap dan yang secara opsional menerima pola gumpalan seperti *.mp3
. Itu tidak akan menangani titik tunggal dan ganda di jalur relatif, tapi sejauh ini saya tidak membutuhkannya. Jika Anda perlu fragmen URL parsing mengandung ../
atau ./
kemudian urlparse.urljoin mungkin akan berguna.
CATATAN : Penguraian url lxml langsung tidak menangani pemuatan dari https
dan tidak melakukan pengalihan, jadi untuk alasan ini versi di bawah ini menggunakan 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
Penggunaannya adalah sebagai berikut:
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
hanya dapat menangani input yang valid, bagaimana cara menggantinya BeautifulSoup
?
lxml.html
sedikit lebih lunak daripada lxml.etree
. Jika input Anda tidak terbentuk dengan baik maka Anda dapat secara eksplisit mengatur parser BeautifulSoup: lxml.de/elementsoup.html . Dan jika Anda menggunakan BeatifulSoup maka BS3 adalah pilihan yang lebih baik.
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']
Mungkin ada banyak duplikat tautan bersama dengan tautan eksternal dan internal. Untuk membedakan antara keduanya dan dapatkan tautan unik menggunakan 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)