WEBVTT

1
00:00:00.120 --> 00:00:04.480
<v Speaker 1>Imagine you have just scraped this massive, maybe ten thousand

2
00:00:04.519 --> 00:00:08.320
<v Speaker 1>page data set to train a new AI model, right,

3
00:00:08.480 --> 00:00:12.480
<v Speaker 1>and your crawler did its job perfectly. But well, there

4
00:00:12.560 --> 00:00:14.240
<v Speaker 1>is a catastrophic.

5
00:00:13.640 --> 00:00:15.560
<v Speaker 2>Problem there usually is with raw data.

6
00:00:15.679 --> 00:00:19.519
<v Speaker 1>Exactly, every single page in that data set is just poisoned.

7
00:00:19.519 --> 00:00:23.480
<v Speaker 1>It's full of these invisible memory hogging script tags and

8
00:00:23.559 --> 00:00:24.679
<v Speaker 1>tracking pixel.

9
00:00:24.480 --> 00:00:26.960
<v Speaker 2>Oh yeah, that will absolutely kill your processing pipeline.

10
00:00:27.079 --> 00:00:29.920
<v Speaker 1>Right, if you feed that raw HTML into your system,

11
00:00:30.239 --> 00:00:32.600
<v Speaker 1>it's going to crash from memory overload. You don't even

12
00:00:32.640 --> 00:00:34.240
<v Speaker 1>need to read the data at that point, you just

13
00:00:34.280 --> 00:00:36.880
<v Speaker 1>need to like surgically destroy parts of it on the fly.

14
00:00:36.960 --> 00:00:38.880
<v Speaker 2>Which is exactly what we're getting into today.

15
00:00:38.960 --> 00:00:42.520
<v Speaker 1>Yes, welcome to the deep dive. Today we are learning

16
00:00:42.520 --> 00:00:46.479
<v Speaker 1>how to actively hack and manipulate the HTML parse tree.

17
00:00:46.640 --> 00:00:49.079
<v Speaker 2>And this is really a complete paradigm shift for anyone

18
00:00:49.119 --> 00:00:52.119
<v Speaker 2>dealing with web data. I mean, when most developers start

19
00:00:52.119 --> 00:00:55.359
<v Speaker 2>out using Python and beautiful Soup, they treat the pars

20
00:00:55.399 --> 00:00:58.439
<v Speaker 2>tree like a static painting in a museum like look,

21
00:00:58.479 --> 00:01:01.640
<v Speaker 2>but don't touch exactly. You can look at it, you

22
00:01:01.679 --> 00:01:04.879
<v Speaker 2>can measure it, extract the colors, but you absolutely cannot

23
00:01:04.959 --> 00:01:08.760
<v Speaker 2>touch the canvas. But today we are moving past that you,

24
00:01:09.120 --> 00:01:11.879
<v Speaker 2>the listener, are stepping over the velvet rope. You weren't

25
00:01:11.920 --> 00:01:15.120
<v Speaker 2>just reading a text file anymore. You are manipulating a live,

26
00:01:15.319 --> 00:01:18.480
<v Speaker 2>intertrected database directly inside your computer's ram.

27
00:01:18.719 --> 00:01:21.000
<v Speaker 1>And that is the crucial mental leap, right if you're

28
00:01:21.000 --> 00:01:25.239
<v Speaker 1>building cybersecurity tools or you're just normalizing really messy data

29
00:01:25.280 --> 00:01:28.239
<v Speaker 1>for machine learning, you have to transition from being just

30
00:01:28.280 --> 00:01:30.400
<v Speaker 1>an observer to being an architect.

31
00:01:30.519 --> 00:01:33.640
<v Speaker 2>Yes, every single tag in that document is a node,

32
00:01:34.359 --> 00:01:39.680
<v Speaker 2>and beautiful soup gives you essentially root access to modify, delete,

33
00:01:39.840 --> 00:01:41.959
<v Speaker 2>or even rebuild those nodes in real time.

34
00:01:42.519 --> 00:01:44.400
<v Speaker 1>So to really unpack this and get a feel for

35
00:01:44.480 --> 00:01:47.359
<v Speaker 1>how powerful it is, let's establish a sort of conceptual

36
00:01:47.439 --> 00:01:48.719
<v Speaker 1>sandbox for you to picture.

37
00:01:48.840 --> 00:01:49.519
<v Speaker 2>Okay, I like that.

38
00:01:49.680 --> 00:01:54.079
<v Speaker 1>Imagine an incredibly simple HTML document loaded into your computer's memory.

39
00:01:54.280 --> 00:01:56.519
<v Speaker 1>Let's call it the three Apples document.

40
00:01:56.560 --> 00:01:57.719
<v Speaker 2>The three Apples document, got it?

41
00:01:57.840 --> 00:02:00.280
<v Speaker 1>Yeah? It is a standard header a body, and inside

42
00:02:00.359 --> 00:02:03.359
<v Speaker 1>that body there are just three very simple paragraphs. One

43
00:02:03.439 --> 00:02:06.159
<v Speaker 1>is about Eve's Apple, the second is about Isaac Newton's Apple,

44
00:02:06.239 --> 00:02:08.080
<v Speaker 1>and the third is about Steve jobs Apple.

45
00:02:08.360 --> 00:02:12.080
<v Speaker 2>So no links, no images, just basic structural tags.

46
00:02:11.840 --> 00:02:15.719
<v Speaker 1>Right now, exactly, just the bare bones, and I'm visualizing

47
00:02:15.840 --> 00:02:19.000
<v Speaker 1>this HTML parstree like a living Bonzie tree.

48
00:02:19.039 --> 00:02:20.199
<v Speaker 2>Oh that's a great analogy.

49
00:02:20.360 --> 00:02:22.479
<v Speaker 1>Yeah, you have the root at the base, the trunk

50
00:02:22.560 --> 00:02:25.840
<v Speaker 1>is the body tag, and the branches are your specific tags.

51
00:02:26.719 --> 00:02:29.199
<v Speaker 1>Up until now in your scraping journey, you've just been

52
00:02:29.240 --> 00:02:32.479
<v Speaker 1>observing the Bonzie. But today we are learning how to

