<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ David Karolyi - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ David Karolyi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 10:20:55 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/davidkarolyi/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Diffie-Hellman: The Genius Algorithm Behind Secure Network Communication ]]>
                </title>
                <description>
                    <![CDATA[ Let's start with a quick thought experiment. You have a network of 3 computers, used by Alice, Bob, and Charlie. All 3 participants can send messages, but just in a way that all other clients who connected to the network can read it. This is the only... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/diffie-hellman-key-exchange/</link>
                <guid isPermaLink="false">66be1f210eaa7ceb6eaeaa16</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ computer network ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cryptography ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Karolyi ]]>
                </dc:creator>
                <pubDate>Mon, 11 May 2020 18:53:11 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9b21740569d1a4ca29e4.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let's start with a quick thought experiment.</p>
<p>You have a network of 3 computers, used by Alice, Bob, and Charlie. All 3 participants can send messages, but just in a way that all other clients who connected to the network can read it. This is the only possible communication form between participants.</p>
<p>If Alice sends a message through the wires, both Bob and Charlie get it. In other words, Alice cannot send a direct message to Bob without Charlie receiving it as well.</p>
<p>But Alice wants to send a confidential message to Bob and doesn't want Charlie to be able to read it.</p>
<p>Seems impossible with these strict rules, right? The beautiful thing that this problem is solved in 1976 by Whitfield Diffie and Martin Hellman.</p>
<p>This is a simplified version of the real world, but we face the same problem when communicating through the biggest network that's ever existed.</p>
<p>Usually, you are not directly connected to the internet, but you are part of a local smaller network, called Ethernet. </p>
<p>This smaller network can be wired or wireless (Wi-Fi), but the base concept remains. If you send a signal through the network this signal can be read by all other clients connected to the same network.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/network-ethernet.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once you emit a message to your bank's server with your credit card information, all other clients in the local network will get the message, including the router. It will then forward it to the actual server of the bank. All other clients will ignore the message.</p>
<p>But what if there is a malicious client in the network who won't ignore your confidential messages, but read them instead? How is it possible you still have money on your bank account?</p>
<h2 id="heading-encryption">Encryption</h2>
<p>It's kind of clear at this point that we need to use some kind of encryption to make sure that the message is readable for Alice and Bob, but complete gibberish for Charlie.</p>
<p>Encrypting information is done by an encryption algorithm, which takes a key (for example a string) and gives back an encrypted value, called ciphertext. The ciphertext is just a completely random-looking string.</p>
<p>It's important that the encrypted value (ciphertext) can be decrypted only with the original key. This is called a symmetric-key algorithm because you need the same key for decrypting the message as it was encrypted with. There are also asymmetric-key algorithms, but we don't need them right now.</p>
<p>To make it easier to understand this, here is a dummy encryption algorithm implemented in JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encrypt</span>(<span class="hljs-params">message, key</span>) </span>{
    <span class="hljs-keyword">return</span> message.split(<span class="hljs-string">""</span>).map(<span class="hljs-function"><span class="hljs-params">character</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> characterAsciiCode = character.charCodeAt(<span class="hljs-number">0</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>.fromCharCode(characterAsciiCode+key.length)
    }).join(<span class="hljs-string">""</span>);
}
</code></pre>
<p>In this function, I mapped each character into another character based on the length of the given key.</p>
<p>Every character has an integer representation, called ASCII code. There is a dictionary that contains all characters with its code, called the ASCII table. So we incremented this integer by the length of the key:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/image-13.png" alt="Image" width="600" height="400" loading="lazy">
<em>Character mapping</em></p>
<p>Decrypting the ciphertext is pretty similar. But instead of addition, we subtract the key length from every character in the ciphertext, so we get back the original message.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">decrypt</span>(<span class="hljs-params">cipher, key</span>) </span>{
    <span class="hljs-keyword">return</span> cipher.split(<span class="hljs-string">""</span>).map(<span class="hljs-function"><span class="hljs-params">character</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> characterAsciiCode = character.charCodeAt(<span class="hljs-number">0</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>.fromCharCode(characterAsciiCode-key.length)
    }).join(<span class="hljs-string">""</span>);
}
</code></pre>
<p>Finally here is the dummy encryption in action:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> message = <span class="hljs-string">"Hi Bob, here is a confidential message!"</span>;
<span class="hljs-keyword">const</span> key = <span class="hljs-string">"password"</span>;

