News You Can Bruise

Permalink [No comments] Beautiful Soup 4.13.0:

After a beta period lasting nearly a year, I've released the biggest update to Beautiful Soup in many years. For version 4.13.0 I added type hints to the Python code, and in doing so uncovered a large number of very small inconsistencies in the code. I've fixed the inconsistencies, but the result is a larger-than-usual number of deprecations and changes that may break backwards compatibility.

The CHANGELOG for 4.13.0 is quite large so I'm writing this blog post to highlight just the most important changes, specifically the changes most likely to make you need (or want) to change your code.

Deprecations and backwards-incompatible changes

New features

beautifulsoup

Permalink [Comments] (2) My PyCon US 2024 talk:

I've put up a transcript of the talk I gave at the PyCon US Maintainers Summit last month, about the lessons I learned while being the solo maintainer of Beautiful Soup, over 20 years and through two periods of professional burnout:

How to maintain a popular Python library for most of your life without with burning out

The quick takeaway is that strong boundaries are important: both the software boundaries provided by published APIs and packaging dependencies, and the decision as to where your volunteer open source work ends and the rest of your life begins. I have some suggestions for the ways the two interact, and an anecdote about how we mentally rewrite our memories of our struggles to make ourselves more active participants. If you're the maintainer of an open source project, I recommend checking it out!

beautifulsoup programming

Permalink [No comments] Beautiful Soup 4.8.0:

I'm getting back into the swing of putting up a NYCB post when I complete a project. Yesterday I published a feature release of Beautiful Soup, 4.8.0. This release makes it easy to make fine-grained customizations to the input mechanism (the TreeBuilder class) and the output mechanism (the Formatter class).

This makes it easy to do things like change the rules about which attributes are treated as multi-value attributes. If you don't like how Beautiful Soup parses class into a list of CSS classes, this is the release for you. It's not a huge release, but this project's now fifteen years old so I'm relieved at how stable it's been.

Speaking of CSS, although this is a feature release, it's a little smaller than the 4.7.0 release I put out at the end of 2018. That one took out the lackluster implementation of CSS selectors, based on Simon Willison's "soupselect" project from the early 2010s. I replaced it with a dependency on Isaac Muse's SoupSieve project, which has a nearly complete CSS selector implementation. The old implementation was a common cause of complaints, but—like the HTML5 parsing algorithm—it's not something I have a strong interest in and I'm happy to give the whole job to an external dependency.

There was a period of about a year in 2017-2018 when I wasn't interested in doing Beautiful Soup work, but Tidelift changed that. Tidelift gathers subscription money from companies that rely on free software, and distributes the money to the developers in exchange for a level of support that I find sustainable.

Nobody builds an entire product around Beautiful Soup (or at least nobody will admit do doing this), but thousands of people have used Beautiful Soup to save time at their day jobs. Bundling Beautiful Soup together with bigger projects like Flask and numpy is a solution that works really well for me.

beautifulsoup

Permalink [Comments] (2) Beautiful Soup 4.4.0 beta:

I've found an agent for Situation Normal and the book is out to publishers and I don't have to think about it for a while. As seems to be my tradition after finishing a big project, I went through the accumulated Beautiful Soup backlog and closed it out. I've put out a beta release which I'd like you to try out and report any problems.

I've fixed 17 bugs, added some minor new features, and changed the implementations of __copy__ and __repr__ to work more like you'd expect from Python objects. But in my mind the major new change is this: I've added a warning that displays when you create a BeautifulSoup object without explicitly specifying a parser:

UserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for this system ("lxml"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently. To get rid of this warning, change this:
BeautifulSoup([your markup])
to this:
BeautifulSoup([your markup], "lxml")

It's a little annoying to get this message, but it's also annoying to have your code silently behave differently because you copied it to a machine that didn't have lxml installed, and it's also annoying when I have to check pretty much every reported bug to see whether this is the problem. Whenever I think I can eliminate a class of support question with a warning, I put in the warning. It saves everybody time.

The other possibility: now that Python's built-in HTMLParser is decent, I could make it so that it's always the default unless you specify another parser. This would cause a big one-time wrench, as even machines which have lxml installed would start using HTMLParser, but once it shook out the problem would be solved. I might still do that, but I think I'll give everyone about a year to get rid of this annoying warning.

Anyway, try out the beta. Unless there's a big problem I'll be releasing 4.4.0 on Friday.

beautifulsoup

Permalink [No comments] Beautiful Soup 4.3.2, and all previous versions:

Through long practice I'm able to write decent code while I'm sick, but I should not try to release code while I'm sick. While putting up the release of Beautiful Soup 4.3.2, I accidentally deleted the entire beautifulsoup4 project on PyPI and had to recreate it manually. I've given PyPI all the crummy.com tarball URLs for releases going back to 4.0.1, and I've installed each one via pip to verify that it works, so if your build process depends on installing a specific version of Beautiful Soup 4 via PyPI, it should still work. And indeed, random versions of BS4 have been downloaded about 200 times since I switched over. I'm sorry about this screwup. Let me know if there are any remaining problems.

4.3.2 itself is a pretty minor bugfix release. Still left unfixed is a bug I can't reproduce because the federal government is shut down. When you file a bug that happens with a specific web page, please provide the HTML of the web page, not the URL.

beautifulsoup

Permalink [No comments] Beautiful Soup 4.2.0:

My work on RESTful Web APIs is pretty much done, so I went through the Beautiful Soup bug tracker and fixed everything I could. The result is a new, stoner-iffic release of Beautiful Soup.

Here are the release notes. The main new features are a much more capable CSS selector engine, and a diagnostics module that should help with tech support.

beautifulsoup

Permalink [No comments] In Search of the Beautiful Soup Double-Dippers:

Recently I noticed that certain IPs were using distribute or setuptools to download the Beautiful Soup tarball multiple times in a row. For one thing, I'm not sure why distribute and setuptools are downloading Beautiful Soup from crummy.com instead of using PyPI, especially since PyPI registers almost 150k downloads of the latest BS4--why are some people using PyPI and not others?

If anyone knows how to convince everyone to use PyPI, I'd appreciate the knowledge. But it's not a big deal right now, and it gives me some visibility into how people are using Beautiful Soup. Visibility which I will share with you.

Yesterday, the 17th, the Beautiful Soup 4.1.3 tarball was downloaded 2223 times. It is by far the most popular thing on crummy.com. The second most popular thing is the Beautiful Soup 3.2.1 tarball, which was downloaded 381 times. The vast majority of the downloads were from installation scripts: distribute or setuptools.

1516 distinct IP addresses were responsible for the 2223 downloads of 4.1.3. I wrote a script to find out how many IP addresses downloaded Beautiful Soup more than once. The results:

Downloads from a single IP Number of times this happened
551
351
151
131
111
52
412
343
2453
11001

Naturally my attention was drawn to the outliers at the top of the table. I investigated them individually. The IP address responsible for 55 downloads is a software company of the sort that might be deploying to a bunch of computers behind a proxy. The 35 is an individual on a cable modem who, judging from their other traces on the Internet, is deploying to a bunch of computers using Puppet. The 15, the 13, and the 11 are all from Travis CI, a continuous integration service.

One of the two 5s was an Amazon EC2 instance. Five of the twelve 4s were Amazon EC2 instances. Thirty-seven of the forty-three 3s were Amazon EC2 instances. And 395 of the 453 double-dippers were Amazon EC2 instances. Something's clearly going on with EC2. (There was also one download from within Amazon corporate, among other BigCo downloaders.)

I hypothesized that the overall majority of duplicate requests are from Amazon EC2 instances being wiped and redeployed. To test this hypothesis I went through all the double-dippers and calculated the time between the first request and the second. My results are in this scatter plot. Each point on the plot represents an IP address that downloaded Beautiful Soup twice yesterday.

For EC2 instances, the median time between requests is 11 hours and 45 minutes. So EC2 instances are being automatically redeployed twice a day. For non-EC2 instances, the median time between requests is 51 minutes, and the modal time is about zero. Those people set up a dev environment, discover that something doesn't work, and try it again from scratch.

beautifulsoup

Permalink [No comments] For Aaron:

import urllib2
from bs4 import BeautifulSoup

url = 'http://www.poemhunter.com/best-poems/william-stafford/thinking-for-berky/'
soup = BeautifulSoup(urllib2.urlopen(url))

print soup.find(attrs="title").text
for s in soup.find(attrs="poem").strings:
    print s.strip()

people beautifulsoup

Permalink [No comments] Beautiful Soup 4.1.2:

Another small release. The big new feature is the ability to use the class_ keyword argument to search by CSS class. The big bug fix has to do with XML that uses namespaced attribute names. And the big performance improvement comes from cchardet, a Python binding to Mozilla's charset detector that's much faster (but gives slightly different results) than the pure-Python chardet. If you have cchardet installed, UnicodeDammit will use it in preference to chardet.

beautifulsoup

Permalink [No comments] Beautiful Soup 4.1.1:

This release fixes some bugs, especially having to do with tags that use namespaces, and including a very serious performance bug that made BS4 slower than BS3, even when using the lxml parser. The previous, much-better-than-BS3 performance has been restored.

beautifulsoup

Permalink [No comments] Beautiful Soup 4.1.0 and detwingle():

Due to the contigencies of fate I get asked a lot of questions about bad HTML. Recently I noticed a problem cropping up which I haven't seen discussed much: documents with mixed encodings. This is typically a document that claims to be UTF-8, and mostly is UTF-8, but which contains bytestrings that only make sense according to some other encoding, usually Windows-1252.

I'll stop beating around the bush: sometimes otherwise UTF-8 documents contain Microsoft smart quotes. This isn't terribly common, but when it happens there's been no easy way to convert that document to Unicode... until now. Beautiful Soup 4.1.0, released today, adds the method UnicodeDammit.detwingle(). This method converts a mixed UTF-8/Windows-1252 document to pure UTF-8, allowing you to run it through BeautifulSoup() or UnicodeDammit() and get Unicode.

I'll let the documentation give the details. In theory I can expand detwingle() to handle other pairs of encodings, but UTF-8/Windows-1252 is the only one currently supported. I'm imagining adding support for other popular encoding pairs, maybe EUC-JP + Shift JIS. But I'm not imagining writing that code, just incorporating patches from other people.

If you're ever in this situation, try it out and let me know how it works.

Beautiful Soup 4.1.0 also includes a bunch of medium-level bug fixes, and a major refactoring of the search code that will hopefully have no effect whatsoever on the way searches work.

beautifulsoup

Permalink [No comments] Beautiful Soup 4.0.4:

I haven't been mentioning all the Beautiful Soup releases I've been doing, because they're just maintenance releases, but I'll mention them occasionally because fixing bugs (and determining what's a bug and what's not) still takes up a fair amount of my time. We're up to 4.0.4 and I've fixed/worked around a number of bugs, including one that prevented Beautiful Soup from parsing an XML document larger than about 512 bytes.

I've also updated the docs quite a bit to help people solve common problems. I'm not sure where to stop, because Beautiful Soup is the first Python library a lot of people use, so it gets caught up in questions like "how do I install Python packages on Windows?" (It's not easy.)

beautifulsoup

Permalink [No comments] Beautiful Soup 4.0.1:

It's been nearly two weeks since the release of the last BS4 beta, and no one has reported problems with the code. I'm sure there are still problems, but at this point the best way to find them is to do an official release. So, I present the first full release of Beautiful Soup 4, 4.0.1![0]

If you're just tuning in, Beautiful Soup 4 is nearly a complete rewrite that works on Python 2 and Python 3. Instead of a custom-built parser from 2006, Beautiful Soup 4 sits on top of lxml (for speed) or html5lib (for browser-like parsing) or the built-in HTMLParser (for convenience). Methods and attributes are renamed for PEP 8 compliance, and Beautiful Soup 4 incorporates the soupselect project to provide basic CSS selector support. I completely rewrote the documentation, Beautiful Soup's secret weapon since 3.0, for clarity and completeness.

That's the major stuff. Even though most of the code has changed, my goal was not to add a bunch more features, but to make sure Beautiful Soup will still be usable and useful years into the future.

Beautiful Soup 4 is mostly but not entirely backwards compatible with Beautiful Soup 3. Most users should be able to switch from 3 to 4 just by changing an import line. In the Python tradition of sticking a number on the end of your module name when you break backwards compatibility, I've released it as a separate package, beautifulsoup4.

This release also inaugurates the Beautiful Soup Hall of Fame, featuring the uses of Beautiful Soup that I personally find the coolest or highest-profile.

So, try out Beautiful Soup 4 the next time you need to do some screen-scraping. If you've used Beautiful Soup 3, I think you'll be pleasantly surprised. If not, I'll just say I hope you like it.

I've thanked them before, but special thanks are once again due to Thomas Kluyver and Ezio Melotti for helping me get everything working under Python 3.

[0] The first release is called 4.0.1 instead of 4.0.0 because I've been bitten by clever packagers before and I don't want them thinking "4.0.0" is an earlier version than "4.0.0b10".

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 10:

Hey, two in a row. The "release candidate" thing was a lie; the big change is that I ported and incorporated Simon Willison's soupselect project. So you can now combine the Beautiful Soup API with CSS selectors. Except I just realized that I ported an old version of the code, so I'll be doing another release. Anyway, here it is in the BS4 docs.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 9:

The latest beta is the first one I'm calling a release candidate, so if you've been waiting to try it out, now's your chance.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 8:

I didn't even mention beta 7 on NYCB because it was oriented towards getting rid of test failures. Test failures that had a lot to do with what versions of what parsers were installed, but nothing to do with whether or not Beautiful Soup itself was broken.

Beta 8 adds very basic namespace awareness. By "basic" I mean:

  1. Handle documents that include namespaced tags and attributes without crashing or mangling the document on output.
  2. If the parser provides namespace information for a tag or attribute, store it for the user's reference instead of discarding it.

That's it. No one responded to my request for namespace-related feature requests, so I'm doing the bare minimum.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 6, Beautiful Soup 3.2.1:

There are two ongoing serials here at crummy.com: Constellation Games and Beautiful Soup 4. Here's the announcement message for the latest installment in the latter saga.

The big news is a new release of the 3.x series, Beautiful Soup 3.2.1. This fixes a pretty bad problem that can let through cross-site scripting attacks if you use Beautiful Soup to sanitize HTML. If that's you, you should upgrade ASAP.

That was certainly worth fixing, but I don't do much work on Beautiful Soup 3 anymore. I mean, if I fixed every bug in BS3, I'd have... Beautiful Soup 4, which is now almost done. All the bugs are closed out. There's one more big feature I may add, and some minor cleanup I want to do, but mainly I want to make sure people are comfortable with the new API.

Thanks to Stefano Rivera, BS4 is now in Debian unstable and Ubuntu Pangolin, as beautifulsoup4. So the clock is ticking on freezing the API. This would be a great time to try to port your BS3 scripts to BS4, and let me know how difficult it was and what you had to change.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 5:

Just going to link to my description message this time. Today I focused on clearing out the bug backlog. It's mostly minor stuff, but I'd like opinions on one change, relating to how a tag is treated if it has multiple CSS classes.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta 4:

Beautiful Soup 4 beta 4 is out! You can install it with easy_install beautifulsoup4 or pip install beautifulsoup4. You can also download the tarball or check out the Bazaar repository.

Big changes:

beautifulsoup

Permalink [No comments] :

Earlier I ran some speed/accuracy tests of Beautiful Soup driven by various parsers. Python's built-in HTMLParser scored very poorly, parsing only 52% (Python 2.7.1) or 57% (3.2.2) of my test pages without raising an exception. Well, Ezio Melotti, the maintainer of HTMLParser, has been working for a while on improving HTMLParser's handling of bad HTML. Most of this code is in Python 3.2.2, so I should have been getting the benefit, but it wasn't working for me because of a semi-related bug in HTMLParser, which is fixed in the as-yet-unreleased 3.2.3.

After talking with Ezio today, I was able to monkeypatch BS4 to avoid the bug in 3.2.2. This means on Python 3, BS4 with no external parser installed will give reliability comparable to BS4+lxml (98% versus 99%). It's still about 50% slower, though, parsing about 1300 kb of HTML per second, versus 2100 kb/second for BS4+lxml.

beautifulsoup

Permalink [Comments] (3) easy_install beautifulsoup4:

This is an HTMLized version of an email I sent to the Beautiful Soup discussion group, about the impending release of Beautiful Soup 4.

Introduction

When Beautiful Soup was first released in 2004, the state of HTML parsing in Python was appalling. Over the past eight years, things have improved so dramatically that Beautiful Soup's HTML parser is no longer a competitive advantage. I don't want to duplicate other peoples', work, so I'm getting Beautiful Soup out of the parser businesss. Beautiful Soup's job is now to provide a Pythonic screen-scraping API on top of a data structure created by a third-party parser.

This will be Beautiful Soup 4, and I've been planning it for years. With help from Thomas Kluyver and Ezio Melotti, I've now met the three main goals of Beautiful Soup 4:

  1. Make a single codebase that works under Python 2 and Python 3.
  2. Stop using SGMLParser (removed in Python 3) and make it possible to swap out one parser for another.
  3. Support two major Python parsers (lxml and html5lib) as well as Python's (not currently very good) batteries-included parser, html.parser.
The first version of BS4 is almost ready for release, and I'd like you to test it out, if you haven't already. I still to fix some things, in particular some performance problems. But, note that even with the performance problems, BS4 is faster than BS3 across the board.

On Python 2 or Python 3 you can install the BS4 beta with this command:

easy_install beautifulsoup4

You can also get the source tarball.

The documentation has been completely rewritten. You may find the section on porting BS3 code to BS4 especially interesting.

There are three major things I'd like your feedback on before completing the release.

Hall of Fame

The BS3 documentation lists open-source projects that use Beautiful Soup. I stopped maintaining this list many years ago because there are hundreds of these projects, and since most of them are screen-scrapers, they're pretty ephemeral.

I'd like to bring this feature back as a "hall of fame", featuring applications of Beautiful Soup that grab a reader's attention. People who used Beautiful Soup in a high-profile way or to tackle a big issue. Projects that are interesting to hear about even if the software doesn't work anymore, or uses an old version of Beautiful Soup, or if Beautiful Soup was used internally and the public only saw the results.

My bias is towards projects having to do with space, science, journalism, politics and social justice. Here are some examples so you know the kind of thing I'm thinking of:

If you did anything of this sort, or know of someone who did, I'd like to hear about it.

Do you prefer lxml or html5lib?

Right now, the parser ranking goes lxml, html5lib, html.parser. I like lxml because it's incredibly fast and it can parse anything. But I'd like to see what you think of the trees it generates. Would html5lib, with its web-browser-like heuristics, be a better default?

substitute_html_entities

BS3 had a number of overlapping and inconsistent ways of turning HTML/XML entities into Unicode characters, and possibly turning Microsoft smart quotes into HTML entities at the same time. In BS4, all this stuff is gone. HTML and XML entities are *always* converted into Unicode characters.

This is great but there's one problem: output. If you want to turn those Unicode characters back into entities when outputting as a string, you need to call soup.encode(substitute_html_entities=True), which is a little clunky. I'm thinking of adding an output_html_entities attribute that you can set on a soup or tag to control whether this substitution happens. Do you like this idea?

I think I also need to ensure that characters like "&" and "always converted to XML entities on output, even though this will hurt performance a bit.

Conclusion

What you install with easy_install beautifulsoup4 is a beta release. If I hear of a problem soon, there's still time to fix it, even if it means a major change to the API. So please try it out and give me feedback.

beautifulsoup

Permalink [Comments] (1) Beautiful Soup 4 Benchmark:

This is going to go into the Beautiful Soup 4 documentation, but you might find it interesting. It's my first legitimate benchmark of BS4, and the first benchmark of this stuff I've seen since Ian Bicking's excellent 2008 benchmark.

Ezio Melotti pointed me to a list of the top 10,000 domains worldwide, according to some random source. It looked legit, so I wrote a script to download the homepages of the top 200 domains as served to a desktop web browser. My dataset included many pages written in Chinese, Japanese, Russian, Portuguese, Polish, and German.

For every parser I was interested in, I parsed each homepage and timed the parse. This gave me 200 numbers for every parser. To reduce that to a single non-huge number I calculated a mean: how many kilobytes of real-world HTML the parser could process in a second. I also noted each parser's success rate: how many of the 200 homepages it had handled without raising an exception.

Here are the results, ordered by their performance under Python 2.7.

Python 2.7 Python 3.2
Parser Speed (KB/s) Success rate Speed (KB/s) Success rate
Beautiful Soup 3.2 (SGMLParser) 211 100% - -
html5lib (BS3 treebuilder) 253 99% - -
Beautiful Soup 4.0 + lxml 255 100% 2140 96%
html5lib (lxml treebuilder) 270 99% - -
Beautiful Soup 4.0 + html5lib 271 98% - -
Beautiful Soup 4.0 + HTMLParser 299 59% 1705 57%
html5lib (simpletree treebuilder) 332 100% - -
HTMLParser 5194 52% 3918 57%
lxml 17925 100% 14258 96%

Note that the "HTMLParser" tests don't actually produce anything. HTMLParser is an event-based parser, so when the HTML is parsed, nothing comes out because I didn't include any handler code. All the other tests build a parse tree in memory.

Another thing to keep in mind about the html5lib results: html5lib is kind of the opposite of BS4. BS4 always builds a tree of Beautiful Soup objects, but you can tell it to generate that tree using html5lib, lxml, or HTMLParser. Whereas html5lib always uses its own parser, but you can tell it to build a tree of lxml objects, a tree of BS3 objects, etc.

The big surprise for me is that on Python 2.7, lxml is the worst choice for a parser to drive BS4. It's a worse choice than html5lib! How did that happen? I have no idea. I was hoping to cash in on the lxml magic (see below), and it's not working. I need to look into this. Notice that html5lib takes a performance hit from using lxml's treebuilder. If the magic's not in the treebuilder and it's not in the parser, where is it?

Unless I can find that magic and exploit it, it remains the case that if you're paying by the minute for computer time, you should use lxml. It's written in C, and on Python 2.7 it builds a parse tree sixty times faster than BS4, three times faster than a pure-Python parser that does absolutely nothing with the data. Even on Python 3, lxml alone is seven times faster than BS4+lxml. I said stuff like this in the BS3 documentation, but I think I need to be more forceful about it in the BS4 docs.

The good news is that Beautiful Soup is 6-8 times faster on Python 3 than it is on Python 2, and even at its slowest, BS4 is noticeably faster than BS3.

The big caveat is that my definition of "success" is pretty minimal. Just because the parser parsed the file without crashing doesn't mean it will give you a useful parse tree.

Another caveat: on Python 3, I couldn't get HTMLParser to take raw bytes as input, so I ran the data through UnicodeDammit first. I counted this time as part of the parse time. This probably explains HTMLParser's slower speed on Python 3 and its higher success rate.

Update: Argh, I found out about this a year ago. The problem is that Unicode, Dammit is incredibly slow in some cases. Here are the results on 2.7 if I take out the prepare_markup methods in the builders for HTMLParser and lxml, and just assume everything's UTF-8:

Python 2.7 Python 3.2
Parser Speed (KB/s) Success rate Speed (KB/s) Success rate
Beautiful Soup 4.0 + lxml 2287 96%260096%
Beautiful Soup 4.0 + HTMLParser 2069 48%168057%

That's more like it! The problem is that reliability suffers. Both parsers crash in the 4% of cases where it's not UTF-8 but the encoding is declared in a <meta> tag. And there's an unknown number of cases where the data's not UTF-8 but the conversion doesn't crash, leading to garbled data. But at least now I remember this problem.

Also note that on Python 3.2, getting rid of Unicode, Dammit doesn't matter nearly as much. (It doesn't matter for HTMLParser at all.) Presumably Python 3.2 has better built-in support for encoding autodetection.

beautifulsoup

Permalink [Comments] (1) Beautiful Soup 4 Beta 2:

Thanks to some help from Ezio Melotti I've got the Beautiful Soup test suite passing on Python 2.7 and Python 3.2. Here's a tarball containing the original Python 2 module in bs4, plus the same code autoconverted to Python 3.2 in py3k/bs4.

I'm still not sure about the best way to distribute this package, either while it's beta or afterwards. I'll probably end up creating a new project on PyPi, because otherwise people who install programs that easy_install beautifulsoup will crash due to the module's new name. Does that make any sense?

Anyhow, we're almost at the end of this fitfully travelled road. Once I figure out distribution and rewrite the documentation, a) no one should need to use BS3 anymore if they don't want to, and b) it should be possible to get lxml-like performance or html5lib-like flexibility with a Beautiful Soup API, by actually using lxml or html5lib as the underlying parser.