53
00:02:32.520 --> 00:02:36.240
<v Speaker 1>prune branches, graft entirely new ones on, and reshape the

54
00:02:36.360 --> 00:02:37.120
<v Speaker 1>entire plant.

55
00:02:37.280 --> 00:02:40.000
<v Speaker 2>And the cool thing is because that bon side tree

56
00:02:40.080 --> 00:02:44.400
<v Speaker 2>exists entirely in your computer's active memory. Any modification you

57
00:02:44.479 --> 00:02:48.199
<v Speaker 2>make instantly reorganizes the physical structure of the plant.

58
00:02:47.919 --> 00:02:50.280
<v Speaker 1>Meaning if you change a branch, the leaves attached to

59
00:02:50.319 --> 00:02:51.919
<v Speaker 1>it just move automatically exactly.

60
00:02:52.120 --> 00:02:55.000
<v Speaker 2>So, if our scraper is working perfectly, but we've hit

61
00:02:55.039 --> 00:02:58.560
<v Speaker 2>a roadblock with data inconsistency, we need to learn how

62
00:02:58.560 --> 00:03:01.280
<v Speaker 2>to shape the branches we already have before we start

63
00:03:01.560 --> 00:03:02.360
<v Speaker 2>planting new seeds.

64
00:03:02.400 --> 00:03:05.120
<v Speaker 1>Okay, let's talk about data normalization then. Say I'm pulling

65
00:03:05.240 --> 00:03:08.719
<v Speaker 1>data from thousands of older websites. Some developers use the

66
00:03:08.719 --> 00:03:11.319
<v Speaker 1>bold tag the B tag, and others use the strong tag.

67
00:03:11.840 --> 00:03:15.759
<v Speaker 1>But my AI model treats those as two completely different things.

68
00:03:15.840 --> 00:03:18.400
<v Speaker 2>Which is a classic data science headache. I mean, we've

69
00:03:18.400 --> 00:03:21.199
<v Speaker 2>all been there. Oh yeah, But solving it is incredibly

70
00:03:21.240 --> 00:03:24.840
<v Speaker 2>straightforward once you understand that tag names are just mutable properties.

71
00:03:25.400 --> 00:03:28.400
<v Speaker 1>Wait, so they're just variables you can change pretty much.

72
00:03:29.120 --> 00:03:32.759
<v Speaker 2>If you locate a B tag in your soup object

73
00:03:32.919 --> 00:03:35.719
<v Speaker 2>it has a name attribute, you can literally write a

74
00:03:35.759 --> 00:03:38.719
<v Speaker 2>single line of Python code that assigns a new string

75
00:03:38.759 --> 00:03:39.479
<v Speaker 2>to that property.

76
00:03:39.599 --> 00:03:40.719
<v Speaker 1>Really, just like that.

77
00:03:40.919 --> 00:03:42.680
<v Speaker 2>Yeah, you just tell the script to change the dot

78
00:03:42.759 --> 00:03:44.199
<v Speaker 2>name property from b to strong.

79
00:03:44.639 --> 00:03:47.879
<v Speaker 1>So I am not running some complex, error prone string

80
00:03:47.919 --> 00:03:51.039
<v Speaker 1>replacement across the whole text document. I'm just modifying an

81
00:03:51.039 --> 00:03:53.759
<v Speaker 1>object property and the tree instantly updates itself.

82
00:03:53.879 --> 00:03:58.439
<v Speaker 2>That is the exact mechanism. The underlying HTML tag just transforms.

83
00:03:58.840 --> 00:04:02.360
<v Speaker 2>If you use the Pretti five function, which outputs your

84
00:04:02.400 --> 00:04:05.599
<v Speaker 2>live HTML structure in a clean indented format, you will

85
00:04:05.639 --> 00:04:08.960
<v Speaker 2>see that your change just cascaded through the entire object.

86
00:04:09.080 --> 00:04:11.919
<v Speaker 1>Wow. So your data set is instantly uniform, exactly, And

87
00:04:11.960 --> 00:04:14.039
<v Speaker 1>it gets even more powerful when we look at the

88
00:04:14.080 --> 00:04:17.680
<v Speaker 1>attributes buried inside those tags. Let's go back to our

89
00:04:17.720 --> 00:04:22.800
<v Speaker 1>Bondsie tree, specifically the paragraph branch for Newton's apple. Okay,

90
00:04:23.079 --> 00:04:25.759
<v Speaker 1>let's assume it currently has a class attribute called Apple,

91
00:04:26.399 --> 00:04:29.319
<v Speaker 1>but my scraper needs that class to be more specific.

92
00:04:29.519 --> 00:04:31.839
<v Speaker 1>I needed to say Newton. Can I just dive into

93
00:04:31.879 --> 00:04:32.839
<v Speaker 1>the tag's dictionary?

94
00:04:32.959 --> 00:04:37.680
<v Speaker 2>Yes you can, beautiful soup treats tag attributes identically to

95
00:04:37.800 --> 00:04:39.279
<v Speaker 2>standard Python dictionaries.

96
00:04:39.319 --> 00:04:40.279
<v Speaker 1>Oh that's so easy.

97
00:04:40.519 --> 00:04:44.079
<v Speaker 2>It really is. You locate that specific paragraph tag, you

98
00:04:44.160 --> 00:04:46.879
<v Speaker 2>look up the class key, and you just overwrite the

99
00:04:46.959 --> 00:04:48.160
<v Speaker 2>value with Newton.

100
00:04:47.959 --> 00:04:50.680
<v Speaker 1>And the moment that code executes.

101
00:04:50.279 --> 00:04:53.160
<v Speaker 2>The metadata of that element is permanently altered in your

102
00:04:53.199 --> 00:04:54.120
<v Speaker 2>computer's memory.

103
00:04:54.279 --> 00:04:58.319
<v Speaker 1>That's amazing. But here's a pushback question. What if I'm

104
00:04:58.360 --> 00:05:01.879
<v Speaker 1>working in a cybersecurity context. Say I find attributes that

105
00:05:01.920 --> 00:05:03.720
<v Speaker 1>are actively malicious.