<span class="hljs-keyword">const</span> cipher = encrypt(message, key);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Encrypted message:"</span>, cipher);
<span class="hljs-comment">// Encrypted message: Pq(Jwj4(pmzm(q{(i(kwvnqlmv|qit(um{{iom)</span>

<span class="hljs-keyword">const</span> decryptedMessage = decrypt(cipher, key);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Decrypted message:"</span>, decryptedMessage);
<span class="hljs-comment">// Decrypted message: Hi Bob, here is a confidential message!</span>
</code></pre>
<p>We applied some degree of encryption to the message, but this algorithm was only useful for demonstration purposes, to get a sense of how symmetric-key encryption algorithms behave. </p>
<p>There are a couple of problems with this implementation besides handling corner cases and parameter types poorly.</p>
<p>First of all every 8 character-long key can decrypt the message which was encrypted with the key "password". We want an encryption algorithm to only be able to decrypt a message if we give it the same key that the message was encrypted with. A door lock that can be opened by every other key isn't that useful.</p>
<p>Secondly, the logic is too simple – every character is shifted the same amount in the ASCII table, which is too predictable. We need something more complex to make it harder to find out the message without the key.</p>
<p>Thirdly, there isn't a minimal key length. Modern algorithms work with at least 128 bit long keys (~16 characters). This significantly increases the number of possible keys, and with this the secureness of encryption.</p>
<p>Lastly, it takes too little time to encrypt or decrypt the message. This is a problem because it doesn't take too much time to try out all possible keys and crack the encrypted message. </p>
<p>This is hand in hand with the key length: An algorithm is secure if I as an attacker want to find the key, then I need to try a large number of key combinations and it takes a relatively long time to try a single combination.</p>
<p>There is a wide range of symmetric encryption algorithms that addressed all of these claims, often used together to find a good ratio of speed and secureness for every situation.</p>
<p>The more popular symmetric-key algorithms are <a target="_blank" href="http://en.wikipedia.org/wiki/Twofish">Twofish</a>, <a target="_blank" href="http://en.wikipedia.org/wiki/Serpent_%28cipher%29">Serpent</a>, <a target="_blank" href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a> (<a target="_blank" href="http://en.wikipedia.org/wiki/Rijndael">Rijndael</a>), <a target="_blank" href="http://en.wikipedia.org/wiki/Blowfish_%28cipher%29">Blowfish</a>, <a target="_blank" href="http://en.wikipedia.org/wiki/CAST5">CAST5</a>, <a target="_blank" href="http://en.wikipedia.org/wiki/RC4">RC4</a>, <a target="_blank" href="http://en.wikipedia.org/wiki/Triple_DES">TDES</a>, and <a target="_blank" href="http://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm">IDEA</a>.</p>
<p>If you want to learn more about cryptography in general check out <a target="_blank" href="https://www.youtube.com/watch?v=cqgtdkURzTE">this talk</a>.</p>
<h2 id="heading-key-exchange">Key exchange</h2>
<p>It looks like we reduced the original problem space. With encryption, we can create a message which is meaningful for parties who are eligible to read the information, but which is unreadable for others.</p>
<p>When Alice wants to write a confidential message, she would pick a key and encrypt her message with it and send the ciphertext through the wires. Both Bob and Charlie would receive the encrypted message, but none of them could interpret it without Alice's key.</p>
<p>Now the only question to answer is how Alice and Bob can find a common key just by communicating through the network and prevent Charlie from finding out that same key.</p>
<p>If Alice sends her key directly through the wires Charlie would intercept it and would be able to decrypt all Alice's messages. So this is not a solution. This is called the key exchange problem in computer science.</p>
<h3 id="heading-diffiehellman-key-exchange">Diffie–Hellman key exchange</h3>
<p>This cool algorithm provides a way of generating a shared key between two people in such a way that the key can't be seen by observing the communication.</p>
<p>As a first step, we'll say that there is a huge prime number, known to all participants, it's public information. We call it <strong>"p" or modulus</strong>. </p>
<p>There is also another public number called <strong>"g" or base</strong>, which is less than <strong>p</strong>. </p>
<p>Don't worry about how these numbers are generated. For the sake of simplicity let's just say Alice picks a very big prime number (<strong>p</strong>) and a number which is considerably less than <strong>p</strong>. She then sends them through the wires without any encryption, so all participants will know these numbers.</p>
<p><strong>Example:</strong> To understand this through an example, we'll use small numbers. Let's say <strong>p=23</strong> and <strong>g=5</strong>.</p>
<p>As a second step both Alice (<strong>a</strong>) and Bob (<strong>b</strong>) will pick a secret number, which they won't tell anybody, it's just locally living in their computers.</p>
<p><strong>Example:</strong> Let's say Alice picked 4 (<strong>a=4</strong>), and Bob picked 3 (<strong>b=3</strong>).</p>
<p>As a next step, they will do some math on their secret numbers, they will calculate:</p>
<ol>
<li>the base (<strong>g</strong>) in the power of their secret number,</li>
<li>and take the calculated number's modulo to <strong>p</strong>.</li>
<li>Call the result <strong>A</strong> (for Alice) and <strong>B</strong> (for Bob).</li>
</ol>
<p>Modulo is a simple mathematical statement, and we use it to find the remainder after dividing one number by another. Here is an example: <strong>23 mod 4 = 3</strong>, because 23/4 is 5 and 3 remains.</p>
<p>Maybe it's easier to see all of this in code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// base</span>
<span class="hljs-keyword">const</span> g = <span class="hljs-number">5</span>;
<span class="hljs-comment">// modulus</span>
<span class="hljs-keyword">const</span> p = <span class="hljs-number">23</span>;

