BeautifulSoup vuonna 2026: ystävällisin HTML-parseri on myös hitain (12–17x)

Viimeksi päivitetty July 17, 2026
BeautifulSoup vuonna 2026: ystävällisin HTML-parseri on myös hitain (12–17x)
AI-yhteenveto
Tämä arvio mittaa BeautifulSoupin kaikkein helposti lähestyttävimmäksi Pythonin HTML-parseriksi ja antaa tarkat luvut tuon helppokäyttöisyyden hinnasta. Se vertaa bs4:ää C-pohjaisiin parser-eihin nopeuden, rikkinäisen HTML:n sietokyvyn, CSS-valitsintuen, objektien säilymisen ja koodauksen tunnistuksen näkökulmista. Artikkeli osoittaa, että BeautifulSoup on selvästi hitaampi, usein 12–17 kertaa, mutta selittää myös, miksi kehittäjät silti valitsevat sen: luettavat API:t, armollinen parsinta, vahva soupsieve-valitsintuki ja erinomainen ergonomia sotkuisiin kertaluonteisiin scrapeihin. Se on käytännön opas siihen, milloin nopeusvero on hyväksyttävä ja milloin nopeampi parseri on parempi tekninen valinta.

BeautifulSoup on se kirjasto, johon lähes kaikki tarttuvat ensimmäisenä, kun he scrapaavat verkkosivua Pythonilla, ja se on ihan oikeasti vakavissaan tehtyjen HTML-parserien hitain. Molemmat väitteet pitävät paikkansa, eikä kumpikaan ole moite. Kiinnostava juttu on se, että “hitain” ei jää vain fiilikseksi, vaan siitä tulee tarkka ja mitattava luku.

Ajoin bs4:n (eli beautifulsoup4:n, versio 4.15.0, julkaistu kesäkuussa 2026, MIT-lisensoitu) läpi sekä uusien ominaisuustestien että saman benchmark-ympäristön aiempien ajoaikojen yhdistelmän, ja lopputulos on selvä: vaihdat noin kertaluokan verran nopeutta siinä, että saat kaikkein käyttäjäystävällisimmän API:n ja alan parhaan virhesiedon. Onko vaihto sen arvoinen, riippuu täysin omasta työkuormastasi, joten tämä arvio näyttää molemmat puolet reilusti.

Mitä BeautifulSoup oikeastaan on — ja mitä se ei ole

Useimmat oppaat ohittavat tärkeimmän pointin: BeautifulSoup ei itse parsii HTML:ää. Se on wrapperi. Kulissien takana se antaa dokumenttisi yhdelle kolmesta varsinaisesta parserista — Pythoniin sisäänrakennetulle html.parserille, lxml:lle tai html5lib:lle — ja pakkaa niiden rakentaman puun yhteen todella helppokäyttöiseen selaus- ja hakukerrokseen. bs4:n tehtävä ei ole parsia. Sen tehtävä on tehdä lopputuloksesta mukava käyttää.

Sen tekijä itse kutsuu sitä “screen-scraping libraryksi”, ja lupaus on ollut alusta asti sama: anna sille HTML, joka olisi selaimellekin hankala tapaus, ja se kaivaa silti esiin sen datan, jota pyydät. Tämä maine on ansaittu — yhdellä mutta tärkeällä varauksella, johon palataan kohta.

Muutama fakta kannattaa kirjata heti alkuun:

KenttäArvo
Pakettibeautifulsoup4 (importtina bs4)
Testattu versio4.15.0 (ladattu 2026-06-07)
Python-vaatimus>=3.7.0
LisenssiMIT
Virallinen kotisivucrummy.com/software/BeautifulSoup
Lähdekoodi + bugiseurantaLaunchpadei GitHub
YlläpitoAktiivinen (4.15.0 kesäkuussa 2026, kuusi julkaisua viimeisen vuoden aikana)

“Ei GitHub” on tärkeämpi huomio kuin miltä se ensiksi näyttää. bs4 on 20 vuotta vanha kirjasto, joka elää crummy.comissa ja Launchpadissa, joten tavallinen GitHub-tähtiin perustuva terveystsekkaus ei oikein toimi. Sen sijaan kuntoa kannattaa katsoa julkaisutahtiin — ja sillä mittarilla projekti on hyvin hengissä.