PS: remember, it's now from bs4 import BeautifulSoup.

beautifulsoup

Permalink [Comments] (1) "No Module Named BeautifulSoup":

Since Beautiful Soup 4 is not backwards compatible with Beautiful Soup 3, I put it in a different module: bs4 instead of BeautifulSoup. In a non-ironic twist, the module rename has itself turned out to be the biggest compatibility problem between BS3 and BS4. The new module name has caused problems on several occasions where users thought BS4 worked just like BS3, or didn't even know they were using BS4 (1 2 3).

Why would you be using BS4 without knowing it? It's an unreleased beta. Well, that's happened before. When I made the BS4 alpha release, I put the tarball in /software/BeautifulSoup/download/4.x, and PyPI picked it up because it knows /software/BeautifulSoup/download/ is where I keep my tarballs. PyPI believed the 4.0 alpha to be the latest release of BeautifulSoup and started recommending it it to all and sundry, which was not what I wanted. So I moved the 4.x tarballs into a different directory that PyPI doesn't know about: /software/BeautifulSoup/unreleased/4.x/, and that solved the problem.

But now it was happening again. Some installation process or other was finding my /unreleased/ directory, picking up the beta tarball, and installing it by default as the latest version of Beautiful Soup. Why?

Thanks to the bug Brian Shumate filed today I tracked the problem down. It turns out the pip package-installer program scrapes the Beautiful Soup homepage (using regexes, not Beautiful Soup, ha). It looks for tarballs and picks the one with the biggest version number. So just by linking to the beta tarball and giving it a "4.0" name I was declaring 4.0 ready for prime time.