106
00:05:03.639 --> 00:05:07.040
<v Speaker 2>Like an onload attribute hiding a nasty JavaScript.

107
00:05:06.600 --> 00:05:08.720
<v Speaker 1>Paylost exactly, I definitely do not want to rename that.

108
00:05:08.759 --> 00:05:10.759
<v Speaker 1>I want it completely eradicated from the document.

109
00:05:10.800 --> 00:05:13.079
<v Speaker 2>Well, you just rely on standard Python syntax for that.

110
00:05:13.199 --> 00:05:13.399
<v Speaker 1>Yeah.

111
00:05:13.439 --> 00:05:15.639
<v Speaker 2>If you want an attribute gone, you use the Python

112
00:05:15.720 --> 00:05:17.360
<v Speaker 2>dell do keyword.

113
00:05:17.000 --> 00:05:18.000
<v Speaker 1>The delete keyword.

114
00:05:18.120 --> 00:05:21.680
<v Speaker 2>Yeah, del you simply tell Python to delete that specific

115
00:05:21.720 --> 00:05:23.120
<v Speaker 2>attribute key from the tag.

116
00:05:23.399 --> 00:05:27.600
<v Speaker 1>That is just so elegant. Just dell a space and

117
00:05:27.879 --> 00:05:30.959
<v Speaker 1>the attribute reference and the parstree doesn't complain that I'm

118
00:05:31.040 --> 00:05:33.319
<v Speaker 1>just tearing metadata right out of it.

119
00:05:33.279 --> 00:05:36.319
<v Speaker 2>Not at all. The attribute just vanishes. If you run

120
00:05:36.399 --> 00:05:39.759
<v Speaker 2>prettify immediately after, you will see a bear tag. It's

121
00:05:39.800 --> 00:05:41.639
<v Speaker 2>basically surgical data sanitization.

122
00:05:42.040 --> 00:05:45.920
<v Speaker 1>You're stripping away the poison without damaging the surrounding text. Precisely,

123
00:05:46.079 --> 00:05:49.519
<v Speaker 1>speaking of the surrounding text, I'm imagining we can manipulate

124
00:05:49.560 --> 00:05:53.319
<v Speaker 1>the visible content just as easily. Right, Like, say, the

125
00:05:53.319 --> 00:05:56.600
<v Speaker 1>text for Steve Jobs Apple has a glaring typo? Do

126
00:05:56.680 --> 00:05:59.279
<v Speaker 1>I need to write a messy regular expression to find

127
00:05:59.319 --> 00:06:00.759
<v Speaker 1>and fix that specific words?

128
00:06:00.839 --> 00:06:04.439
<v Speaker 2>Oh? Please don't. Regular expressions would be massive overkill there.

129
00:06:04.480 --> 00:06:06.480
<v Speaker 1>They're dangerous, right, very.

130
00:06:06.560 --> 00:06:09.720
<v Speaker 2>They're highly prone to breaking the HTML structure. If you

131
00:06:09.800 --> 00:06:13.160
<v Speaker 2>accidentally match a tag name instead of the visible text.

132
00:06:12.959 --> 00:06:15.040
<v Speaker 1>Right, you could ruin the whole document exactly.

133
00:06:15.079 --> 00:06:17.879
<v Speaker 2>So instead, you locate the tag containing the typo and

134
00:06:17.920 --> 00:06:21.240
<v Speaker 2>you just assign a brand new, corrected string to its

135
00:06:21.279 --> 00:06:22.199
<v Speaker 2>dot string property.

136
00:06:22.240 --> 00:06:24.800
<v Speaker 1>Ah, so the old visible text is just overwritten.

137
00:06:25.120 --> 00:06:28.759
<v Speaker 2>Yep. The new text drops right into place, and all

138
00:06:28.920 --> 00:06:32.519
<v Speaker 2>those complex EACHTML brackets surrounding it are completely untouched.

139
00:06:32.879 --> 00:06:36.360
<v Speaker 1>Okay, So we've reshaped the leaves, we've pruned the bad attributes,

140
00:06:36.399 --> 00:06:39.480
<v Speaker 1>we've fixed the text. We've proven we can manipulate what's

141
00:06:39.519 --> 00:06:44.160
<v Speaker 1>already there, but a digital architect needs to build. Let's

142
00:06:44.160 --> 00:06:47.319
<v Speaker 1>talk about making this document actually grow. If I want

143
00:06:47.319 --> 00:06:50.920
<v Speaker 1>to graft entirely new branches onto this Bonzi tree, how

144
00:06:50.920 --> 00:06:52.319
<v Speaker 1>do I inject new data?

145
00:06:52.639 --> 00:06:56.160
<v Speaker 2>Well, when it comes to adding content, the logic closely

146
00:06:56.160 --> 00:07:00.040
<v Speaker 2>mirrors standard Python lists. Your main entry point is the

147
00:07:00.199 --> 00:07:02.639
<v Speaker 2>pen function dot a pend. Okay, let's say you have

148
00:07:02.639 --> 00:07:05.920
<v Speaker 2>a paragraph tag with the ID three apples, and the

149
00:07:05.959 --> 00:07:09.160
<v Speaker 2>text inside says not really an insignificant fruit. If you

150
00:07:09.199 --> 00:07:10.959
<v Speaker 2>want to expand on that thought, you called dot a

151
00:07:11.040 --> 00:07:12.639
<v Speaker 2>pend on that tag and pass it a new strings

152
00:07:12.720 --> 00:07:14.240
<v Speaker 2>something like quite interesting.

153
00:07:14.480 --> 00:07:17.600
<v Speaker 1>Now, if I remember how Python lists work, dota pend

154
00:07:17.720 --> 00:07:20.319
<v Speaker 1>always shoves the new item to the very back of

155
00:07:20.319 --> 00:07:22.839
<v Speaker 1>the line, right, that's right, so that new text drops

156
00:07:22.839 --> 00:07:25.759
<v Speaker 1>in at the absolute end of whatever is already inside

157
00:07:25.759 --> 00:07:26.720
<v Speaker 1>that paragraph tag.

158
00:07:26.959 --> 00:07:29.160
<v Speaker 2>It does. And if you have a massive list of