Yksi lisensointiin liittyvä yksityiskohta niille, joiden pitää raportoida compliance-tiimille: wrapperi on MIT-lisensoitu, mutta se, mitä “bs4:n käyttäminen” oikeasti tuo riippuvuuksiisi, riippuu siitä, minkä backendin asennat. html.parser on Pythonin standardikirjastoa (PSF-lisenssi, ei lisäriippuvuuksia). lxml on BSD-lisensoitu, mutta nojaa libxml2:een/libxslt:hen — ulkoiseen C-riippuvuuteen, joka pitää joko kääntää itse tai asentaa valmiina wheelinä. html5lib on puhdasta Pythonia ja MIT-lisensoitu. Jos haluat siisteimmän riippuvuuspinnan, sisäänrakennettu html.parser on siinä paras — ja sattumalta se on myös se backend, jossa on isoin miinus. Siitä lisää pian.

Nopeushinta, numeroiksi muutettuna

Laitetaan luvut ensin pöydälle, koska se on tämän jutun pääpointti, ja sen piilottaminen olisi epärehellistä. Realistinen parse-then-extract-tehtävä — parsitaan merkkijono, poimitaan kaikki <h3 class="title">-otsikot ja kaikki <a href> -linkit — tekee BeautifulSoupista tämän vertailun hitaimman parserin, eikä ero ole pieni.

BeautifulSoup speed tax: 232 ms versus 15 ms C parsers

Nämä ajoajat on lainattu selectolaxin benchmark-ympäristöstä (sama kone, sama 3 ajon metodologia, tilanne 2026-07-13); tätä arviota varten ei ajettu omia timing-benchmarikeja, jotta vältettiin CPU-ruuhka ja päällekkäinen työ. Mediaani p50-latenssi millisekunteina:

Sivun kokobs4 (html.parser)bs4 (lxml)selectolax-Lexborlxmlbs4-hp hitaampibs4-lxml hitaampi
1 KB0.3230.2810.0270.03612.0x10.5x
10 KB2.0491.7050.1600.16612.8x10.7x
100 KB20.59916.5401.4641.42314.1x11.3x
1 MB232.558181.85514.90114.17715.6x12.2x
10 MB2788.7462261.557159.935172.93317.4x14.1x

Eli bs4(html.parser) toimii noin 12–17x hitaammin kuin C-pohjainen parseri kuten selectolax-Lexbor, ja siirtyminen lxml-backendiin vie sen silti vain 10.5–14x hitaammaksi — eli edelleen selvästi yhden kertaluokan verran jäljessä. Syy on rakenteellinen, ei bugi: riippumatta siitä, mikä backend varsinaisesti parsii, bs4 rakentaa jokaisesta solmusta täyden Python-olion (Tag tai NavigableString). Tätä objektiksi muuttamisen kerrosta C-parserit eivät joudu maksamaan.

Huomaa myös, että kerroin kasvaa sivun kasvaessa — 12.0x 1 KB:lla, 17.4x 10 MB:lla. Se kertoo, ettei kyse ole kiinteästä käynnistysviiveestä, jonka voisi vain tasata isolla ajolla. Kyse on per-solmu-verosta, joka skaalautuu lineaarisesti rakennettavien solmujen määrän mukana.

Käännetään asia vielä toisin päin, koska “10x hitaampi” kuulostaa usein pahemmalta kuin se käytännössä on. 1 MB sivulla ero on 232 ms vs. 15 ms. Jos työsi on “scrapata muutamasta sadasta muutamaan tuhanteen sivua, muutamia satoja KB kerrallaan”, tämä absoluuttinen ero on käytännössä näkymätön — et tunne sitä, eikä optimoinnista ole sinulle hyötyä. Jos taas käsissäsi on miljoonan sivun putki, sama suhde tarkoittaa eroa työn valmistumisen ja epäonnistumisen välillä. Sama luku, täysin eri johtopäätös. Arvioi siis omaa volyymiasi, älä benchmarkia, vasten.

Ei, backendin vaihtaminen ei korjaa tätä

Pyörii sitkeä myytti, että jos annat bs4:lle lxml-backendin, saat lxml:n nopeuden. Et saa, ja on tärkeää ymmärtää miksi. 100 000 solmun CSS-kyselyssä (poimi jokainen <a> ja lue sen href, puu valmiiksi rakennettuna) läpäisykyky eroaa selvästi:

ParseriQuery p50Solmua/sek
lxml33.30 ms3,002,646
selectolax-Modest34.19 ms2,924,550
selectolax-Lexbor39.46 ms2,534,027
bs4 (lxml)250.56 ms399,111

bs4(lxml) pääsee noin 399 000 solmuun sekunnissa — eli noin 6.3–7.5x hitaammin kuin kolme C-moottoria, vaikka sen oma backend onkin lxml. Backend nopeuttaa puun rakentamista. Haku ja läpikäynti kulkevat silti soupsieven ja bs4:n Tag-objektien kautta, ja jokainen osuma paketoidaan edelleen Pythonissa. Siksi ajatus “anna bs4:lle lxml, niin siitä tulee lxml-nopea” on väärä: backend nopeuttaa vain yhtä vaihetta, eikä hitaus ole siinä vaiheessa.

