BeautifulSoup 2026: Den mest lättanvända HTML-parsern är också den långsammaste (med 12–17x)

Senast uppdaterad July 17, 2026
BeautifulSoup 2026: Den mest lättanvända HTML-parsern är också den långsammaste (med 12–17x)
AI-sammanfattning
Den här recensionen visar BeautifulSoup som den mest lättillgängliga HTML-parsern i Python och sätter en exakt kostnad på den bekvämligheten. Den jämför bs4 med C-baserade parsers över hastighet, feltolerans för trasig HTML, täckning av CSS-selektorer, objektbevaring och återhämtning av teckenkodning. Artikeln visar att BeautifulSoup är dramatiskt långsammare, ofta med 12 till 17 gånger, men förklarar också varför utvecklare ändå väljer det: läsbart API, förlåtande parsning, starkt stöd för soupsieve-selektorer och mycket bra ergonomi för stökiga engångsskrapningar. Det är en praktisk guide till när hastighetskostnaden är acceptabel och när en snabbare parser är det bättre tekniska valet.

BeautifulSoup är biblioteket nästan alla tar till första gången de ska skrapa en webbsida i Python, och det är också faktiskt den långsammaste av de seriösa HTML-parsers som finns. Båda sakerna stämmer, och det är ingen kritik. Det intressanta är att ”långsammaste” visar sig vara en exakt och mätbar kostnad, inte bara en magkänsla.

Jag körde bs4 (alltså beautifulsoup4, version 4.15.0, släppt i juni 2026 och licensierad under MIT) genom en kombination av nya funktionstester och återanvända timingdata från samma benchmarkmiljö. Bilden är tydlig: du byter ungefär en tiopotens i hastighet mot marknadens mest lättanvända API och starkaste feltolerans. Om det är värt det beror helt på din arbetsbelastning, så den här genomgången håller båda sidorna i fokus.

Vad BeautifulSoup faktiskt är – och inte är

Många guider hoppar över det viktigaste: BeautifulSoup tolkar inte HTML själv. Det är ett skal. Under huven lämnar det vidare dokumentet till en av tre riktiga parsers — Pythons inbyggda html.parser, lxml eller html5lib — och sveper sedan in trädet de skapar i ett enda, väldigt användarvänligt API för navigering och sökning. bs4:s uppgift är alltså inte att parsa. Den ska göra resultatet trevligt att arbeta med.

Dess egen skapare kallar det ett ”screen-scraping library”, och idén har alltid varit densamma: peka det mot HTML som är så trasig att en webbläsare nästan ryggar tillbaka, och ändå få ut datan du bad om. Det ryktet är välförtjänt, med ett litet förbehåll som vi kommer till.

Några fakta som är bra att få på plats direkt:

FältVärde
Paketbeautifulsoup4 (importeras som bs4)
Testad version4.15.0 (uppladdad 2026-06-07)
Krav på Python>=3.7.0
LicensMIT
Officiell hemsidacrummy.com/software/BeautifulSoup
Källkod + bugghanteringLaunchpadinte GitHub
UnderhållAktivt (4.15.0 i juni 2026, sex releaser under det senaste året)

”Inte GitHub” är mer relevant än det låter. bs4 är ett 20 år gammalt bibliotek som lever på crummy.com och Launchpad, så den vanliga känslan från GitHub-stjärnor säger inte så mycket. Bedöm i stället hälsan efter hur ofta det släpps nya versioner — och där mår det bra.

En liten detalj om licens, för dig som måste förklara detta för en compliance-avdelning: själva wrappern är MIT, men vad ”att använda bs4” faktiskt drar in i beroendeträdet beror på vilken backend du installerar. html.parser finns i standardbiblioteket i Python (PSF-licens, inga extra beroenden). lxml är BSD-licensierat, men bygger på libxml2/libxslt — ett externt C-beroende som du antingen kompilerar eller får som färdigt wheel-paket. html5lib är ren Python och MIT. Vill du ha minsta möjliga beroendefotavtryck är den inbyggda html.parser bäst — vilket också råkar vara backend med den största nackdelen. Mer om det strax.

Hastighetskostnaden, i siffror