159
00:07:29.240 --> 00:07:31.920
<v Speaker 2>new data points you want to add, maybe an array

160
00:07:31.959 --> 00:07:33.920
<v Speaker 2>of a dozen new strings, you can graduate to the

161
00:07:33.959 --> 00:07:34.639
<v Speaker 2>extend function.

162
00:07:34.759 --> 00:07:37.480
<v Speaker 1>Oh, dot extend like lists again.

163
00:07:37.240 --> 00:07:40.360
<v Speaker 2>Exactly, You pass in your list and it iterates through

164
00:07:40.639 --> 00:07:43.680
<v Speaker 2>adding each element sequentially to the end of the tag's contents.

165
00:07:43.759 --> 00:07:45.519
<v Speaker 1>But I'm guessing I don't always want to be at

166
00:07:45.560 --> 00:07:48.000
<v Speaker 1>the back of the line. Usually not Like if I'm

167
00:07:48.040 --> 00:07:51.399
<v Speaker 1>injecting a high priority warning label into a diff container,

168
00:07:52.040 --> 00:07:54.800
<v Speaker 1>I need that warning at the very beginning before any

169
00:07:54.839 --> 00:07:56.560
<v Speaker 1>of the existing texts even renders.

170
00:07:56.839 --> 00:08:00.279
<v Speaker 2>For precision placement inside a tag, you use the inside.

171
00:08:00.240 --> 00:08:01.639
<v Speaker 1>Function dot insert.

172
00:08:01.839 --> 00:08:05.680
<v Speaker 2>Yes, it takes two arguments. The first is an index

173
00:08:05.759 --> 00:08:09.079
<v Speaker 2>number representing this spatial position. So if you pass an

174
00:08:09.079 --> 00:08:12.279
<v Speaker 2>index of zero, you are instructing the parse tree to

175
00:08:12.360 --> 00:08:15.839
<v Speaker 2>propend your new content to the absolute beginning of the

176
00:08:15.879 --> 00:08:17.120
<v Speaker 2>tag's internal structure.

177
00:08:17.240 --> 00:08:18.759
<v Speaker 1>So you're basically cutting the line.

178
00:08:18.800 --> 00:08:19.480
<v Speaker 2>You're cutting the line.

179
00:08:19.480 --> 00:08:22.199
<v Speaker 1>Okay, picture the htmltree on your screen right now. Both

180
00:08:22.279 --> 00:08:25.079
<v Speaker 1>dot a pend and dot insert are operating inside the

181
00:08:25.079 --> 00:08:27.920
<v Speaker 1>boundaries of a specific tag. You are putting new leaves

182
00:08:27.920 --> 00:08:31.399
<v Speaker 1>on an existing branch. But what if my scraper decides

183
00:08:31.439 --> 00:08:33.399
<v Speaker 1>we need a brand new branch right next door.

184
00:08:33.519 --> 00:08:36.399
<v Speaker 2>To step outside the tag, you have to use relative insertion,

185
00:08:37.000 --> 00:08:39.919
<v Speaker 2>and the functions are beautifully explicit for this. They're called

186
00:08:39.960 --> 00:08:42.159
<v Speaker 2>dot insert before and dot insert after.

187
00:08:42.399 --> 00:08:45.399
<v Speaker 1>Oh, that is explicit. So if I locate the paragraph

188
00:08:45.559 --> 00:08:49.039
<v Speaker 1>for Steve Jobs's apple, and I use dot in for tafter,

189
00:08:49.480 --> 00:08:51.559
<v Speaker 1>I can place new data directly beneath it.

190
00:08:51.679 --> 00:08:54.919
<v Speaker 2>Yep, it becomes a sibling in the overarching HTML hierarchy.

191
00:08:55.080 --> 00:08:58.159
<v Speaker 1>That kind of spatial control is everything when you're dynamically

192
00:08:58.240 --> 00:09:02.519
<v Speaker 1>generating reports or like rebuilding a web interface on the fly.

193
00:09:02.879 --> 00:09:05.639
<v Speaker 2>It really is. However, this brings us to a really

194
00:09:05.720 --> 00:09:07.279
<v Speaker 2>critical architectural rule.

195
00:09:07.440 --> 00:09:08.279
<v Speaker 1>Okay, what is it?

196
00:09:08.440 --> 00:09:10.879
<v Speaker 2>Well, so far we've just been pushing raw text strings

197
00:09:10.879 --> 00:09:14.039
<v Speaker 2>into the tree. But HTML isn't just flat text. It's

198
00:09:14.080 --> 00:09:15.840
<v Speaker 2>a strictly formatted ecosystem.

199
00:09:16.120 --> 00:09:19.240
<v Speaker 1>Right. If I just shove raw Python strings everywhere, the

200
00:09:19.240 --> 00:09:21.639
<v Speaker 1>tree won't recognize them as structural nodes, will it.

201
00:09:22.000 --> 00:09:22.720
<v Speaker 2>No, it won't.

202
00:09:22.799 --> 00:09:25.519
<v Speaker 1>I'm guessing that completely breaks the navigation logic if I

203
00:09:25.559 --> 00:09:27.080
<v Speaker 1>try to run a search on the tree later.

204
00:09:27.440 --> 00:09:30.919
<v Speaker 2>That is the exact reason why we need to introduce specialized,

205
00:09:31.440 --> 00:09:35.879
<v Speaker 2>beautiful soup objects. If you inject a standard Python string

206
00:09:36.240 --> 00:09:39.840
<v Speaker 2>into the tree, and later your scraper runs a command

207
00:09:40.000 --> 00:09:43.639
<v Speaker 2>like dot find parent on that text, the program will crash.

208
00:09:43.879 --> 00:09:45.840
<v Speaker 1>Oh wow, Why.

209
00:09:45.559 --> 00:09:47.720
<v Speaker 2>Because the native Python string does not have a parent,

210
00:09:47.799 --> 00:09:51.360
<v Speaker 2>it does not have siblings, it has zero spatial awareness.

211
00:09:51.639 --> 00:09:54.679
<v Speaker 1>So it's just a dumb string floating in the voyage essentially, yes,