Muisti ja cold start täydentävät kustannuksen. 10 MB dokumentilla bs4 käyttää noin 1.5–1.75x enemmän residenttiä muistia kuin selectolax tai lxml (218–226 MB vs. 129–145 MB) — sama juurisyy, yksi Python-olio per solmu. Ja bs4:n importointi kestää noin 33.4 ms verrattuna lxml.html:n 14.1 ms:iin, eli se on 2.36x hitaampi tuoda sisään. Jälkimmäinen on pitkään ajavassa prosessissa käytännössä merkityksetön, mutta CLI-työkalulle tai serverless-funktiolle, joka kylmäkäynnistyy usein, se on pieni mutta todellinen kustannus, joka kannattaa tietää.

Miksi useampi säie ei pelasta tätä

Jos ensimmäinen reaktiosi hitaaseen CPU-sidonnaiseen tehtävään on “laita säikeitä päälle”, bs4 rankaisee siitä. 1 MB sivulla, 48 ajokertaa, yksi säie vs. neljä säiettä:

Parseri1 säie4 säiettäNopeutus
selectolax-Lexbor0.563 s0.159 s3.54x
lxml0.459 s0.378 s1.21x
bs4 (lxml)6.945 s26.842 s0.26x

Lue alin rivi kahdesti. Neljä säiettä teki bs4:stä noin 3.9x hitaamman, ei nopeamman. Empiirinen signaali viittaa siihen, että se pitää GIL:n: bs4:n puunrakennus on puhdasta Pythonia, joten se serialisoituu Global Interpreter Lockin alle, ja säikeiden lisääminen vain kasvattaa ajoituskustannusta tehtävässä, jota ei oikeasti voi ajaa rinnakkain. selectolax saa noin 3.5x nopeutuksen, koska sen C-ydin vapauttaa lukon; bs4:llä ei ole sitä mahdollisuutta.

Free-threading-aikakaudella käytännön opetus on tämä: jos sinun täytyy rinnakkaistaa BeautifulSoup, käytä multiprocessingia (ProcessPoolExecutor), älä säikeitä. selectolax ja lxml voivat skaalautua säikeillä; bs4 ei. Yksi tarkennus tarkkuuteen: tämä on yksi havainto yhdellä säitemäärällä (4) ja yhdellä sivukoolla (1 MB), ja “pitää GIL:n” -mekanismi on wall-clock-käyttäytymisestä johdettu hypoteesi, ei asia, jonka vahvistin instrumentoimalla, mikä koodipolku pitää lukon. Suunta on selvä; tarkka mekanismi on alustava.

Oletusbackend on ansa. Lue tämä ensin.

Jos otat tästä arviosta vain yhden asian, ota tämä. Pelkkä BeautifulSoup(html) ilman toista argumenttia käyttää html.parseria, eikä html.parser toteuta HTML5:n optional-end-tag-sääntöjä. Kuulostaa akateemiselta — kunnes se korruptoi datasi hiljaa.

BeautifulSoup backend tolerance matrix: html.parser 12/15, lxml and html5lib 15/15

Ajoin 15 tarkoituksella rikottua HTML-esimerkkiä kaikkien kolmen backendin läpi, ja jokaiselle oli ennen ajoa määritelty backend-agnostinen rakenteellinen tarkistus, jotta kukaan ei pääsisi valitsemaan voittajaa jälkikäteen. Tulokset:

BackendVastaa odotusta / 15
lxml15
html5lib15
html.parser12

Kolmella epäonnistumisella on sama juurisyy. Otetaan esimerkiksi sulkematon taulukko: <table><tr><td>a<td>b<tr><td>c<td>d</table>. html.parserilla poimittu soluteksti tulee ulos muodossa ['abcd','bcd','cd','d'] — jokainen <td> nielee kaiken jälkeisen, koska parseri sisäkkäistää solut sen sijaan että sulkisi ne. lxml ja html5lib palauttavat oikein ['a','b','c','d']. Sama tapahtuu paljaiden listaelementtien kanssa: <li>a<li>b<li>c antaa html.parserilla sisäkkäisen ['abc','bc','c'], kun taas kaksi muuta palauttavat siistin ['a','b','c']. Myös duplikaatti-attribuutit kääntyvät: <div id="first" id="second"> pitää html.parserilla arvon "second", mutta lxml/html5lib säilyttävät "first", ja HTML5-spesifikaatio sanoo, että ensimmäinen kuuluu säilyttää.