So I got a couple problems:

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Beta: Now With Python 3 Support:

The main thing holding back Beautiful Soup 4 from release was that it didn't work with Python 3. Fortunately, Thomas Kluyver stepped in and wrote some code, and now I can present the first BS4 beta release.

There's still some work to do, and it'll be a while before I get to it, but the work that remains is pretty minor compared to the advantages you get from using BS4 instead of BS3. Try out the beta, and if it gets good reviews I may just make a 4.0 release and deal with the minor things afterwards.

beautifulsoup

Permalink [Comments] (1) Beautiful Soup 4.0.0a:

I feel good enough about Beautiful Soup 4 to do an alpha release. It does not yet work with Python 3, but at this point I feel like the BS4 series is better than the BS3 series. BS4 will use html5lib if you've got it, or lxml if you've got that, or if all else fails it will use the built-in HTMLParser, with all its problems.

Try it out and file bugs. Check out my weblog archives for a glimpse of what's new in BS4.

beautifulsoup

Permalink [No comments] :

Got XML parsing working in Beautiful Soup 4, and then added a feature I've been wanting to add for a while. Instead of separate BeautifulSoup and BeautifulStoneSoup classes[0], in BS4 there's just BeautifulSoup. To get a tree-builder that's optimized for XML, you write BeautifulSoup(markup, "xml"). HTML is the default, but if you want to make it explicit, you write BeautifulSoup(markup, "html").

But this is just the tip of a general feature. "html" and "xml" are just strings, features for which a tree-builder might or might not advertise support. The tree builders also publish other features, like "fast", "permissive", "html5", and library names like "lxml". So you can make semi-fine distinctions:

BeautifulSoup(markup, ["html", "fast"])
BeautifulSoup(markup, ["html", "permissive"])
BeautifulSoup(markup, ["html", "lxml"])

The BS constructor will try to find the best tree-builder that matches all the features you specify, and will raise an exception if it can't match them all (because you don't have lxml installed or something).

This is overkill right now because there are only three tree-builders (["lxml", "xml"], ["lxml", "html"], and ["html5lib"]). But this gives me an easy way to add tree-builders to the code base, and for you to plug in additional builders, without making end-users learn where the classes are.

This is looking good enough that I can do an alpha release soon. I'm not sure why I've been putting so much work into BS, but I'm sure it has something to do with the fact that my other projects are stalled, blocked, or I want to procrastinate on them.

[0] Those little classes like ICantBelieveItsBeautifulSoup are also gone, because distinguishing between different techniques for parsing markup is now the parser's job. And those classes were kind of silly to begin with.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Status Report:

What an exciting weblog I run. Actually this update is pretty cool. I've ported all the non-XML tests for BS4, which means you should now be able to use the code for all HTML processing purposes. If you want to try it, note that the module is now called 'beautifulsoup', not 'BeautifulSoup'. So from beautifulsoup import BeautifulSoup. I may rename it to bs4 just because I'm tired of typing "from BeautifulSoup import BeautifulSoup" for the past six years.

I also decided this would be a good time to run a performance test. Here's a moderately sized document:

Document is 66409 bytes
BS4 lxml time: 0.06
BS4 html5lib time: 0.25
BS3 time: 0.15

("BS3" here is the latest released version, 3.2.0.)

Pretty good! And here's a huge complicated document:

Document is 1329825 bytes
BS4 lxml time: 12.60
BS4 html5lib time: 2.88
BS3 time: 14.11

Okay, that's kind of random. The problem is in Unicode, Dammit. It takes a long time to figure out the encoding for this particular page. This is ultimately because the document is in ISO-8859-2, but it includes a <meta> tag that claims the document is in UTF-8. I don't yet understand the problem on any deeper level than that. If it's gonna be like that, I may just stop believing anything I see in a <meta> tag.

If you specify the encoding up front, the lxml time drops to 0.90 seconds. The html5lib tree builder doesn't have the problem because it uses html5lib's native Unicode conversion functionality instead of Unicode, Dammit.

Incidentally, BS3 has the same problem. Specify the encoding up front, and BS3 takes about 2 seconds on this page, which makes sense--faster than html5lib but slower than lxml. I find it very annoying that I'm only discovering this problem now—I think this has wasted a lot of cumulative time over the past couple years.

Anyway, now is a good time to start trying out BS4, if you're a fan of new things. I renamed all the major methods to be PEP-8 compliant--details are in the CHANGELOG.

Update: Profiler shows the bottleneck is in the chardet library, sbchardetprober.py, which goes through the file character by character and crunches some numbers for each character. If it can't make a decision until late in a huge file, there's your twelve seconds. So... I have a couple ideas, but it's not a bug in my code that I can just fix. But html5lib uses chardet, so it must be doable.

beautifulsoup

Permalink [Comments] (1) :

I felt listless today, so I did some Beautiful Soup work so that I wouldn't have wasted the day. I fixed the handling of CDATA sections and doctype declarations.

Fun fact: it doesn't seem to be legal to stick a CDATA section into an HTML document. (By this I mean something like "<![CDATA[foo]]>", not the contents of a <pre> tag.) My knowledge of weird HTML constructs like CDATA comes mostly from studying Python's SGMLParser, which handles CDATA sections just fine since it's an SGML parser. So I had BS3 just create objects for CDATA sections, even when they occurred in HTML documents. But the two parsers I'm using as my testbeds for BS4 basically ignore CDATA sections in HTML documents:

By default, lxml's elementtree implementation replaces CDATA sections with the actual character data, and has an option to leave the CDATA sections alone, but this only works for XML. When parsing HTML, CDATA sections are ignored altogether. The HTMLParser constructor has a "strip_cdata" argument, inherited from XMLParser, but setting it to False does nothing.

BS4 can't be used to parse XML yet (unless you want to parse it by HTML rules), but once I add that, I'll have the lxml elementtree builder preserve CDATA sections.