212
00:09:54.799 --> 00:09:56.679
<v Speaker 1>So how do we give it spatial awareness.

213
00:09:56.759 --> 00:09:59.200
<v Speaker 2>You have to import a tool called navigable.

214
00:09:58.720 --> 00:10:00.000
<v Speaker 1>String navigable strength.

215
00:10:00.120 --> 00:10:05.080
<v Speaker 2>It's beautiful soups special wrapper for text. When you instantiate

216
00:10:05.159 --> 00:10:08.039
<v Speaker 2>a navigal string, it retains all the dictionary methods and

217
00:10:08.080 --> 00:10:11.000
<v Speaker 2>traversal pointers of the whole HTML.

218
00:10:10.600 --> 00:10:13.279
<v Speaker 1>Parstree ra I see. So it's a string that actually

219
00:10:13.320 --> 00:10:16.000
<v Speaker 1>knows where it lives and knows who its parent tag is,

220
00:10:16.080 --> 00:10:17.759
<v Speaker 1>and it knows what tags are sitting right next to

221
00:10:17.799 --> 00:10:18.480
<v Speaker 1>it precisely.

222
00:10:18.519 --> 00:10:21.000
<v Speaker 2>It becomes a fully integrated resident of the ecosystem.

223
00:10:21.080 --> 00:10:22.039
<v Speaker 1>That makes total sense.

224
00:10:22.200 --> 00:10:25.000
<v Speaker 2>Now, to be fair, the library is quite smart. If

225
00:10:25.039 --> 00:10:27.759
<v Speaker 2>you pass a regular string into Dota pend, it will

226
00:10:27.799 --> 00:10:30.120
<v Speaker 2>usually convert it to a navigable string under the hood

227
00:10:30.120 --> 00:10:30.360
<v Speaker 2>for you.

228
00:10:30.440 --> 00:10:33.039
<v Speaker 1>Oh so it handles it automatically sometimes.

229
00:10:32.879 --> 00:10:37.080
<v Speaker 2>Sometimes, But when you are building complex structures from scratch,

230
00:10:37.679 --> 00:10:41.759
<v Speaker 2>explicitly defining your navigable objects prevents a lot of really

231
00:10:41.799 --> 00:10:43.480
<v Speaker 2>painful debugging down the line.

232
00:10:43.639 --> 00:10:47.679
<v Speaker 1>Best practices, Right, What about other structural elements that aren't

233
00:10:47.759 --> 00:10:50.279
<v Speaker 1>visible text like developer comments.

234
00:10:50.320 --> 00:10:51.320
<v Speaker 2>Well that's a good one.

235
00:10:51.200 --> 00:10:54.080
<v Speaker 1>Because sometimes if I'm sanitizing a page, I don't just

236
00:10:54.080 --> 00:10:56.200
<v Speaker 1>want it to lead a bad tag. I want to

237
00:10:56.240 --> 00:10:58.879
<v Speaker 1>comment it out and leave a breadcrumb for the auditing team.

238
00:10:59.120 --> 00:11:02.279
<v Speaker 2>You handle that by by importing the comment object just

239
00:11:02.320 --> 00:11:05.759
<v Speaker 2>called comment yep, capital C. You instantiate it with your

240
00:11:05.759 --> 00:11:08.480
<v Speaker 2>auditing note and you inject it using dot insert for

241
00:11:08.720 --> 00:11:09.559
<v Speaker 2>dot pend.

242
00:11:09.360 --> 00:11:10.799
<v Speaker 1>And it formats it correctly.

243
00:11:10.919 --> 00:11:13.399
<v Speaker 2>Yeah. When you export the HTML, it doesn't render as

244
00:11:13.519 --> 00:11:17.440
<v Speaker 2>raw text. It formats perfectly as an official HTML comment,

245
00:11:17.879 --> 00:11:20.399
<v Speaker 2>you know, complete with the angle brackets, the exclamation point,

246
00:11:20.440 --> 00:11:21.120
<v Speaker 2>and the dashes.

247
00:11:21.200 --> 00:11:23.080
<v Speaker 1>Okay, this is where we get to the real heavy lifting.

248
00:11:23.159 --> 00:11:26.200
<v Speaker 1>We have moved texts, we've injected comments, but I want

249
00:11:26.200 --> 00:11:28.759
<v Speaker 1>to build a totally new structural container.

250
00:11:29.000 --> 00:11:29.480
<v Speaker 2>Let's do it.

251
00:11:29.639 --> 00:11:32.840
<v Speaker 1>I want to build a brand new HTML tag from scratch,

252
00:11:32.960 --> 00:11:35.679
<v Speaker 1>like an image tag or a hyperlink, and weave it

253
00:11:35.720 --> 00:11:36.600
<v Speaker 1>into the document.

254
00:11:36.919 --> 00:11:40.240
<v Speaker 2>Then you need the soup dot new tag function. Let's

255
00:11:40.240 --> 00:11:41.679
<v Speaker 2>say you want to add a hyperlink, which is the

256
00:11:41.720 --> 00:11:44.279
<v Speaker 2>tag tag. Right, you literally just call soup dot new

257
00:11:44.360 --> 00:11:47.840
<v Speaker 2>tag and pass it the letter A. You now have

258
00:11:47.960 --> 00:11:50.360
<v Speaker 2>a tag object floating in your system's RAM.

259
00:11:50.440 --> 00:11:53.480
<v Speaker 1>But it's totally empty, right, It's just a blank node exactly.

260
00:11:53.480 --> 00:11:56.279
<v Speaker 2>It's an empty show. Yeah, so you have to populate it.

261
00:11:56.720 --> 00:11:59.519
<v Speaker 2>You set its string property to the word apple. Then

262
00:11:59.559 --> 00:12:02.200
<v Speaker 2>you act it's href attribute, just like we did when

263
00:12:02.240 --> 00:12:05.679
<v Speaker 2>we were modifying existing attributes earlier, and you assign it

264
00:12:05.919 --> 00:12:07.480
<v Speaker 2>the URL for the Apple.

265
00:12:07.200 --> 00:12:09.879
<v Speaker 1>Store and boom, now you have a fully formed, functional