Miksi tämä on vaarallista eikä vain ärsyttävää: se tapahtuu ilman virheilmoitusta. Scraperi, joka huolettomasti käyttää BeautifulSoup(html)-kutsua ja törmää sulkemattomaan taulukkoon tai listaan — mikä on harmillisen yleistä vanhoilla sivuilla, käsin kirjoitetussa HTML:ssä ja templateissa, joista jäi sulkumerkki pois — vuotaa viereisen solun tekstin samaan kenttään, antaa likaisen datan eikä sano asiasta mitään. Korjaus on yksi argumentti: BeautifulSoup(html, "lxml") tai BeautifulSoup(html, "html5lib").

Annetaan reilusti tunnustusta html.parserille: muut 12/15 rikkinäisestä esimerkistä tulivat kaikilla kolmella backendillä identtisinä ulos — väärin sisäkkäiset tagit kuten <b><i></b></i>, puuttuva html/body-runko, lainausmerkeettömät attribuutit, orvot suljetut tagit, sulkeutumattomat kommentit, sisäkkäiset lomakkeet, sekamerkkikoko ja paljon muuta. bs4:n sietokyky on oikeasti vahva kautta linjan; erot keskittyvät lähes kokonaan optional-end-tag -tapauksiin. Eikä mikään tästä ole uusi havainto — bs4:n oma “Differences between parsers” -dokumentaatio sanoo jo suoraan, että html.parser on “less lenient”. Rikkinäisten esimerkkien matriisi tuo vain näkyviin täsmälliset, toistettavat tapaukset, joissa “vähemmän armollinen” muuttuu vääräksi tulokseksi.

Mitä et menetä: API ja CSS ovat tämän kirjaston parasta antia

bs4 on siis hidas, yksisäikeinen ja siinä on oletusbackendin sudenkuoppa. Silti ihmiset tarttuvat siihen, koska vaihdon “mukava” puoli on täysin todellinen — ja testit vahvistavat sen.

BeautifulSoup soupsieve CSS coverage wins with 41/41 plus 20/20

Ajoin 29 API-testiä, jotka kattoivat haun, CSS:n, puunavigoinnin, tekstin poiminnan ja DOM-muutokset. Kaikki 29 menivät läpi, ja jokaisen tulos laskettiin vertaamalla todellista palautusarvoa odotettuun arvoon, ei silmämääräisesti. Kaksi näistä kyvykkyyksistä ovat juuri sitä ergonomiaa, jota C-parserit eivät vain tarjoa:

  • Funktiopredikaatit find / find_all -kutsuissa. Voit kirjoittaa soup.find(lambda t: t.name == "a" and "btn" in t.get("class", [])) ja ilmaista monimutkaisen ehdon yhdellä Python-rivillä — ei tarvitse tehdä “valitse kaikki, suodata sitten” -kierrosta.
  • Nimetty, kaksisuuntainen puunavigointi. .parent, .next_sibling, .find_parent, .stripped_strings, .descendants — läpikäynnit ovat luettavia kuin englanti ja kulkevat molempiin suuntiin. selectolax vaatii joissain näistä useita vaiheita tai ei tarjoa niitä lainkaan.

Tässä on se konkreettinen osa “säästää kehittäjän aikaa” -väitteestä. Se ei ole markkinointia; se on 29 vihreää checkiä.

Kaksi ansaa on silti hyvä nostaa esiin, koska reilu arvio kertoo molemmat puolet. Ensimmäinen liittyy boolean-attribuutteihin: <input disabled> palauttaa bs4:ssä disabled-arvoksi tyhjän merkkijonon "" (selectolax palauttaa None). Molemmat ovat falsy-arvoja, joten if node.get("disabled") ohittaa hiljaa boolean-attribuutin, joka on oikeasti olemassa kummassakin kirjastossa — turvallinen tarkistus on "disabled" in tag.attrs. Toinen: get_text(strip=True) liittää solujen tekstin yhteen ilman erotinta stripin jälkeen, joten "...with " + "link1" muuttuu muotoon "withlink1". Anna separator=" ", kun tarvitset sanakatkoja. Kumpikaan ansa ei ole bs4-spesifinen; molemmat ovat eri kirjastojen yhteisiä kompastuskiviä.

Ja nyt se osa, joka yllättää monia: bs4:n valitseminen ei vie sinulta CSS-tukea. Sen CSS-moottori, soupsieve, on tämän vertailun kattavin toteutus. 41 tapauksen perusmatriisissa (lainattu selectolax-ympäristöstä) soupsieve sai tuloksen 41/41 — alan ainoa täydet pisteet saanut toteutus, edellä selectolax-Lexborin 39/41 ja cssselectin (lxml/parsel) 37/41. Sen jälkeen ajoin 20 lisättyä laajennettua tapausta, joita soupsieven dokumentaatio mainostaa, ja tulos oli 20/20, mukaan lukien sellaiset valitsimet, jotka Lexbor hylkää täysin: :lang(en), soupsieve-uniikin :-soup-contains('featured')-valitsimen, :is(), :where() ja :has(> a). Ainoat oikeat puutteet ovat XPath (soupsieve on vain CSS) ja parselin ::text / ::attr()-pseudoelementit, jotka ovat Scrapy-laajennuksia. Jos elät XPath-maailmassa, migraatio sattuu.