<span class="hljs-comment">// Alice's randomly picked number</span>
<span class="hljs-keyword">const</span> a = <span class="hljs-number">4</span>;
<span class="hljs-comment">// Alice's calculated value</span>
<span class="hljs-keyword">const</span> A = <span class="hljs-built_in">Math</span>.pow(g, a)%p;

<span class="hljs-comment">// Do the same for Bob</span>
<span class="hljs-keyword">const</span> b = <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> B = <span class="hljs-built_in">Math</span>.pow(g, b)%p;

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Alice's calculated value (A):"</span>, A);
<span class="hljs-comment">// Alice's calculated value (A): 4</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Bob's calculated value (B):"</span>, B);
<span class="hljs-comment">// Bob's calculated value (B): 10</span>
</code></pre>
<p>Now both Alice and Bob will send their calculated values (<strong>A</strong>, <strong>B</strong>) through the network, so all participants will know them.</p>
<p>As a last step Alice and Bob will take each other's calculated values and do the following:</p>
<ol>
<li>Alice will take Bob's calculated value (<strong>B</strong>) in the power of his secret number (<strong>a</strong>),</li>
<li>and calculate this number's modulo to <strong>p</strong> and will call the result <strong>s</strong> (secret).</li>
<li>Bob will do the same but with Alice's calculated value (<strong>A</strong>), and his secret number (<strong>b</strong>).</li>
</ol>
<p>At this point, they successfully generated a common secret (<strong>s</strong>), even if it's hard to see right now. We will explore this in more detail in a second.</p>
<p>In code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Alice calculate the common secret</span>
<span class="hljs-keyword">const</span> secretOfAlice = <span class="hljs-built_in">Math</span>.pow(B, a)%p;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Alice's calculated secret:"</span>, secretOfAlice);
<span class="hljs-comment">// Alice's calculated secret: 18</span>