266
00:12:09.960 --> 00:12:13.120
<v Speaker 1>hyperlink in memory exactly. And from there I can just

267
00:12:13.279 --> 00:12:16.320
<v Speaker 1>use dot insert after to graft this brand new branch

268
00:12:16.480 --> 00:12:18.879
<v Speaker 1>directly on my Bonzi tree, like right next to the

269
00:12:18.919 --> 00:12:20.159
<v Speaker 1>Steve jobs paragraph.

270
00:12:20.240 --> 00:12:23.159
<v Speaker 2>You are literally engineering new architecture on the fly. It's

271
00:12:23.200 --> 00:12:26.799
<v Speaker 2>pretty amazing, it is. But as any good gardener knows,

272
00:12:27.080 --> 00:12:30.200
<v Speaker 2>creation is only half the job. Sometimes you really have

273
00:12:30.240 --> 00:12:31.399
<v Speaker 2>to cut things away.

274
00:12:31.360 --> 00:12:33.799
<v Speaker 1>Right, which brings us back to the massive problem we

275
00:12:33.840 --> 00:12:36.200
<v Speaker 1>discussed at the very beginning of the deem dive, the

276
00:12:36.240 --> 00:12:39.440
<v Speaker 1>ten thousand page data set that is just choking on

277
00:12:39.600 --> 00:12:41.039
<v Speaker 1>memory heavy script tags.

278
00:12:41.440 --> 00:12:45.200
<v Speaker 2>Yes, we need to prune the tree, and beautiful soup

279
00:12:45.440 --> 00:12:47.960
<v Speaker 2>gives you a choice of how you destroy an element,

280
00:12:48.320 --> 00:12:50.720
<v Speaker 2>which is absolutely vital for memory management.

281
00:12:50.960 --> 00:12:52.159
<v Speaker 1>Okay, what are the choices?

282
00:12:52.399 --> 00:12:56.000
<v Speaker 2>There are two main functions for removing elements dot extract

283
00:12:56.039 --> 00:12:57.000
<v Speaker 2>and dot decomposed.

284
00:12:57.200 --> 00:12:59.000
<v Speaker 1>Extract and decomposed, yes.

285
00:12:59.440 --> 00:13:03.399
<v Speaker 2>And understanding the stark difference between them is probably the

286
00:13:03.480 --> 00:13:06.759
<v Speaker 2>single most important takeaway for anyone dealing with large scale

287
00:13:06.799 --> 00:13:07.919
<v Speaker 2>data processing today.

288
00:13:08.039 --> 00:13:10.559
<v Speaker 1>All right, Well, let's start with dot extract. If my

289
00:13:10.720 --> 00:13:15.000
<v Speaker 1>script finds a massive embedded video player tag and I

290
00:13:15.039 --> 00:13:18.159
<v Speaker 1>called dot extract on it, what actually happens inside the.

291
00:13:18.200 --> 00:13:21.879
<v Speaker 2>Ram beautiful soup immediately severs that tag and all of

292
00:13:21.919 --> 00:13:24.679
<v Speaker 2>its nested contents from the HTML parstree.

293
00:13:24.720 --> 00:13:26.279
<v Speaker 1>Okay, so it's out of the tree, right.

294
00:13:26.360 --> 00:13:29.360
<v Speaker 2>If you look the tree afterwards, the video player is gone.

295
00:13:29.639 --> 00:13:33.000
<v Speaker 2>If you check the extracted tags, carn't attribute, it returns

296
00:13:33.519 --> 00:13:35.120
<v Speaker 2>none because it's been orphaned.

297
00:13:35.279 --> 00:13:36.919
<v Speaker 1>But the tag isn't actually dead, is it.

298
00:13:37.039 --> 00:13:40.279
<v Speaker 2>No, The dot extract function returns the data you just removed,

299
00:13:40.399 --> 00:13:43.559
<v Speaker 2>So that massive video tag is still sitting there, completely intact,

300
00:13:43.840 --> 00:13:45.480
<v Speaker 2>in a variable in your Python script.

301
00:13:45.679 --> 00:13:48.679
<v Speaker 1>Ah. So, to use our gardening metaphor, dot extract is

302
00:13:48.720 --> 00:13:51.120
<v Speaker 1>like carefully uprooting a weed and placing it into a

303
00:13:51.159 --> 00:13:54.200
<v Speaker 1>temporary pot on your patio. Exactly, it's no longer in

304
00:13:54.240 --> 00:13:56.879
<v Speaker 1>the garden soil, but it's still drinking water. It's still

305
00:13:56.919 --> 00:14:02.240
<v Speaker 1>consuming your computer's memory. I could theoretically take that potted

306
00:14:02.279 --> 00:14:05.320
<v Speaker 1>plant and graft it into a completely different HTML file

307
00:14:05.519 --> 00:14:07.879
<v Speaker 1>if I wanted to, if there's an excellent.

308
00:14:07.559 --> 00:14:11.519
<v Speaker 2>Way to visualize it. You preserve the data structure for

309
00:14:11.639 --> 00:14:14.960
<v Speaker 2>future use. But think about our data set scenario. You

310
00:14:15.080 --> 00:14:18.720
<v Speaker 2>have millions of malicious script tags. You do not want

311
00:14:18.720 --> 00:14:19.879
<v Speaker 2>them in a pot on your patio.

312
00:14:19.960 --> 00:14:22.440
<v Speaker 1>Oh definitely not. If I extract millions of script tags

313
00:14:22.480 --> 00:14:25.000
<v Speaker 1>and hold them in variables, my RAM usage is going

314
00:14:25.080 --> 00:14:27.519
<v Speaker 1>to spike until the whole system crashes. I don't want

315
00:14:27.519 --> 00:14:29.080
<v Speaker 1>a pot, I want an incinerator.

316
00:14:29.080 --> 00:14:32.159
<v Speaker 2>And that incinerator is the dot decomposed.

317
00:14:31.600 --> 00:14:33.080
<v Speaker 1>Function dot decomposed Yep.

318
00:14:33.200 --> 00:14:36.240
<v Speaker 2>When you locate a tag and call dot decompose on it,