Låt oss börja med siffran, eftersom det är rubriken och det vore oärligt att dölja den. I en realistisk uppgift där man först parsa och sedan extraherar — parsa strängen, plocka ut alla <h3 class="title"> och alla <a href> — är BeautifulSoup långsammast i jämförelsen, och inte ens nära.

BeautifulSoup-hastighetskostnad: 232 ms jämfört med 15 ms för C-parserar

Tiderna här är återanvända från selectolax-benchmarken (samma maskin, samma metod med tre körningar, per 2026-07-13); den här recensionen kör inga egna timingtester för att undvika CPU-krockar och dubbelarbete. Medianlatens p50, i millisekunder:

Sidstorlekbs4 (html.parser)bs4 (lxml)selectolax-Lexborlxmlbs4-hp långsammarebs4-lxml långsammare
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

Alltså är bs4(html.parser) ungefär 12–17x långsammare än en C-baserad parser som selectolax-Lexbor, och att byta till lxml-backend drar bara tillbaka det till 10.5–14x — fortfarande en hel storleksordning efter. Orsaken är strukturell, inte ett fel: oavsett vilken backend som gör själva parsningen bygger bs4 ett komplett Python-objekt (en Tag eller NavigableString) för varje nod. Det där objektlagret är en kostnad som C-parserarna helt enkelt slipper.

Notera också att multipeln ökar ju större sidan blir — 12.0x vid 1 KB, 17.4x vid 10 MB. Det visar att det här inte är en fast startkostnad som går att ”äta upp” med större volym. Det är en kostnad per nod som skalar linjärt med hur många noder du bygger.

Men låt oss sätta det i perspektiv, för ”10x långsammare” låter värre än det ofta är. På en sida på 1 MB handlar det om 232 ms jämfört med 15 ms. Om jobbet är ”skrapa några hundra till några tusen sidor på några hundra KB vardera”, då märker du sannolikt inte skillnaden alls — och att optimera bort den ger dig inget. Om jobbet däremot är en pipeline på en miljon sidor, då avgör samma förhållande om arbetet blir klart eller inte. Samma siffra, helt olika slutsats. Bedöm mot din faktiska volym, inte mot benchmarken.

Nej, att byta backend löser inte problemet

Det finns en seglivad myt om att du kan ge bs4 lxml som backend och få lxml:s hastighet. Det går inte, och det är viktigt att förstå varför. På en CSS-fråga över 100 000 noder (hämta alla <a> och läs deras href, med trädet redan uppbyggt) ser genomströmningen ut så här:

ParserQuery p50Noder/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) klarar ungefär 399 000 noder per sekund — runt 6.3–7.5x långsammare än de tre C-motorerna, trots att dess egen backend faktiskt är lxml. Backend:en snabbar upp själva byggandet av trädet. Frågor och traversal går fortfarande via soupsieve och bs4:s Tag-objekt, och varje matchad nod boxas fortfarande in i Python. Så tankemodellen ”ge bs4 lxml så blir det lxml-snabbt” är fel: backend:en snabbar upp ett steg, men inte det långsammaste steget.

Minne och kallstart kompletterar bilden. På ett dokument på 10 MB använder bs4 cirka 1.5–1.75x mer resident minne än selectolax eller lxml (218–226 MB mot 129–145 MB) — samma grundorsak, ett Python-objekt per nod. Och att importera bs4 tar ungefär 33.4 ms jämfört med 14.1 ms för lxml.html, så importen är 2.36x långsammare. Det sista spelar liten roll i en långlivad process, men för ett CLI-verktyg eller en serverless-funktion som kallstartar ofta är det en liten men verklig kostnad att känna till.

Varför fler trådar inte räddar dig

Om din första instinkt vid en långsam CPU-bunden uppgift är ”släng in fler trådar”, så kommer bs4 att straffa den instinkten. På en 1 MB sida som parsas 48 gånger, jämförelse mellan en tråd och fyra trådar:

Parser1 tråd4 trådarSpeedup
selectolax-Lexbor0.563 s0.159 s3.54x
lxml0.459 s0.378 s1.21x
bs4 (lxml)6.945 s26.842 s0.26x

Läs nedersta raden två gånger. Fyra trådar gjorde bs4 ungefär 3.9x långsammare, inte snabbare. Empirin pekar mot att det troligen håller GIL: bs4:s trädbygge är ren Python-kod, så det serialiseras under Global Interpreter Lock, och fler trådar lägger bara på schemaläggningskostnad på ett jobb som ändå inte kan köras parallellt. selectolax får sin ~3.5x snabbare körning eftersom dess C-kärna släpper låset; bs4 har inget sådant utrymme.