Tämän osion johtopäätös on selvä: se, mistä luovut valitsemalla BeautifulSoupin, on nopeus. Ei API-ergonomia, eikä todellakaan CSS-tuki.

Kaksi tuotantokäytön ansaa, joihin kannattaa budjetoida aikaa

Oletusbackendin lisäksi kaksi käyttäytymistä puree erityisesti pitkäikäisissä tai ei-UTF-8-työkuormissa.

Viittauskierrot: kutsu decompose() pitkissä silmukoissa

Jokainen bs4:n Tag pitää viittausta sekä vanhempaansa että lapsiinsa, mikä muodostaa viittauskierron. CPythonin viitelaskenta ei pysty vapauttamaan kiertoa itsenäisesti — se on sukupolvisen roskienkerääjän tehtävä. Jotta näkee, kuinka paljon sillä on väliä, rakensin ja poistin puun 300 kertaa GC pois päältä, ja laskin sitten muistissa vielä roikkuvat Tag-oliot:

BeautifulSoup reference cycles retain 120,900 objects with GC off and 0 with GC on

TilanneJäljelle jääneet Tag-oliot del-kutsun jälkeen
GC pois päältä120,900 (300 sykliä, mitään ei vapautunut)
GC päällä26,598 (sukupolvinen GC käynnistyi kesken silmukan)
Pakotetun gc.collect()-kutsun jälkeen0 (kaikki vapautuivat)
Ei-kierron kontrolli (merkkijonolista, GC pois päältä)delta 0

Kun GC oli pois päältä, del soup ei vapauttanut mitään — kaikki 120 900 oliota jäivät muistiin, koska viittauskierto kaataa viitelaskennan. Yksi gc.collect() tyhjensi ne kaikki. Ei-kiertoinen kontrolliryhmä (pelkkä merkkijonolista, jonka tiedetään olevan kiertoton) pysyi nollassa, mikä todistaa, että kasaantuminen tuli bs4:n kierrosta eikä mittauskohinasta. bs4:n omat dokumentit sanovat, että objektit ovat “densely interconnected ... exactly the sort a garbage collector would have trouble with”, joten tämä on dokumentoitua käyttäytymistä; testi tuo mukaan säilytettyjen objektien määrän ja todisteen siitä, että collect() nollaa tilanteen.

Käytännön sääntö: putkessa, joka parsii monta suurta sivua tiukassa loopissa, jos koodisi (tai jokin korkean läpimenon asetus) kytkee GC:n pois tai ei laukaise sitä tarpeeksi usein, bs4-puut jäävät roikkumaan ja muisti kasvaa. Kutsu soup.decompose() jokaisen sivun jälkeen — bs4 tarjoaa sen juuri siksi, että kierrot katkeavat ja vapautus tapahtuu aikaisemmin. selectolaxin ja lxml:n C-puut eivät kärsi tästä lainkaan.

Koodaus: UnicodeDammit on bs4:n hiljainen etu

bs4:n mukana tulee komponentti, jota nopeat parserit eivät tarjoa: UnicodeDammit, joka tunnistaa dokumentin koodauksen ja muuntaa sen automaattisesti Unicodeksi. Annoin sille 8 tapauksen “ilmoitettu vs. todellinen charset” -matriisin:

BeautifulSoup UnicodeDammit recovers 5 of 8 encoding cases

TapausTodellinen koodausUnicodeDammit arveliPalautuiko?
utf8_no_declutf-8utf-8Kyllä
utf16_bomutf-16utf-16leKyllä
gbk_chinesegbkgb18030Kyllä (superset)
shiftjisshift_jiscp932Kyllä (superset)
latin1_declared_utf8latin-1 (ilmoitettu utf-8)iso-8859-1Kyllä (ohitti valheen)
latin1_no_decllatin-1cp720Ei
cp1252_no_declcp1252cp862Ei
utf8_declared_latin1utf-8 (ilmoitettu latin-1)iso-8859-1Ei (seurasi valhetta)