<span class="hljs-comment">// Bob will calculate</span>
<span class="hljs-keyword">const</span> secretOfBob = <span class="hljs-built_in">Math</span>.pow(A, b)%p;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Bob's calculated secret:"</span>, secretOfBob);
<span class="hljs-comment">// Bob's calculated secret: 18</span>
</code></pre>
<p>As you can see both Alice and Bob got the number 18, which they can use as a key to encrypt messages. It seems magic at this point, but it's just some math. </p>
<p>Let's see why they got the same number by splitting up the calculations into elementary pieces:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/Screenshot-2020-05-09-at-12.11.18.png" alt="Image" width="600" height="400" loading="lazy">
<em>The process as an equation</em></p>
<p>In the last step, we used a <a target="_blank" href="https://en.wikipedia.org/wiki/Modulo_operation#Properties_(identities)">modulo arithmetic identity</a> and its distributive properties to simplify nested modulo statements.</p>
<p>So Alice and Bob have the same key, but let's see what Charlie saw from all of this. We know that <strong>p</strong> and <strong>g</strong> are public numbers, available for everyone. </p>
<p>We also know that Alice and Bob sent their calculated values (<strong>A</strong>, <strong>B</strong>) through the network, so that can be also caught by Charlie.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/Screenshot-2020-05-09-at-20.12.35.png" alt="Image" width="600" height="400" loading="lazy">
<em>Charlie's perspective</em></p>
<p>Charlie knows almost all parameters of this equation, just <strong>a</strong> and <strong>b</strong> remain hidden. To stay with the example, if he knows that <strong>A</strong> is 4 and <strong>p</strong> is 23, <strong>g</strong> to the power of <strong>a</strong> can be 4, 27, 50, 73, ... and infinite other numbers which result in 4 in the modulo space.</p>
<p>He also knows that only the subset of these numbers are possible options because not all numbers are an exponent of 5 (<strong>g</strong>), but this is still an infinite number of options to try.</p>
<p>This doesn't seem too secure with small numbers. But at the beginning I said that <strong>p</strong> is a really large number, often 2000 or 4000 bits long. This makes it almost impossible to guess the value of <strong>a</strong> or <strong>b</strong> in the real world.</p>
<p>The common key Alice and Bob both possess only can be generated by knowing <strong>a</strong> or <strong>b</strong>, besides the information that traveled through the network.</p>
<p>If you're more visual, here is a great diagram shows this whole process by mixing buckets of paint instead of numbers.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/Diffie-Hellman_Key_Exchange.svg" alt="Image" width="600" height="400" loading="lazy">
_source: <a target="_blank" href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Wikipedia</a>_</p>
<p>Here <strong>p</strong> and <strong>g</strong> shared constants represented by the yellow "Common paint". Secret numbers of Alice and Bob (<strong>a</strong>, <strong>b</strong>) is "Secret colours", and "Common secret" is what we called <strong>s</strong>.</p>
<p>This is a great analogy because it's representing the irreversibility of the modulo operation. As mixed paints can't be unmixed to their original components, the result of a modulo operation can't be reversed.</p>
<h2 id="heading-summary">Summary</h2>
<p>Now the original problem can be solved by encrypting messages using a shared key, which was exchanged with the Diffie-Hellman algorithm. </p>
<p>With this Alice and Bob can communicate securely, and Charlie cannot read their messages even if he is part of the same network.</p>
<p>Thanks for reading this far! I hope you got some value from this post and understood some parts of this interesting communication flow.</p>
<p>If it was hard to follow the math of this explanation, <a target="_blank" href="https://www.youtube.com/watch?v=NmM9HA2MQGI">here</a> is a great video to help you understand the algorithm without math, from a higher level.</p>
<p>If you liked this post, you may want to follow me on <a target="_blank" href="https://twitter.com/karolyidav">Twitter</a> to find some more exciting resources about programming and software development.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Web scraping for web developers: a concise summary ]]>
                </title>
                <description>
                    <![CDATA[ Knowing one approach to web scraping may solve your problem in the short term, but all methods have their own strengths and weaknesses. Being aware of this can save you time and help you to solve a task more efficiently. Numerous resources exist, whi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/web-scraping-for-web-developers-a-concise-summary-3af3d0ca4069/</link>
                <guid isPermaLink="false">66be1f25b712ab343b0b915d</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web scraping ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Karolyi ]]>
                </dc:creator>
                <pubDate>Wed, 13 Feb 2019 17:59:30 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*QYXgeKvQq5M0lMGMRFXJvA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Knowing one approach to web scraping may solve your problem in the short term, but all methods have their own strengths and weaknesses. Being aware of this can save you time and help you to solve a task more efficiently.</p>