För eran med free-threading är det praktiska rådet därför: om du behöver parallellisera BeautifulSoup, använd multiprocessing (ProcessPoolExecutor), inte trådar. selectolax och lxml kan skalas med trådar; bs4 kan inte det. Ett förbehåll om noggrannhet: detta bygger på en enda observation vid en trådmodell (4) på en sidstorlek (1 MB), och mekanismen ”håller GIL” är en slutsats dragen från wall-clock-beteende, inte något jag verifierat genom att instrumentera exakt vilken kodväg som håller låset. Riktningen är tydlig; den exakta mekanismen är preliminär.

Standardbackenden är fällan. Läs detta först.

Om du tar med dig en enda sak från den här genomgången, ta den här. Ett vanligt BeautifulSoup(html) utan andra argument använder html.parser, och html.parser implementerar inte HTML5:s regler för valfria avslutande taggar. Det låter akademiskt tills det tyst korrumperar din data.

BeautifulSoup backend-tolerans: html.parser 12/15, lxml och html5lib 15/15

Jag körde 15 medvetet trasiga HTML-exempel genom alla tre backends, med en backend-agnostisk strukturell kontroll förregistrerad för var och en innan körningen (så ingen kan utse vinnaren i efterhand). Resultatet:

BackendUppfyller förväntan / 15
lxml15
html5lib15
html.parser12

De tre felen har samma grundorsak. Ta en oavslutad tabell: <table><tr><td>a<td>b<tr><td>c<td>d</table>. Med html.parser blir den extraherade celltexten ['abcd','bcd','cd','d'] — varje <td> sväljer allt efter sig, eftersom parsern nästlar cellerna i stället för att avsluta dem. lxml och html5lib ger korrekt ['a','b','c','d']. Vanliga listobjekt beter sig likadant: <li>a<li>b<li>c ger den nästlade varianten ['abc','bc','c'] i html.parser, men den rena ['a','b','c'] i de andra två. Dubblettattribut beter sig också annorlunda — <div id="first" id="second"> behåller "second" i html.parser men "first" i lxml/html5lib, och HTML5-specen säger att den första ska behållas.

Här är varför det är farligt, inte bara irriterande: det sker utan felmeddelande. En scraper som slentrianmässigt kör BeautifulSoup(html) och råkar stöta på en oavslutad tabell eller lista — vilket är pinsamt vanligt på gamla sajter, handskriven HTML och mallar som glömde en stängande tagg — kommer att blanda ihop närliggande celltext till ett enda fält, ge dig smutsig data och aldrig säga ifrån. Lösningen är ett enda argument: BeautifulSoup(html, "lxml") eller BeautifulSoup(html, "html5lib").

För rättvisans skull ska sägas att de andra 12 av 15 trasiga exempel gav identiskt resultat i alla tre backends: felnästlade taggar som <b><i></b></i>, saknad html/body-struktur, oquotade attribut, överblivna avslutande taggar, oavslutade kommentarer, nästlade formulär, blandade versaler och mer. bs4:s tolerans är verkligen stark överlag; avvikelserna koncentrerar sig nästan helt till familjen med valfria avslutande taggar. Och inget av detta är någon ny upptäckt — bs4:s egen dokumentation om ”Differences between parsers” säger redan med enkel text att html.parser är ”less lenient”. Det den trasiga-matrisen lägger till är de specifika, reproducerbara fall där ”mindre förlåtande” blir fel output.

Det du inte ger upp: API:t och CSS-stödet är dess bästa sida

Så bs4 är långsamt, enkeltrådat och har en fälla i standardbackenden. Ändå tar folk det fortfarande först, eftersom den ”vänliga” delen av kompromissen verkligen är verklig — och den håller även under test.

BeautifulSoup och soupsieve vinner CSS-stödet med 41/41 plus 20/20