319
00:14:36.240 --> 00:14:38.879
<v Speaker 2>it doesn't just sever the node from the tree. It

320
00:14:38.919 --> 00:14:41.360
<v Speaker 2>completely obliterates the tag and all of its.

321
00:14:41.240 --> 00:14:43.399
<v Speaker 1>Contents, so it wipes it from memory entirely.

322
00:14:43.559 --> 00:14:47.120
<v Speaker 2>Entirely, it reclaims that RAM the data is gone forever.

323
00:14:47.559 --> 00:14:51.200
<v Speaker 1>Wow. That is a life saving distinction for AI developers.

324
00:14:51.480 --> 00:14:53.960
<v Speaker 1>If you know you absolutely do not need the data,

325
00:14:54.159 --> 00:14:56.960
<v Speaker 1>you should always use dot decomposed to keep your processing

326
00:14:56.960 --> 00:14:57.840
<v Speaker 1>pipeline lightweight.

327
00:14:57.879 --> 00:15:02.080
<v Speaker 2>Absolutely. But you know data cleaning is always binary. Sometimes

328
00:15:02.080 --> 00:15:03.639
<v Speaker 2>you don't want to destroy a tag and you don't

329
00:15:03.679 --> 00:15:05.399
<v Speaker 2>want to extract it either. You just need to give

330
00:15:05.399 --> 00:15:06.840
<v Speaker 2>it a structural makeover.

331
00:15:06.639 --> 00:15:10.120
<v Speaker 1>Like swapping an element out entirely exactly. Say I have

332
00:15:10.159 --> 00:15:13.720
<v Speaker 1>an outdated H one header tag and my new design

333
00:15:13.759 --> 00:15:16.720
<v Speaker 1>system requires it to be an italicized itag.

334
00:15:16.960 --> 00:15:19.120
<v Speaker 2>For that, you use the dot replaced with function.

335
00:15:19.480 --> 00:15:20.639
<v Speaker 1>Okay, how does that flow?

336
00:15:20.840 --> 00:15:23.320
<v Speaker 2>Well? You use soup dot new tag to build your

337
00:15:23.360 --> 00:15:27.200
<v Speaker 2>brand new italic tag in memory first, Then you locate

338
00:15:27.240 --> 00:15:29.600
<v Speaker 2>the old header in your tree and you call dot

339
00:15:29.639 --> 00:15:32.639
<v Speaker 2>replaced with passing in the new tag. It just swaps

340
00:15:32.679 --> 00:15:35.799
<v Speaker 2>them instantly. The old branch is pulled out and the

341
00:15:35.840 --> 00:15:39.000
<v Speaker 2>new branch is slided perfectly into its exact spatial position.

342
00:15:39.240 --> 00:15:42.279
<v Speaker 1>That is incredibly clean. But what if the problem is

343
00:15:42.320 --> 00:15:45.759
<v Speaker 1>a bit more insidious. I'm thinking of legacy websites, you know,

344
00:15:45.799 --> 00:15:48.320
<v Speaker 1>the kind to built fifteen years ago, where every single

345
00:15:48.360 --> 00:15:51.759
<v Speaker 1>piece of text is buried inside a nightmare of useless

346
00:15:51.879 --> 00:15:53.519
<v Speaker 1>nested div and span tags.

347
00:15:53.840 --> 00:15:56.799
<v Speaker 2>Ah yes, develvers call that div soup div soup.

348
00:15:56.879 --> 00:15:57.919
<v Speaker 1>Yeah, it's awful.

349
00:15:58.120 --> 00:16:01.360
<v Speaker 2>Dealing with div soup is notoriously difficult with regular expressions

350
00:16:01.600 --> 00:16:04.279
<v Speaker 2>because you risk deleting the core text while you're trying

351
00:16:04.279 --> 00:16:05.559
<v Speaker 2>to strip away all the brackets.

352
00:16:05.799 --> 00:16:08.159
<v Speaker 1>Right, So how does beautiful soup handle it?

353
00:16:08.159 --> 00:16:10.559
<v Speaker 2>It solves this beautifully with the dota and wrap function.

354
00:16:10.840 --> 00:16:14.039
<v Speaker 1>Unwrap How does that work in the purse tree. If

355
00:16:14.080 --> 00:16:16.120
<v Speaker 1>I have a core piece of text but it's wrapped

356
00:16:16.120 --> 00:16:20.120
<v Speaker 1>in let's say three layers of useless span tags, how

357
00:16:20.159 --> 00:16:23.159
<v Speaker 1>do I get to the center without destroying the whole branch.

358
00:16:23.240 --> 00:16:26.240
<v Speaker 2>You locate the outermost span tag and you just called

359
00:16:26.320 --> 00:16:29.960
<v Speaker 2>dota unwrap and what happens. Beautiful soup magically peels away

360
00:16:30.080 --> 00:16:33.240
<v Speaker 2>that specific outer container, kind of like stripping bark from

361
00:16:33.240 --> 00:16:36.720
<v Speaker 2>a branch. The core text and anything else nested inside

362
00:16:37.000 --> 00:16:39.919
<v Speaker 2>is left completely untouched. Oh wow, Yeah, it occupies the

363
00:16:40.000 --> 00:16:42.759
<v Speaker 2>exact same position in the document. And you could just

364
00:16:42.840 --> 00:16:45.840
<v Speaker 2>loop this to strip away endless layers of garbage containers

365
00:16:46.200 --> 00:16:48.080
<v Speaker 2>until your core data is pristine.

366
00:16:48.279 --> 00:16:52.200
<v Speaker 1>That is brilliant. It's literally structural peeling. And I assume

367
00:16:52.200 --> 00:16:55.039
<v Speaker 1>the inverse is also true. Like if I have raw

368
00:16:55.120 --> 00:16:57.639
<v Speaker 1>texts that suddenly needs to be contained within a new

369
00:16:57.679 --> 00:16:59.240
<v Speaker 1>formatting tag, I can wrap it up.

370
00:16:59.360 --> 00:17:02.679
<v Speaker 2>Yes, the dot wrap function. You can struck a new tag,

371
00:17:02.799 --> 00:17:05.680
<v Speaker 2>say a bold B tag, and you called dot wrap