html5lib treats CDATA sections as broken comments, so "<![CDATA[foo]]>" becomes "<!-- [CDATA[foo]] -->" The latest version of html5lib will replace a CDATA section with the character data if the CDATA section happens within a <svg> or <math> tag (see test), but this is not in any released version.

That took me the morning to figure out, so I hope it saves someone some time. But that person would have to bear a suspicious resemblance to me.

beautifulsoup

Permalink [No comments] Beautiful Soup 4 Status Report:

Yesterday I ported some more tests and added basic doctype handling to the parser plugins. The work is slowing down a little because I'm porting tests where html5lib and lxml handle the same markup differently, such as incorrectly nested tables. I'm not going to find and test every such difference, but I want to have all the old tests working, and it'll give you an idea of what the differences are in common situations.

In BS3 you could choose to convert incoming entities into Unicode characters, or to leave them alone. You could also choose to convert Microsoft smart quotes into Unicode characters, XML entities, or HTML entities. In BS4 this will depend on the parser. Both lxml and html5lib convert everything to Unicode. I think this makes more sense--convert absolutely everything to Unicode, use Unicode internally, and optionally convert back to entities when writing the document out. (I'll probably have to write the "convert back to entities" part.)

beautifulsoup

Permalink [No comments] A Project for 2011: Beautiful Soup 4:

I'm breaking my normal rule of not announcing projects until they're done, because I think it might help some people make plans if they know about this. In 2011 I'll be coming out with a major new version of Beautiful Soup that will work with Python 3, but that won't have the problems of the failed 3.1 branch.

The story so far: the most recent release of Beautiful Soup (3.2) uses a custom parser based on Python's standard-library SGMLParser. This was a really good parser back in 2005. Here in 2011, html5lib is better at handling bad markup, and lxml and ElementTree are much faster if the markup isn't too bad. Beautiful Soup's parser is no longer a competitive advantage.

What's more, SGMLParser goes away in Python 3, and its replacement is awful at handling bad markup. I tried to switch over in early 2009 and it just didn't work for anyone. So, Beautiful Soup has had the specter of death looming over it for two years.

Beautiful Soup 4 will not be a parser at all. It will be a tree-builder. You will plug a parser into Beautiful Soup, and you'll get an object tree that reflects how that parser sees a document. I have this working reasonably well for lxml and html5lib, which is why I'm comfortable announcing the project now.

Problems this will solve:

Problems this will not solve

I'll be spending alternate Fridays working on Beautiful Soup 4. I'll probably have a beta release in a few months. I'll be pushing my progress to this branch.

(nb. My code-in-progress includes some code from html5lib, and I'm not sure how the licenses interact, but since html5lib is MIT licensed I think it's just a matter of adding some more boilerplate, so I'm not too worried about it.)

beautifulsoup

Permalink [Comments] (3) Beautiful Soup 3.2.0:

Ta-da. This is not a release to fix bugs or add new features. There are almost no changes to the code, which is why it took me so long to do this—it seemed silly. This is a release to stop people from getting confused.

Previously, there were a couple versions of Beautiful Soup you might conceivably use. They were 3.0.8.1 and 3.1.0. Version 3.0.8.1 was the stable release, and version 3.1.0 was the development branch. Except 3.1.0 wasn't actually the "development" branch, since a) no development is happening right now, and b) 3.1.0 in particular turned out to be a huge mistake. But for almost two years now, 3.1.0 has been hanging around with a higher version number than 3.0.x, looking like the latest version. And people have been using it, and having problems.

I'm not working on BS right now, but the least I can do is stop actively misleading people, so I added a tiny new feature suggested by Aaron and leapfrogged the 3.1 series with the UNAMBIGUOUSLY THE VERSION YOU SHOULD USE version 3.2.0. I've also set up a new build system which uploads the new releases to Crummy, to PyPI, and to Launchpad. So if my hosting provider randomly shuts down my instance, as happened last night, your craving for the tarball can still be fulfilled.

beautifulsoup

Permalink [No comments] :

Despite a tough political environment, Beautiful Soup remains the incumbent in Kansas after four years. (thanks, clickolinko)

beautifulsoup

Permalink [Comments] (3) Beautiful Type:

Well, my cool gallery-generation script doesn't work anymore because Maverick got rid of I stopped using F-Spot [see UPDATE below], but here are the photos from yesterday. What happened was a few days ago Mark Hansen sent me a donation for Beautiful Soup. I saw that Mark had a ucla.edu address and as a UCLA alum myself, I was curious and went to investigate. Pretty awesome guy—professor of statistics, spam poetry on his webpage. "[M]y time will be split between the Research and Development Group at the New York Times..."

So I emailed Mark and asked when he would be in New York next. He said he would be in New York starting Thursday! Okay then. Yesterday I went to the Times building (near Times Square, strangely enough) and had lunch with Mark and with Jer Thorp, in the second best corporate cafeteria I've ever eaten in, not that I've eaten in very many.

Great conversation and then a little tour of (selected floors of) the Times building, which you can see a bit of in the pictures. Don't miss the "Page One" room, a room dedicated to a single ritual, where the editors gather every day at 4 PM to decide what goes on the next day's front page.

But, you may ask, what is this picture I've embedded align="right" in this entry, this photo of me standing in front of some screens? Well, I'm looking at Movable Type, a 2007 piece of digital art by Mark Hansen and Ben Rubin. It's on public display in the Times lobby. It shows a constantly changing tableau of sentences, photo captions, crossword puzzle clues, etc. from the latest edition of the NYT. And it turns out that the "custom software" mentioned in the artist's statement includes Beautiful Soup! Yes, the New York Times is using my software to scrape its own data stores for use in a work of art. I did not begin this week expecting to learn that!

[UPDATE] I should be more precise in my language, especially since I work for Canonical. Maverick did not "get rid of" F-Spot. It was moved from "main" to "universe", and replaced by Shotwell. Shotwell is much better, except that when you tell it to export photos to a directory, it exports only the photos themselves and no extra metadata or thumbnails.

beautifulsoup

Permalink [Comments] (1) :

This weblog has been silent because I was in India attending Sumana's father's funeral. I have some stuff to say about the people I met there, etc. but I'm going to think about it for a little while longer.

I want to briefly mention a weird thing I came home to. Although I don't use Twitter, I do occasionally search for people who link to my site in their Twitter feeds. Usually they're linking to Beautiful Soup. Today I saw a typical example:

Rubyist, pythonista or even php fan. We can all love http://bit.ly/7aRD8T amazing parser library #beautifulsoup

The atypical part was that this was from Paula Deen's Twitter feed. Paula Deen has a show on the Food Network, and I associate her with making food out of butter and hanging out with Jimmy Carter. I could see her tweeting about beautiful soup, but not Beautiful Soup.

I guess what happened is that, like many celebrities, Deen has a tech person who posts on her behalf, and the tech person forgot to sign out as Paula_Deen before posting to what they thought would be their own Twitter feed. The rogue tweet alarmed some people (example), and has been removed, but this follow-up that refers to Deen in the third person bolsters the "tech person" theory:

Paula hasn't gone techy, she's off today but getting ready to head to Alaska for a working vacation.

Paula Deen's tech person, I'm glad you like Beautiful Soup, and everyone makes embarrassing mistakes, so don't feel bad.

beautifulsoup

Permalink [No comments] Beautiful Soup 3.0.8.1:

Two very minor bugfixes in this release. Remember, I'll be giving a talk on (among other things) the history of Beautiful Soup at TriZPUG in Raleigh on the 27th.

beautifulsoup

Permalink [No comments] Beautiful Soup 3.0.8:

I bet you didn't expect a Beautiful Soup release (unless you saw the foreshadowing a couple days earlier). But Aaron DeVore made a bunch of improvements, and 3.0.x is still the branch you'd use if you had a choice, and I decided to spend a few hours applying patches and writing unit tests, and there you go.

I've come up with a Linux kernel-like system where 3.1.x is retconned into the 'development' branch. Hopefully sometime on this vacation I'll decide to finish the 'development' work and release a Beautiful Soup version 4 that everyone can use.

Here's the direct download.

beautifulsoup

Permalink [No comments] :

I was able to get the Beautiful Soup refactoring done and a simple compatibility suite working on Friday. Here are details. More work on this will probably wait for the next Launchpad Performance Week.

beautifulsoup

Permalink [No comments] :

Check out the trunk of Beautiful Soup and you'll see the future. I've created a simple interface that lets any parser build a Beautiful Soup tree. There are large built-in builders that encapsulate the old HTMLParser logic with their lists of nestable tags, and there's now a very small builder that delegates everything to lxml. In a simple performance test the lxml builder was about twice as fast as the HTMLParser builder.

Hopefully this will be useful to the maintainers of libraries like lxml and html5lib who currently jump through hoops to make their parsers generate a Beautiful Soup parse tree. Now you should be able to just maintain a TreeBuilder implementation. The tree builder has a very simple interface, so take a look and make sure it does what you need. I'll be writing an html5lib tree builder and packaging it and the lxml builder in Beautiful Soup for a while, but I think long-term the TreeBuilders should live with their parent projects.

Tomorrow I'll be figuring out how to package this and trying to come up with a compatibility suite that will ensure your tree builder reacts sanely to different trees and different Beautiful Soup setups like SoupStrainers.

beautifulsoup

Permalink [No comments] :

A while back I mentioned some problems with the most recent version of Beautiful Soup, outlined how I planned to fix those problems, and explained that I didn't plan to work on those fixes anytime soon because my job at Canonical leaves me burnt-out w/r/t writing software in my spare time.

But as part of Launchpad Performance Week, everyone's working on improving Launchpad's performance. As it happens, Launchpad uses Beautiful Soup behind the scenes in the test suite, and I was offered the chance to fix the problem on company time. (Fixing the problem will allow us to plug in lxml as the parser, making the tests run faster.)

It's going pretty well. I don't know if I'll be done with the whole thing by Friday, but it's definite progress on something I'd pretty much written off for the near future.

beautifulsoup

Permalink [No comments] :

I've put the Beautiful Soup source code onto Launchpad, so it's now easy instead of just technically possible for you to hack on the code and publish your changes.

beautifulsoup

Permalink [No comments] :

A lot of people are having problems parsing bad HTML in the latest version of Beautiful Soup. It's a complex situation and I got tired of talking about it in email, so I made a webpage about it.

beautifulsoup

Permalink [No comments] Beautiful Soup 3.1.0.1:

It's out. All it does is fix a parser crash on boolean attributes like <td nowrap>. But that's a pretty bad crash, so you should probably upgrade.

beautifulsoup

Permalink [No comments] :

While I'm at it I should mention there's now a Squeak port of Beautiful Soup.

beautifulsoup

Permalink [No comments] Beautiful Soup 3.1.0:

It's out. It's not useful unless you need to use Beautiful Soup with Python 3. But now I'm free to try my parser replacement experiments, which are at least more interesting than screwing around with bytestrings.

beautifulsoup

Permalink [No comments] Beautiful Soup Progress Report #3:

OK, phase one is almost complete. There's just one test failure left in the generated Python 3 version, and I don't think it can be fixed; HTMLParser is just different between 2 and 3.

I haven't found a way in Python 2 code to indicate that a string should be converted into a byte string when the conversion script runs. In some cases I can stick .encode() onto the end and it works in both 2 and 3, but some of my tests have random binary data that's not in any particular encoding. And in some cases calling .encode() is just ugly. Kind of frustrating because about 40% of my test failures ultimately boiled down to marking such-and-such a string as a byte string. So I'd appreciate any ideas.

beautifulsoup

Permalink [No comments] Beautiful Soup Progress #2:

Another glorious vacation day squandered porting Beautiful Soup to Python 3 for you ungrateful sods! I have a script that runs 2to3-3.0 on the core codebase and applies a little patch of my own, and I've used it to fix almost all of the Unicode problems. We've still got some kind of problem with the search mechanism, and some problems with HTMLParser (?) differences involving how HTML entities and self-closing tags are handled between Python 2 and Python 3. I'm down to 15 failing tests in the converted code, without breaking any tests in the Python 2 version.

I think a couple people were confused by my earlier statement that you'd be able to "write a plugin for lxml [or] lib5html." I'm talking about using another parser to drive Beautiful Soup tree generation. Turning events generated by some other parser into a generic set of "start tag", "end tag" type events. Thus giving you an alternative to the okay-for-2004-but-not-for-2008 Beautiful Soup rules about parsing bad HTML, and eventually getting rid of those rules altogether, because I don't want to be in that business.

beautifulsoup

Permalink [No comments] Beautiful Soup Progress:

I spent some time today trying to get BS in shape to run under Python 3. Here's the branch I'm working on.

sgmllib doesn't exist in Python 3, so I switched to HTMLParser, which has gotten a lot better at parsing bad HTML. With my hacks in place, only 3 of my unit tests pass under sgmllib but fail under HTMLParser. That's acceptable given that my switching to HTMLParser creates part of the framework I'll use so that you can write a plugin for lxml, html5lib (not as slow as I'd thought), or some other parser. Eventually I'll get rid of the HTMLParser plugin, or at least strip it down so that it doesn't know anything about HTML, making my life easier.

What's left is some minor syntax problems and some huge problems dealing with the way strings work in Python 3 as they go in and out of encodings. At this point I need to stop hacking on BS and do some experiments to get a good understanding of the string changes.

beautifulsoup

Permalink [No comments] :

There are four Beautiful Soup-related tasks that ought to happen in the near future.
  1. Convert the codebase to Python 3; or rather, convert it to Python 2 that can be automatically converted to Python 3.
  2. Aaron DeVore has a number of interesting additions to the API. They need to be added.
  3. Separate the tree-walker from the tree-builder. Start getting out of the business of writing tree-builders.
  4. Simplify the API.

Aaron is doing the integration work. I'm doing the conversion. Once that's done we'll have a big soup that people can use into the distant future if they don't like what I do in steps 3 and 4, or if I give the whole thing up, which is a distant possibility.

Step 3 is still a mystery. Apparently html5lib is even slower than SGMLParser, so generic is probably the way to go. Maybe something with bindings to lxml and html5lib (this is why you may not like what I do in this step and may want to stick with the all-Python, not-as-slow-as-html5lib version).

What's going to happen in step 4? Ian Bicking wrote an appreciation of lxml that ties in with my Beautiful Soup angst. Mostly to do with the tree-builder, but also rightly bashing BS's primitive CSS matcher. Implementing CSS selectors or XPath is another business I don't want to be in, but I wouldn't mind bundling with someone else's strategies for walking the tree according to CSS selectors or XPath.

I'm not terribly motivated to make these changes because I don't really use Beautiful Soup anymore. Partly because I don't do as much non-work programming as I did pre-Canonical, and partly because the sites I used to screen-scrape back in 2004 have wised up and developed web services or syndication feeds. Redoing the library doesn't feel like a fun use of my end-of-year vacation; I'd rather write stories.

But here's where I start when I think about the changes. For me, the core users of Beautiful Soup are the total newbies, people for whom BS is their first or second Python library. I've never made a secret of the fact that BS is slower than other parsers, and although coupling it with a C tree-builder would speed it up a lot, I'm totally serious when I say the point of BS is to save programmer time at the expense of processor time. If you need speed, you've got options. My overriding concern is people who've just realized they can get the information they need off a web site by writing a computer program.

Unfortunately, about 30% of those people have some specific need that goes beyond the simple API you see in Beautiful Soup 1.x, and over time those additional ways of searching got stuck into the API, and you get the Microsoft Word problem. The complexity of the API is now itself costing programmer time. It needs to stop. (But after I get Aaron's additions in so that I'll have more raw data to do my redesign with.)

So what I'd like to try is a stripped-down Beautiful Soup based on list comprehensions, with a little bit of syntactic sugar for total newbies. Matching is done by calling a factory function that returns the equivalent of a SoupStrainer. The factory functions contain most of what's currently in searchTag. Additional factory functions can implement CSS selectors or XPath, except at this point you should be able to just use a parser that has its own support for CSS selectors or XPath.

Don't even get me started on what I need to do to NewsBruiser eventually...

beautifulsoup

Permalink [Comments] (4) Beautiful Soup Future:

I've got a chunk of time off at the end of the year, not having used it earlier in the year. Among my other projects I'm going to redo Beautiful Soup. This entry is an early spelling out of my rationales and my plans.

Earlier this year I quietly retired Rubyful Soup because I think _why's hpricot does a better job of being a Rubyish screen-scraping parser than RS can be. But nothing similar has happened in Python, mainly because BS is the market leader. I want to keep that going, but I also want to take advantage of the work that's gone on in this field since 2004.

So, what are the useful features of Beautiful Soup?

  1. It can build an object model out of bad HTML.
  2. It can build an object model out of bad XML, if you tell it the rules of your XML vocabulary. (This is just the general case of #1.)
  3. It can convert almost any encoding into Unicode, usually in the absence of an explicit encoding marker or the presence of an incorrect one.
  4. It exposes a useful API. It's easy to learn, more Pythonic than CSS selectors or XPath, and it includes most common ways of traversing the tree.

Of these, the only one I really care about is #4. If I could rid myself of the need to handle all the edge cases in #1-#3, edge cases that in many cases have outstripped my ability to solve them with my current tools and sanity, I'd be happy.

Fortunately, there's html5lib, which is supposed to be as good at parsing HTML as a web browser.

My current plan is to write something that goes on top of html5lib and gives the BS API to whatever DOM you've built. This would take care of #1 and #3. It's not clear to me how you tell html5lib the rules of your XML vocabulary; maybe it only parses valid XML. But BS is relatively rarely used to parse invalid XML, so if I could outsource all the HTML and Unicode crap to html5lib I'd be much more inclined to hack randomly on BS, so I think it's a fair trade.

html5lib already has a "beautifulsoup" tree builder, which creates a tree of Beautiful Soup objects. So in theory I would just need to maintain those objects? I'll find out soon enough.

beautifulsoup

Permalink [No comments] Richie Yan:

I meant to post this earlier. About two weeks ago I had a long chat with Richie Yan, Beautiful Soup documentation translator, and got some information about him to share with the English-speaking world. He's got a weblog, which I recognized because it's shown up in my referrer logs. He works at jobmet, the Chinese equivalent of monster.com, and for jobmet he's written a web crawler that uses Beautiful Soup. Sounds a little slow to me, but he claims it's fast and easy.

beautifulsoup

Permalink [No comments] :

The Beautiful Soup documentation has been translated into Chinese. Translator Richie Yan says, "I hope this would help some guys who're poor in English." So hope we all.

beautifulsoup

Permalink [No comments] Beautiful Soup 3.0.7a:

Tiny bugfix that makes it work with Python 2.3. Enjoy, you Python 2.3-using saps.

beautifulsoup

Permalink [Comments] (1) Beautiful Soup 3.0.7 Released:

Guards! Seize it! Includes the chardet-avoidance code that drove me crazy and will save a lot of time in circumstances I can only describe as "rare".

beautifulsoup

Permalink [No comments] Beautiful Soup 3.0.6:

It's out. Basically I had a few hours to look at peoples' complaints to the mailing list, so you get a few bugfixes. Kind of a grind to be honest.

beautifulsoup

Permalink [No comments] :

Beautiful Soup 3.0.5 is out. Nothing to get excited about, unless you really like pickling and deep copying.

beautifulsoup

Permalink [No comments] Some books about computer history:

I mentioned a while back that I was reading Steven Levy's book about the development of the Macintosh. I guess that triggered something in my to clear out my stock of books about computer history, because then I read The Dream Machine, a book about ARPA by M. Mitchel Waldrop (almost as ponderous a name as J. C. R. Licklider), and Andrew Hodges's famous Alan Turing: The Enigma. And there was also the matter of the intermittently fascinating Whole Earth Software Catalog.

I bought the Waldrop book as a biography of Licklider (actually I bought it because of the BRIGHT YELLOW COVER that blocked out all other books in the bookstore), but it really strains to fill a whole book with his life and make it interesting. Fortunately the book's method of straining is to bring in all the other characters of American computer science from the 1940s through the 1970s, with a theme of Licklider as the networker between them, and that was very entertaining. Though I admit part of the entertainingness was the celebrity-spotting equivalent of those old Looney Tunes cartoons that feature charicatures of all the Warner Bros. contract stars of the 1940s. Oh look, it's {Peter Lorre,John McCarthy}.

The most interesting new information I found in the book is the history of ARPA, specifically of the Information Processing Techniques Office (Licklider was IPTO head twice). The major theme is the Vietnam-era mission anticreep that slowly pushed ARPA from advanced research projects, to the more circumscribed realm of advanced research projects that we can use on the battlefield within two years without fundamentally changing anything (somewhat parallel to the problems Xerox PARC had). Meanwhile Licklider's trying to scrape together a couple million to fund his crazy "Intergalactic Network" project, and his office is sticking boilerplate defense justifications onto incoming grant proposals so they can get ARPA money.

"All that language about military rationale wasn't in the Stanford version of the proposals," explains Ed Geigenbaum: it was slapped on at the very end by the ARPA funding officers back in Washington. "The only people who ever saw it were the students who would later dig it up under the Freedom of Information Act. Then they'd bring it on campus and say, 'See, McCarthy is working on such and such.' McCarthy would say, 'What do you mean? I never heard of that!'"

But the book also ties together things that I learned about in college with no that they were connected. For instance, ALOHA and Ethernet use the same collision resolution mechanism not because it's an obvious fact about the universe, but because Bob Metcalfe read a paper slamming ALOHA and decided to prove it could work. And multitasking came directly from time-sharing, as cheaper computers made time-sharing less of a selling point.

Fun fact: the book made it sound like ARPA was originally planned to encompass space research, and that NASA was created as a separate agency after a bureaucratic turf war. So the space program could have been part of ARPA.

Understanding the book requires no technical knowledge, but I don't think I would have enjoyed it as much if I didn't already have all these pieces in my mind ready to be connected. The book made me more interested in another item on my wishlist, John Markoff's What the Dormouse Said.

This entry got kind of long, so I'll talk about the Turing book in another entry.

beautifulsoup history computers

Permalink [No comments] A Plan Comes Together:

with ElementSoup

beautifulsoup

Permalink [No comments] :

Beautiful Soup used to stream election results in Lawrence. As part of big ole chain of hacks. Lovely.

beautifulsoup

Permalink [No comments] Monde mondial:

From a French message board:
BeautifulSoup, c'est l'un des meilleurs parseurs HTML du monde mondial, c'est ce que j'utilise pour mon script de conversion blueflagz => RSS

"Monde mondial"? Does that mean "real-world"? If so, that's a great idiom.

I don't know when I'm going to be fixing Rubyful Soup to have the features of Beautiful Soup 3.0, but it's probably going to be after a bunch of WADL work.

beautifulsoup

Permalink [No comments] :

Bonus: Beautiful Soup 3.0.3

beautifulsoup

Permalink [No comments] Beautiful Soup 3.0.2:

No real user-visible changes; it should do what you expect, more reliably. I did add back aliases for the old method names, so your 2.x scripts are pretty likely to work again now.

beautifulsoup

Permalink [Comments] (5) I Have Arrived:

Sam Ruby and Mark Pilgrim endorse Beautiful Soup.

beautifulsoup

Permalink [No comments] :

I fixed a bug, reintroduced an old shortcut, and the result is Beautiful Soup 3.0.1.

beautifulsoup

Permalink [Comments] (4) Beautiful Soup Trickery:

Here's a nice bit of trickery I discovered, based on code sent me by Staffan Malmgren. A common task among the target audience for Beautiful Soup is to strip HTML tags from a document. It turns out this is a one-liner:
''.join([e for e in soup.recursiveChildGenerator() if isinstance(e,unicode)])

Where soup is your soup object. I really like the if conditional in list comprehensions; I don't think Ruby has anything like it. I can think of ways to simulate it but they kill its simplicity, which is what I like about it.

beautifulsoup

Permalink [No comments] :

I declare Beautiful Soup 3.0 released. Here's the changelog. Go to town.

beautifulsoup

Permalink [No comments] :

Beautiful Soup 3.0 release coming soon. Big ole book of documentation. Get ready!

beautifulsoup

Permalink [No comments] :

Newest Beautiful Soup beta solves everyone's problems that I know of. Except my problem of having to rewrite the documentation.

beautifulsoup

Permalink [Comments] (5) :

OK, the Beautiful Soup beta now stores everything internally as Unicode. You specify an encoding when you turn part of the tree into a string. Now I'm definitely going to work on my book outline.

beautifulsoup

Permalink [Comments] (10) Beautiful Soup 3.0 Beta:

For your delectability. The major new feature is that Beautiful Soup 3.0 takes XML or HTML documents in any encoding and turns them into UTF-8; in most cases you don't have to know the current encoding. I wrote this without really knowing anything about encodings: most of the code is stolen from Mark Pilgrim's Universal Feed Parser. But I am able to write tests, and the tests work.

The other major new feature is that you can now rip out a chunk from the parse tree with the extract method. You can use the chunk and abandon the rest of the tree, or vice versa. This is especially useful because the data structures you abandoned can now be garbage-collected: in current Beautiful Soup, the whole tree stays in memory forever because every Tag and NavigableText is connected to every other Tag and NavigableText through an intricate web of lies. And by "lies", I mean "instance variables".

There are some more new features, but I have to take a shower now to go and meet Pete Peterson II for dinner. Test it out; I'll be rewriting the documentation over the next month or so, and hopefully by then I'll have gotten enough feedback to release it.

beautifulsoup

Permalink [No comments] :

Okay, the Cookbook is basically done. Yay! I went through my mail looking for old feedback and cleared out about 100 messages pertaining to the book. What's left? Mostly Beautiful Soup stuff I haven't really looked at. Hopefully it's 50 people all saying how great Beautiful Soup is and reporting the same three bugs. Somehow I doubt it.

beautifulsoup

Permalink [No comments] :

At last, the not-incredibly-slow version of Rubyful Soup is released! Just gem install rubyful_soup once the list of gems refreshes on RubyForge. Also includes a new way of programming the parser for better performance, which I need to document. But first I need to make the documentation page not look horribly ugly.

Beautiful Soup needs an update, too, which will come probably this weekend.

beautifulsoup

Permalink [Comments] (2) :

People have been complaining that Rubyful Soup is too slow. Bah! What do they know? Except they're right; it is too slow. Today I fought back the sickness that has consumed my corporeal form, and stayed in the hotel room all day and profiled the bugger (bonus: I can write a profiling recipe for the cookbook at the same time). I sped it up by a factor of three, which I think is about as good as I can do without going down a level and optimizing the SGML parser. I don't have the wherewithall to rebuild the gem since the gemspec is on my other computer, but you'll get it soon enough.

beautifulsoup

Permalink [No comments] :

There's a new gem of Rubyful Soup over at Rubyforge, if you're in to that kind of thing. If you tried the gem before and it didn't work, try it again. Just KEEP TRYING IT.

beautifulsoup

Permalink [No comments] I'm Hip, I'm With It:

Takka takka takka takka. Beautiful Soup is mentioned in a podcast! Or, as we used to call it, an "MP3 file". Specifically, this MP3 file, about 17 minutes in. It also mentions a cool module called NLQ for doing natural language queries.

Incidentally, when I saw Austin Powers I did not get the Macarena reference at all. I only got it a couple days ago after Sumana painstakingly explained every aspect of the joke. I thought Dr. Evil was walking like a toy robot, making a sound a toy robot might make while walking. I actually like this explanation of his behavior a lot better.

beautifulsoup

Permalink [No comments] Rubyful Soup 1.0:

It's got documentation, it's got some minor improvements, it's got the version number that means "at last, perfect!" in the open source world and "crappy rushed-to-market" everywhere else. Enjoy it! It will be there.

beautifulsoup

Permalink [Comments] (2) It Exists!:

Sometimes Sumana uses "it exists" to indicate indifference about something. But today I use it in celebration. We went to the new Cody's books, and they had Beginning Python! I finally got to see my book, and it was great to see stuff I wrote in print. The only downside is that my last hopes for the actual book cover being the non-freaky picture have been dashed.

I started writing some documentation for Rubyful Soup. Only about halfway done, but it's a good start. I've learned a lot about technical writing in the past couple months, as you can tell by comparing the documentation files for the two Soups.

beautifulsoup

Permalink [Comments] (3) Is It Beautiful Soup Yet?:

I guess it is. I thought I would just fix the performance problem, but then I started looking through the backlog and I couldn't stop myself and I spent 45 minutes fixing bugs. I held off on fixing some of my backlog because I don't trust myself to write test cases for them at the moment.

I finished my writing quota early and spent most of the rest of the day at the beach with Sumana, which was lots of fun: interesting rocks, and snowy plovers. On the way there Sumana came up with the term "Goohoo" to describe the Oxbridge-like brain sinks where about 80% of my CollabNet ex-coworkers work now.

beautifulsoup

Permalink [Comments] (1) :

Rubyful Soup is basically ready. I just need to add in some of the other stuff of the Python version. The parser is now based on a port of Python's SGMLLib parser, which means the main problem (choking on bad XML) is now gone. In fact it handles bad XML better than the Python version.

I made deep-dish pizza in the cast-iron skillet and it was pretty good. I also made brownies with this egg/fat substitute made from prunes and they were quite acceptable, though not as good as other brownies I've made (Sumana disagrees; she says they're great). Also she says I should keep my cell phone on.

beautifulsoup

Permalink [Comments] (1) Rubyful Soup:

I should have done this yesterday because I don't have time to go into a lot of detail this morning, but I want to get some feedback before I go on the dinosaur hunt. I've ported Beautiful Soup to Ruby. It's not terribly useful yet because it's based on the standard library parser in Ruby, which throws exceptions when it encounters bad markup, but it's getting there. Try it out and let me know what you think.

beautifulsoup

Permalink [Comments] (2) :

Decklin Foster, who seems to have some connection to the Chicago cabal, has written roux. This is a screen-scraper->RSS feed gateway that uses Beautiful Soup, much like Scrape 'N' Feed, but where S'N'F gives you the page and has you write a screen-scraper, roux has you define some suspiciously regexp-looking things that magically reach into the soup and extract the things you want. As Vic Fontaine would say, crazy! Decklin, how does it work?

beautifulsoup

Permalink [Comments] (1) :

There seems to be some interest in a Ruby port of Beautiful Soup, and I'm thinking of doing the port myself. I need to stop being a dilletante and do a good-sized Ruby project, and that's a good place to start. Do any Ruby people read this weblog? Would you like to have this?

beautifulsoup ruby

Permalink [No comments] Out Of Order:

This was supposed to have been published yesterday:

New Beautiful Soup, with minor fixes and real Python 2.2 support. Now going to see Hitchhiker's with Sumana and Zack.

beautifulsoup deliverables

Permalink [No comments] Beautiful Soup 2.0.2:

People who complained about Beautiful Soup not being set up with distutils, it is now. I also made real unit tests out of my ad hoc tests, so you get the Good Programming Practices two-for-one deal. In conjunction with the unannounced version 2.0.1 there are also some fixes for bugs I found while getting the tests into place.

beautifulsoup

Permalink [Comments] (5) Beautiful Soup 2.0:

After much last-minute tweaking of Python and CSS, Beautiful Soup 2.0 is released! I really outdid myself this time. Still in one file, much better at parsing bad HTML, has pretty-printer, lots of idiomatic shortcuts for writing more concise code, lots of documentation. And, in contradiction of Leonard's Immutable Law of Open Source Software, it's not plugin-based. Go for it. No Python 1.5 support in this one, unfortunately.

beautifulsoup

Permalink [No comments] Belated Soup:

Man, I hate it when I save as draft instead of publishing something. Anyway, here's what I wrote last night:

Well, I tried, but no Beautiful Soup 2.0 tonight. I'm almost done with the online docs but not quite, and I still have some things on the TODO list I want to put into 2.0 as long as I'm breaking backward compatibility. Tomorrow I get my reviewed copy of the web applications/services chapter back, (contrary to my fears the tech reviewer thinks I wrote a good chapter) so if I can't somehow squeeze it in tomorrow it's probably not going to happen for over a week.

beautifulsoup