Jag körde 29 API-tester som täckte sökning, CSS, trädnavigation, textextraktion och DOM-modifiering. Alla 29 klarade sig, och varje resultat beräknades genom att jämföra faktiskt utfall mot förväntat värde, inte genom ögonmått. Två av funktionerna är ergonomi som C-parserarna helt enkelt inte erbjuder:

  • Funktionspredikat i find / find_all. Du kan skriva soup.find(lambda t: t.name == "a" and "btn" in t.get("class", [])) och uttrycka ett komplext villkor på en enda Pythonrad — inget ”selekt allt först, filtrera sen”-tvåsteg behövs.
  • Namngiven, dubbelriktad trädnavigation. .parent, .next_sibling, .find_parent, .stripped_strings, .descendants — traversalerna läses nästan som vanlig text och går åt båda håll. selectolax kräver flera steg för vissa av dessa, eller saknar dem helt.

Det här är den konkreta delen av ”det sparar utvecklartid”. Det är inte marknadsföring; det är 29 gröna bockar.

Två fallgropar att notera, eftersom en rättvis recension måste visa båda sidor. Först booleska attribut: <input disabled> ger bs4 en tom sträng "" för disabled (selectolax ger None). Båda är falska, så if node.get("disabled") missar tyst ett booleskt attribut som faktiskt finns i båda biblioteken — säkrare test är "disabled" in tag.attrs. För det andra sammanfogar get_text(strip=True) nodtext utan något avgränsande tecken efter att texten strippats, så "...with " + "link1" blir "withlink1". Ange separator=" " när du behöver ordgränser. Inget av detta är bs4-specifikt; båda är fallgropar som finns i flera bibliotek.

Och nu det som överraskar många: att välja bs4 kostar dig inte CSS-täckning. Dess CSS-motor, soupsieve, är den mest kompletta implementeringen i hela jämförelsen. I basmatrisen med 41 fall (återanvänd från selectolax-bänken) fick soupsieve 41/41 — enda fullträffen i fältet, före selectolax-Lexbors 39/41 och cssselects (lxml/parsel) 37/41. Därefter körde jag 20 extra utökade fall som soupsieve-dokumentationen lyfter fram, och det blev 20/20, inklusive selektorer som Lexbor helt avvisar: :lang(en), soupsieve-specifika :-soup-contains('featured'), :is(), :where() och :has(> a). De enda verkliga luckorna är XPath (soupsieve är bara CSS) och parsels ::text / ::attr()-pseudoelement, som är Scrapy-tillägg. Lever du i XPath-land kommer den migrationen att kännas.

Slutsatsen för det här avsnittet är tydlig: det du offrar genom att välja BeautifulSoup är hastighet. Inte API-ergonomi, och definitivt inte CSS-stöd.

Två produktionsfällor värda att budgetera för

Utöver standardbackenden finns två beteenden som kommer att bita dig särskilt i långkörande eller icke-UTF-8-baserade arbetsflöden.

Referenscykler: anropa decompose() i långa loopar

Varje bs4-Tag har en referens både till sin förälder och till sina barn, vilket skapar en referenscykel. CPythons referensräkning kan inte rensa en cykel på egen hand — det är generationsgarbagecollectorns jobb. För att se hur mycket det betyder byggde och tog jag bort ett träd 300 gånger med GC avstängd, och räknade sedan hur många Tag-objekt som fortfarande låg kvar i minnet:

BeautifulSoup referenscykler behåller 120,900 objekt med GC av och 0 med GC på

ScenarioTags kvar efter del
GC av120,900 (300 cykler, inget återvunnet)
GC på26,598 (generations-GC kördes mitt i loopen)
Efter tvingad gc.collect()0 (allt återvunnet)
Kontroll utan cykel (lista med strängar, GC av)delta 0

Med GC avstängd återvann del soup ingenting — alla 120 900 objekt låg kvar, eftersom referenscykeln bryter referensräkningen. En enda gc.collect() rensade bort allting. Kontrollgruppen utan cykel (en vanlig lista med strängar, känd för att inte ha någon cykel) hade delta noll, vilket visar att uppbyggnaden kom från bs4:s cykel och inte från mätbrus. bs4:s egen dokumentation säger att objekten är ”densely interconnected ... exactly the sort a garbage collector would have trouble with”, så detta är dokumenterat beteende; det testet tillför är antalet kvarhållna objekt och beviset att collect() nollställer det.