<p>Numerous resources exist, which will show you a single technique for extracting data from a web page. The reality is that multiple solutions and tools can be used for that.</p>
<p>What are your options to programmatically extract data from a web page?</p>
<p>What are the pros and cons of each approach?</p>
<p>How to use cloud services to increase the degree of automation?</p>
<p><strong>This guide meant to answer these questions.</strong></p>
<p>I assume you have a basic understanding of browsers in general, <strong>HTTP</strong> requests, the <strong>DOM</strong> (Document Object Model), <strong>HTML</strong>, <strong>CSS selectors</strong>, and <strong>Async JavaScript</strong>.</p>
<p>If these phrases sound unfamiliar, I suggest checking out those topics before continue reading. Examples are implemented in Node.js, but hopefully you can transfer the theory into other languages if needed.</p>
<h3 id="heading-static-content">Static content</h3>
<h4 id="heading-html-source">HTML source</h4>
<p>Let’s start with the simplest approach.</p>
<p>If you are planning to scrape a web page, this is the first method to try. It requires a negligible amount of computing power and the least time to implement.</p>
<p>However, it <strong>only works if the HTML source code contains the data</strong> you are targeting. To check that in Chrome, right-click the page and choose <em>View page source</em>. Now you should see the HTML source code.</p>
<p>It’s important to note here, that you won’t see the same code by using Chrome’s inspect tool, because it shows the HTML structure related to the current state of the page, which is not necessarily the same as the source HTML document that you can get from the server.</p>
<p>Once you find the data here, write a <a target="_blank" href="https://www.w3schools.com/cssref/css_selectors.asp">CSS selector</a> belonging to the wrapping element, to have a reference later on.</p>
<p>To implement, you can send an HTTP GET request to the URL of the page and will get back the HTML source code.</p>
<p>In <strong>Node</strong>, you can use a tool called <a target="_blank" href="https://github.com/cheeriojs/cheerio">CheerioJS</a> to parse this raw HTML and extract the data using a selector. The code looks something like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fetch = <span class="hljs-built_in">require</span>(<span class="hljs-string">'node-fetch'</span>);
<span class="hljs-keyword">const</span> cheerio = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cheerio'</span>);

<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://example.com/'</span>;
<span class="hljs-keyword">const</span> selector = <span class="hljs-string">'.example'</span>;

fetch(url)
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.text())
  .then(<span class="hljs-function"><span class="hljs-params">html</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> $ = cheerio.load(html);
    <span class="hljs-keyword">const</span> data = $(selector);
    <span class="hljs-built_in">console</span>.log(data.text());
  });
