(Ebook) Web Scraping with Python by Ryan Mitchell ISBN 9781491910276, 1491910275 - The full ebook version is just one click away
(Ebook) Web Scraping with Python by Ryan Mitchell ISBN 9781491910276, 1491910275 - The full ebook version is just one click away
com
https://ebooknice.com/product/web-scraping-with-
python-42892068
OR CLICK BUTTON
DOWLOAD EBOOK
https://ebooknice.com/product/web-scraping-with-python-55694830
ebooknice.com
(Ebook) Web Scraping with Python by Ryan Mitchell ISBN 9781491985564, 1491985569
https://ebooknice.com/product/web-scraping-with-python-10811388
ebooknice.com
(Ebook) Web Scraping with Python: Data Extraction from the Modern Web by Ryan
Mitchell
https://ebooknice.com/product/web-scraping-with-python-data-extraction-from-the-
modern-web-56680970
ebooknice.com
(Ebook) Web Scraping with Python: Collecting Data from the Modern Web by Ryan
Mitchell ISBN 9781491910290, 1491910291
https://ebooknice.com/product/web-scraping-with-python-collecting-data-from-the-
modern-web-5151034
ebooknice.com
(Ebook) Web scraping with Python: collecting more data from the modern web by Ryan
E. Mitchell ISBN 9781427027276, 9781491985571, 1427027277, 1491985577
https://ebooknice.com/product/web-scraping-with-python-collecting-more-data-
from-the-modern-web-11897446
ebooknice.com
(Ebook) Python web scraping: fetching data from the web by Jarmul, Katharine;Lawson,
Richard ISBN 9781786462589, 9781786464293, 1786462583, 1786464292
https://ebooknice.com/product/python-web-scraping-fetching-data-from-the-
web-11793486
ebooknice.com
(Ebook) Practical Web Scraping for Data Science: Best Practices and Examples with
Python by Seppe vanden Broucke, Bart Baesens ISBN 9781484235812, 1484235819
https://ebooknice.com/product/practical-web-scraping-for-data-science-best-
practices-and-examples-with-python-7008218
ebooknice.com
(Ebook) Flask Web Development: Developing Web Applications with Python by Miguel
Grinberg ISBN 9781449372620, 1449372627
https://ebooknice.com/product/flask-web-development-developing-web-applications-
with-python-4680280
ebooknice.com
(Ebook) Flask Web Development: Developing Web Applications With Python by Miguel
Grinberg ISBN 9781491991732, 1491991739
https://ebooknice.com/product/flask-web-development-developing-web-applications-
with-python-10539006
ebooknice.com
Web Scraping with Python
Collecting Data from the Modern Web
Ryan Mitchell
Boston
Web Scraping with Python
by Ryan Mitchell
Copyright © 2015 Ryan Mitchell. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are
also available for most titles (http://safaribooksonline.com). For more information, contact our corporate/
institutional sales department: 800-998-9938 or [email protected].
Editors: Simon St. Laurent and Allyson MacDonald Indexer: Lucie Haskins
Production Editor: Shiny Kalapurakkel Interior Designer: David Futato
Copyeditor: Jasmine Kwityn Cover Designer: Karen Montgomery
Proofreader: Carla Thornton Illustrator: Rebecca Demarest
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Web Scraping with Python, the cover
image, and related trade dress are trademarks of O’Reilly Media, Inc.
While the publisher and the author have used good faith efforts to ensure that the information and
instructions contained in this work are accurate, the publisher and the author disclaim all responsibility
for errors or omissions, including without limitation responsibility for damages resulting from the use of
or reliance on this work. Use of the information and instructions contained in this work is at your own
risk. If any code samples or other technology this work contains or describes is subject to open source
licenses or the intellectual property rights of others, it is your responsibility to ensure that your use
thereof complies with such licenses and/or rights.
978-1-491-91027-6
[LSI]
Table of Contents
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
3. Starting to Crawl. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Traversing a Single Domain 31
Crawling an Entire Site 35
Collecting Data Across an Entire Site 38
Crawling Across the Internet 40
Crawling with Scrapy 45
4. Using APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
How APIs Work 50
iii
Common Conventions 50
Methods 51
Authentication 52
Responses 52
API Calls 53
Echo Nest 54
A Few Examples 54
Twitter 55
Getting Started 56
A Few Examples 57
Google APIs 60
Getting Started 60
A Few Examples 61
Parsing JSON 63
Bringing It All Back Home 64
More About APIs 68
5. Storing Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Media Files 71
Storing Data to CSV 74
MySQL 76
Installing MySQL 77
Some Basic Commands 79
Integrating with Python 82
Database Techniques and Good Practice 85
“Six Degrees” in MySQL 87
Email 90
6. Reading Documents. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Document Encoding 93
Text 94
Text Encoding and the Global Internet 94
CSV 98
Reading CSV Files 98
PDF 100
Microsoft Word and .docx 102
iv | Table of Contents
Data Normalization 112
Cleaning After the Fact 113
OpenRefine 114
Table of Contents | v
12. Avoiding Scraping Traps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
A Note on Ethics 177
Looking Like a Human 178
Adjust Your Headers 179
Handling Cookies 181
Timing Is Everything 182
Common Form Security Features 183
Hidden Input Field Values 183
Avoiding Honeypots 184
The Human Checklist 186
Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
vi | Table of Contents
Preface
To those who have not developed the skill, computer programming can seem like a
kind of magic. If programming is magic, then web scraping is wizardry; that is, the
application of magic for particularly impressive and useful—yet surprisingly effortless
—feats.
In fact, in my years as a software engineer, I’ve found that very few programming
practices capture the excitement of both programmers and laymen alike quite like
web scraping. The ability to write a simple bot that collects data and streams it down
a terminal or stores it in a database, while not difficult, never fails to provide a certain
thrill and sense of possibility, no matter how many times you might have done it
before.
It’s unfortunate that when I speak to other programmers about web scraping, there’s a
lot of misunderstanding and confusion about the practice. Some people aren’t sure if
it’s legal (it is), or how to handle the modern Web, with all its JavaScript, multimedia,
and cookies. Some get confused about the distinction between APIs and web scra‐
pers.
This book seeks to put an end to many of these common questions and misconcep‐
tions about web scraping, while providing a comprehensive guide to most common
web-scraping tasks.
Beginning in Chapter 1, I’ll provide code samples periodically to demonstrate con‐
cepts. These code samples are in the public domain, and can be used with or without
attribution (although acknowledgment is always appreciated). All code samples also
will be available on the website for viewing and downloading.
vii
What Is Web Scraping?
The automated gathering of data from the Internet is nearly as old as the Internet
itself. Although web scraping is not a new term, in years past the practice has been
more commonly known as screen scraping, data mining, web harvesting, or similar
variations. General consensus today seems to favor web scraping, so that is the term
I’ll use throughout the book, although I will occasionally refer to the web-scraping
programs themselves as bots.
In theory, web scraping is the practice of gathering data through any means other
than a program interacting with an API (or, obviously, through a human using a web
browser). This is most commonly accomplished by writing an automated program
that queries a web server, requests data (usually in the form of the HTML and other
files that comprise web pages), and then parses that data to extract needed informa‐
tion.
In practice, web scraping encompasses a wide variety of programming techniques
and technologies, such as data analysis and information security. This book will cover
the basics of web scraping and crawling (Part I), and delve into some of the advanced
topics in Part II.
viii | Preface
want to use such as Twitter posts or Wikipedia pages. In general, it is preferable to use
an API (if one exists), rather than build a bot to get the same data. However, there are
several reasons why an API might not exist:
• You are gathering data across a collection of sites that do not have a cohesive API.
• The data you want is a fairly small, finite set that the webmaster did not think
warranted an API.
• The source does not have the infrastructure or technical ability to create an API.
Even when an API does exist, request volume and rate limits, the types of data, or the
format of data that it provides might be insufficient for your purposes.
This is where web scraping steps in. With few exceptions, if you can view it in your
browser, you can access it via a Python script. If you can access it in a script, you can
store it in a database. And if you can store it in a database, you can do virtually any‐
thing with that data.
There are obviously many extremely practical applications of having access to nearly
unlimited data: market forecasting, machine language translation, and even medical
diagnostics have benefited tremendously from the ability to retrieve and analyze data
from news sites, translated texts, and health forums, respectively.
Even in the art world, web scraping has opened up new frontiers for creation. The
2006 project “We Feel Fine” by Jonathan Harris and Sep Kamvar, scraped a variety of
English-language blog sites for phrases starting with “I feel” or “I am feeling.” This led
to a popular data visualization, describing how the world was feeling day by day and
minute by minute.
Regardless of your field, there is almost always a way web scraping can guide business
practices more effectively, improve productivity, or even branch off into a brand-new
field entirely.
Preface | ix
If you’re looking for a more comprehensive Python resource, the book Introducing
Python by Bill Lubanovic is a very good, if lengthy, guide. For those with shorter
attention spans, the video series Introduction to Python by Jessica McKellar is an
excellent resource.
Appendix C includes case studies, as well as a breakdown of key issues that might
affect how you can legally run scrapers in the United States and use the data that they
produce.
Technical books are often able to focus on a single language or technology, but web
scraping is a relatively disparate subject, with practices that require the use of databa‐
ses, web servers, HTTP, HTML, Internet security, image processing, data science, and
other tools. This book attempts to cover all of these to an extent for the purpose of
gathering data from remote sources across the Internet.
Part I covers the subject of web scraping and web crawling in depth, with a strong
focus on a small handful of libraries used throughout the book. Part I can easily be
used as a comprehensive reference for these libraries and techniques (with certain
exceptions, where additional references will be provided).
Part II covers additional subjects that the reader might find useful when writing web
scrapers. These subjects are, unfortunately, too broad to be neatly wrapped up in a
single chapter. Because of this, frequent references will be made to other resources
for additional information.
The structure of this book is arranged to be easy to jump around among chapters to
find only the web-scraping technique or information that you are looking for. When
a concept or piece of code builds on another mentioned in a previous chapter, I will
explicitly reference the section that it was addressed in.
x | Preface
Constant width italic
Shows text that should be replaced with user-supplied values or by values deter‐
mined by context.
Preface | xi
Safari® Books Online
Safari Books Online is an on-demand digital library that deliv‐
ers expert content in both book and video form from the
world’s leading authors in technology and business.
Technology professionals, software developers, web designers, and business and crea‐
tive professionals use Safari Books Online as their primary resource for research,
problem solving, learning, and certification training.
Safari Books Online offers a range of product mixes and pricing programs for organi‐
zations, government agencies, and individuals. Subscribers have access to thousands
of books, training videos, and prepublication manuscripts in one fully searchable
database from publishers like O’Reilly Media, Prentice Hall Professional, Addison-
Wesley Professional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco
Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt,
Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett,
Course Technology, and dozens more. For more information about Safari Books
Online, please visit us online.
How to Contact Us
Please address comments and questions concerning this book to the publisher:
We have a web page for this book, where we list errata, examples, and any additional
information. You can access this page at http://oreil.ly/1ePG2Uj.
To comment or ask technical questions about this book, send email to bookques‐
[email protected].
For more information about our books, courses, conferences, and news, see our web‐
site at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
xii | Preface
Acknowledgments
Just like some of the best products arise out of a sea of user feedback, this book could
have never existed in any useful form without the help of many collaborators, cheer‐
leaders, and editors. Thank you to the O’Reilly staff and their amazing support for
this somewhat unconventional subject, to my friends and family who have offered
advice and put up with impromptu readings, and to my coworkers at LinkeDrive who
I now likely owe many hours of work to.
Thank you, in particular, to Allyson MacDonald, Brian Anderson, Miguel Grinberg,
and Eric VanWyk for their feedback, guidance, and occasional tough love. Quite a few
sections and code samples were written as a direct result of their inspirational sugges‐
tions.
Thank you to Yale Specht for his limitless patience throughout the past nine months,
providing the initial encouragement to pursue this project, and stylistic feedback dur‐
ing the writing process. Without him, this book would have been written in half the
time but would not be nearly as useful.
Finally, thanks to Jim Waldo, who really started this whole thing many years ago
when he mailed a Linux box and The Art and Science of C to a young and impression‐
able teenager.
Preface | xiii
PART I
Building Scrapers
This section focuses on the basic mechanics of web scraping: how to use Python to
request information from a web server, how to perform basic handling of the server’s
response, and how to begin interacting with a website in an automated fashion. By
the end, you’ll be cruising around the Internet with ease, building scrapers that can
hop from one domain to another, gather information, and store that information for
later use.
To be honest, web scraping is a fantastic field to get into if you want a huge payout for
relatively little upfront investment. In all likelihood, 90% of web scraping projects
you’ll encounter will draw on techniques used in just the next six chapters. This sec‐
tion covers what the general (albeit technically savvy) public tends to think of when
they think of “web scrapers”:
Once you start web scraping, you start to appreciate all the little things that browsers
do for us. The Web, without a layer of HTML formatting, CSS styling, JavaScript exe‐
cution, and image rendering, can look a little intimidating at first, but in this chapter,
as well as the next one, we’ll cover how to format and interpret data without the help
of a browser.
This chapter will start with the basics of sending a GET request to a web server for a
specific page, reading the HTML output from that page, and doing some simple data
extraction in order to isolate the content that we are looking for.
Connecting
If you haven’t spent much time in networking, or network security, the mechanics of
the Internet might seem a little mysterious. We don’t want to think about what,
exactly, the network is doing every time we open a browser and go to http://
google.com, and, these days, we don’t have to. In fact, I would argue that it’s fantastic
that computer interfaces have advanced to the point where most people who use the
Internet don’t have the faintest idea about how it works.
However, web scraping requires stripping away some of this shroud of interface, not
just at the browser level (how it interprets all of this HTML, CSS, and JavaScript), but
occasionally at the level of the network connection.
To give you some idea of the infrastructure required to get information to your
browser, let’s use the following example. Alice owns a web server. Bob uses a desktop
computer, which is trying to connect to Alice’s server. When one machine wants to
talk to another machine, something like the following exchange takes place:
3
1. Bob’s computer sends along a stream of 1 and 0 bits, indicated by high and low
voltages on a wire. These bits form some information, containing a header and
body. The header contains an immediate destination of his local router’s MAC
address, with a final destination of Alice’s IP address. The body contains his
request for Alice’s server application.
2. Bob’s local router receives all these 1’s and 0’s and interprets them as a packet,
from Bob’s own MAC address, and destined for Alice’s IP address. His router
stamps its own IP address on the packet as the “from” IP address, and sends it off
across the Internet.
3. Bob’s packet traverses several intermediary servers, which direct his packet
toward the correct physical/wired path, on to Alice’s server.
4. Alice’s server receives the packet, at her IP address.
5. Alice’s server reads the packet port destination (almost always port 80 for web
applications, this can be thought of as something like an “apartment number” for
packet data, where the IP address is the “street address”), in the header, and
passes it off to the appropriate application – the web server application.
6. The web server application receives a stream of data from the server processor.
This data says something like:
- This is a GET request
- The following file is requested: index.html
7. The web server locates the correct HTML file, bundles it up into a new packet to
send to Bob, and sends it through to its local router, for transport back to Bob’s
machine, through the same process.
And voilà! We have The Internet.
So, where in this exchange did the web browser come into play? Absolutely nowhere.
In fact, browsers are a relatively recent invention in the history of the Internet, when
Nexus was released in 1990.
Yes, the web browser is a very useful application for creating these packets of infor‐
mation, sending them off, and interpreting the data you get back as pretty pic‐
tures, sounds, videos, and text. However, a web browser is just code, and code can be
taken apart, broken into its basic components, re-written, re-used, and made to do
anything we want. A web browser can tell the processor to send some data to the
application that handles your wireless (or wired) interface, but many languages have
libraries that can do that just as well.
Let’s take a look at how this is done in Python:
from urllib.request import urlopen
html = urlopen("http://pythonscraping.com/pages/page1.html")
print(html.read())
You can save this code as scrapetest.py and run it in your terminal using the com‐
mand:
urllib or urllib2?
If you’ve used the urllib2 library in Python 2.x, you might have
noticed that things have changed somewhat between urllib2 and
urllib. In Python 3.x, urllib2 was renamed urllib and was split into
several submodules: urllib.request, urllib.parse, and url
lib.error. Although function names mostly remain the same, you
might want to note which functions have moved to submodules
when using the new urllib.
urllib is a standard Python library (meaning you don’t have to install anything extra
to run this example) and contains functions for requesting data across the web, han‐
dling cookies, and even changing metadata such as headers and your user agent. We
will be using urllib extensively throughout the book, so we recommend you read the
Python documentation for the library (https://docs.python.org/3/library/urllib.html).
urlopen is used to open a remote object across a network and read it. Because it is a
fairly generic library (it can read HTML files, image files, or any other file stream
with ease), we will be using it quite frequently throughout the book.
Connecting | 5
An Introduction to BeautifulSoup
“Beautiful Soup, so rich and green,
Waiting in a hot tureen!
Who for such dainties would not stoop?
Soup of the evening, beautiful Soup!”
The BeautifulSoup library was named after a Lewis Carroll poem of the same name in
Alice’s Adventures in Wonderland. In the story, this poem is sung by a character called
the Mock Turtle (itself a pun on the popular Victorian dish Mock Turtle Soup made
not of turtle but of cow).
Like its Wonderland namesake, BeautifulSoup tries to make sense of the nonsensical;
it helps format and organize the messy web by fixing bad HTML and presenting us
with easily-traversible Python objects representing XML structures.
Installing BeautifulSoup
Because the BeautifulSoup library is not a default Python library, it must be installed.
We will be using the BeautifulSoup 4 library (also known as BS4) throughout this
book. The complete instructions for installing BeautifulSoup 4 can be found at
Crummy.com; however, the basic method for Linux is:
$sudo apt-get install python-bs4
and for Macs:
$sudo easy_install pip
This installs the Python package manager pip. Then run the following:
$pip install beautifulsoup4
to install the library.
Again, note that if you have both Python 2.x and 3.x installed on your machine, you
might need to call python3 explicitly:
$python3 myScript.py
Make sure to also use this when installing packages, or the packages might be
installed under Python 2.x, but not Python 3.x:
$sudo python3 setup.py install
If using pip, you can also call pip3 to install the Python 3.x versions of packages:
$pip3 install beautifulsoup4
In addition, there is an .exe installer for pip on Windows, so you can easily install and
manage packages:
>pip install beautifulsoup4
An Introduction to BeautifulSoup | 7
I can leave the environment with the deactivate command, after which I can no
longer access any libraries that were installed inside the virtual environment:
(scrapingEnv)ryan$ deactivate
ryan$ python
> from bs4 import BeautifulSoup
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named 'bs4'
Keeping all your libraries separated by project also makes it easy to zip up the entire
environment folder and send it to someone else. As long as they have the same ver‐
sion of Python installed on their machine, your code will work from the virtual envi‐
ronment without requiring them to install any libraries themselves.
Although we won’t explicitly instruct you to use a virtual environment in all of this
book’s examples, keep in mind that you can apply virtual environment any time sim‐
ply by activating it beforehand.
Running BeautifulSoup
The most commonly used object in the BeautifulSoup library is, appropriately, the
BeautifulSoup object. Let’s take a look at it in action, modifying the example found in
the beginning of this chapter:
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
bsObj = BeautifulSoup(html.read())
print(bsObj.h1)
The output is:
<h1>An Interesting Title</h1>
As in the example before, we are importing the urlopen library and calling
html.read() in order to get the HTML content of the page. This HTML content is
then transformed into a BeautifulSoup object, with the following structure:
• html → <html><head>...</head><body>...</body></html>
— head → <head><title>A Useful Page<title></head>
— title → <title>A Useful Page</title>
— body → <body><h1>An Int...</h1><div>Lorem ip...</div></body>
— h1 → <h1>An Interesting Title</h1>
— div → <div>Lorem Ipsum dolor...</div>
We hope this small taste of BeautifulSoup has given you an idea of the power and
simplicity of this library. Virtually any information can be extracted from any HTML
(or XML) file, as long as it has some identifying tag surrounding it, or near it. In
chapter 3, we’ll delve more deeply into some more-complex BeautifulSoup function
calls, as well as take a look at regular expressions and how they can be used with Beau
tifulSoup in order to extract information from websites.
Connecting Reliably
The web is messy. Data is poorly formatted, websites go down, and closing tags go
missing. One of the most frustrating experiences in web scraping is to go to sleep
with a scraper running, dreaming of all the data you’ll have in your database the next
day—only to find out that the scraper hit an error on some unexpected data format
and stopped execution shortly after you stopped looking at the screen. In situations
like these, you might be tempted to curse the name of the developer who created the
website (and the oddly formatted data), but the person you should really be kicking is
yourself, for not anticipating the exception in the first place!
Let’s take a look at the first line of our scraper, after the import statements, and figure
out how to handle any exceptions this might throw:
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
There are two main things that can go wrong in this line:
• The page is not found on the server (or there was some error in retrieving it)
• The server is not found
In the first situation, an HTTP error will be returned. This HTTP error may be “404
Page Not Found,” “500 Internal Server Error,” etc. In all of these cases, the urlopen
function will throw the generic exception “HTTPError” We can handle this exception
in the following way:
An Introduction to BeautifulSoup | 9
try:
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
except HTTPError as e:
print(e)
#return null, break, or do some other "Plan B"
else:
#program continues. Note: If you return or break in the
#exception catch, you do not need to use the "else" statement
If an HTTP error code is returned, the program now prints the error, and does not
execute the rest of the program under the else statement.
If the server is not found at all (if, say, http://www.pythonscraping.com was down, or
the URL was mistyped), urlopen returns a None object. This object is analogous to
null in other programming languages. We can add a check to see if the returned html
is None:
if html is None:
print("URL is not found")
else:
#program continues
Of course, if the page is retrieved successfully from the server, there is still the issue of
the content on the page not quite being what we expected. Every time you access a tag
in a BeautifulSoup object, it’s smart to add a check to make sure the tag actually
exists. If you attempt to access a tag that does not exist, BeautifulSoup will return a
None object. The problem is, attempting to access a tag on a None object itself will
result in an AttributeError being thrown.
The following line (where nonExistentTag is a made-up tag, not the name of a real
BeautifulSoup function):
print(bsObj.nonExistentTag)
returns a None object. This object is perfectly reasonable to handle and check for. The
trouble comes if you don’t check for it, but instead go on and try to call some other
function on the None object, as illustrated in the following:
print(bsObj.nonExistentTag.someTag)
which returns the exception:
AttributeError: 'NoneType' object has no attribute 'someTag'
So how can we guard against these two situations? The easiest way is to explicitly
check for both situations:
try:
badContent = bsObj.nonExistingTag.anotherTag
except AttributeError as e:
print("Tag was not found")
else:
In this example, we’re creating a function getTitle, which returns either the title of
the page, or a None object if there was some problem with retrieving it. Inside getTi
tle, we check for an HTTPError, as in the previous example, and also encapsulate two
of the BeautifulSoup lines inside one try statement. An AttributeError might be
thrown from either of these lines (if the server did not exist, html would be a None
object, and html.read() would throw an AttributeError). We could, in fact,
encompass as many lines as we wanted inside one try statement, or call another func‐
tion entirely, which can throw an AttributeError at any point.
When writing scrapers, it’s important to think about the overall pattern of your code
in order to handle exceptions and make it readable at the same time. You’ll also likely
want to heavily reuse code. Having generic functions such as getSiteHTML and getTi
tle (complete with thorough exception handling) makes it easy to quickly—and reli‐
ably—scrape the web.
An Introduction to BeautifulSoup | 11
CHAPTER 2
Advanced HTML Parsing
When Michelangelo was asked how he could sculpt a work of art as masterful as his
David, he is famously reported to have said: “It is easy. You just chip away the stone
that doesn’t look like David.”
Although web scraping is unlike marble sculpting in most other respects, we must
take a similar attitude when it comes to extracting the information we’re seeking from
complicated web pages. There are many techniques to chip away the content that
doesn’t look like the content that we’re searching for, until we arrive at the informa‐
tion we’re seeking. In this chapter, we’ll take look at parsing complicated HTML pages
in order to extract only the information we’re looking for.
13
• Look for a “print this page” link, or perhaps a mobile version of the site that has
better-formatted HTML (more on presenting yourself as a mobile device—and
receiving mobile site versions—in Chapter 12).
• Look for the information hidden in a JavaScript file. Remember, you might need
to examine the imported JavaScript files in order to do this. For example, I once
collected street addresses (along with latitude and longitude) off a website in a
neatly formatted array by looking at the JavaScript for the embedded Google Map
that displayed a pinpoint over each address.
• This is more common for page titles, but the information might be available in
the URL of the page itself.
• If the information you are looking for is unique to this website for some reason,
you’re out of luck. If not, try to think of other sources you could get this informa‐
tion from. Is there another website with the same data? Is this website displaying
data that it scraped or aggregated from another website?
Especially when faced with buried or poorly formatted data, it’s important not to just
start digging. Take a deep breath and think of alternatives. If you’re certain no alter‐
natives exist, the rest of this chapter is for you.
Using this BeautifulSoup object, we can use the findAll function to extract a Python
list of proper nouns found by selecting only the text within <span class="green"></
span> tags (findAll is an extremely flexible function we’ll be using a lot later in this
book):
nameList = bsObj.findAll("span", {"class":"green"})
for name in nameList:
print(name.get_text())
When run, it should list all the proper nouns in the text, in the order they appear in
War and Peace. So what’s going on here? Previously, we’ve called bsObj.tagName in
order to get the first occurrence of that tag on the page. Now, we’re calling
bsObj.findAll(tagName, tagAttributes) in order to get a list of all of the tags on
the page, rather than just the first.
After getting a list of names, the program iterates through all names in the list, and
prints name.get_text() in order to separate the content from the tags.
The attributes argument takes a Python dictionary of attributes and matches tags
that contain any one of those attributes. For example, the following function would
return both the green and red span tags in the HTML document:
.findAll("span", {"class":"green", "class":"red"})
The recursive argument is a boolean. How deeply into the document do you want to
go? If recursion is set to True, the findAll function looks into children, and child‐
ren’s children, for tags that match your parameters. If it is false, it will look only at
the top-level tags in your document. By default, findAll works recursively (recur
sive is set to True); it’s generally a good idea to leave this as is, unless you really know
what you need to do and performance is an issue.
The text argument is unusual in that it matches based on the text content of the tags,
rather than properties of the tags themselves. For instance, if we want to find the
number of times “the prince” was surrounded by tags on the example page, we could
replace our .findAll() function in the previous example with the following lines:
nameList = bsObj.findAll(text="the prince")
print(len(nameList))
The output of this is “7.”
1 If you’re looking to get a list of all h<some_level> tags in the document, there are more succinct ways of writ‐
ing this code to accomplish the same thing. We’ll take a look at other ways of approaching these types of prob‐
lems in the section BeautifulSoup and regular expressions.
At this point, you might be asking yourself, “But wait, don’t I already know how to get
a list of tags by attribute—by passing attributes to the function in a dictionary list?”
BeautifulSoup objects
Seen in previous code examples as bsObj
Tag objects
Retrieved in lists or individually by calling find and findAll on a Beauti
fulSoup object, or drilling down, as in:
However, there are two more objects in the library that, although less commonly
used, are still important to know about:
NavigableString objects
Used to represent text within tags, rather than the tags themselves (some func‐
tions operate on, and produce, NavigableStrings, rather than tag objects).
The Comment object
Used to find HTML comments in comment tags, <!--like this one-->
These four objects are the only objects you will ever encounter (as of the time of this
writing) in the BeautifulSoup library.
Navigating Trees
The findAll function is responsible for finding tags based on their name and
attribute. But what if you need to find a tag based on its location in a document?
That’s where tree navigation comes in handy. In Chapter 1, we looked at navigating a
BeautifulSoup tree in a single direction:
bsObj.tag.subTag.anotherSubTag
Now let’s look at navigating up, across, and diagonally through HTML trees using our
highly questionable online shopping site http://www.pythonscraping.com/pages/
page3.html as an example page for scraping (see Figure 2-1):
The HTML for this page, mapped out as a tree (with some tags omitted for brevity),
looks like:
• html
— body
— div.wrapper
— h1
— div.content
— table#giftList
— tr
— th
— th
— th
— th
— tr.gift#gift1
— td
— td
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
This code prints out the list of product rows in the giftList table. If you were to
write it using the descendants() function instead of the children() function, about
two dozen tags would be found within the table and printed, including img tags, span
tags, and individual td tags. It’s definitely important to differentiate between children
and descendants!
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
print(bsObj.find("img",{"src":"../img/gifts/img1.jpg"
}).parent.previous_sibling.get_text())
This code will print out the price of the object represented by the image at the loca‐
tion ../img/gifts/img1.jpg (in this case, the price is “$15.00”).
How does this work? The following diagram represents the tree structure of the por‐
tion of the HTML page we are working with, with numbered steps:
• <tr>
— <td>
— <td>
— <td>(3)
— “$15.00” (4)
— s<td> (2)
— <img src=”../img/gifts/img1.jpg">(1)
Regular Expressions
As the old computer-science joke goes: “Let’s say you have a problem, and you decide
to solve it with regular expressions. Well, now you have two problems.”
Unfortunately, regular expressions (often shortened to regex) are often taught using
large tables of random symbols, strung together to look like a lot of nonsense. This
3 You might be asking yourself, “Are there ‘irregular’ expressions?” Nonregular expressions are beyond the
scope of this book, but they encompass strings such as “write a prime number of a’s, followed by exactly twice
that number of b’s” or “write a palindrome.” It’s impossible to identify strings of this type with a regular
expression. Fortunately, I’ve never been in a situation where my web scraper needed to identify these kinds of
strings.
Regular Expressions | 23
(cc)*
Any even number of things can be grouped into pairs, so in order to enforce this
rule about even things, you can write two c’s, surround them in parentheses, and
write an asterisk after it, meaning that you can have any number of pairs of c’s
(note that this can mean 0 pairs, as well).
(d | )
Adding a bar in the middle of two expressions means that it can be “this thing or
that thing.” In this case, we are saying “add a d followed by a space or just add a
space without a d.” In this way we can guarantee that there is, at most, one d, fol‐
lowed by a space, completing the string.
One classic example of regular expressions can be found in the practice of identifying
email addresses. Although the exact rules governing email addresses vary slightly
from mail server to mail server, we can create a few general rules. The corresponding
regular expression for each of these rules is shown in the second column:
Rule 1 [A-Za-z0-9\._+]+
The first part of an email address The regular expression shorthand is pretty smart. For
contains at least one of the example, it knows that “A-Z” means “any uppercase
following: uppercase letters, letter, A through Z.” By putting all these possible
lowercase letters, the numbers 0-9, sequences and symbols in brackets (as opposed to
periods (.), plus signs (+), or parentheses) we are saying “this symbol can be any one
underscores (_). of these things we’ve listed in the brackets.” Note also
that the + sign means “these characters can occur as
many times as they want to, but must occur at least
once.”
Rule 2 @
After this, the email address contains This is fairly straightforward: the @ symbol must occur in
the @ symbol. the middle, and it must occur exactly once.
Rule 4 \.
This is followed by a period (.). You must include a period (.) before the domain name.
Rule 5 (com|org|edu|net)
Finally, the email address ends with This lists the possible sequences of letters that can occur
com, org, edu, or net (in reality, there after the period in the second part of an email address.
are many possible top-level
domains, but, these four should
suffice for the sake of example).
Regular Expressions | 25
[] Matches any character within the brackets (i.e., “Pick any one of these [A-Z]* APPLE,
things”) CAPITALS,
QWERTY
() A grouped subexpression (these are evaluated first, in the “order of (a*b)* aaabaab, abaaab,
operations” of regular expressions) ababaaaaab
{m, n} Matches the preceding character, subexpression, or bracketed character a{2,3}b{2,3} aabbb, aaabbb,
between m and n times (inclusive) aabb
[^] Matches any single character that is not in the brackets [^A-Z]* apple,
lowercase,
qwerty
| Matches any character, string of characters, or subexpression, separated b(a|i|e)d bad, bid, bed
by the “I” (note that this is a vertical bar, or “pipe,” not a capital “i”)
. Matches any single character (including symbols, numbers, a space, etc.) b.d bad, bzd, b$d, b d
^ Indicates that a character or subexpression occurs at the beginning of a ^a apple, asdf, a
string
\ An escape character (this allows you to use “special” characters as their \. \| \\ .|\
literal meaning)
$ Often used at the end of a regular expression, it means “match this up [A-Z]*[a-z]*$ ABCabc, zzzyx, Bob
to the end of the string.” Without it, every regular expression has a
defacto “.*” at the end of it, accepting strings where only the first part
of the string matches. This can be thougt of as analogous to the ^
symbol.
?! “Does not contain.” This odd pairing of symbols, immediately preceding ^((?![A-Z]).)*$ no-caps-here,
a character (or regular expression), indicates that that character should $ymb0ls a4e f!ne
not be found in that specific place in the larger string. This can be tricky
to use; after all, the character might be found in a different part of the
string. If trying to eliminate a character entirely, use in conjunction with
a ^ and $ at either end.
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
images = bsObj.findAll("img", {"src":re.compile("\.\.\/img\/gifts/img.*\.jpg")})
for image in images:
print(image["src"])
This prints out only the relative image paths that start with ../img/gifts/img and end
in .jpg, the output of which is the following:
../img/gifts/img1.jpg
../img/gifts/img2.jpg
../img/gifts/img3.jpg
../img/gifts/img4.jpg
../img/gifts/img6.jpg
Accessing Attributes
So far, we’ve looked at how to access and filter tags and access content within them.
However, very often in web scraping you’re not looking for the content of a tag; you’re
looking for its attributes. This becomes especially useful for tags such as <a>, where
the URL it is pointing to is contained within the href attribute, or the <img> tag,
where the target image is contained within the src attribute.
With tag objects, a Python list of attributes can be automatically accessed by calling:
myTag.attrs
Keep in mind that this literally returns a Python dictionary object, which makes
retrieval and manipulation of these attributes trivial. The source location for an
image, for example, can be found using the following line:
myImgTag.attrs['src']
Lambda Expressions
If you have a formal education in computer science, you probably learned about
lambda expressions once in school and then never used them again. If you don’t, they
might be unfamiliar to you (or familiar only as “that thing I’ve been meaning to learn
at some point”). In this section, we won’t go deeply into these extremely useful func‐
tions, but we will look at a few examples of how they can be useful in web scraping.
Essentially, a lambda expression is a function that is passed into another function as a
variable; that is, instead of defining a function as f(x, y), you may define a function as
f(g(x), y), or even f(g(x), h(x)).
BeautifulSoup allows us to pass certain types of functions as parameters into the fin
dAll function. The only restriction is that these functions must take a tag object as an
argument and return a boolean. Every tag object that BeautifulSoup encounters is
evaluated in this function, and tags that evaluate to “true” are returned while the rest
are discarded.
For example, the following retrieves all tags that have exactly two attributes:
soup.findAll(lambda tag: len(tag.attrs) == 2)
That is, it will find tags such as the following:
<div class="body" id="content"></div>
<span style="color:red" class="title"></span>
Beyond BeautifulSoup
Although BeautifulSoup is used throughout this book (and is one of the most popu‐
lar HTML libraries available for Python), keep in mind that it’s not the only option. If
BeautifulSoup does not meet your needs, check out these other widely used libraries:
lxml
This library is used for parsing both HTML and XML documents, and is known
for being very low level and heavily based on C. Although it takes a while to learn
(a steep learning curve actually means you learn it very fast), it is very fast at
parsing most HTML documents.
HTML Parser
This is Python’s built-in parsing library. Because it requires no installation (other
than, obviously, having Python installed in the first place), it can be extremely
convenient to use.
Beyond BeautifulSoup | 29
Random documents with unrelated
content Scribd suggests to you:
*** END OF THE PROJECT GUTENBERG EBOOK GRAHAM'S
MAGAZINE, VOL. XXXV, NO. 2, AUGUST 1849 ***
1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside
the United States, check the laws of your country in addition to
the terms of this agreement before downloading, copying,
displaying, performing, distributing or creating derivative works
based on this work or any other Project Gutenberg™ work. The
Foundation makes no representations concerning the copyright
status of any work in any country other than the United States.
1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if
you provide access to or distribute copies of a Project
Gutenberg™ work in a format other than “Plain Vanilla ASCII” or
other format used in the official version posted on the official
Project Gutenberg™ website (www.gutenberg.org), you must,
at no additional cost, fee or expense to the user, provide a copy,
a means of exporting a copy, or a means of obtaining a copy
upon request, of the work in its original “Plain Vanilla ASCII” or
other form. Any alternate format must include the full Project
Gutenberg™ License as specified in paragraph 1.E.1.
• You pay a royalty fee of 20% of the gross profits you derive
from the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
about donations to the Project Gutenberg Literary Archive
Foundation.”
• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.
1.F.
Most people start at our website which has the main PG search
facility: www.gutenberg.org.
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
ebooknice.com