Den praktiska tumregeln: i en pipeline som parsar många stora sidor i en tät loop, om din kod (eller någon högpresterande inställning) stänger av GC eller inte låter den köras tillräckligt ofta, kommer bs4-träd att hänga kvar och minnet att växa. Anropa soup.decompose() efter varje sida — bs4 erbjuder den just för att bryta cykeln och frigöra minnet tidigt. C-träden från selectolax och lxml har inte det här problemet alls.

Kodning: UnicodeDammit är bs4:s tysta fördel

bs4 levererar en komponent som de snabba parserarna saknar: UnicodeDammit, som försöker gissa dokumentets kodning och konvertera det till Unicode automatiskt. Jag testade den mot en matris med 8 fall där deklarerad och faktisk teckenkodning inte stämde:

BeautifulSoup UnicodeDammit återställer 5 av 8 kodningsfall

FallVerklig kodningUnicodeDammit gissadeÅterställd?
utf8_no_declutf-8utf-8Ja
utf16_bomutf-16utf-16leJa
gbk_chinesegbkgb18030Ja (superset)
shiftjisshift_jiscp932Ja (superset)
latin1_declared_utf8latin-1 (deklarerad utf-8)iso-8859-1Ja (ignorerade lögnen)
latin1_no_decllatin-1cp720Nej
cp1252_no_declcp1252cp862Nej
utf8_declared_latin1utf-8 (deklarerad latin-1)iso-8859-1Nej (följde lögnen)

Fem av åtta återställdes. UTF-8, UTF-16 med BOM, GBK, Shift-JIS och även felmärkt latin-1 blev rätt, och superset-gissningarna (GBK→gb18030, Shift-JIS→cp932) fungerar fortfarande att avkoda. Två felmoder är värda att känna till: korta byteexempel i latin-1/cp1252 tolkas ibland som DOS-kodpages, eftersom den statistiska detektorn är opålitlig på korta inputs och DOS box-drawing-tecken överlappar Latin-1:s kodpunkter; och när en <meta charset>-deklaration helt enkelt är fel, litar UnicodeDammit på deklarationen. bs4:s dokumentation flaggar båda — ett prov kan vara ”so short that Unicode, Dammit can't get a lock on it”, och mer data ger en bättre gissning.

Jämfört med selectolax, som tyst korrumperar icke-UTF-8-bytes och förväntar sig att du avkodar dem själv, är detta en verklig fördel: bs4 försöker åtminstone gissa och lyckas ofta. Men det är ingen garanti. Om du känner till kodningen, hoppa över gissningen och var explicit: BeautifulSoup(bytes, from_encoding="...").

Skiljer sig backends någonsin på riktiga sidor?

Den trasiga matrisen visar att backends kan skilja sig på medvetet trasig input. Nästa naturliga fråga är om det spelar någon roll i verkligheten, så jag körde alla tre backends över 11 riktiga hämtade sidor — BBC, Wikipedia, Craigslist, MDN, old.reddit, Python docs, Hacker News, Books to Scrape, webscraper.io, whitehouse.gov och en JS-renderad quotes-sida — och jämförde antal länkar, rubriker och bilder.

Alla tre var överens på alla 11 sidor. Noll avvikelse. Det betyder att backend-skillnaden från fällavsnittet bara dyker upp på medvetet trasig HTML; när en modern produktionssajt är tillräckligt välstrukturerad — även en ”stökig” sådan — ändrar backend-valet inte vad du extraherar. Den praktiska tolkningen: för vanliga, välformade sajter är html.parser fullt okej och slipper extra beroenden. Först när du skrapar tydligt icke-standardiserad, handskriven eller gammal HTML börjar backend-valet påverka resultatet, och då byter du till lxml eller html5lib.

En detalj från den körningen, eftersom det är ett verkligt edge case. MDN-sidan innehåller ett <template>-element, och alla bs4-backends returnerade 508 länkar — vilket betyder att bs4 plattar ut innehållet i <template> till huvudträdet. Det placerar bs4 på samma sida som lxml, och på motsatt sida från selectolax-Lexbor, som strikt följer HTML5-specen (ett <template> är ett inert DocumentFragment) och returnerar 497, alltså 11 länkar färre eftersom de i template-elementet försvinner. Så bs4 fångar data inne i ett <template> — användbart, men också ett sätt att plocka upp ”spök-innehåll” som en webbläsare aldrig skulle rendera. Ingen av beteendena är fel; de följer bara olika tolkningar av specifikationen, och du bör veta vilken du får.