</code></pre>
<h3 id="heading-dynamic-content">Dynamic content</h3>
<p>In many cases, you can’t access the information from the raw HTML code, because the DOM was manipulated by some JavaScript, executed in the background. A typical example of that is a SPA (Single Page Application), where the HTML document contains a minimal amount of information, and the JavaScript populates it at runtime.</p>
<p>In this situation, a solution is to build the DOM and execute the scripts located in the HTML source code, just like a browser does. After that, the data can be extracted from this object with selectors.</p>
<h4 id="heading-headless-browsers">Headless browsers</h4>
<p>This can be achieved by using a headless browser. A headless browser is almost the same thing as the normal one you are probably using every day but without a user interface. It’s running in the background and you can programmatically control it instead of clicking with your mouse and typing with a keyboard.</p>
<p>A popular choice for a headless browser is <a target="_blank" href="https://github.com/GoogleChrome/puppeteer">Puppeteer</a>. It is an easy to use Node library which provides a high-level API to control Chrome in headless mode. It can be configured to run non-headless, which comes in handy during development. The following code does the same thing as before, but it will work with dynamic pages as well:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> puppeteer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'puppeteer'</span>);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getData</span>(<span class="hljs-params">url, selector</span>)</span>{
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> puppeteer.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(url);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> page.evaluate(<span class="hljs-function"><span class="hljs-params">selector</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">document</span>.querySelector(selector).innerText;
  }, selector);
  <span class="hljs-keyword">await</span> browser.close();
  <span class="hljs-keyword">return</span> data;
}

<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://example.com'</span>;
<span class="hljs-keyword">const</span> selector = <span class="hljs-string">'.example'</span>;
getData(url,selector)
  .then(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(result));
</code></pre>
<p>Of course, you can do more interesting things with Puppeteer, so it is worth checking out the <a target="_blank" href="https://pptr.dev/">documentation</a>. Here is a code snippet which navigates to a URL, takes a screenshot and saves it:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> puppeteer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'puppeteer'</span>);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">takeScreenshot</span>(<span class="hljs-params">url,path</span>)</span>{
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> puppeteer.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(url);
  <span class="hljs-keyword">await</span> page.screenshot({<span class="hljs-attr">path</span>: path});
  <span class="hljs-keyword">await</span> browser.close();
}

<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://example.com'</span>;
<span class="hljs-keyword">const</span> path = <span class="hljs-string">'example.png'</span>;
takeScreenshot(url, path);
</code></pre>
<p>As you can imagine, running a browser requires much more computing power than sending a simple GET request and parsing the response. Therefore execution is relatively costly and slow. Not only that but including a browser as a dependency makes the deployment package massive.</p>
<p>On the upside, this method is highly flexible. You can use it for navigating around pages, simulating clicks, mouse moves, and keyboard events, filling out forms, taking screenshots or generating PDFs of pages, executing commands in the console, selecting elements to extract its text content. Basically, everything can be done that is possible manually in a browser.</p>
<h4 id="heading-building-just-the-dom">Building just the DOM</h4>
<p>You may think it’s a little bit of overkill to simulate a whole browser just for building a DOM. Actually, it is, at least under certain circumstances.</p>
<p>There is a Node library, called <a target="_blank" href="https://github.com/jsdom/jsdom">Jsdom</a>, which will parse the HTML you pass it, just like a browser does. However, it isn’t a browser, but <strong>a tool for building a DOM from a given HTML source code</strong>, while also executing the JavaScript code within that HTML.</p>
<p>Thanks to this abstraction, Jsdom is able to run faster than a headless browser. If it’s faster, why don’t use it instead of headless browsers all the time?</p>
<p>Quote from the documentation:</p>
<blockquote>
<p>People often have trouble with asynchronous script loading when using jsdom. Many pages load scripts asynchronously, but there is no way to tell when they’re done doing so, and thus when it’s a good time to run your code and inspect the resulting DOM structure. This is a fundamental limitation.</p>
<p>… This can be worked around by polling for the presence of a specific element.</p>
</blockquote>
<p>This solution is shown in the example. It checks every 100 ms if the element either appeared or timed out (after 2 seconds).</p>
<p>It also often throws nasty error messages when some browser feature in the page is not implemented by Jsdom, such as: “<em>Error: Not implemented: window.alert…” or “Error: Not implemented: window.scrollTo…”.</em> This issue also can be solved with some workarounds (<a target="_blank" href="https://github.com/jsdom/jsdom#virtual-consoles">virtual consoles</a>).</p>
<p>Generally, it’s a lower level API than Puppeteer, so you need to implement certain things yourself.</p>
<p>These things make it a little messier to use, as you will see in the example. Puppeteer solves all these things for you behind the scenes and makes it extremely easy to use. Jsdom for this extra work will offer a fast and lean solution.</p>
<p>Let’s see the same example as previously, but with Jsdom:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> jsdom = <span class="hljs-built_in">require</span>(<span class="hljs-string">"jsdom"</span>);
<span class="hljs-keyword">const</span> { JSDOM } = jsdom;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getData</span>(<span class="hljs-params">url,selector,timeout</span>) </span>{
  <span class="hljs-keyword">const</span> virtualConsole = <span class="hljs-keyword">new</span> jsdom.VirtualConsole();
  virtualConsole.sendTo(<span class="hljs-built_in">console</span>, { <span class="hljs-attr">omitJSDOMErrors</span>: <span class="hljs-literal">true</span> });
  <span class="hljs-keyword">const</span> dom = <span class="hljs-keyword">await</span> JSDOM.fromURL(url, {
    <span class="hljs-attr">runScripts</span>: <span class="hljs-string">"dangerously"</span>,
    <span class="hljs-attr">resources</span>: <span class="hljs-string">"usable"</span>,
    virtualConsole
  });
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">res,rej</span>)=&gt;</span>{
    <span class="hljs-keyword">const</span> started = <span class="hljs-built_in">Date</span>.now();
    <span class="hljs-keyword">const</span> timer = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> element = dom.window.document.querySelector(selector)
      <span class="hljs-keyword">if</span> (element) {
        res(element.textContent);
        <span class="hljs-built_in">clearInterval</span>(timer);
      }
      <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-built_in">Date</span>.now()-started &gt; timeout){
        rej(<span class="hljs-string">"Timed out"</span>);
        <span class="hljs-built_in">clearInterval</span>(timer);
      }
    }, <span class="hljs-number">100</span>);
  });
  dom.window.close();
  <span class="hljs-keyword">return</span> data;
}

