Speaker 1: Imagine for a second that you are staring at a
Speaker 1: web page, okay, and it is just packed with thousands
Speaker 1: of data points. Like we are talking, pages upon pages
Speaker 1: of you know, randomly generated addresses, phone numbers, product reviews, whatever.
Speaker 2: Oh yeah, I know that feeling.
Speaker 1: Right, You get that sinking feeling in your stomach, that
Speaker 1: creeping dread when you realize you need all of that
Speaker 1: data neatly organized in a spreadsheet and the only way
Speaker 1: you know how to do it is by uh spending
Speaker 1: the next four maybe five hours mindlessly highlighting, copying, and pasting.
Speaker 2: Yeah, it's the ultimate digital bottleneck. I mean, your brain
Speaker 2: is totally ready to analyze the information and find patterns,
Speaker 2: but your hands are just stuck doing this manual, repetitive
Speaker 2: labor from like the nineteen nineties exactly.
Speaker 1: It completely breaks your momentum. But you know what if
Speaker 1: you didn't have to do that, What if you could
Speaker 1: write just a few lines of code and instantly download, extract,
Speaker 1: and organize every single piece of that data in a
Speaker 1: matter of seconds. So today we're taking a deep dive
Speaker 1: into the superpower of webscraping and data extraction using Python.
Speaker 2: It really is a superpower.
Speaker 1: Honestly, it really is, and we're going to map out
Speaker 1: a very specific journey for you today. First, we'll figure
Speaker 1: out how to get a web page to actually talk
Speaker 1: to our Python environment without blocking us. Second, we're going
Speaker 1: to explore how to search through all that chaotic raw
Speaker 1: code using some incredibly powerful pattern matching tools. And finally,
Speaker 1: we will look at how to elegantly carve up the
Speaker 1: structural matrix of the web so you can pull out
Speaker 1: exactly what you need.
Speaker 2: It's a massive shift in how you interact with the Internet.
Speaker 2: You basically move from being a passive consumer manually browsing
Speaker 2: pages to becoming an active curator who automates the ingestion
Speaker 2: of knowledge.
Speaker 1: I love that active curator. So let's start at the
Speaker 1: very beginning. Before we can parse any data, we actually
Speaker 1: have to get the raw web page into our Python environment.
Speaker 1: I always picture this as like sending a highly efficient
Speaker 1: robot butler to a store to fetch a specific document
Speaker 1: for you. Great analogy, thanks, But how do we actually
Speaker 1: build that robot.
Speaker 2: Well, in the language of the web, setting that robot
Speaker 2: butler is called making an HTTP get request. You're pointing
Speaker 2: your code at a target URL and asking or getting
Speaker 2: the information from that server, right, and within the Python ecosystem,
Speaker 2: there are really three primary libraries you'll encounter for this job.
Speaker 1: Let's walk through those because I know they each have
Speaker 1: a very different philosophy.
Speaker 2: They definitely do. So first you have httplib two. This
Speaker 2: is actually a really comprehensive, powerful upgrade that was originally
Speaker 2: driven by developers at Google.
Speaker 1: Oh wow, okay, yeah, and it.
Speaker 2: Gives you very fine grained, granular control over your requests.
Speaker 2: But you know that power comes with a cost. It
Speaker 2: requires you to manually manage a lot of the underlying connection.
Speaker 1: Details, which sounds like a headache for a beginner.
Speaker 2: It can be. Then you have earlib, which.
Speaker 1: I understand is incredibly common just because it comes built
Speaker 1: in with standard Python right like, you don't have to
Speaker 1: install a third party package to use it exactly.
Speaker 2: That is its main appeal. Historically, older versions of Python
Speaker 2: actually had two separate, somewhat overlapping libraries, arlib and earlib two.
Speaker 1: Oh that sounds confusing.
Speaker 2: Its big confusing for developers, so they eventually cleaned house
Speaker 2: and merge them into a single unified arlib module. It
Speaker 2: sits at a higher level than httplib two, so it
Speaker 2: handles some of those low level connection headaches for you, okay,
Speaker 2: that's better, But honestly, for most modern developers, the undisputed
Speaker 2: favorite is a library simply called Requests just requests.
Speaker 1: The name itself kind of tells you it's trying to
Speaker 1: be user friendly.
Speaker 2: It is beautifully designed. The Request library provides a high level,
Speaker 2: incredibly intuitive interface. It abstracts away all the tedious, complex
Speaker 2: connection logic.
Speaker 1: Nice.
Speaker 2: If your primary goal is just to grab data quickly
Speaker 2: and cleanly without fighting with connection protocols, Requests is the
Speaker 2: tool you reach for.
Speaker 1: Okay, So we pick our library, say requests, and we
Speaker 1: send our automated requests out to a website. When the
Speaker 1: server responds, it doesn't just hand over a clean, readable
Speaker 1: text document. The robot comes back with a response object.
Speaker 1: What is actually inside that package?
Speaker 2: So the response object fundamentally breaks down into two parts,
Speaker 2: the headers and the content. The headers are basically the
Speaker 2: metadata of the transaction.
Speaker 1: Like a shipping label.
Speaker 2: Exactly like a shipping label, they tell you the status code,
Speaker 2: whether the server actually sound the page or if it crashed,
Speaker 2: what kind of server software is running, and various caching rules.
Speaker 1: I noticed something interesting in the notes about those headers
Speaker 1: regarding security. If you accidentally point your code to an old,
Speaker 1: unsecure HTTP address, the server often fires back an HTTP
Speaker 1: three oh one redirect status.
Speaker 2: Yes, that happens a lot.
Speaker 1: It basically intercepts your robot and automatically reroutes it to
Speaker 1: the secure HTTPS version of the site.
Speaker 2: Modern servers are heavily configured to prioritize encrypted traffic. They'll
Speaker 2: seamlessly bounce your automated request to the secure lane, just
Speaker 2: like they would for a human typing an address into
Speaker 2: a browser.
Speaker 1: That makes sense.
Speaker 2: Now, underneath those metadata headers is the actual content you
Speaker 2: ask for. But as you hinted earlier, it does not
Speaker 2: arrive as readable text. It arises raw bytes machine language.
Speaker 1: Which looks like absolute garbage to a human eye. If
Speaker 1: you try to print it.
Speaker 2: Out total garbage. To make it readable, you have to
Speaker 2: decode that byte stream. The vast majority of the web
Speaker 2: is encoded using a standard called UTF eight. Okay, so
Speaker 2: within your Python script, you take those raw bytes, apply
Speaker 2: a UTF eight decoding method, and suddenly that machine code
Speaker 2: blossoms into the giant, messy HTML string that actually builds
Speaker 2: the web page.
Speaker 1: I want to pause here for a second, because there
Speaker 1: is a glaring real world problem with this robot butler analogy.
Speaker 2: Oh what's that?
Speaker 1: Well, what if the store doesn't like robots? Like if
Speaker 1: I write a script right now and send it to
Speaker 1: scrape search results from Google, Google isn't just going to
Speaker 1: hand over it to data with a smile.
Speaker 2: Right, Oh? Absolutely not. If you try that using the
Speaker 2: default settings of erlib or requests, you will immediately hit
Speaker 2: a brick wall. Specifically, the server will turn an HTTP
Speaker 2: air four h three forbidden.
Speaker 1: Yikes, forbidden.
Speaker 2: Yeah. Major tech companies actively monitor incoming traffic. When they
Speaker 2: see a request, they check the headers, and by default,
Speaker 2: your Python library includes a header called the user agent
Speaker 2: and essentially wears a giant name pag that says hello,
Speaker 2: I'm an automated Python script.
Speaker 1: And companies block that because they don't want armies of
Speaker 1: bots chewing up their server bandwidth or mass downloading their
Speaker 1: proprietary data.
Speaker 2: Exactly.
Speaker 1: So, how do we get past the bouncer?
Speaker 2: You have to change your outfit. Yeah, you modify the
Speaker 2: user agent header in your code before you send the request.
Speaker 2: Sneaky very. Instead of broadcasting that you're a Python script,
Speaker 2: you spoof the identity of a standard web browser. You
Speaker 2: pass a string that tells the server, Hey, I'm not
Speaker 2: a bot. I'm just a normal user running the Safari
Speaker 2: browser on a MacBook.
Speaker 1: And that actually works.
Speaker 2: It does. Once the server reads that fabricated header, it
Speaker 2: drops the velvet rope and lets you in. Wow.
Speaker 1: Okay, so we spoof our identity, the bouncer lets us through,
Speaker 1: and our script downloads the page. Now we're staring at
Speaker 1: a massive, thousands of lines long wall of raw HTML text.
Speaker 1: Standard searching methods like hitting ctrlf on your keyboard to
Speaker 1: find a word are completely useless here.
Speaker 2: Yeah, completely.
Speaker 1: If we want to extract thousands of dynamic data points,
Speaker 1: we need something much more robust.
Speaker 2: We need regular expressions, or as it's often abbreviated, redjax.
Speaker 2: You access this in Python through the built in a
Speaker 2: EARS module. Rojex isn't just a search tool. It is
Speaker 2: an entire micro language dedicated to advanced pattern matching within
Speaker 2: strings of text.
Speaker 1: I want to break down how we actually deploy this,
Speaker 1: because there are several different functions inside that module depending
Speaker 1: on your strategy. Sure, let's look at the match function first.
Speaker 1: I picture match as like walking into a train station
Speaker 1: to see if a specific train is sitting exactly the
Speaker 1: very first platform the moment you.
Speaker 2: Arrive that analogy holds up perfectly. The match function is
Speaker 2: incredibly strict. It only checks for your pattern at the
Speaker 2: absolute literal beginning of a string position zero. If the
Speaker 2: data you want is buried even one space deep into
Speaker 2: the text, match will return absolutely nothing.
Speaker 1: Wow. Okay.
Speaker 2: There's also full match, which goes a step further. It
Speaker 2: demands that the entire string, from the very first character
Speaker 2: to the absolute last matches your pattern perfectly.
Speaker 1: But in the context of web scraping, I rarely care
Speaker 1: if the data is sitting perfectly at the start of
Speaker 1: the document. I just want to find it wherever it's
Speaker 1: hiding in the chaos. You know. So, if match is
Speaker 1: checking the starting platform, the search function is like flying
Speaker 1: a helicopter down the entire length of the train tracks.
Speaker 2: Yes, the search function scans through the entire string and
Speaker 2: stops the exact moment it finds the very first instance
Speaker 2: of your pattern. It finds that first train reports its
Speaker 2: location and stops looking.
Speaker 1: Stops looking entirely entirely.
Speaker 2: But in data extraction, stopping at the first hit kind
Speaker 2: of defeats the purpose. If you are scraping a directory
Speaker 2: for phone numbers, you want every single number on.
Speaker 1: The page, right, not just the first one.
Speaker 2: For that, you use findal.
Speaker 1: Okay, find ol does exactly what it says on the ten.
Speaker 1: It sweeps the entire document and hands you back a clean,
Speaker 1: comprehensive list of every single matchup.
Speaker 2: Fat It's the most common one you'll use, for sure.
Speaker 1: I also saw a function called find it or, which
Speaker 1: sounds like it's doing something similar but with more complexity.
Speaker 2: So find it or is the tool you use when
Speaker 2: you need spatial awareness. Findal just gives you the extracted words,
Speaker 2: but find it or returns an iterator that provides the
Speaker 2: exact character index ranges for every match. Oh interesting, Yeah,
Speaker 2: it tells you I found your target data and it
Speaker 2: exists precisely from character position one fifty to one sixty two.
Speaker 2: It acts like a GPS coordinate system for your data.
Speaker 1: That is super useful, and beyond just locating data, you
Speaker 1: can manipulate it too. You can use the split function
Speaker 1: to chop a massive paragraph into individual tokens based on spaces,
Speaker 1: or the subfunction to rapidly substitute or replace specific patterns
Speaker 1: across the entire document.
Speaker 2: Absolutely, so, here's the real hurdle.
Speaker 1: To truly utilize regular expressions. We have to stop searching
Speaker 1: for literal words like apple or banana. We need to
Speaker 1: search for abstract concepts. We need to find any decimal
Speaker 1: number or any capitalized word.
Speaker 2: And this is where we introduce special sequences. But a
Speaker 2: quick best practice note first, before you write these sequences,
Speaker 2: you should prefix your pattern string with a lowercase R.
Speaker 2: This creates what's called a raw string.
Speaker 1: Wait, why is that necessary?
Speaker 2: Well, by default, Python uses backslashes as escape characters for
Speaker 2: things like creating new lines. Because Rejects relies heavily on
Speaker 2: backslashes for its own syntax, Using a raw string tells
Speaker 2: Python to ignore its default rules and treat every backslash
Speaker 2: as a literal character for the Rejects engine to interpret.
Speaker 2: Oh I see, yeah, it saves you from writing incredibly
Speaker 2: messy tangled code. Additionally, you will often wrap your raw
Speaker 2: string in the redot compile method. This translates your pattern
Speaker 2: into a highly efficient, reusable blueprint, which fads things up
Speaker 2: dramatically if you're applying it thousands of times.
Speaker 1: Okay, let's talk about these special sequences because they are
Speaker 1: the building blocks of that blueprint. But I'll be honest.
Speaker 1: When you look at them on paper, they look like
Speaker 1: a secret code.
Speaker 2: They do require a slight mental chef. Let's look at
Speaker 2: those common ones. A backslash followed by a lowercase D.
Speaker 2: So backslash D represents any single digit zero through nine.
Speaker 1: Got it, And if you capitalize the letter, it flips
Speaker 1: the meaning. Right. Yeah, So a backslash and an uppercase
Speaker 1: D matches any character that is not a digit exactly.
Speaker 2: That capitalization rule applies across the board. A backslash lowercase
Speaker 2: S hunts for white space, spaces, tabs, line breaks, A
Speaker 2: backslash uppercase S finds anything that is into space.
Speaker 1: Okay, tracking so far.
Speaker 2: Then there's backslash lowercase W, which is your workhourse for
Speaker 2: alphanumeric characters. It matches letters, numbers, and underscores. Capitalize it
Speaker 2: to backslash W, and it matches non alphanumeric characters like
Speaker 2: punctuation marks.
Speaker 1: You can also define your own custom ranges using square back.
Speaker 1: That's right. Like if I only want to find numbers
Speaker 1: between five and nine, I just write bracket five dash nine,
Speaker 1: pracket yeap. It tells the engine only match characters that
Speaker 1: fall inside this specific parameter.
Speaker 2: You've got it. You also need a way to tell
Speaker 2: the engine how many times a character should occur. We
Speaker 2: use multipliers for this. An asterisk means the preceding character
Speaker 2: can appear zero or more times. A plus sign means
Speaker 2: it must appear one or more times.
Speaker 1: Okay, I understand the individual puzzle pieces, but when you
Speaker 1: string them together, the syntax becomes incredibly intimidating.
Speaker 2: It can look a bit overwhelming at first glance.
Speaker 1: Let's look at a real world example from a script.
Speaker 1: I see a pattern that reads backslash D curly brace
Speaker 1: three dash, backslash d curly brace three dash backslash d
Speaker 1: curly brace four. That looks like someone dropped a coffee
Speaker 1: mug on their keyboard. How do you read that fluently?
Speaker 2: It looks chaotic until you apply the rules we just discussed.
Speaker 2: Let's translate it piece by piece. The backslash d means
Speaker 2: we're looking for a digit. The curly braces the three
Speaker 2: inside specify an exact multiplier. We don't want zero or more,
Speaker 2: we want exactly three. So backslash d curly brace three
Speaker 2: translates to find exactly three numbers in a row.
Speaker 1: Okay, Then there is a literal hyphen.
Speaker 2: Then another backslash curly brace three, so three more numbers,
Speaker 2: another hyphen and finally backslash curly brase four meaning exactly four.
Speaker 1: Numbers wait three digits, dash three digits, dash four digits.
Speaker 1: When you look at it that way, the gibberish becomes
Speaker 1: a surgically precise blueprint for a standard US phone number exactly.
Speaker 2: You have just created a custom extraction tool. You can
Speaker 2: pass that exact tiny string of characters into a final function,
Speaker 2: unleash it on a ten thousand line HTML document, and
Speaker 2: it will effortlessly sift through the chaos and hand you
Speaker 2: a perfect list of every single phone number on the page.
Speaker 1: That is wild. Okay, let's take this new found power
Speaker 1: and apply it to the HTML we fetched earlier. Web
Speaker 1: pages are built using tags, so the title of a
Speaker 1: page is wrapped in title tags.
Speaker 2: Yes, and if you want to extract that title using rejects,
Speaker 2: your first instinct might be to write a pattern like
Speaker 2: title tag, dot asterisk closing title tag. The dot represents
Speaker 2: any character, and the asterisk means repeated zero or more times. Right,
Speaker 2: it will grab the opening tag, everything in the middle,
Speaker 2: and the closing tag.
Speaker 1: But the problem with that is you don't actually want
Speaker 1: the literal HTML tags in your final spreadsheet, you only
Speaker 1: want the human readable text sandwiched between them spot on.
Speaker 2: To isolate that text, you introduce capturing groups, which are
Speaker 2: created using parentheses. You update your pattern to put parentheses
Speaker 2: around the dot asterisk A. The rejex engine sees those
Speaker 2: parentheses and understands the instruction. I need you to locate
Speaker 2: this entire structure to ensure we are in the right place,
Speaker 2: but I only want you to extract and return the
Speaker 2: specific data trapped inside the parentheses.
Speaker 1: That's brilliant. Let's scale that up. Imagine we are scraping
Speaker 1: a directory of employees. The web page lists them using
Speaker 1: HTML bullet points, which are designated by lie tags. We
Speaker 1: want to extract the names of everyone whose first name
Speaker 1: starts with the letter C.
Speaker 2: Construct a pattern that looks for the lie tag followed
Speaker 2: by a capital C followed by a backslash W plus
Speaker 2: to capture the rest of the alphanumeric characters in the name.
Speaker 2: You wrap that entire name segment in parentheses, and the
Speaker 2: engine will spit out a clean list of first names.
Speaker 1: And you can even set up multiple capturing groups in
Speaker 1: a single pattern to extract complex data sets. Right like,
Speaker 1: you could create one group for a city, another group
Speaker 1: for the two letter state code, and a final group
Speaker 1: for the five digit zip code.
Speaker 2: Exactly. The rejects engine will return those matches as neatly
Speaker 2: organized data tipples. You are pulling highly structured, relational data
Speaker 2: straight out of raw code.
Speaker 1: But as we walk through these HTML examples, a massive
Speaker 1: vulnerability becomes obvious to me. Look that HTML is notoriously messy.
Speaker 1: Web developers are human. They make mistakes, they update site layouts,
Speaker 1: they do weird things. What if a developer decides a
Speaker 1: specific employee's name needs to stand out, so they inject
Speaker 1: an HTML bold tag right in the middle of the
Speaker 1: first name. Oh yeah, Or what if they accidentally leave
Speaker 1: a line break where there shouldn't be one. Rajex doesn't
Speaker 1: actually understand HTML. It just blindly reads raw characters from
Speaker 1: left to right.
Speaker 2: That is the fundamental limitation of using regular expressions as
Speaker 2: your primary webscribing tool. It completely lacks structural awareness.
Speaker 1: It feels like trying to navigate a sprawling, multi level
Speaker 1: city while totally blindfolded, relying exclusively on a compass like
Speaker 1: the compass might tell you that you are heading directly
Speaker 1: north toward your target data, but it has no idea
Speaker 1: that there is a brick wall in front of you,
Speaker 1: or that the data is actually on the third floor
Speaker 1: of a building.
Speaker 2: The compass analogy is perfect. Rejects is rigid and literal.
Speaker 2: If the underlying structure of the HTML shifts even slightly,
Speaker 2: your carefully crafted rejects pattern instantly shatters. Right. If rejects
Speaker 2: is a blind compass, what we actually need is a detailed,
Speaker 2: three dimensional map of the city. We need a tool
Speaker 2: that understands the architecture, and in Python that map is
Speaker 2: a library called Beautiful Soup.
Speaker 1: Beautiful Soup or BS four for short, HUG. I want
Speaker 1: to understand how it actually creates this map, because the
Speaker 1: HTML we feed it is still just a chaotic string
Speaker 1: of text.
Speaker 2: Well, beautiful Soup does not work alone. It relies on
Speaker 2: underlying parsing engines, most commonly LexML or HTML five lib.
Speaker 2: Think of these parsers as the heavy machinery. Okay, they
Speaker 2: ingest the messy, broken HTML string, figure out how all
Speaker 2: the open and closed tags relate to each other, and
Speaker 2: transform it into a highly structured part tree that Python
Speaker 2: can interact with. Beautiful Soup is just the elegant interface
Speaker 2: that lets you navigate that tree, and.
Speaker 1: One of the first commands you run when you load
Speaker 1: that tree is soup dot pritiphy. It takes that unreadable
Speaker 1: block of code and magically applies proper indentation. It visually
Speaker 1: structures the HTML so a human can instantly see which
Speaker 1: tags are nested inside of other tags. It restores your sanity,
Speaker 1: it really does.
Speaker 2: But the true magic is in the navigation using what
Speaker 2: is known as dot notation.
Speaker 1: Right with rejex, if we wanted the page title, we
Speaker 1: had to build wild cards and capturing groups to sift
Speaker 1: through characters. With beautiful soup, you don't use wild cards.
Speaker 1: You just type soup dot title.
Speaker 2: Dot string exactly. You literally ask the soup object for
Speaker 2: the title element and then ask for the string inside it,
Speaker 2: and it just hands it to you. If you want
Speaker 2: the very first paragraph inside the body of the web page,
Speaker 2: you don't write a pattern. You just type soup dot
Speaker 2: body dot p It elegantly walks down the branches of
Speaker 2: the parch.
Speaker 1: Tree that is so clean. It understands the concept of
Speaker 1: a hierarchical family tree. It knows what a parent element is,
Speaker 1: what a child element is, and what a sibling element is.
Speaker 2: Every single navigable piece of data in that tree has
Speaker 2: a parent property. So if your code locates a specific
Speaker 2: hyperlink in a tag, but you need to know what
Speaker 2: container is actually holding that link, you simply call soup
Speaker 2: dot a dot parent. The library instantly traverses up the
Speaker 2: tree and hands you the surrounding tag, whether it's a paragraph,
Speaker 2: a menu list, or a sidebar.
Speaker 1: So we have the map. Does that mean we throw
Speaker 1: our rejecs compass in the trash?
Speaker 2: Not at all. The two tools are meant to be combined.
Speaker 2: Beautiful Soup is vastly superior for the heavy lifting of
Speaker 2: structural navigation because it understands the architecture of the web page,
Speaker 2: but ridjex remains the undisputed king of highly specific string extraction.
Speaker 2: I see think about your city analogy. You use Beautiful
Speaker 2: Soup's map to confidently navigate down the street, enter the
Speaker 2: correct house, walk down the hallway, and step into the
Speaker 2: exact right room. Okay, And then once you are safely
Speaker 2: in that room, you pull out your regular expressions compass
Speaker 2: to search the drawers for the specific item you need.
Speaker 1: That makes total sense. You use the tools for what
Speaker 1: they're best at. Let's look at the journey we've mapped
Speaker 1: out today. We started by sending our robot Butler out
Speaker 1: using the requests library, and we learned how to spoof
Speaker 1: our headers to slip past servers trying to block us.
Speaker 1: Then we explored the remodule, learning how to assemble special
Speaker 1: sequences into powerful rejects blueprints to find hidden data. And
Speaker 1: finally we realized that to build robust, break resistant scrapers,
Speaker 1: we need the parsers behind Beautiful Soup to map the
Speaker 1: HTML matrix for us.
Speaker 2: You are basically stacking these tools to give yourself total
Speaker 2: control over the raw data of the web.
Speaker 1: Before we wrap up this deep dive, we'll want to
Speaker 1: leave you with a mental exercise to really lock these
Speaker 1: concepts in definitely.
Speaker 2: Here's a quick challenge to test your understanding of how
Speaker 2: these tools combine. Imagine you were scraping a web page
Speaker 2: that contains one hundred product reviews. The page itself is
Speaker 2: visually cluttered. It is navigation menus, footers, sidebars, advertising banners.
Speaker 2: How would you extract only the five star ratings?
Speaker 1: Oh, that's a good one.
Speaker 2: Think about it logically using the room and drawer concept. First,
Speaker 2: you leverage Beautiful soups structural awareness. You use it to
Speaker 2: navigate straight past the menus and footers directly into the
Speaker 2: paragraph tags that contain the reviews. You navigate to the
Speaker 2: right room.
Speaker 1: By passing all the junk exactly.
Speaker 2: Then, once you have isolated just that review text, you
Speaker 2: apply a RIDGEX pattern like backslash, dash, ass star to
Speaker 2: comb through the text and extract only the precise ratings
Speaker 2: You search the drawer, use.
Speaker 1: The map to navigate the maze, use the compass to
Speaker 1: extract the treasure. Love. The absolute best way to internalize
Speaker 1: this is to get your hands dirty. Open up a
Speaker 1: Python terminal, import those libraries, and start exploring the hidden
Speaker 1: data structures of your favorite website.
Speaker 2: Absolutely, just go build something.
Speaker 1: There is a massive, incredibly valuable world of data waiting
Speaker 1: to be unlocked. We'll see you on the next deep dive.