Var BeautifulSoup passar – och var det inte gör det

I stället för att pressa ihop allt till en enda poäng mellan 0 och 100 (vilket skulle dölja just de kompromisser som spelar roll), kommer här en dimensionsbaserad resultattavla, med ett förbehåll på varje rad:

DimensionVad testerna visadeAtt tänka på
Installation / första körningRen wrapper, ingen browser/setup; html.parser utan extra beroenden; alla färdiga wheelslxml-backend kräver ett C-beroende
Hastighet mot C-parserar12–17x långsammare (html.parser) / 10.5–14x (lxml-backend), alla storlekarEn enda testmiljö; återanvänd selectolax-data
CSS-frågegenomströmning~6–7.5x långsammare på 100k noder; lxml-backend räddar inte dettaÅteranvänd data; betalar Python-Tag-skatten
Minne1.5–1.75x selectolax/lxml; tyngstÅteranvänd data; mätt som RSS
Import / kallstart2.36x långsammare (33.4 mot 14.1 ms)Återanvänd data; liten post
Skalning med trådarbs4-lxml ~3.9x långsammare med 4 trådar (håller GIL)En observation; använd multiprocessing
API-ergonomi29/29 tester; funktionspredikat i find + dubbelriktad navigationTom sträng för booleska attribut och strip-gränsfällor
CSS-täckningsoupsieve starkast: 41/41 bas + 20/20 utökad; stöd för :langIngen XPath, inga ::text
Tolerans mellan 3 backendslxml/html5lib 15/15; html.parser 12/15Avvikelser bara på trasig HTML
Överensstämmelse på riktiga sidor3 backends eniga 11/11; alla plattar ut <template> (508)Välformade sajter: backend spelar ingen roll
GC för referenscyklerTrädet är en cykel; 300 loopar behöll 120,900 objekt, collect rensade alltLånga loopar behöver decompose()
KodningUnicodeDammit återställer 5/8; felbedömer korta sample, följer dåliga deklarationerEn observation
UnderhållAktivt (4.15.0, juni 2026); MITHemma på crummy/Launchpad, inte GitHub

Så vem är BeautifulSoup för? Alla som värdesätter ett lättläst API och förlåtande parsning framför rå genomströmning, och som jobbar i måttlig skala — prototyper, engångsskrapningar, interna verktyg, team där utvecklartid kostar mer än körtid. Vem bör titta någon annanstans? Pipelines med miljontals sidor där hastighetskostnaden växer till riktiga pengar, arbetslaster som behöver trådnivå-parallellism, och alla som är bundna till XPath.

En notis om var detta passar i en riktig scraping-stack, och var vårt eget verktyg kommer in. BeautifulSoup utgår från att du redan har HTML:en. Det hämtar inte sidor, renderar inte JavaScript och gör inget åt anti-bot-skydd eller CAPTCHA — det är ett separat och i dag genuint svårt jobb. Det är här ett AI-baserat scraping-API ligger i ett annat lager: Thunderbit:s utvecklarstack — ett REST API, en MCP-server och ett CLI — hanterar hämtning, JS-rendering och anti-bot-problemet, och returnerar sedan antingen ren Markdown (POST /distill) eller schemamatchad strukturerad JSON (POST /extract) utan att du behöver skriva selektorer alls. De två konkurrerar inte; de kompletterar varandra. bs4 parsa HTML du redan har, medan Thunderbit:s API, MCP och CLI hjälper dig få tag på HTML du annars inte når. Om flaskhalsen är parsning är bs4 ett bra svar. Om flaskhalsen är insamling är det ett annat lager.

Testa Thunderbit för extrahering av webdata

Slutsatsen

BeautifulSoup ger dig det mest lättanvända API:t, den starkaste toleransen för trasig HTML och den mest kompletta CSS-motorn i den här jämförelsen — men till priset av ungefär en tiopotens i hastighet och den tyngsta minnesprofilen. Det är hela affären, rakt beskriven. Standardbackenden html.parser är den enda verkliga fällan: den förstör tyst oavslutade tabeller och listor, så skicka alltid "lxml" eller "html5lib" när inputen kan vara ful. Trådar gör det inte snabbare — multiprocessing gör det. Och i långa loopar bör du köra decompose() på varje sida för att undvika att referenscyklerna växer.