<span class="hljs-keyword">const</span> url = <span class="hljs-string">"https://example.com/"</span>;
<span class="hljs-keyword">const</span> selector = <span class="hljs-string">".example"</span>;
getData(url,selector,<span class="hljs-number">2000</span>).then(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(result));
</code></pre>
<h4 id="heading-reverse-engineering">Reverse engineering</h4>
<p>Jsdom is a fast and lightweight solution, but it’s possible even further to simplify things.</p>
<p>Do we even need to simulate the DOM?</p>
<p>Generally speaking, the webpage that you want to scrape consists of the same HTML, same JavaScript, same technologies you’ve already know. So, <strong>if you</strong> <strong>find that piece of code from where the targeted data was derived, you can repeat the same operation in order to get the same result.</strong></p>
<p>If we <strong>oversimplify</strong> things, the data you’re looking for can be:</p>
<ul>
<li>part of the HTML source code (as we saw in the first paragraph),</li>
<li>part of a static file, referenced in the HTML document (for example a string in a javascript file),</li>
<li>a response for a network request (for example some JavaScript code sent an AJAX request to a server, which responded with a JSON string).</li>
</ul>
<p><strong>All of these data sources can be accessed with network requests.</strong> From our perspective, it doesn’t matter if the webpage uses HTTP, WebSockets or any other communication protocol, because all of them are reproducible in theory.</p>
<p>Once you locate the resource housing the data, you can send a similar network request to the same server as the original page does. As a result, you get the response, containing the targeted data, which can be easily extracted with regular expressions, string methods, JSON.parse etc…</p>
<p>With simple words, you can just take the resource where the data is located, instead of processing and loading the whole stuff. This way the problem, showed in the previous examples, can be solved with a single HTTP request instead of controlling a browser or a complex JavaScript object.</p>
<p>This solution seems easy in theory, but most of the times it can be <strong>really time-consuming</strong> to carry out and requires some experience of working with web pages and servers.</p>
<p>A possible place to start researching is to observe network traffic. A great tool for that is the <a target="_blank" href="https://developers.google.com/web/tools/chrome-devtools/network-performance/">Network tab in Chrome DevTools</a>. You will see all outgoing requests with the responses (including static files, AJAX requests, etc…), so you can iterate through them and look for the data.</p>
<p>This can be even more sluggish if the response is modified by some code before being rendered on the screen. In that case, you have to find that piece of code and understand what’s going on.</p>
<p>As you see, this solution may require way more work than the methods featured so far. On the other hand, once it’s implemented, it provides the best performance.</p>
<p>This chart shows the required execution time, and the package size compared to Jsdom and Puppeteer:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*36D8phqv-iUx6SVmrqhJcQ.jpeg" alt="Image" width="800" height="466" loading="lazy"></p>
<p>These results aren’t based on precise measurements and can vary in every situation, but shows well the approximate difference between these techniques.</p>
<h3 id="heading-cloud-service-integration">Cloud service integration</h3>
<p>Let’s say you implemented one of the solutions listed so far. One way to execute your script is to power on your computer, open a terminal and execute it manually.</p>
<p>This can become annoying and inefficient very quickly, so it would be better if we could just upload the script to a server and it would execute the code on a regular basis depending on how it’s configured.</p>
<p>This can be done by running an actual server and configuring some rules on when to execute the script. Servers shine when you keep observing an element in a page. In other cases, a cloud function is probably a simpler way to go.</p>
<p>Cloud functions are basically containers intended to execute the uploaded code when a triggering event occurs. This means you don’t have to manage servers, it’s done automatically by the cloud provider of your choice.</p>
<p>A possible trigger can be a schedule, a network request, and numerous other events. You can save the collected data in a database, write it in a <a target="_blank" href="https://developers.google.com/sheets/api/">Google sheet</a> or send it in an <a target="_blank" href="https://www.w3schools.com/nodejs/nodejs_email.asp">email</a>. It all depends on your creativity.</p>
<p>Popular cloud providers are <a target="_blank" href="https://aws.amazon.com">Amazon Web Services</a>(AWS), <a target="_blank" href="https://cloud.google.com/">Google Cloud Platform</a>(GCP), and <a target="_blank" href="https://azure.microsoft.com">Microsoft Azure</a> and all of them has a function service:</p>
<ul>
<li><a target="_blank" href="https://aws.amazon.com/lambda/">AWS Lambda</a></li>
<li><a target="_blank" href="https://cloud.google.com/functions/">GCP Cloud Functions</a></li>
<li><a target="_blank" href="https://azure.microsoft.com/services/functions/">Azure Functions</a></li>
</ul>
<p>They offer some amount of free usage every month, which your single script probably won’t exceed, unless in extreme cases, but <strong>please check the pricing before use</strong>.</p>
<p>If you are using Puppeteer, Google’s C<em>loud Functions</em> is the simplest solution. Headless Chrome’s zipped package size (~130MB) exceeds AWS Lambda’s limit of maximum zipped size (50MB). There are some techniques to make it work with Lambda, but GCP functions <a target="_blank" href="https://cloud.google.com/blog/products/gcp/introducing-headless-chrome-support-in-cloud-functions-and-app-engine">support headless Chrome by default</a>, you just need to include Puppeteer as a dependency in <em>package.json</em>.</p>
<p>If you want to learn more about cloud functions in general, do some research on serverless architectures. Many great guides have already been written on this topic and most providers have an easy to follow documentation.</p>
<h3 id="heading-summary">Summary</h3>
<p>I know that every topic was a bit compressed. You probably can’t implement every solution just with this knowledge, but with the documentation and some custom research, it shouldn’t be a problem.</p>
<p>Hopefully, now you have a high-level overview of techniques used for collecting data from the web, so you can dive deeper into each topic accordingly.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