372
00:17:05.720 --> 00:17:09.039
<v Speaker 2>on your existing text node. The parse tree takes your

373
00:17:09.039 --> 00:17:12.799
<v Speaker 2>text and meatly encapsulates it inside the new bold container.

374
00:17:13.119 --> 00:17:18.200
<v Speaker 1>Man, we have sliced, diced, unwrapped, extracted, and decomposed our

375
00:17:18.240 --> 00:17:20.680
<v Speaker 1>way through this entire document. We really have, But all

376
00:17:20.759 --> 00:17:25.160
<v Speaker 1>of this intricate surgery has been happening invisibly suspended inside

377
00:17:25.200 --> 00:17:28.880
<v Speaker 1>the Python object's memory. The Bondzi tree is perfect, but

378
00:17:28.920 --> 00:17:31.119
<v Speaker 1>it's sort of trapped in the matrix. How do we

379
00:17:31.160 --> 00:17:33.000
<v Speaker 1>pull it out into the real world.

380
00:17:33.240 --> 00:17:35.640
<v Speaker 2>This is where you realize the ultimate payoff of everything

381
00:17:35.640 --> 00:17:38.720
<v Speaker 2>we've discussed today. Now, once all your structural makeovers are complete,

382
00:17:39.039 --> 00:17:41.480
<v Speaker 2>you just return to basic Python file operations.

383
00:17:41.519 --> 00:17:43.640
<v Speaker 1>Okay, so you just open a brand new file, let's

384
00:17:43.640 --> 00:17:46.720
<v Speaker 1>call it sanitized dataset dot HTML and write mode.

385
00:17:46.960 --> 00:17:50.440
<v Speaker 2>Right. So, after all this complex object oriented memory manipulation,

386
00:17:51.079 --> 00:17:53.640
<v Speaker 2>exporting it is really just saving a standard text file.

387
00:17:53.759 --> 00:17:55.759
<v Speaker 1>Really, it's that simple exactly.

388
00:17:56.039 --> 00:17:58.240
<v Speaker 2>You call the standard righte function and you pass it

389
00:17:58.319 --> 00:18:01.880
<v Speaker 2>soup dot pritiphi. That de man takes your dynamically modified

390
00:18:01.920 --> 00:18:05.000
<v Speaker 2>live in memory parse tree, flattens it back into beautify

391
00:18:05.039 --> 00:18:08.519
<v Speaker 2>formatted HTML text, and writes it directly to your hard drive.

392
00:18:08.680 --> 00:18:12.759
<v Speaker 1>Wow. You essentially freeze the Bonzai tree in time, perfectly pruned,

393
00:18:12.920 --> 00:18:15.079
<v Speaker 1>and save it as a brand new reality. When you

394
00:18:15.119 --> 00:18:18.920
<v Speaker 1>open that new file, every single change, the renamed tags,

395
00:18:18.960 --> 00:18:23.079
<v Speaker 1>the eradicated attributes, the newly grafted hyperlinks. It's all prominently

396
00:18:23.119 --> 00:18:23.519
<v Speaker 1>locked in.

397
00:18:23.880 --> 00:18:27.519
<v Speaker 2>You started with a messy, potentially dangerous read only document,

398
00:18:27.839 --> 00:18:31.960
<v Speaker 2>and you programmatically engineered a completely sanitized, normalized structure.

399
00:18:32.200 --> 00:18:35.359
<v Speaker 1>You are now officially a digital architect. You have the

400
00:18:35.400 --> 00:18:39.599
<v Speaker 1>toolkit to modify properties, precisely, navigate the spatial structure of

401
00:18:39.640 --> 00:18:42.759
<v Speaker 1>the dom, cleanly extract what you might need later, and

402
00:18:42.920 --> 00:18:45.559
<v Speaker 1>ruthlessly decompose what is dragging your system down.

403
00:18:45.799 --> 00:18:48.759
<v Speaker 2>It is an absolute necessity for anyone serious about large

404
00:18:48.759 --> 00:18:50.599
<v Speaker 2>scale data engineering or web security.

405
00:18:50.720 --> 00:18:53.160
<v Speaker 1>It really is. Now. Before we sign off, we want

406
00:18:53.200 --> 00:18:55.279
<v Speaker 1>to leave you with a quick review exercise to lock

407
00:18:55.359 --> 00:18:57.559
<v Speaker 1>in the most critical concept from today's session.

408
00:18:57.680 --> 00:18:58.759
<v Speaker 2>This is a good one to think about.

409
00:18:58.960 --> 00:19:01.559
<v Speaker 1>Imagine you were writing a Python script to prepare a

410
00:19:01.640 --> 00:19:05.119
<v Speaker 1>data set for a highly sensitive machine learning model. You

411
00:19:05.240 --> 00:19:10.319
<v Speaker 1>identify thousands of script tags containing tracking malware. Your priority

412
00:19:10.400 --> 00:19:13.559
<v Speaker 1>is to ensure these tags consume absolutely zero memory on

413
00:19:13.599 --> 00:19:16.839
<v Speaker 1>your processing server. Based on the mechanics we discussed, which

414
00:19:16.920 --> 00:19:19.640
<v Speaker 1>function must you call on those tags? Would you use

415
00:19:19.640 --> 00:19:21.480
<v Speaker 1>dot extract or dot decompose?

416
00:19:21.640 --> 00:19:24.519
<v Speaker 2>Think carefully about the difference between uprooting a weed into

417
00:19:24.559 --> 00:19:27.440
<v Speaker 2>a pod and throwing it into a literal incinerator.

418
00:19:27.559 --> 00:19:29.559
<v Speaker 1>Keep that in mind next time you are staring at

419
00:19:29.559 --> 00:19:32.640
<v Speaker 1>a messy dom. The digital world isn't just a museum

420
00:19:32.680 --> 00:19:35.200
<v Speaker 1>exhibit waiting to be observed. You have the tools to

421
00:19:35.240 --> 00:19:38.759
<v Speaker 1>alter the architecture. Keep experimenting, keep building, and we will

422
00:19:38.759 --> 00:19:40.480
<v Speaker 1>catch you on the next deep dive.