Två begränsningar att avsluta med. Allt här mättes på en enda plattform (macOS arm64, Python 3.14, färdiga wheels), och timingmultiplarna är återanvända från selectolax-bänken (samma benchmark, per 2026-07-13) snarare än körda igen — så de ärver den en-plattform-begränsningen, och en Linux x86_64-miljö eller en setup där man kompilerar från källkod kan flytta de exakta siffrorna. Och inget av resultaten här är någon ny upptäckt: bs4 är ett 20 år gammalt bibliotek, så varje testat beteende är antingen dokumenterat eller offentligt känt. Värdet är inte ett scoop. Det är att sätta ett riktigt tal på avvägningar som dokumentationen bara beskriver kvalitativt.

Vanliga frågor

Är BeautifulSoup långsamt? Ja, mätbart. I en uppgift med både parsning och extraktion kör det ungefär 12–17x långsammare än en C-parser som selectolax-Lexbor med standardbackenden html.parser, och 10.5–14x långsammare med lxml-backend, eftersom det bygger ett Python-objekt för varje nod. Om det spelar roll beror på skala: på en sida på 1 MB handlar det om 232 ms mot 15 ms — oviktigt för några tusen sidor, men avgörande för en pipeline med en miljon sidor.

Vilken BeautifulSoup-parser ska jag använda — html.parser, lxml eller html5lib? För välformade, vanliga sajter är standarden html.parser okej och kräver inga extra beroenden. Men den implementerar inte HTML5:s valfria avslutande taggar, så på oavslutade tabeller eller listor blandar den ihop intilliggande text utan att ge fel. När inputen kan vara trasig, handskriven eller gammal bör du ange "lxml" eller "html5lib" uttryckligen — båda fick 15/15 i en matris med trasig HTML, medan html.parser stannade på 12/15.

Kan BeautifulSoup köra parallellt med trådar? Nej. bs4:s trädbygge är ren Python-kod och håller GIL, så fler trådar gör det långsammare, inte snabbare — i testet tog fyra trådar ungefär 3.9x längre tid än en tråd för en 1 MB-parse. För att parallellisera bs4 ska du använda multiprocessing (ProcessPoolExecutor). Bibliotek med C-kärna, som selectolax och lxml, är de som verkligen vinner på trådnivå-parallellism.

Hanterar BeautifulSoup trasig HTML bra? Överlag ja — över ett brett urval trasiga exempel (felnästlade taggar, saknad grundstruktur, oquotade attribut och mer) återhämta de sig fint. Den svagaste punkten är standardbackenden html.parser och valfria avslutande taggar: oavslutade <td>/<li> nästlas i stället för att avslutas, vilket korrumperar extraherad text. Byt till lxml eller html5lib så försvinner den typen av problem.

BeautifulSoup jämfört med lxml – vilket är bäst? De är olika verktyg. lxml är mycket snabbare både för att bygga träd och för att fråga i dem, och det stödjer XPath. BeautifulSoup kapslar in lxml (bland annat) i ett mycket vänligare API och har faktiskt bredare CSS-stöd via soupsieve. Men räkna inte med att lxml-backenden gör bs4 lika snabbt som lxml — backend:en accelererar bara parsningen, medan frågor och traversal fortfarande betalar bs4:s per-nod-kostnad i Python, vilket gör det ungefär 6–7.5x långsammare vid stora batch-selektioner.

Testa Thunderbit för extrahering av webdata Get Started Free

Ke
Ke
CTO på Thunderbit | Senior data scientist & ML-expert Med nästan ett decenniums erfarenhet inom maskininlärning och datavetenskap är Ke Shen alumn från Columbia University och tidigare Senior Data Scientist på Walmart Labs. Med djup, av branschen erkänd expertis inom Python, R, Java och statistik delar han väl beprövade insikter om hur man förvandlar komplexa AI-algoritmer från teori till produktionsklar arkitektur.

Testa Thunderbit

Scrapa leads och annan data på bara 2 klick. Drivs av AI.

Hämta Thunderbit Det är gratis
Extrahera data med AI
Överför enkelt data till Google Sheets, Airtable eller Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week