Viisi kahdeksasta palautui oikein. UTF-8, BOM:illinen UTF-16, GBK, Shift-JIS ja jopa väärin merkitty latin-1 tulivat kaikki takaisin oikein, ja superset-arviot (GBK→gb18030, Shift-JIS→cp932) dekoodaavat silti hyvin. Kaksi epäonnistumistapaa kannattaa tuntea: lyhyet latin-1/cp1252-bittinäytteet tulkitaan joskus DOS-koodisivuiksi, koska tilastollinen tunnistus ei ole lyhyillä syötteillä luotettava ja DOS:n laatikkopiirto-merkkejä löytyy samoista pisteistä kuin latin-1:n koodeja; ja kun <meta charset>-ilmoitus on yksinkertaisesti väärä, UnicodeDammit uskoo ilmoitusta. bs4:n dokumentaatio huomauttaa molemmista — näyte voi olla “so short that Unicode, Dammit can't get a lock on it”, ja enemmän dataa tarkoittaa parempaa arvausta.

selectolaxiin verrattuna, joka korruptoi non-UTF-8-bitit hiljaa ja odottaa sinun dekoodaavan ne itse, tämä on aito etu: bs4 ainakin yrittää tunnistaa koodauksen ja onnistuu usein. Mutta se ei ole tae. Kun koodaus on tiedossa, ohita arvaaminen ja ole eksplisiittinen: BeautifulSoup(bytes, from_encoding="...").

Eroavatko backendit oikeasti koskaan todellisilla sivuilla?

Rikkinäinen HTML-matriisi näyttää backendien eroavan tarkoituksella rikotulla syötteellä. Luonnollinen seuraava kysymys on, onko sillä väliä oikeassa maailmassa, joten ajoin kaikki kolme backendia 11 oikean, haetun sivun läpi — BBC, Wikipedia, Craigslist, MDN, old.reddit, Python docs, Hacker News, Books to Scrape, webscraper.io, whitehouse.gov ja JS-renderöity quotes-sivu — ja vertasin linkki-, otsikko- ja kuvamääriä.

Kaikki kolme olivat samaa mieltä kaikilla 11 sivulla. Ei yhtään eroa. Se tarkoittaa, että trap-osiossa nähty backend-erimielisyys näkyy vain tarkoituksella rikotussa HTML:ssä; kun moderni tuotantosivu on rakenteeltaan riittävän kunnossa — vaikka se olisi “sekava” — backendin valinta ei muuta sitä, mitä poimit. Käytännössä: valtavirran, hyvin muodostetuilla sivuilla html.parser on täysin kelvollinen ja säästää riippuvuuden. Vasta kun scrapat näkyvästi epästandardia, käsin kirjoitettua tai ikivanhaa HTML:ää, backendin valinta alkaa muuttaa tulosta, ja silloin kannattaa vaihtaa lxml:ään tai html5libiin.

Yksi sivuhuomio tuosta ajosta, koska se on oikea reunatapaus. MDN-sivulla on <template>-elementti, ja kaikki bs4-backendit palauttivat 508 linkkiä — eli bs4 litistää <template>-sisällön pääpuuhun. Tässä bs4 on samalla puolella kuin lxml, ja vastakkaisella puolella kuin selectolax-Lexbor, joka noudattaa tiukasti HTML5-spesifikaatiota (<template> on inertti DocumentFragment) ja palauttaa 497, pudottaen hiljaa 11 linkkiä templateen sisälle. bs4 siis nappaa datan <template>-elementin sisältä — hyödyllistä, mutta samalla tapa poimia “haamisisältöä”, jota selain ei koskaan renderöisi. Kumpikaan käytös ei ole väärin; ne ovat eri spesifikaatiotulkintoja, ja sinun kannattaa tietää, kumman saat.

Mihin BeautifulSoup sopii — ja mihin ei

Sen sijaan, että puristaisi kaiken yhteen 0–100-pisteeseen, mikä peittäisi juuri ne kompromissit, joilla on oikeasti väliä, tässä on dimensioittainen arviointitaulukko, ja jokaisella rivillä on oma huomionsa:

UlottuvuusMitä testit löysivätLukijan huomio
Asennus / ensimmäinen ajoPelkkä wrapperi, ei selainta tai asetuksia; html.parserissa ei riippuvuuksia; kaikille valmiit wheelitlxml-backend tarvitsee C-riippuvuuden
Nopeus vs C-parserit12–17x hitaampi (html.parser) / 10.5–14x (lxml-backend), kaikissa kokoluokissaYksi testialusta; selectolax-dataa käytettiin uudelleen
CSS-kyselyiden läpimeno~6–7.5x hitaampi 100k solmulla; lxml-backend ei pelastaKäytetty uudelleen; maksaa Python Tag -veron
Muisti1.5–1.75x selectolax/lxml; raskainKäytetty uudelleen; mitattu RSS:nä
Importin cold start2.36x hitaampi (33.4 vs 14.1 ms)Käytetty uudelleen; pieni yksittäinen kustannus
Säie-skalausbs4-lxml ~3.9x hitaampi 4 säikeellä (pitää GIL:n)Yksi havainto; käytä multiprocessingia
API-ergonomia29/29 testiä; funktiopredikaatti-find + kaksisuuntainen navigointiTyhjä merkkijono boolean-attribuuteissa ja strip-sanakatkojen ansat
CSS-tukisoupsieve on vahvin: 41/41 perus + 20/20 laajennettu; tukee :langEi XPathia, ei ::text-elementtejä
3 backendin sietokykylxml/html5lib 15/15; html.parser 12/15Erot vain rikkinäisessä HTML:ssä
Oikeiden sivujen yhtenäisyys3 backendia samaa mieltä 11/11; kaikki litistävät <template>-sisällön (508)Hyvin muodostetuilla sivuilla backendillä ei ole väliä
Viittauskierrot / GCPuu on kierto; 300 kierrosta jätti 120,900 objektia, collect nollasi nePitkissä loopissa tarvitaan decompose()
KoodausUnicodeDammit palauttaa 5/8; arvioi väärin lyhyitä näytteitä, uskoo huonoja ilmoituksiaYksi havainto
YlläpitoAktiivinen (4.15.0, kesäkuu 2026); MITKotisivu crummy/Launchpadissa, ei GitHubissa

Kenelle BeautifulSoup siis sopii? Kaikille, jotka arvostavat luettavaa API:a ja armollista parsintaa enemmän kuin raakaa läpimenoa — prototyypeille, kertaluonteisiin scrapeihin, sisäisiin työkaluihin, tiimeille, joissa kehittäjäaika on kalliimpaa kuin ajonaika. Kenen kannattaa katsoa muualle? Miljoonan sivun putket, joissa nopeusvero kasautuu oikeaksi rahaksi, työkuormat, jotka tarvitsevat säietason rinnakkaisuutta, ja kaikki, jotka ovat sidottuja XPathiin.

Pieni huomio siitä, mihin tämä sijoittuu oikeassa scraping-pinossa, ja missä oma työkalumme tulee kuvaan. BeautifulSoup olettaa, että sinulla on jo HTML hallussasi. Se ei hae sivuja, se ei renderöi JavaScriptiä, eikä se tee mitään bot-suojauksille tai CAPTCHA-haasteille — se on täysin eri ja nykyverkossa oikeasti vaikea tehtävä. Tässä kohtaa AI-scraping-API istuu eri kerrokseen: Thunderbit:n kehittäjäpino — REST API, MCP-serveri ja CLI — hoitaa haun, JS-renderöinnin ja bot-ongelman, ja palauttaa joko siistin Markdownin (POST /distill) tai skeemaan sopivan jäsennellyn JSONin (POST /extract) ilman että kirjoitat valitsimia lainkaan. Ne eivät ole kilpailijoita; ne täydentävät toisiaan. bs4 parsii HTML:n, joka sinulla jo on; Thunderbitin API, MCP ja CLI hankkivat HTML:n, johon et muuten pääse helposti käsiksi. Jos pullonkaulasi on parsinta, bs4 on hyvä vastaus. Jos pullonkaulasi on hankinta, se on toinen kerros.

Kokeile Thunderbitiä verkkodatan poimintaan

Lopputulos

BeautifulSoup antaa sinulle ystävällisimmän API:n, vahvimman rikkinäisen HTML:n sietokyvyn ja tämän vertailun kattavimman CSS-moottorin — mutta hinta on noin kertaluokkaa oleva nopeusvero ja raskain muistijalanjälki. Siinä koko diili, suoraan sanottuna. Oletusarvoinen html.parser-backend on ainoa oikea ansa: se sotkee hiljaa sulkemattomat taulukot ja listat, joten anna aina "lxml" tai "html5lib", kun syöte voi olla ruma. Säikeillä se ei nopeudu — multiprocessingilla se nopeutuu. Ja pitkäikäisissä silmukoissa kutsu jokaisen sivun jälkeen decompose(), jotta viittauskierrot eivät pääse kasaantumaan.

Kaksi rajoitusta lopuksi. Kaikki tässä mitattiin yhdellä alustalla (macOS arm64, Python 3.14, valmiit wheelit), ja ajoaikojen kertoimet on lainattu selectolax-ympäristöstä (sama bench, tilanne 2026-07-13) eikä ajettu uudestaan — joten ne perivät tämän yhden alustan rajoitteen, ja Linux x86_64- tai lähdekoodista käännetty ympäristö voisi siirtää tarkkoja lukuja. Eikä mikään näistä tuloksista ole uusi löydös: bs4 on 20 vuotta vanha kirjasto, joten jokainen testattu käyttäytyminen on joko dokumentoitu tai julkisesti tallennettu. Arvo ei ole uutispommi. Arvo on siinä, että kompromisseille annetaan oikeat numerot, kun dokumentaatio kuvaa ne vain laadullisesti.

Usein kysytyt kysymykset

Onko BeautifulSoup hidas? Kyllä, mitattavasti. Parse + extract -tehtävässä se on noin 12–17x hitaampi kuin C-parseri kuten selectolax-Lexbor oletuksella html.parser, ja 10.5–14x hitaampi lxml-backendillä, koska se rakentaa Python-olion jokaiselle solmulle. Onko sillä väliä, riippuu mittakaavasta: 1 MB sivulla ero on 232 ms vs. 15 ms — muutamalle tuhannelle sivulle käytännössä näkymätön, mutta miljoonan sivun putkessa ratkaiseva.

Minkä BeautifulSoup-parserin minun pitäisi valita — html.parser, lxml vai html5lib? Hyvin muodostetuille valtavirran sivustoille oletus html.parser on täysin kelvollinen eikä tuo lisäriippuvuuksia. Mutta se ei toteuta HTML5:n optional-end-tag-sääntöjä, joten sulkemattomissa taulukoissa tai listoissa se vuotaa viereistä tekstiä yhteen ilman virheilmoitusta. Kun syöte voi olla rikkinäinen, käsin kirjoitettu tai vanha, anna "lxml" tai "html5lib" eksplisiittisesti — molemmat saivat siistin 15/15 tuloksen rikkinäisen HTML:n matriisissa, jossa html.parser sai 12/15.

Voiko BeautifulSoup parsoa rinnakkain säikeillä? Ei. bs4:n puunrakennus on puhdasta Pythonia ja pitää GIL:n, joten säikeiden lisääminen tekee siitä hitaamman, ei nopeamman — testissä neljä säiettä ajoi 1 MB parsinnan noin 3.9x hitaammin kuin yksi säie. Jos haluat rinnakkaistaa bs4:n, käytä multiprocessingia (ProcessPoolExecutor). C-ytimiset kirjastot kuten selectolax ja lxml ovat niitä, joista säietason rinnakkaisuus oikeasti hyötyy.

Käsitteleekö BeautifulSoup rikkinäistä HTML:ää hyvin? Yleisesti kyllä — useilla rikkinäisillä esimerkeillä (väärin sisäkkäiset tagit, puuttuvat rungot, lainausmerkeettömät attribuutit ja muuta) kaikki kolme backendia toipuivat siististi. Ainoa heikko kohta on oletus html.parser ja optional-end-tags: sulkemattomat <td>/<li>-elementit sisäkkäistyvät sulkeutumisen sijaan ja sotkevat poimitun tekstin. Vaihda lxml- tai html5lib-backendiin, niin tuo ongelmaluokka poistuu.

BeautifulSoup vs lxml — kumpi on parempi? Ne ovat eri työkaluja. lxml on paljon nopeampi sekä puunrakennuksessa että hauissa, ja tukee XPathia. BeautifulSoup taas käärii lxml:n (ja muut) paljon ystävällisempään API:in, ja sillä on itse asiassa laajempi CSS-tuki soupsieven kautta. Älä kuitenkaan odota, että lxml-backend tekisi bs4:stä lxml-nopean — backend nopeuttaa vain parsintaa, kun taas kyselyt ja läpikäynti maksavat edelleen bs4:n solmukohtaisen Python-oliokustannuksen, joten suurissa massavalinnoissa se jää noin 6–7.5x hitaammaksi.

Kokeile Thunderbitiä verkkodatan poimintaan Get Started Free

Ke
Ke
Thunderbitin CTO | Senior Data Scientist & ML-asiantuntija Lähes vuosikymmenen kokemuksella koneoppimisesta ja data science -työstä Ke Shen on Columbia Universityn alumni ja entinen Senior Data Scientist Walmart Labsilla. Hänellä on syvällistä, alan kollegoiden tunnustamaa asiantuntemusta Pythonista, R:stä, Javasta ja tilastotieteestä, ja hän jakaa käytännössä koeteltuja oivalluksia siitä, miten monimutkaiset tekoälyalgoritmit viedään teoriasta tuotantokäyttöön sopivaksi arkkitehtuuriksi.

Kokeile Thunderbitia

Poimi liidejä ja muuta dataa vain kahdella klikkauksella. AI:n voimin.

Hanki Thunderbit Se on ilmainen
Poimi dataa AI:n avulla
Siirrä data helposti Google Sheetsiin, Airtableen tai Notioniin
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week