<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Trickster Dev</title>
    <link>https://www.trickster.dev/post/</link>
    <description>Recent content in Posts on Trickster Dev</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 31 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.trickster.dev/post/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Advanced network traffic interception techniques with mitmproxy</title>
      <link>https://www.trickster.dev/post/advanced-network-traffic-interception-techniques-with-mitmproxy/</link>
      <pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/advanced-network-traffic-interception-techniques-with-mitmproxy/</guid>
      <description>&lt;p&gt;The most common way to use mitmproxy for API traffic interception is to use it
in a default forward proxy mode. One would run mitmproxy server on a laptop or
desktop computer, then configure proxy settings and install X.509 certificate
on a client device (e.g. smart phone). But there is more to mitmproxy than that.
It can also be used as reverse proxy, transparent proxy (on Linux and macOS),
Wireguard VPN server that also intercepts network traffic, SOCKS proxy server,
customisable DNS server and even as a virtual network interface on Linux. In
this article we will go through some of the lesser known mitmproxy features
for uncovering what goes on between servers and clients.&lt;/p&gt;
&lt;h2 id=&#34;reverse-proxy-mode&#34;&gt;Reverse proxy mode&lt;/h2&gt;
&lt;p&gt;In some cases you have a server that you want to spy on, or for some reason
setting up proxy configuration on the client is not viable for your use case.
To cover situations like this, mitmproxy allows you to run in reverse proxy mode.
Client would access mitmproxy as if it was a real server, but mitmproxy would
act as a spy in the middle between client and the server being &amp;ldquo;protected&amp;rdquo;. This
is similar to a common use case of nginx server, except that it is being done
for the purpose of network interception. Running mitmproxy in reverse proxy
mode is rather simple on a Linux server:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --mode reverse:https://trickster.dev@443 --set block_global=false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(I ran into X.509 certificate issues when trying to do this locally on macOS.
If you run mitmproxy server locally &lt;code&gt;--set block_global=false&lt;/code&gt; is not needed.)&lt;/p&gt;
&lt;p&gt;However, there are things that we still must do on client side. First, we must
override DNS name of the proxied target so that it resolves to proxy IP address.
On Linux and Unix systems the simplest way to do so is to add a single line to
/etc/hosts file, such as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;165.232.168.159 trickster.dev 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But now there&amp;rsquo;s a matter of setting up X.509 certificate generated by mitmproxy
so that it is trusted by client application(s). For example, Chrome won&amp;rsquo;t let
us access the proxied site if we don&amp;rsquo;t address this matter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_17.55.27.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To download the certificate from mitmproxy, click the GUI element saying &amp;ldquo;Not
Secure&amp;rdquo; in red, then choose &amp;ldquo;Certificate Details&amp;rdquo; out of the drop down menu.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_17.55.40.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A modal view will present information about certificate. In Details tab we
can verify that it is indeed issued for the target domain and press &amp;ldquo;Export&amp;hellip;&amp;rdquo;
button to download it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_17.55.57.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we need to go to &lt;code&gt;chrome://certificate-manager&lt;/code&gt;,
press the &amp;ldquo;Installed by you&amp;hellip;&amp;rdquo; table row, then press the &amp;ldquo;Import&amp;rdquo; button next
to &amp;ldquo;Trusted certificates&amp;rdquo;. The GUI will present a window to pick a file from
local file system and we need to pick a certificate file we downloaded earlier.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_17.57.18.png&#34;&gt;Screenshot 4&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2025-11-29_18.20.25.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once we do all of this, we can use the original target address to access the
site and have HTTP flows intercepted for further analysis.&lt;/p&gt;
&lt;h2 id=&#34;transparent-proxy-mode&#34;&gt;Transparent proxy mode&lt;/h2&gt;
&lt;p&gt;Transparent mode involves rerouting traffic at network (IP) layer so that
applications without any way to configure network proxying can be covered by
traffic interception. Instructions on how to do this are provided on
&lt;a href=&#34;https://docs.mitmproxy.org/stable/howto/transparent/&#34;&gt;mitmproxy documentation page&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;mitmproxy-as-wireguard-vpn-server&#34;&gt;mitmproxy as Wireguard VPN server&lt;/h2&gt;
&lt;p&gt;The easiest way to use Wireguard mode is to choose mitmproxy web UI option:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmweb --mode wireguard
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In Capture tab we will have QR code we can scan with the official WireGuard
app, which will guide us through a flow of setting it up on a mobile device.
However, before you do that it is best to have X.509 certificate installed by
going through the default mode setup procedure or some other way, so that TLS
flows can be properly intercepted.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_18.34.37.png&#34;&gt;Screenshot 6&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2025-11-29_18.40.04.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ff you want to use TUI instead, the WireGuard configuration will be available at
&lt;code&gt;~/.mitmproxy/wireguard.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This mode has an added benefit of capturing not only HTTP(S) flows, but also
covering DNS and some other protocols.&lt;/p&gt;
&lt;h2 id=&#34;socks-proxy-mode&#34;&gt;SOCKS proxy mode&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --mode socks5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will launch mitmproxy as SOCKS5 server, bound to a standard port - 1080.
We can try it with curl:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --socks5 localhost --head https://nike.com -k -v
* Host localhost:1080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:1080...
* Host nike.com:443 was resolved.
* IPv6: (none)
* IPv4: 13.226.69.50, 13.226.69.57, 13.226.69.98, 13.226.69.103
* SOCKS5 connect to 13.226.69.50:443 (locally resolved)
* SOCKS5 request granted.
* Connected to localhost () port 1080
&amp;lt;...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;mitmproxy-as-dns-server&#34;&gt;mitmproxy as DNS server&lt;/h2&gt;
&lt;p&gt;We can also use mitmproxy as a simple DNS server that implements a subset of
the protocol (only A and AAAA queries) by running it in DNS mode:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --mode dns@5333
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can test it by using &lt;a href=&#34;https://github.com/projectdiscovery/dnsx&#34;&gt;dnsx&lt;/a&gt; from
ProjectDiscovery:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;google.com&amp;#34; | dnsx -resp -a -resolver &amp;#34;127.0.0.1:5333&amp;#34;    

      _             __  __
   __| | _ __   ___ \ \/ /
  / _&amp;#39; || &amp;#39;_ \ / __| \  / 
 | (_| || | | |\__ \ /  \ 
  \__,_||_| |_||___//_/\_\

		projectdiscovery.io

[INF] Current dnsx version 1.2.2 (latest)
google.com [A] [172.217.25.78]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_19.30.00.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;passing-traffic-to-upstream-proxy&#34;&gt;Passing traffic to upstream proxy&lt;/h2&gt;
&lt;p&gt;In some cases, such as dealing with georestricted APIs, one may want to introduce
a second hop to some other proxy and pass the requests there. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --mode upstream:http://brd.superproxy.io:33335 --upstream-auth [REDACTED]:[REDACTED]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Value for &lt;code&gt;--upstream-auth&lt;/code&gt; (username and password) is formatted in the same
way as &lt;code&gt;--proxy-auth&lt;/code&gt; of curl.&lt;/p&gt;
&lt;h2 id=&#34;mitmproxy-virtual-network-interface&#34;&gt;mitmproxy virtual network interface&lt;/h2&gt;
&lt;p&gt;This one will require Linux system with root access. It entails creating a
virtual network interface that intercepts all traffic it passes through.
For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --mode tun
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some programs, such as curl will let you specify a network interface it will
use:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --interface tun0 --head https://nike.com -v -k
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For others you will need to modify a routing table to make interception happen.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-11-29_19.43.38.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping Next.js web sites in 2025</title>
      <link>https://www.trickster.dev/post/scraping-nextjs-web-sites-in-2025/</link>
      <pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-nextjs-web-sites-in-2025/</guid>
      <description>&lt;p&gt;When looking into some targets for web scraping, you may come across pages
that contain a lot of data represented in JSON-like (but not quite JSON) format
passed to &lt;code&gt;self.__next_f.push()&lt;/code&gt; Javascript function calls. What&amp;rsquo;s going on
here and how do we parse this stuff? To understand what this is about, we must
go through a little journey across the technological landscape of the modern
web. Note that I am not a frontend developer and don&amp;rsquo;t aspire to be. For the
most part, I extract data from web apps and web sites, not develop them. The
following is what I managed to understand from reading around.&lt;/p&gt;
&lt;p&gt;So, it&amp;rsquo;s widely known that &lt;a href=&#34;https://react.dev/&#34;&gt;React&lt;/a&gt; is a very prominent
frontend framework to develop web apps in JavaScript. Although one can develop
web apps with the very basics - HTML, CSS and vanilla JavaScript, doing so is
too simple for enterprise software teams and tech startups drunk on that sweet
VC funding. They need more powerful abstractions, such as reusable components,
UI state management, Virtual DOM to optimize performance by only redrawing
parts of UI that changed and JSX syntax that extends Javascript language into
Javascript-HTML hybrid dialect. More generally, React has emerged as a winner
among many ZIRP-era Javascript frameworks by addressing the reactivity problem
in web app development: how do we associate internal app state with visible UI
stuff and how do we keep them in sync when one or the other changes.&lt;/p&gt;
&lt;p&gt;Primarily React is only a frontend framework meant to develop code that runs in
a browser. React alone cannot be used for full stack web development, as we
also need something for the backend side of the app. To fill this gap, Next.js
was developed. Next.js is another Javascript framework that works with React
to provide features like request routing, performance optimization (e.g.
making images smaller depending on the needs of frontend side) and Server Side
Rendering (SSR). The latter will become important for our purposes in a moment.
In addition to all of that, Next.js provides some tooling for easier deployment
and nicer developer experience.&lt;/p&gt;
&lt;p&gt;Those of us who work in web scraping have seen that many sites tend to have pages
with &lt;code&gt;&amp;lt;script id=&amp;quot;__NEXT_DATA__&amp;quot; type=&amp;quot;application/json&amp;quot;&amp;gt;&lt;/code&gt; tag that contains
a large JSON document with all kinds of juicy data we can parse out of there.
This is related to concept called hydration - embedding data within HTML
document during server-side rendering so that it could be attached to React
data model when bootstrapping all the React client-side machinery that makes
the page interactive. For example, the first page of the product list is
fetched with this &lt;code&gt;__NEXT_DATA__&lt;/code&gt; shebang, but if you go to the next page of the
product list, REST API call will be initiated to fetch further data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-10-31_19.08.28.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One simple way to browse this data is to type &lt;code&gt;__NEXT_DATA__&lt;/code&gt; into Console tab
of browser dev tools panel. You will be presented with tree structure UI to
explore it. &lt;a href=&#34;https://www.trickster.dev/post/scrapy-simplified-developing-a-single-file-web-scraper&#34;&gt;An older post&lt;/a&gt;
gives an example of parsing real data from &lt;code&gt;__NEXT_DATA__&lt;/code&gt; JSON document.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-10-31_19.09.30.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2025-10-31_19.09.44.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In some newer React/Next.js websites another approach is used to hydrate the
pages that relies on something called React Server Components - a new feature
of React that moves even more computation back to the backend side.
If React Server Components is used, that involves splitting the initial
data into chunks, serializing it to React-specific wire format and including
it into the page through those &lt;code&gt;self.__next_f.push()&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-10-31_19.14.26.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That may or may not make the pages smaller (see Next.js
&lt;a href=&#34;https://github.com/vercel/next.js/discussions/42170&#34;&gt;discussion #42170&lt;/a&gt;) and
from a distance it looks like they&amp;rsquo;re making already complicated systems even
more complicated. But as web scrapers developers we primarily care about
extracting data from this mess. So how do we do that?&lt;/p&gt;
&lt;p&gt;Serialised stuff in &lt;code&gt;self.__next_f.push()&lt;/code&gt; calls is known as Next.js flight
data. We need to parse it somehow. We could read through React.js/Next.js
framework code and try to work it out, but fortunately someone already did the
hard part for us. There&amp;rsquo;s &lt;a href=&#34;https://github.com/novitae/njsparser&#34;&gt;njsparser&lt;/a&gt;
Python module that we can use to scrape pages with Next.js flight data.&lt;/p&gt;
&lt;p&gt;In njsparser project documentation we see the following Python snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; njsparser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Here I get my page&amp;#39;s html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://mediux.pro/user/r3draid3r04&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Then I parse it with njsparser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; njsparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BeautifulFD(response)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Then I iterate over the different classes `Data` in my flight data.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; data &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; fd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;find_iter([njsparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;T&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Data]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Then I make sure that the content of my data is not None, and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# check if the key `&amp;#34;user&amp;#34;` is in the data&amp;#39;s content. If it is,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# then i break the loop of searching.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;content &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;content:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# If i didn&amp;#39;t find it, i raise an error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ValueError&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Now i have the data of my user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;content[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# And I can print the string i was searching for before&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(user[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tagline&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us extend this snippet a little to provide a way to input proxy URL with
page URL to see what can be parsed out of the page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; njsparser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;proxy_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Proxy URL: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; proxy_url&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;proxies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;: proxy_url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Page URL: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, proxies&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;proxies, verify&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; njsparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BeautifulFD(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; data &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; fd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;find_iter([njsparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;T&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Data, njsparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;T&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Element]):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(type(data))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(data)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, we can explore a real page with Next.js flight data:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 njsparser_text.py 
Proxy URL: 
Page URL: https://www.chilis.com/locations
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/urllib3/connectionpool.py:1099: InsecureRequestWarning: Unverified HTTPS request is being made to host &amp;#39;www.chilis.com&amp;#39;. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(
https://www.chilis.com/locations?https= 200
Couldn&amp;#39;t find an appropriate type for given class `None`. Giving `Element`.
Couldn&amp;#39;t find an appropriate type for given class `None`. Giving `Element`.
&amp;lt;class &amp;#39;njsparser.parser.types.Data&amp;#39;&amp;gt;
Data(value=[&amp;#39;$&amp;#39;,
            &amp;#39;$10&amp;#39;,
            None,
            {&amp;#39;children&amp;#39;: [&amp;#39;$&amp;#39;, &amp;#39;$L11&amp;#39;, None, {&amp;#39;promise&amp;#39;: &amp;#39;$@12&amp;#39;}],
             &amp;#39;fallback&amp;#39;: None}],
     value_class=None,
     index=7)
&amp;lt;class &amp;#39;njsparser.parser.types.Element&amp;#39;&amp;gt;
Element(value={&amp;#39;digest&amp;#39;: &amp;#39;$undefined&amp;#39;,
               &amp;#39;error&amp;#39;: None,
               &amp;#39;metadata&amp;#39;: [[&amp;#39;$&amp;#39;,
                             &amp;#39;title&amp;#39;,
                             &amp;#39;0&amp;#39;,
                             {&amp;#39;children&amp;#39;: &amp;#34;Chili&amp;#39;s Grill &amp;amp; Bar&amp;#34;}],
                            [&amp;#39;$&amp;#39;,
                             &amp;#39;meta&amp;#39;,
                             &amp;#39;1&amp;#39;,
                             {&amp;#39;content&amp;#39;: &amp;#34;Chili&amp;#39;s Grill &amp;amp; Bar. View menu, &amp;#34;
                                         &amp;#39;order online, and view restaurant &amp;#39;
                                         &amp;#39;hours.&amp;#39;,
                              &amp;#39;name&amp;#39;: &amp;#39;description&amp;#39;}]]},
        value_class=None,
        index=18)
&amp;lt;class &amp;#39;njsparser.parser.types.Element&amp;#39;&amp;gt;
Element(value={&amp;#39;digest&amp;#39;: &amp;#39;$undefined&amp;#39;,
               &amp;#39;error&amp;#39;: None,
               &amp;#39;metadata&amp;#39;: &amp;#39;$12:metadata&amp;#39;},
        value_class=None,
        index=12)
&amp;lt;class &amp;#39;njsparser.parser.types.Data&amp;#39;&amp;gt;
Data(value=[&amp;#39;$&amp;#39;,
            &amp;#39;$L19&amp;#39;,
            None,
            {&amp;#39;apiKey&amp;#39;: &amp;#39;4967176e01a141828a5fad701f6faa79&amp;#39;,
             &amp;#39;content&amp;#39;: {&amp;#39;createdBy&amp;#39;: &amp;#39;g4MEiL4rhtSHs8OUcuU01J0Q8EN2&amp;#39;,
                         &amp;#39;createdDate&amp;#39;: 1712696242376,
                         &amp;#39;data&amp;#39;: {&amp;#39;blocks&amp;#39;: [{&amp;#39;@type&amp;#39;: &amp;#39;@builder.io/sdk:Element&amp;#39;,
                                              &amp;#39;@version&amp;#39;: 2,
                                              &amp;#39;component&amp;#39;: {&amp;#39;name&amp;#39;: &amp;#39;Image&amp;#39;,
                                                            &amp;#39;options&amp;#39;: {&amp;#39;altText&amp;#39;: &amp;#39;404 &amp;#39;
                                                                                   &amp;#39;Error &amp;#39;
                                                                                   &amp;#39;- &amp;#39;
                                                                                   &amp;#39;Page &amp;#39;
                                                                                   &amp;#39;Not &amp;#39;
                                                                                   &amp;#39;Found. &amp;#39;
                                                                                   &amp;#39;Click &amp;#39;
                                                                                   &amp;#39;to &amp;#39;
                                                                                   &amp;#39;head &amp;#39;
                                                                                   &amp;#39;back &amp;#39;
                                                                                   &amp;#39;to &amp;#39;
                                                                                   &amp;#39;chilis.com &amp;#39;
                                                                                   &amp;#39;homepage. &amp;#39;,
                                                                        &amp;#39;aspectRatio&amp;#39;: 0.563,
                                                                        &amp;#39;backgroundPosition&amp;#39;: &amp;#39;center&amp;#39;,
                                                                        &amp;#39;backgroundSize&amp;#39;: &amp;#39;contain&amp;#39;,
                                                                        &amp;#39;fitContent&amp;#39;: True,
                                                                        &amp;#39;height&amp;#39;: 594,
                                                                        &amp;#39;image&amp;#39;: &amp;#39;https://cdn.builder.io/api/v1/image/assets%2F4967176e01a141828a5fad701f6faa79%2Fe821f528372449eca0aaeb5e230dbcc5?width=2000&amp;#39;,
                                                                        &amp;#39;lazy&amp;#39;: False,
                                                                        &amp;#39;lockAspectRatio&amp;#39;: False,
                                                                        &amp;#39;sizes&amp;#39;: &amp;#39;(max-width: &amp;#39;
                                                                                 &amp;#39;638px) &amp;#39;
                                                                                 &amp;#39;100vw, &amp;#39;
                                                                                 &amp;#39;(max-width: &amp;#39;
                                                                                 &amp;#39;998px) &amp;#39;
                                                                                 &amp;#39;100vw, &amp;#39;
                                                                                 &amp;#39;143vw&amp;#39;,
                                                                        &amp;#39;srcset&amp;#39;: &amp;#39;$1a&amp;#39;,
                                                                        &amp;#39;width&amp;#39;: 1056}},
                                              &amp;#39;id&amp;#39;: &amp;#39;builder-30ef217134004c3aad5e4895a0017b5e&amp;#39;,
                                              &amp;#39;linkUrl&amp;#39;: &amp;#39;/&amp;#39;,
                                              &amp;#39;properties&amp;#39;: {&amp;#39;href&amp;#39;: &amp;#39;/&amp;#39;},
                                              &amp;#39;responsiveStyles&amp;#39;: {&amp;#39;large&amp;#39;: {&amp;#39;boxSizing&amp;#39;: &amp;#39;border-box&amp;#39;,
                                                                             &amp;#39;cursor&amp;#39;: &amp;#39;pointer&amp;#39;,
                                                                             &amp;#39;display&amp;#39;: &amp;#39;flex&amp;#39;,
                                                                             &amp;#39;flexDirection&amp;#39;: &amp;#39;column&amp;#39;,
                                                                             &amp;#39;flexShrink&amp;#39;: &amp;#39;0&amp;#39;,
                                                                             &amp;#39;height&amp;#39;: &amp;#39;100&amp;#39;,
                                                                             &amp;#39;minHeight&amp;#39;: &amp;#39;20px&amp;#39;,
                                                                             &amp;#39;minWidth&amp;#39;: &amp;#39;20px&amp;#39;,
                                                                             &amp;#39;overflow&amp;#39;: &amp;#39;hidden&amp;#39;,
                                                                             &amp;#39;pointerEvents&amp;#39;: &amp;#39;auto&amp;#39;,
                                                                             &amp;#39;position&amp;#39;: &amp;#39;relative&amp;#39;,
                                                                             &amp;#39;width&amp;#39;: &amp;#39;100%&amp;#39;}},
                                              &amp;#39;tagName&amp;#39;: &amp;#39;a&amp;#39;},
                                             {&amp;#39;@type&amp;#39;: &amp;#39;@builder.io/sdk:Element&amp;#39;,
                                              &amp;#39;id&amp;#39;: &amp;#39;builder-pixel-vyr9xl1zoz&amp;#39;,
                                              &amp;#39;properties&amp;#39;: {&amp;#39;alt&amp;#39;: &amp;#39;&amp;#39;,
                                                             &amp;#39;aria-hidden&amp;#39;: &amp;#39;true&amp;#39;,
                                                             &amp;#39;height&amp;#39;: &amp;#39;0&amp;#39;,
                                                             &amp;#39;role&amp;#39;: &amp;#39;presentation&amp;#39;,
                                                             &amp;#39;src&amp;#39;: &amp;#39;https://cdn.builder.io/api/v1/pixel?apiKey=4967176e01a141828a5fad701f6faa79&amp;#39;,
                                                             &amp;#39;width&amp;#39;: &amp;#39;0&amp;#39;},
                                              &amp;#39;responsiveStyles&amp;#39;: {&amp;#39;large&amp;#39;: {&amp;#39;display&amp;#39;: &amp;#39;block&amp;#39;,
                                                                             &amp;#39;height&amp;#39;: &amp;#39;0&amp;#39;,
                                                                             &amp;#39;opacity&amp;#39;: &amp;#39;0&amp;#39;,
                                                                             &amp;#39;overflow&amp;#39;: &amp;#39;hidden&amp;#39;,
                                                                             &amp;#39;pointerEvents&amp;#39;: &amp;#39;none&amp;#39;,
                                                                             &amp;#39;width&amp;#39;: &amp;#39;0&amp;#39;}},
                                              &amp;#39;tagName&amp;#39;: &amp;#39;img&amp;#39;}],
                                  &amp;#39;state&amp;#39;: {&amp;#39;deviceSize&amp;#39;: &amp;#39;large&amp;#39;,
                                            &amp;#39;location&amp;#39;: {&amp;#39;path&amp;#39;: [&amp;#39;not-found&amp;#39;],
                                                         &amp;#39;pathname&amp;#39;: &amp;#39;/not-found&amp;#39;,
                                                         &amp;#39;query&amp;#39;: {}}},
                                  &amp;#39;themeId&amp;#39;: False,
                                  &amp;#39;title&amp;#39;: &amp;#39;404 Error Page&amp;#39;,
                                  &amp;#39;url&amp;#39;: &amp;#39;/not-found&amp;#39;},
                         &amp;#39;firstPublished&amp;#39;: 1712696453909,
                         &amp;#39;folders&amp;#39;: [],
                         &amp;#39;id&amp;#39;: &amp;#39;a6f4d1a5049d47839e72c55aecda96cb&amp;#39;,
                         &amp;#39;lastUpdated&amp;#39;: 1712959355757,
                         &amp;#39;lastUpdatedBy&amp;#39;: &amp;#39;g4MEiL4rhtSHs8OUcuU01J0Q8EN2&amp;#39;,
                         &amp;#39;meta&amp;#39;: {&amp;#39;breakpoints&amp;#39;: {&amp;#39;medium&amp;#39;: 1023, &amp;#39;small&amp;#39;: 768},
                                  &amp;#39;hasLinks&amp;#39;: True,
                                  &amp;#39;kind&amp;#39;: &amp;#39;page&amp;#39;,
                                  &amp;#39;lastPreviewUrl&amp;#39;: &amp;#39;https://pp1.preview.chilis.com/not-found?builder.space=eecd8b66e2c94dd393b6f3fdd27468e7&amp;amp;builder.cachebust=true&amp;amp;builder.preview=page&amp;amp;builder.noCache=true&amp;amp;builder.allowTextEdit=true&amp;amp;__builder_editing__=true&amp;amp;builder.overrides.page=eecd8b66e2c94dd393b6f3fdd27468e7_a6f4d1a5049d47839e72c55aecda96cb&amp;amp;builder.overrides.eecd8b66e2c94dd393b6f3fdd27468e7_a6f4d1a5049d47839e72c55aecda96cb=eecd8b66e2c94dd393b6f3fdd27468e7_a6f4d1a5049d47839e72c55aecda96cb&amp;amp;builder.overrides.page:/not-found=eecd8b66e2c94dd393b6f3fdd27468e7_a6f4d1a5049d47839e72c55aecda96cb&amp;#39;},
                         &amp;#39;modelId&amp;#39;: &amp;#39;fc380e45ad4e4406a61d6c7dfb9d2479&amp;#39;,
                         &amp;#39;name&amp;#39;: &amp;#39;404 Error Page&amp;#39;,
                         &amp;#39;published&amp;#39;: &amp;#39;published&amp;#39;,
                         &amp;#39;query&amp;#39;: [{&amp;#39;@type&amp;#39;: &amp;#39;@builder.io/core:Query&amp;#39;,
                                    &amp;#39;operator&amp;#39;: &amp;#39;is&amp;#39;,
                                    &amp;#39;property&amp;#39;: &amp;#39;urlPath&amp;#39;,
                                    &amp;#39;value&amp;#39;: &amp;#39;/not-found&amp;#39;}],
                         &amp;#39;rev&amp;#39;: &amp;#39;z7bgev4he7i&amp;#39;,
                         &amp;#39;screenshot&amp;#39;: &amp;#39;https://cdn.builder.io/api/v1/image/assets%2F4967176e01a141828a5fad701f6faa79%2Ffdcffe6f669b4e7586b2f75ab97c59fb&amp;#39;,
                         &amp;#39;testRatio&amp;#39;: 1,
                         &amp;#39;variations&amp;#39;: {}}}],
     value_class=None,
     index=24)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just like in the original form, we get the data in chunks. Each chunk is Python
object with nested structure inside. Not much to explain here - just traverse
the structure and extract the fields you need.&lt;/p&gt;
&lt;p&gt;Lastly, njsparser project ships with a small CLI tool to check the pages if
they have Next.js data to parse:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ njsparser &amp;#34;https://www.chilis.com/locations&amp;#34;
Couldn&amp;#39;t find an appropriate type for given class `None`. Giving `Element`.
Couldn&amp;#39;t find an appropriate type for given class `None`. Giving `Element`.
Build Id: -KU-QA00B1JuhGcqPQ6eQ
The site contains flight data.
Pages:
- https://www.chilis.com/_app
- https://www.chilis.com/_error
$ njsparser &amp;#34;https://www.nike.com/t/air-jordan-1-retro-high-og-pro-green-womens-shoes-Hnf3kV/FD2596-101&amp;#34;
Build Id: ixvlnhp8VLcjaN1TTvfqm
The site contains a __NEXT_DATA__ script.
Pages:
- https://www.nike.com/_app
- https://www.nike.com/_error
- https://www.nike.com/t/[slug]
- https://www.nike.com/t/[slug]/[styleColor]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To conclude, we learned about Next.js flight data and how to use an open source
Python library to make it tractable within web scrapers written in Python.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up rooted Android emulator with Frida and mitmproxy </title>
      <link>https://www.trickster.dev/post/setting-up-rooted-android-emulator-with-frida-and-mitmproxy/</link>
      <pubDate>Fri, 12 Sep 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/setting-up-rooted-android-emulator-with-frida-and-mitmproxy/</guid>
      <description>&lt;p&gt;Previously I have covered how to set up &lt;a href=&#34;https://www.mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt;
with &lt;a href=&#34;https://www.trickster.dev/post/setting-up-mitmproxy-with-android/&#34;&gt;Android emulator&lt;/a&gt; or
&lt;a href=&#34;https://www.trickster.dev/post/setting-up-mitmproxy-with-ios17.1/&#34;&gt;iOS device&lt;/a&gt;
for the purposes of mobile app traffic interception. However, there is more to
mobile app hacking, as not all apps allow their API calls to be
hijacked by the simple setup I have described earlier. Some of the more secure
apps implement a security mechanism called X.509 certificate pinning that entails
checking if certificate on the server matches the expected one, thus requiring
a more invasive approach to see intercept communications with backend
systems. Furthermore, there is more to the app functionality than what is
visible on the GUI and within traffic logs. Certain mobile apps implement
proprietary HTTP headers to make adversarial API integrations more difficult to
implement as they cannot be reverse engineered from intercepted API logs
alone.&lt;/p&gt;
&lt;p&gt;We will rely on following software, all free to download:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.android.com/studio&#34;&gt;Android Studio&lt;/a&gt; - comprehensive IDE
for Android app development that ships with some tooling we will need.
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.android.com/tools/adb&#34;&gt;ADB&lt;/a&gt; - CLI tool and communication
interface to manage Android devices and emulators.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.android.com/tools/avdmanager&#34;&gt;avdmanager&lt;/a&gt; - CLI tool
to manage Android emulators.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; - MITM proxy server to intercept
network traffic (primarily API calls over HTTPS).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://frida.re/&#34;&gt;Frida&lt;/a&gt; - dynamic instrumentation framework and toolkit.
Think of it as more powerful equivalent of &lt;code&gt;LD_PRELOAD&lt;/code&gt;, but mostly meant for
mobile app reversing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/newbit/rootAVD&#34;&gt;rootAVD&lt;/a&gt; - Android emulator rooting tool
needed to get full access to system within emulator. This tool will also
install &lt;a href=&#34;https://github.com/topjohnwu/Magisk&#34;&gt;Magisk&lt;/a&gt; - a set of tools for
modding/customising Android.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combination of mitmproxy and Frida will allow us to tamper with both
communications and computations of the app, thus enabling a far deeper insight
into inner workings than just sniffing API calls.&lt;/p&gt;
&lt;p&gt;Broadly speaking, there are three kinds of Android emulators (also known as
AVDs - Android Virtual Devices) that you can create through Android Studio
tooling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Play option comes with Play Store app, Chrome browser and several
Google apps. This is the most restricted kind of emulator you can create, but
it still can be rooted with rootAVD tool.&lt;/li&gt;
&lt;li&gt;Google APIs option does not come with the Play Store app, but still contains
some apps from Google and various client libraries needed to interact with
Google services.&lt;/li&gt;
&lt;li&gt;AOSP option is the purest form of Android system that comes from Android
Open Source Project. Only the very basic web view app is available instead of
full Chrome browser. However, this kind of emulator comes with root access.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us launch Virtual Device Manager from Android Studio by picking the option
from context menu next to Clone Repository button. A window with list of
emulators will appear. To create a new emulator, press the button with plus
sign. Now we need to choose a hardware profile. A hardware profile is a set
of characteristics (screen geometry, form factor, appearance and so on) that
correspond to a physical device being emulated. Not only mobile phones and
tablet can be emulated, but also smart TVs, automotive infotainment systems
and XR headsets are supported. Depending on the option you choose, you may
need to download additional system images and SDKs to get it running (in that
case Android studio will walk you through the process).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-09-11_13.08.38.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2025-09-11_13.08.57.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the sake of simplicity, let us pick Small Phone with Play Store and latest
version of Android. We launch the emulator and let it boot.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-09-11_13.29.53.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another way to create an emulator is to use avdmanager CLI tool. While this
tool comes with Android Studio, it may not be loaded in your shell
namespace by not being in one of the directories listed in &lt;code&gt;PATH&lt;/code&gt; environment
variable. I was able to rectify this on macOS by adding the following line to
my ~/.zshrc file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;export PATH=$PATH:$HOME/Library/Android/sdk/cmdline-tools/latest/bin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Command for creating ARM-based small phone AVD is:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ avdmanager create avd -n &amp;#34;SmallPhone&amp;#34; -d &amp;#34;small_phone&amp;#34; -g &amp;#34;google_apis_playstore&amp;#34; -k &amp;#34;system-images;android-36;google_apis_playstore;arm64-v8a&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This command contains specific hardware profile from output of &lt;code&gt;avdmanager list  devices&lt;/code&gt; in &lt;code&gt;-d&lt;/code&gt; parameter, a system image path in &lt;code&gt;-k&lt;/code&gt; parameter and AVD
type in &lt;code&gt;-g&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;Now, if we try to gain root access via ADB we will run into a restriction:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ adb shell
emu64a:/ $ su
/system/bin/sh: su: inaccessible or not found
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now it is time to use rootAVD tool. If we launch this tool without any parameters,
we will get some text explaining the usage and at the bottom there will some
proposed commands we can run (with autodetected ramdisk file paths):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ./rootAVD
&amp;lt;...&amp;gt;

Command Examples:
./rootAVD.sh
./rootAVD.sh ListAllAVDs
./rootAVD.sh InstallApps

./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img FAKEBOOTIMG
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img DEBUG PATCHFSTAB GetUSBHPmodZ
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img restore
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img InstallKernelModules
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img InstallPrebuiltKernelModules
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img InstallPrebuiltKernelModules GetUSBHPmodZ PATCHFSTAB DEBUG
./rootAVD.sh system-images/android-34/google-xr/arm64-v8a/ramdisk.img AddRCscripts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The file in question is crucial for Android boot process, as it contains a
small filesystem needed to bootstrap rest of Android. In this case Small Phone
AVD does not correspond to ramdisk listed, but we can get more options
by launching &lt;code&gt;./rootAVD.sh ListAllAVDs&lt;/code&gt; command, which leads us to the
following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ./rootAVD.sh system-images/android-36/google_apis_playstore/arm64-v8a/ramdisk.img
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At one point it will ask to pick Magisk version. It is fine to pick the default
one by pressing Enter. Once done, it will shut down the emulator. We will need
to force it to go through the entire boot process by picking Cold Boot from
additional menu in Virtual Device Manager window. You will see that Magisk app
is available on the virtual phone. It will require some additional setup
and one more reboot of the emulator.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-09-11_14.09.22.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, if we try gaining root access via ADB again, Magisk GUI will ask us to
approve it for the first time, but we will be able to get it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ adb shell
emu64a:/ $ su
emu64a:/ # whoami
root
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-09-11_14.10.47.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we can launch mitmproxy and go through procedure described in my
&lt;a href=&#34;https://www.trickster.dev/post/setting-up-mitmproxy-with-android/&#34;&gt;older post&lt;/a&gt; up until the point where
we need to trust a certificate. On modern Android we need to go to Settings app,
search for &amp;ldquo;CA certificate&amp;rdquo; and go through the simple flow to install and trust
the certificate downloaded from MITM proxy server. Once that is done, we can
go to some random website via Google Chrome and verify that HTTP traffic
can be inspected via mitmproxy.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/Screenshot_1757575694.png&#34;&gt;Screenshot 6&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/Screenshot_1757575715.png&#34;&gt;Screenshot 7&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/Screenshot_1757575721.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So it&amp;rsquo;s time to install Frida. There are two parts to Frida: 1) Python tooling
to be launched in the host system and 2) binary server running under root user
within Android system. Architecture of binary part has to match your emulator
architecture (ARM or x86).&lt;/p&gt;
&lt;p&gt;Python part of Frida can be install via PIP:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pip3 install frida-tools
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us download Frida server binary, decompress it and rename the binary file
for our convenience.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget https://github.com/frida/frida/releases/download/17.2.17/frida-server-17.2.17-android-arm64.xz
$ unxz frida-server-17.2.17-android-arm64.xz 
$ mv frida-server-17.2.17-android-arm64 frida-server
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can push the Frida server via ADB and launch it under root (keep this
running in terminal session):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ adb push frida-server /data/local/tmp
frida-server: 1 file pushed, 0 skipped. 226.4 MB/s (52019048 bytes in 0.219s)
$ adb shell
emu64a:/ $ su
emu64a:/ # chmod +x /data/local/tmp/frida-server                               
emu64a:/ # /data/local/tmp/frida-server                                        
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One of the Android apps that implement TLS cert pinning is Hollister Co. app.
You can download the APK file from &lt;a href=&#34;https://hollister.en.uptodown.com/android/download&#34;&gt;third party
source&lt;/a&gt; and install it by
dragging the APK file into emulator window. Now everything comes together.
We are going to use generalised &lt;a href=&#34;https://codeshare.frida.re/@Q0120S/bypass-ssl-pinning/&#34;&gt;Bypass SSL Pinning&lt;/a&gt;
script that covers a variety of ways certificate pinning can be implemented
and attempts to disable them.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ frida -U --codeshare Q0120S/bypass-ssl-pinning -f com.abercrombie.hollister
     ____
    / _  |   Frida 17.2.16 - A world-class dynamic instrumentation toolkit
   | (_| |
    &amp;gt; _  |   Commands:
   /_/ |_|       help      -&amp;gt; Displays the help system
   . . . .       object?   -&amp;gt; Display information about &amp;#39;object&amp;#39;
   . . . .       exit/quit -&amp;gt; Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Android Emulator 5554 (id=emulator-5554)
Spawned `com.abercrombie.hollister`. Resuming main thread!              
[Android Emulator 5554::com.abercrombie.hollister ]-&amp;gt; ---
Unpinning Android app...
[+] SSLPeerUnverifiedException auto-patcher
[+] HttpsURLConnection (setDefaultHostnameVerifier)
[+] HttpsURLConnection (setSSLSocketFactory)
[+] HttpsURLConnection (setHostnameVerifier)
[+] SSLContext
[+] TrustManagerImpl
[ ] OkHTTPv3 (list)
[ ] OkHTTPv3 (cert)
[ ] OkHTTPv3 (cert array)
[ ] OkHTTPv3 ($okhttp)
[ ] Trustkit OkHostnameVerifier(SSLSession)
[ ] Trustkit OkHostnameVerifier(cert)
[ ] Trustkit PinningTrustManager
[ ] Appcelerator PinningTrustManager
[ ] OpenSSLSocketImpl Conscrypt
[ ] OpenSSLEngineSocketImpl Conscrypt
[ ] OpenSSLSocketImpl Apache Harmony
[ ] PhoneGap sslCertificateChecker
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string)
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string array)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (cert)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (string string)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession)
[ ] Conscrypt CertPinManager
[ ] CWAC-Netsecurity CertPinManager
[ ] Worklight Androidgap WLCertificatePinningPlugin
[ ] Netty FingerprintTrustManagerFactory
[ ] Squareup CertificatePinner (cert)
[ ] Squareup CertificatePinner (list)
[ ] Squareup OkHostnameVerifier (cert)
[ ] Squareup OkHostnameVerifier (SSLSession)
[+] Android WebViewClient (SslErrorHandler)
[ ] Android WebViewClient (WebResourceError)
[ ] Apache Cordova WebViewClient
[ ] Boye AbstractVerifier
[ ] Appmattus (CertificateTransparencyInterceptor)
[ ] Appmattus (CertificateTransparencyTrustManager)
Unpinning setup completed
---
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing Trustmanager (Android &amp;lt; 7) request
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
  --&amp;gt; Bypassing TrustManagerImpl checkTrusted 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hollister Co. app now can be operated successfully and we can also see the
HTTP flows being intercepted in mitmproxy.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-09-11_16.09.49.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we look through the script we just used, we see some JavaScript code with
many try-catch blocks, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// OpenSSLSocketImpl Conscrypt
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OpenSSLSocketImpl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Java&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;use&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;com.android.org.conscrypt.OpenSSLSocketImpl&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;OpenSSLSocketImpl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;verifyCertificateChain&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;implementation&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;certRefs&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;JavaObject&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;authMethod&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;  --&amp;gt; Bypassing OpenSSLSocketImpl Conscrypt&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[+] OpenSSLSocketImpl Conscrypt&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[ ] OpenSSLSocketImpl Conscrypt&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can see that this piece of code attacks one of the possible cert pinning
implementations by replacing (at runtime!) the exact code to verify certificate
chain with small function that only prints a debug log. This is an example of the
dynamic instrumentation concept that Frida project centers around.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>jq: tool and language for JSON processing</title>
      <link>https://www.trickster.dev/post/jq-tool-and-language-for-json-processing/</link>
      <pubDate>Sat, 14 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/jq-tool-and-language-for-json-processing/</guid>
      <description>&lt;p&gt;JSON is the dominant data representation format for most of the modern
RESTful APIs and certain automation/devops tools (Terraform, AWS CLI, kubectl,
etc.) optionally output data in JSON format to make it machine readable. Some
datasets (e.g. service price lists from health insurance companies) are
available primarily as JSON files. For these reasons parsing, generation,
modification and analysis of JSON documents is nothing new for many explorers
of cyberspace. Indeed, there are libraries and tools to wrangle JSONified data
in pretty much all popular general purpose programming languages. But what
about shell scripts? When one is programming in Bash or Zsh it might be
necessary to run some curl(1) snippet and extract parts of API response, but
your standard Unix text processing tools - awk(1), sed(1), grep(1) and others
largely predate JSON popularity and are not designed with nested data trees in
mind. &lt;a href=&#34;https://jqlang.github.io/jq/&#34;&gt;jq&lt;/a&gt; is CLI tool that was designed to fill
the gap here and addresses the need of making JSON wrangling easier within the
Unix/Linux environment. But it is not merely a CLI tool. Like AWK, it is also a
Turing-complete domain specific language that lets you implement arbitrarily
complex (within limits of computability) JSON document processing scripts.&lt;/p&gt;
&lt;p&gt;One can install jq via the usual package managers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;brew install jq&lt;/code&gt; on macOS with Homebrew.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apt-get install jq&lt;/code&gt; on Debian and many Debian-based distributions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pacman -S jq&lt;/code&gt; on Arch Linux.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkg install jq&lt;/code&gt; on FreeBSD.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, jq project ships ready-to-use binary files as part of their
&lt;a href=&#34;https://github.com/jqlang/jq/releases&#34;&gt;release deliverables&lt;/a&gt;. To get the
official Docker image with jq one can run &lt;code&gt;docker pull ghcr.io/jqlang/jq:latest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is also &lt;a href=&#34;https://jqplay.org/&#34;&gt;jqplay.org&lt;/a&gt; - a web playground for playing
with jq language without installing anything (but not for processing large
JSON documents).&lt;/p&gt;
&lt;p&gt;The syntax of jq has similarities with JSON syntax. The very simplest thing one
can do in jq is pretty printing the input for readability (this can be turned
off with &lt;code&gt;--compact-output&lt;/code&gt; or &lt;code&gt;-c&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;{&amp;#34;x&amp;#34;: 1, &amp;#34;y&amp;#34;: 2}&amp;#39; | jq &amp;#39;.&amp;#39;
{
  &amp;#34;x&amp;#34;: 1,
  &amp;#34;y&amp;#34;: 2
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default jq makes output colored for easier reading. We used jq identity
statement as the first argument.&lt;/p&gt;
&lt;p&gt;We may want to create JSON object or list in jq language. Syntax to do so
is very similar to JSON notation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;{a: 1, b: 2}&amp;#39;
{
  &amp;#34;a&amp;#34;: 1,
  &amp;#34;b&amp;#34;: 2
}
$ jq --null-input &amp;#39;[1,3,5,7,11]&amp;#39;
[
  1,
  3,
  5,
  7,
  11
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To extract a single field from JSON object we can use object identifier-index
syntax - dot character with field name:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;{&amp;#34;x&amp;#34;: 1, &amp;#34;y&amp;#34;: 2}&amp;#39; | jq &amp;#39;.x&amp;#39;
1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be done across multiple levels:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;{&amp;#34;coord&amp;#34;: {&amp;#34;x&amp;#34;: 1, &amp;#34;y&amp;#34;: 2}}&amp;#39; | jq &amp;#39;.coord.y&amp;#39;
2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like other programming languages, JSON arrays can be indexed:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;{&amp;#34;coords&amp;#34;: [{&amp;#34;x&amp;#34;: 1.1, &amp;#34;y&amp;#34;: 2.5}]}&amp;#39; | jq &amp;#39;.coords[0].y&amp;#39;
2.5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like in Python, arrays can be sliced by index range:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;[1,2,3,4,5]&amp;#39; | jq &amp;#39;.[1:3]&amp;#39;
[
  2,
  3
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If used without indices, &lt;code&gt;[]&lt;/code&gt; is iterator - it returns all values within the
input list or object:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;[1,2,3,4,5]&amp;#39; | jq &amp;#39;.[]&amp;#39;   
1
2
3
4
5
$ echo &amp;#39;{&amp;#34;a&amp;#34;: 1, &amp;#34;b&amp;#34;: 2}&amp;#39; | jq &amp;#39;.[]&amp;#39;
1
2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that first index in the range is inclusive, but not the second one - second
index tells jq to extract values &lt;em&gt;up to&lt;/em&gt; that index.&lt;/p&gt;
&lt;p&gt;JSON data types have the same syntax in jq language as in JSON notation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --monochrome-output --null-input &amp;#39;null&amp;#39;   
null
$ jq --null-input &amp;#39;1.2&amp;#39;   
1.2
$ jq --null-input &amp;#39;&amp;#34;str&amp;#34;&amp;#39; 
&amp;#34;str&amp;#34;
$ jq --null-input &amp;#39;false&amp;#39;
false
$ jq --null-input &amp;#39;{&amp;#34;latitude&amp;#34;: 1.1, &amp;#34;longitude&amp;#34;: 2.2}&amp;#39; 
{
  &amp;#34;latitude&amp;#34;: 1.1,
  &amp;#34;longitude&amp;#34;: 2.2
}
$ jq --null-input &amp;#39;[&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;, &amp;#34;c&amp;#34;]&amp;#39; 
[
  &amp;#34;a&amp;#34;,
  &amp;#34;b&amp;#34;,
  &amp;#34;c&amp;#34;
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like in Unix environment, pipe character can be used to build pipelines of
operations:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;{&amp;#34;latitude&amp;#34;: 1.1, &amp;#34;longitude&amp;#34;: 2.2} | .latitude&amp;#39;
1.1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Comma character can be used to concatenate outputs from two or more operations
into single stream:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;{&amp;#34;latitude&amp;#34;: 1.1, &amp;#34;longitude&amp;#34;: 2.2} | .latitude, .longitude&amp;#39;  
1.1
2.2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Furthermore, jq language allows using question mark character as optional
operator. Like in Swift programming language, this allows for parts of input
(e.g. a subtree in JSON document) to be missing without crashing the program.
If something is not missing, further operations will be executed. If it is
missing, the entire chain of operations will return null value:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;{&amp;#34;coords&amp;#34;: [{&amp;#34;x&amp;#34;: 1.1, &amp;#34;y&amp;#34;: 2.5}]}&amp;#39; | jq &amp;#39;.coords?[0].y&amp;#39;
2.5
$ echo &amp;#39;{&amp;#34;coords&amp;#34;: [{&amp;#34;x&amp;#34;: 1.1, &amp;#34;y&amp;#34;: 2.5}]}&amp;#39; | jq &amp;#39;.coordinates?[0].y&amp;#39;
null
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like all the proper programming languages, jq has operators for basic math
operations:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;1 + 1&amp;#39;
2
$ jq --null-input &amp;#39;1 - 1&amp;#39;
0
$ jq --null-input &amp;#39;1 * 2&amp;#39;
2
$ jq --null-input &amp;#39;1 / 2&amp;#39;
0.5
$ jq --null-input &amp;#39;5 % 2&amp;#39; 
1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Multiplication operator can also be used to make repeated string:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;&amp;#34;cyber&amp;#34; * 3&amp;#39;
&amp;#34;cybercybercyber&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Furthermore, jq has some built-in functions for common computations and data
processing operations. For example, &lt;code&gt;length&lt;/code&gt; is equivalent to Python&amp;rsquo;s &lt;code&gt;len()&lt;/code&gt;
function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#39;&amp;#34;abc&amp;#34;&amp;#39; | jq &amp;#39;length&amp;#39;   
3
$ echo &amp;#39;[1, 2, 3, 4]&amp;#39; | jq &amp;#39;length&amp;#39;
4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The jq language does not have for or while loops. Instead, we are supposed to
use two brackets (&lt;code&gt;[]&lt;/code&gt;) - iteration operator and/or &lt;code&gt;range&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq --null-input &amp;#39;range(100)&amp;#39;
0
1
2
3
4
...
96
97
98
99
100
$ jq --null-input &amp;#39;[range(100)]&amp;#39; | jq &amp;#39;.[] | . + 1000&amp;#39; | head
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In jq language &lt;code&gt;map&lt;/code&gt; is equivalent to &lt;code&gt;map()&lt;/code&gt; in Python and &lt;code&gt;select&lt;/code&gt; is
equivalent to &lt;code&gt;filter()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This allows us to implement data processing in FP-like manner:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;[1000, 2000, 3000, 5000, 9001, 10240]&amp;#34; | jq &amp;#39;.[] | select(. &amp;gt; 9000)&amp;#39;
9001
10240
$ echo &amp;#39;[&amp;#34;corpothieves&amp;#34;, &amp;#34;must&amp;#34;, &amp;#34;die&amp;#34;]&amp;#39; | jq &amp;#39;map(length)&amp;#39;
[
  12,
  4,
  3
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we don&amp;rsquo;t want to use &lt;code&gt;select&lt;/code&gt; we can use regular if-then-else control flow
feature of jq:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;9001&amp;#34; | jq &amp;#39;if . &amp;gt; 9000 then &amp;#34;over nine thousand&amp;#34; else &amp;#34;below 9000&amp;#34; end&amp;#39;
&amp;#34;over nine thousand&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I asked Google Gemini 2.5 to write Fizzbuzz code in jq language. Take a look:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;range(1; 101) |
  if . % 15 == 0 then
    &amp;#34;FizzBuzz&amp;#34;
  elif . % 3 == 0 then
    &amp;#34;Fizz&amp;#34;
  elif . % 5 == 0 then
    &amp;#34;Buzz&amp;#34;
  else
    .
  end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It can be launched with following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jq -n -r -f fizzbuzz_standard.jq
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also IEEE754 double precision floating point number support so you
can do trigonometry and stuff. See the
&lt;a href=&#34;https://jqlang.github.io/jq/manual/#builtin-operators-and-functions&#34;&gt;documentation&lt;/a&gt;
for a list of built-in functions available.&lt;/p&gt;
&lt;p&gt;Many programming languages provide a way to develop reusable pieces of code that
can be accessed via API. We already tried some jq functions that are shipped with
standard installation. To define jq module we must create file with &lt;code&gt;.jq&lt;/code&gt;
extension and write one more jq functions in there like it is done in
&lt;a href=&#34;https://github.com/jqlang/jq/blob/master/src/builtin.jq&#34;&gt;src/builtin.jq&lt;/a&gt; file.&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time for a real-worldish example of using jq for data extraction. But
first, let me show you a trick. Pretty much all Shopify stores that don&amp;rsquo;t use
custom frontend expose some APIs with product data. First, &lt;code&gt;/products.json&lt;/code&gt;
endpoint provides a product list. It acts as paginated Product List Page (PLP)
API with parameters &lt;code&gt;page&lt;/code&gt; for page number (starting with 1) and &lt;code&gt;limit&lt;/code&gt; for
page size (can be up to 250). See the following examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/products.json&#34;&gt;https://hypebeastbaltics.com/products.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/products.json?limit=10&amp;amp;page=1&#34;&gt;https://hypebeastbaltics.com/products.json?limit=10&amp;amp;page=1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/products.json?limit=10&amp;amp;page=2&#34;&gt;https://hypebeastbaltics.com/products.json?limit=10&amp;amp;page=2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should be noted that sometimes primary store domain cannot be used and
should be replaced with .myshopify subdomain that can be found in page source.
For our example store that would be online-hypebeastbaltics.myshopify.com.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2025-04-26_15.57.30.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want individual product data from the store that can be retrieved by
appending &lt;code&gt;.js&lt;/code&gt; or &lt;code&gt;.json&lt;/code&gt; to PDP URL path. This will give you slightly
different versions of the data and, depending on the target, fields regarding
inventory or product availability might be present in one form, but not in
another. For example the following URLs can be used for API scraping:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/products/nike-air-force-1-low-supreme-white.js&#34;&gt;https://hypebeastbaltics.com/products/nike-air-force-1-low-supreme-white.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/products/nike-air-force-1-low-supreme-white.json&#34;&gt;https://hypebeastbaltics.com/products/nike-air-force-1-low-supreme-white.json&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is also possible to retrieve products from collection, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/collections/clothing-1/products.json&#34;&gt;https://hypebeastbaltics.com/collections/clothing-1/products.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hypebeastbaltics.com/collections/clothing-1/products.json?limit=5&amp;amp;page=2&#34;&gt;https://hypebeastbaltics.com/collections/clothing-1/products.json?limit=5&amp;amp;page=2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This can be used to gather data programmatically at PLP and PDP levels from
Shopify store with little to no need for HTML page scraping. Since the response
data format is JSON, we can use jq to extract the interesting parts. We will still
need something else to implement the remaining parts of API scraping code. In
this case we will use Bash scripting language.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set -x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;URL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://hypebeastbaltics.com/products.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PER_PAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;250&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id,title,body_html,vendor,product_type,price,handle&amp;#34;&lt;/span&gt; &amp;gt; hbb.csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; true ; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    JSON_STR&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$URL&lt;span style=&#34;color:#e6db74&#34;&gt;?page=&lt;/span&gt;$PAGE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;amp;limit=&lt;/span&gt;$PER_PAGE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$JSON_STR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | jq -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.products[] | [.id, .title, .body_html, .vendor, .product_type, .variants[0].price, .handle] | @csv&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; hbb.csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    N_PRODUCTS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$JSON_STR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | jq -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.products | length&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$N_PRODUCTS&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -lt &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PER_PAGE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        break
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;PAGE+1&lt;span style=&#34;color:#66d9ef&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The jq codebase is in C, but the underlying &amp;ldquo;backend&amp;rdquo; of the language can be
used as a shared library - libjq. This would enable using it as DSL outside shell
scripts. But the thing is, unless what you do is in the realm of
systems programming you probably do not want to use
&lt;a href=&#34;https://github.com/jqlang/jq/wiki/C-API:-jv&#34;&gt;libjq C API&lt;/a&gt; in your own code.
For our convenience there are wrappers in higher level languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pypi.org/project/jq/&#34;&gt;&lt;code&gt;jq&lt;/code&gt; for Python&lt;/a&gt; - available from PIP.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://crates.io/crates/jq-rs&#34;&gt;&lt;code&gt;jq-rs&lt;/code&gt; for Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.npmjs.com/package/node-jq&#34;&gt;&lt;code&gt;node-jq&lt;/code&gt; for Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://metacpan.org/pod/JSON::JQ&#34;&gt;JSON::JQ for Perl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the Python module makes API scraping code less tedious:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; import jq
&amp;gt;&amp;gt;&amp;gt; from pprint import pprint
&amp;gt;&amp;gt;&amp;gt; resp = requests.get(&amp;#34;https://hypebeastbaltics.com/products.json&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; titles = jq.compile(&amp;#34;.products[].title&amp;#34;).input(text=resp.text).all()
&amp;gt;&amp;gt;&amp;gt; pprint(titles)
[&amp;#39;Denver Broncos fitted hat brown corduroy 7 3/4&amp;#39;,
 &amp;#39;Denver Broncos fitted hat Black 8&amp;#39;,
 &amp;#39;Dallas Cowboys Superbowl fitted hat navy 7 1/8&amp;#39;,
 &amp;#39;Minnesota Vikings fitted hat pink 7 1/4&amp;#39;,
 &amp;#39;San Diego Padres fitted hat white green 7 1/2&amp;#39;,
 &amp;#39;San Francisco 49ers fitted hat  7 3/8&amp;#39;,
 &amp;#39;Los Angeles Rams fitted hat white 7 3/8&amp;#39;,
 &amp;#39;San Antonio Spurs fitted hat Grey 7 3/8&amp;#39;,
 &amp;#39;New York Yankees fitted hat Navy 6 3/8&amp;#39;,
 &amp;#39;CDG Polka Dot With Upside Down Heart T-Shirt White&amp;#39;,
 &amp;#39;Used adidas Yeezy Boost 350 V2 Cream&amp;#39;,
 &amp;#39;Used Adidas Ultra Boost 4.0 Bape Camo&amp;#39;,
 &amp;#39;Used Adidas Yeezy Boost 350 V2 Earth&amp;#39;,
 &amp;#39;Used Yeezy 500 Utility Black&amp;#39;,
 &amp;#39;Used adidas Yeezy Boost 350 V2 MX Oat&amp;#39;,
 &amp;#39;Used Yeezy 700 V3 Clay Brown&amp;#39;,
 &amp;#39;Used Yves Saint Laurent Leather high trainers&amp;#39;,
 &amp;#39;Used Louis Vuitton Boxing High Top Triple Black&amp;#39;,
 &amp;#39;Pop Mart Labubu The Monsters Lazy Yoga Series Figures Blind Box&amp;#39;,
 &amp;#39;Pop Mart The Monsters Labubu Zimomo Angel in Clouds Figure (No Tote)&amp;#39;,
 &amp;#39;Nike x Jacquemus Le Polo Off-White&amp;#39;,
 &amp;#39;Nike x NOCTA NRG Knit Long Sleeve Top Cobalt Bliss/Dark Obsidian&amp;#39;,
 &amp;#39;COMING 6TH JUNE Pop Mart Labubu The Monsters Have a Seat Vinyl Plush Sealed &amp;#39;
 &amp;#39;Case (6 Blind Boxes)&amp;#39;,
 &amp;#39;COMING 6TH JUNE Pop Mart Labubu The Monsters Tasty Macarons Vinyl Plush &amp;#39;
 &amp;#39;Pendant Single Blind Box&amp;#39;,
 &amp;#39;Corteiz Olympic Shuku Nigeria Jacket Green/White&amp;#39;,
 &amp;#39;Nike x NOCTA NRG Short Cobalt Bliss/White&amp;#39;,
 &amp;#39;Pop Mart Labubu The Monsters Big into Energy Series Vinyl Plush Pendant &amp;#39;
 &amp;#39;Sealed Case (6 Blind Boxes)&amp;#39;,
 &amp;#39;COMING 6TH JUNE Pop Mart Labubu The Monsters Exciting Macarons Vinyl Face &amp;#39;
 &amp;#39;Blind Box Whole Set&amp;#39;,
 &amp;#39;Used Supreme Bandana Tarp Side Bag&amp;#39;,
 &amp;#39;Used Carolina Panthers Fitted Cap&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/itchyny/gojq&#34;&gt;Gojq project&lt;/a&gt; reimplements the language in
pure Go and does not rely on the C codebase. It can be used as CLI tool and
integrated into other software as library.&lt;/p&gt;
&lt;p&gt;Some interesting codebases using jq language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ynqa/jnv&#34;&gt;jnv&lt;/a&gt; - interactive tool to browse JSON data
and prototype jq one-liners in a terminal.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/fiatjaf/jqview&#34;&gt;jqview&lt;/a&gt; - Qt-based GUI app based on Gojq.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jdolitsky/tq&#34;&gt;tq&lt;/a&gt; - tool to manipulate Terraform objects
with jq language.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Katana: web crawler for offensive security and web exploration</title>
      <link>https://www.trickster.dev/post/katana-web-crawler-for-offensive-security-and-web-exploration/</link>
      <pubDate>Tue, 01 Oct 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/katana-web-crawler-for-offensive-security-and-web-exploration/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/projectdiscovery/katana&#34;&gt;Katana&lt;/a&gt; is CLI tool and Go library
for automatically traversing (crawling) across all pages of given website(s) to
map them out. It can work in two main modes - requests-based and through browser
automation (headful or headless). To allow for discovery of API endpoints it
can optionally do JavaScript parsing even when running in requests-based mode.
Furthermore, Katana can do passive crawling by leveraging pre-crawled data from
Internet Archive Wayback Machine, CommonCrawl and Alien Vault OTX.
Since mapping out site pages and APIs is useful for security research activities
(e.g bug bounty hunting) Katana is designed to fit into larger automation
workflows, esp. when used together with other tooling from Project Discovery.&lt;/p&gt;
&lt;p&gt;On macOS Katana is available through Homebrew and there is also &lt;a href=&#34;https://hub.docker.com/r/projectdiscovery/katana&#34;&gt;official
Docker image&lt;/a&gt; on Docker Hub
that includes a Chromium browser.&lt;/p&gt;
&lt;p&gt;If you have Golang development environment you can install it via Go CLI:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ go install github.com/projectdiscovery/katana/cmd/katana@latest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we simply want to see a list of pages a site has we only have to pass the
site URL via &lt;code&gt;-u&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ katana -u https://public-firing-range.appspot.com/

   __        __                
  / /_____ _/ /____ ____  ___ _
 /  &amp;#39;_/ _  / __/ _  / _ \/ _  /
/_/\_\\_,_/\__/\_,_/_//_/\_,_/							 

		projectdiscovery.io

[INF] Current katana version v1.1.0 (latest)
[INF] Started standard crawling for =&amp;gt; https://public-firing-range.appspot.com/
https://public-firing-range.appspot.com/
https://public-firing-range.appspot.com/vulnerablelibraries/index.html
https://public-firing-range.appspot.com/stricttransportsecurity/index.html
https://public-firing-range.appspot.com/urldom/index.html
https://public-firing-range.appspot.com/reverseclickjacking/
https://public-firing-range.appspot.com/reflected/index.html
https://public-firing-range.appspot.com/mixedcontent/index.html
https://public-firing-range.appspot.com/tags/index.html
https://public-firing-range.appspot.com/redirect/index.html
https://public-firing-range.appspot.com/remoteinclude/index.html
https://public-firing-range.appspot.com/insecurethirdpartyscripts/index.html
https://public-firing-range.appspot.com/leakedcookie/index.html
https://public-firing-range.appspot.com/flashinjection/index.html
https://public-firing-range.appspot.com/vulnerablelibraries/jquery.html
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But now we don&amp;rsquo;t see status codes and we&amp;rsquo;re not even sure if all of these pages
load properly. We can use &lt;code&gt;-mdc&lt;/code&gt; argument with Project Discovery DSL snippet
to filter out only responses with status code 200:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ katana -u https://public-firing-range.appspot.com/ -mdc &amp;#39;status_code == 200&amp;#39; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can use this tool to store responses in case you want to separate web
crawling from HTTP parsing:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ katana -u https://books.toscrape.com/ -store-response
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This decouples former from the latter so that you can download all the pages
once and then work on code that parses the HTML documents. The above command
creates a new directory named &lt;code&gt;katana_responses&lt;/code&gt; with an index.txt file listing
requests, status codes and relative file paths:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ head index.txt 
katana_response/books.toscrape.com/3060d727707e12334c7134afe04df480af3f0fdf.txt https://books.toscrape.com/ (200 OK)
katana_response/books.toscrape.com/f77b190299d9ad01298054c2b1ce13f35765e4d1.txt https://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/locales/bootstrap-datetimepicker.all.js (200 OK)
katana_response/books.toscrape.com/9e4c7d8e5630912730d1194fec599ec3f5f8580e.txt https://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/bootstrap-datetimepicker.js (200 OK)
katana_response/books.toscrape.com/7fdd4edbfcbe5d86df9904b18b6747e7cf7f0efa.txt https://books.toscrape.com/static/oscar/js/oscar/ui.js (200 OK)
katana_response/books.toscrape.com/cd148dbefc26230ec0d29104eb0408ae640720d2.txt https://books.toscrape.com/static/oscar/js/bootstrap3/bootstrap.min.js (200 OK)
katana_response/books.toscrape.com/0eb507686604bb3002bdf01dc8d1b917c7352867.txt https://books.toscrape.com/static/oscar/css/datetimepicker.css (200 OK)
katana_response/books.toscrape.com/0a63ac4de25fb4ba06a76137240cc663a716b506.txt https://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/bootstrap-datetimepicker.css (200 OK)
katana_response/books.toscrape.com/a2652354fa625fb0f225b7cd87f035f8f6d6af2c.txt https://books.toscrape.com/catalogue/its-only-the-himalayas_981/index.html (200 OK)
katana_response/books.toscrape.com/bf2072cbc394277fe024f4992dde7a498b7d6a9f.txt https://books.toscrape.com/catalogue/libertarianism-for-beginners_982/index.html (200 OK)
katana_response/books.toscrape.com/e4f9cda62c7f551a9e54439e5ed27f6e3306902a.txt https://books.toscrape.com/catalogue/mesaerion-the-best-science-fiction-stories-1800-1849_983/index.html (200 OK)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each of the page snapshot files retain original URL, request/response headers and
response payload:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ head -n 50 books.toscrape.com/f77b190299d9ad01298054c2b1ce13f35765e4d1.txt 
https://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/locales/bootstrap-datetimepicker.all.js


GET /static/oscar/js/bootstrap-datetimepicker/locales/bootstrap-datetimepicker.all.js HTTP/1.1
Host: books.toscrape.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept-Encoding: gzip



HTTP/1.1 200 OK
Content-Length: 31995
Accept-Ranges: bytes
Connection: keep-alive
Content-Type: application/javascript
Date: Tue, 20 Aug 2024 09:17:54 GMT
Etag: &amp;#34;63e40de9-7cfb&amp;#34;
Last-Modified: Wed, 08 Feb 2023 21:02:33 GMT
Strict-Transport-Security: max-age=0; includeSubDomains; preload

/**
* Arabic translation for bootstrap-datetimepicker
* Ala&amp;#39; Mohammad &amp;lt;amohammad@birzeit.ecu&amp;gt;
*/
;(function($){
	$.fn.datetimepicker.dates[&amp;#39;ar&amp;#39;] = {
		days: [&amp;#34;الأحد&amp;#34;, &amp;#34;الاثنين&amp;#34;, &amp;#34;الثلاثاء&amp;#34;, &amp;#34;الأربعاء&amp;#34;, &amp;#34;الخميس&amp;#34;, &amp;#34;الجمعة&amp;#34;, &amp;#34;السبت&amp;#34;, &amp;#34;الأحد&amp;#34;],
		daysShort: [&amp;#34;أحد&amp;#34;, &amp;#34;اثنين&amp;#34;, &amp;#34;ثلاثاء&amp;#34;, &amp;#34;أربعاء&amp;#34;, &amp;#34;خميس&amp;#34;, &amp;#34;جمعة&amp;#34;, &amp;#34;سبت&amp;#34;, &amp;#34;أحد&amp;#34;],
		daysMin: [&amp;#34;أح&amp;#34;, &amp;#34;إث&amp;#34;, &amp;#34;ث&amp;#34;, &amp;#34;أر&amp;#34;, &amp;#34;خ&amp;#34;, &amp;#34;ج&amp;#34;, &amp;#34;س&amp;#34;, &amp;#34;أح&amp;#34;],
		months: [&amp;#34;يناير&amp;#34;, &amp;#34;فبراير&amp;#34;, &amp;#34;مارس&amp;#34;, &amp;#34;أبريل&amp;#34;, &amp;#34;مايو&amp;#34;, &amp;#34;يونيو&amp;#34;, &amp;#34;يوليو&amp;#34;, &amp;#34;أغسطس&amp;#34;, &amp;#34;سبتمبر&amp;#34;, &amp;#34;أكتوبر&amp;#34;, &amp;#34;نوفمبر&amp;#34;, &amp;#34;ديسمبر&amp;#34;],
		monthsShort: [&amp;#34;يناير&amp;#34;, &amp;#34;فبراير&amp;#34;, &amp;#34;مارس&amp;#34;, &amp;#34;أبريل&amp;#34;, &amp;#34;مايو&amp;#34;, &amp;#34;يونيو&amp;#34;, &amp;#34;يوليو&amp;#34;, &amp;#34;أغسطس&amp;#34;, &amp;#34;سبتمبر&amp;#34;, &amp;#34;أكتوبر&amp;#34;, &amp;#34;نوفمبر&amp;#34;, &amp;#34;ديسمبر&amp;#34;],
		today: &amp;#34;هذا اليوم&amp;#34;,
		suffix: [],
		meridiem: [],
		rtl: true
	};
}(jQuery));
/**
 * Bulgarian translation for bootstrap-datetimepicker
 * Apostol Apostolov &amp;lt;apostol.s.apostolov@gmail.com&amp;gt;
 */
;(function($){
	$.fn.datetimepicker.dates[&amp;#39;bg&amp;#39;] = {
		days: [&amp;#34;Неделя&amp;#34;, &amp;#34;Понеделник&amp;#34;, &amp;#34;Вторник&amp;#34;, &amp;#34;Сряда&amp;#34;, &amp;#34;Четвъртък&amp;#34;, &amp;#34;Петък&amp;#34;, &amp;#34;Събота&amp;#34;, &amp;#34;Неделя&amp;#34;],
		daysShort: [&amp;#34;Нед&amp;#34;, &amp;#34;Пон&amp;#34;, &amp;#34;Вто&amp;#34;, &amp;#34;Сря&amp;#34;, &amp;#34;Чет&amp;#34;, &amp;#34;Пет&amp;#34;, &amp;#34;Съб&amp;#34;, &amp;#34;Нед&amp;#34;],
		daysMin: [&amp;#34;Н&amp;#34;, &amp;#34;П&amp;#34;, &amp;#34;В&amp;#34;, &amp;#34;С&amp;#34;, &amp;#34;Ч&amp;#34;, &amp;#34;П&amp;#34;, &amp;#34;С&amp;#34;, &amp;#34;Н&amp;#34;],
		months: [&amp;#34;Януари&amp;#34;, &amp;#34;Февруари&amp;#34;, &amp;#34;Март&amp;#34;, &amp;#34;Април&amp;#34;, &amp;#34;Май&amp;#34;, &amp;#34;Юни&amp;#34;, &amp;#34;Юли&amp;#34;, &amp;#34;Август&amp;#34;, &amp;#34;Септември&amp;#34;, &amp;#34;Октомври&amp;#34;, &amp;#34;Ноември&amp;#34;, &amp;#34;Декември&amp;#34;],
		monthsShort: [&amp;#34;Ян&amp;#34;, &amp;#34;Фев&amp;#34;, &amp;#34;Мар&amp;#34;, &amp;#34;Апр&amp;#34;, &amp;#34;Май&amp;#34;, &amp;#34;Юни&amp;#34;, &amp;#34;Юли&amp;#34;, &amp;#34;Авг&amp;#34;, &amp;#34;Сеп&amp;#34;, &amp;#34;Окт&amp;#34;, &amp;#34;Ное&amp;#34;, &amp;#34;Дек&amp;#34;],
		today: &amp;#34;днес&amp;#34;,
		suffix: [],
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Katana is not primarily designed to extract information from web pages - it is
crawling, not scraping tool. For those in need of a comprehensive web scraping
framework &lt;a href=&#34;https://go-colly.org/&#34;&gt;Colly&lt;/a&gt; and &lt;a href=&#34;https://scrapy.org/&#34;&gt;Scrapy&lt;/a&gt; are
well established options. However, it is is possible to do regex-based data
extraction by putting regular expressions in YAML file like the following
example from the README.md file (simplified for brevity):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;email&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;regex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;regex&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once we have fields configured, we can run Katana to extract some data:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ katana -u https://allbirds.com -f email -flc field-config.yaml 


   __        __                
  / /_____ _/ /____ ____  ___ _
 /  &amp;#39;_/ _  / __/ _  / _ \/ _  /
/_/\_\\_,_/\__/\_,_/_//_/\_,_/							 

		projectdiscovery.io

[INF] Current katana version v1.1.0 (latest)
[INF] Started standard crawling for =&amp;gt; https://allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
help@allbirds.com
privacy@allbirds.com
privacy@allbirds.com
privacy@allbirds.com
privacy@allbirds.com
privacy@allbirds.com
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This enables Katana to be used for opportunistic contact data (email and/or phone
number) scraping across a list of websites (which can be passed to Katana one
per line via standard input). For example, one could source a list
of companies within a niche by Google dorking or scraping a business directory
website, then run Katana with the above custom field configuration to traverse
the list of websites and grab emails whenever they happen to be somewhere on the
page. Contacts most likely will be generic to the entire company, but this is
significantly cheaper than running the domain list through enrichment API.
Some growth hackers are using Scrapebox, a proprietary Windows
desktop program to do this, but Katana requires no GUI environment if you want
to run it on the server and is 100% open source.&lt;/p&gt;
&lt;p&gt;However, regular expressions should be used sparingly for web scraping and
one &lt;a href=&#34;https://stackoverflow.com/a/1732454&#34;&gt;should refrain&lt;/a&gt; from trying to parse
HTML with them. Consider that an anti-pattern.&lt;/p&gt;
&lt;p&gt;If you run Katana with &lt;code&gt;-js-crawl&lt;/code&gt; CLI argument it will download JavaScript
files and parse them to find API endpoints:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ katana -u https://allbirds.com -js-crawl

   __        __                
  / /_____ _/ /____ ____  ___ _
 /  &amp;#39;_/ _  / __/ _  / _ \/ _  /
/_/\_\\_,_/\__/\_,_/_//_/\_,_/							 

		projectdiscovery.io

[INF] Current katana version v1.1.0 (latest)
[INF] Started standard crawling for =&amp;gt; https://allbirds.com
https://allbirds.com
https://www.allbirds.com/js.iterable.com/analytics.js
https://www.allbirds.com/cdn/shop/t/2473/assets/homepage.a1ce3f2b8cdbde6e27e6.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/44.1560197b842f08b3fe77.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/34.390ec96b64c918e7a51f.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/33.f428389d95947b0ea31f.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/32.c205e74564192b41988c.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/35.aec7b6810f22105211fb.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/43.8f8d6489f56adf20dcff.chunk.js
https://www.allbirds.com/cdn/shopifycloud/shopify/assets/shop_events_listener-61fa9e0a912c675e178777d2b27f6cbd482f8912a6b0aa31fa3515985a8cd626.js
https://www.allbirds.com/cdn/shop/t/2473/assets/29.81f74e37a33dc7fbe9dd.chunk.js
https://www.allbirds.com/cdn/shop/t/2473/assets/42.b0afa4f09987aaabeeaf.chunk.js
https://www.allbirds.com/filter.json
https://www.allbirds.com/reviews.json
https://www.allbirds.com/cdn/s/trekkie.storefront.d0db9c6b604f2af4af0875dc118feaf816931b65.min.js
https://www.allbirds.com/graphql.json
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To take it a bit further and potentially find more APIs one may add
&lt;code&gt;-jsluice&lt;/code&gt; argument that enables JS parsing with
&lt;a href=&#34;https://github.com/BishopFox/jsluice&#34;&gt;jsluice&lt;/a&gt; library (at the cost of
increased RAM consumption).&lt;/p&gt;
&lt;p&gt;This may be useful when exploring a website for API scraping or security
assessment purposes.&lt;/p&gt;
&lt;p&gt;By default Katana prints results to standard output. You can use &lt;code&gt;-silent&lt;/code&gt; option
to disable the ASCII art logo or &lt;code&gt;-o&lt;/code&gt; to provide a file the output is to be
written. Furthermore, &lt;code&gt;-jsonl&lt;/code&gt; option formats each line into JSONL format
that can be further processed programmatically.&lt;/p&gt;
&lt;p&gt;When used from Go code, Katana is used like framework, not a library - you
have to configure it to run the crawl for you and it will keep calling
the callback function you provide on each page it goes through. See the
following snippet that was made by extending the sample code Katana project
provides:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bytes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;math&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;strconv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/goccy/go-graphviz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/goccy/go-graphviz/cgraph&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/projectdiscovery/gologger&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/projectdiscovery/katana/pkg/engine/standard&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/projectdiscovery/katana/pkg/output&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/projectdiscovery/katana/pkg/types&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;graphviz&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Graph&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;nodes&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cgraph&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Node&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Options&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;MaxDepth&lt;/span&gt;:     &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;,               &lt;span style=&#34;color:#75715e&#34;&gt;// Maximum depth to crawl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;FieldScope&lt;/span&gt;:   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rdn&amp;#34;&lt;/span&gt;,           &lt;span style=&#34;color:#75715e&#34;&gt;// Crawling Scope Field&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;BodyReadSize&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;math&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MaxInt&lt;/span&gt;,     &lt;span style=&#34;color:#75715e&#34;&gt;// Maximum response size to read&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Timeout&lt;/span&gt;:      &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;,              &lt;span style=&#34;color:#75715e&#34;&gt;// Timeout is the time to wait for request in seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Concurrency&lt;/span&gt;:  &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;,              &lt;span style=&#34;color:#75715e&#34;&gt;// Concurrency is the number of concurrent crawling goroutines&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Parallelism&lt;/span&gt;:  &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;,              &lt;span style=&#34;color:#75715e&#34;&gt;// Parallelism is the number of urls processing goroutines&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Delay&lt;/span&gt;:        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,               &lt;span style=&#34;color:#75715e&#34;&gt;// Delay is the delay between each crawl requests in seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;RateLimit&lt;/span&gt;:    &lt;span style=&#34;color:#ae81ff&#34;&gt;150&lt;/span&gt;,             &lt;span style=&#34;color:#75715e&#34;&gt;// Maximum requests to send per second&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Strategy&lt;/span&gt;:     &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;breadth-first&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// Visit strategy (depth-first, breadth-first)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;OnResult&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Result&lt;/span&gt;) { &lt;span style=&#34;color:#75715e&#34;&gt;// Callback function to execute for result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;referer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;referer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;gologger&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;referer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; -&amp;gt; &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;gologger&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CreateNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;nodes&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;] = &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;prevNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;nodes&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;referer&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prevNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CreateEdge&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;prevNode&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetLabel&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Itoa&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;StatusCode&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;crawlerOptions&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewCrawlerOptions&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;gologger&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;crawlerOptions&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;crawler&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;standard&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;crawlerOptions&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;gologger&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Msg&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;crawler&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://public-firing-range.appspot.com/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;crawler&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Crawl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;gologger&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Warning&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;Msgf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Could not crawl %s: %s&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;buf&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bytes&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Render&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dot&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;buf&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RenderFilename&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;graph&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;graphviz&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SVG&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;katana.svg&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we use Graphviz wrapper library to keep track of what links were followed
between pages as crawling was in progress and render the resulting graph into SVG
file. The end result of this is admittedly not very interesting, but the above
code can be viewed as example of some greater points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One must first set up Katana configuration structures with many of parameters
that apply when Katana is used as CLI tool.&lt;/li&gt;
&lt;li&gt;It is possible to do custom stuff in your callback function, such as registering
the HTTP metadata and/or content somewhere for further processing.&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Lesser known parts of Python standard library</title>
      <link>https://www.trickster.dev/post/lesser-known-parts-of-python-standard-library/</link>
      <pubDate>Mon, 19 Aug 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/lesser-known-parts-of-python-standard-library/</guid>
      <description>&lt;p&gt;In this article we will explore some lesser known, but interesting and useful
corners of Python standard library.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Python dictionaries and lists are bread and butter for many applications, but
might be too simple for more advanced data organisation. To provide more
powerful containers for storing data in memory Python ships a &lt;code&gt;collection&lt;/code&gt;
module with things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deque - list-like data structure for efficient insertion and removal of items
from either end.&lt;/li&gt;
&lt;li&gt;Counter - subclass of dictionary to count stuff.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChainMap&lt;/code&gt; - meta-dictionary that provides unified key-value mapping across
multiple dictionaries without copying the data.&lt;/li&gt;
&lt;li&gt;Advanced structures for data mappings:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OrderedDict&lt;/code&gt; - dictionary that maintains order of key-value pairs (e.g. when
HTTP header value matters for dealing with certain security mechanisms).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;namedtuple&lt;/code&gt; - a factory function that creates a subclass of tuple with named
fields. Could be convenient for more OOP approach than just using &lt;code&gt;dict&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;defaultdict&lt;/code&gt; - dictionary with some or all or the values pre-filled. Could
be handy when you want to provide values for fields that may be missing
in web pages being scraped.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserDict&lt;/code&gt;, &lt;code&gt;UserList&lt;/code&gt; and &lt;code&gt;UserString&lt;/code&gt; - wrappers for &lt;code&gt;dict&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;str&lt;/code&gt;
for further subclassing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/collections.html&#34;&gt;https://docs.python.org/3/library/collections.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Python has &lt;code&gt;with&lt;/code&gt; keyword to create context manager that will auto-cleanup
things within lexical scope. For example, you don&amp;rsquo;t have to worry about closing
a file yourself if you open it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;results.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; out_f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;write(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;contextlib&lt;/code&gt; module provide helpers to work with context managers in your code.
For example, you can use &lt;code&gt;@contextmanager&lt;/code&gt; annotation to turn your own function
into one that could be used via &lt;code&gt;with&lt;/code&gt; syntax.&lt;/p&gt;
&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/contextlib.html&#34;&gt;https://docs.python.org/3/library/contextlib.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Due to technical reality of how floating point number are represented,
floating point computations can have some weird results:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17
&amp;gt;&amp;gt;&amp;gt; 1.2 + 2.2
3.4000000000000004
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is mathematically incorrect and a deal breaker for some applications, such
as those dealing with money. To address this problem, Python ships &lt;code&gt;decimal&lt;/code&gt;
module that allows us represent decimal numbers as Python objects with operator
overloading. Computations are adjusted so that we get the correct result
(within a configurable level of precision):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from decimal import *
&amp;gt;&amp;gt;&amp;gt; one_point_two = Decimal(&amp;#39;1.2&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; one_point_two
Decimal(&amp;#39;1.2&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; two_point_two = Decimal(&amp;#39;2.2&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; two_point_two
Decimal(&amp;#39;2.2&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; one_point_two + two_point_two
Decimal(&amp;#39;3.4&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/decimal.html&#34;&gt;https://docs.python.org/3/library/decimal.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Likewise, we can run into problem when dealing with fractions - one third is
not exactly equal to 0.333&amp;hellip; Python ships with &lt;code&gt;fractions&lt;/code&gt; module to represent
them as Python objects with operator overloading:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import fractions
&amp;gt;&amp;gt;&amp;gt; one_third = fractions.Fraction(numerator=1, denominator=3)
&amp;gt;&amp;gt;&amp;gt; 2 * one_third
Fraction(2, 3)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/fractions.html&#34;&gt;https://docs.python.org/3/library/fractions.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Python interpreter is implemented as &lt;a href=&#34;https://en.wikipedia.org/wiki/Stack_machine&#34;&gt;stack machine&lt;/a&gt;
internally. When you run some Python code it is compiled to Python-specific
bytecode that Python VM executes. For debugging or performance optimisation
purposes it might be desirable to disassemble Python bytecode into instruction.
The standard Python installation ships with &lt;code&gt;dis&lt;/code&gt; module that takes Python
code units (e.g. a function) and disassembles the code into a kind of
pseudo-assembler:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import dis
&amp;gt;&amp;gt;&amp;gt; def add(a, b):
...     return a+b
... 
&amp;gt;&amp;gt;&amp;gt; dis.dis(add)
  1           0 RESUME                   0

  2           2 LOAD_FAST                0 (a)
              4 LOAD_FAST                1 (b)
              6 BINARY_OP                0 (+)
             10 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/dis.html&#34;&gt;https://docs.python.org/3/library/dis.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Python &lt;code&gt;statistics&lt;/code&gt; module provides a small toolkit of statistical algorithms
for relatively simple applications where it is an overkill to use Pandas or
Numpy: standard deviation, several kinds of average of numeric data, linear
regression, correlation, normal distribution and others. For people eager to
join the AI/ML revolution the docs provide a sample code for making Naive
Bayes classifier - an algorithm that can be considered a minimum viable
example of machine learning.&lt;/p&gt;
&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/statistics.html&#34;&gt;https://docs.python.org/3/library/statistics.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You may sometimes want to open web browser from your Python code. Since Python
is portable and multiplatform language this may be bit of hassle due to
differences between platforms and setups. To make this easier Python ships a
&lt;code&gt;webbrowser&lt;/code&gt; module with simple API to make a browser show a page. For example,
we can call &lt;code&gt;open_new()&lt;/code&gt; function with URL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import webbrowser
&amp;gt;&amp;gt;&amp;gt; webbrowser.open_new(&amp;#39;https://trickster.dev&amp;#39;)
True
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you want to open a local file (e.g. HTML report with results of some
automation) you need to pass a file URL to one of the functions:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import os
&amp;gt;&amp;gt;&amp;gt; file_url = &amp;#39;file://&amp;#39; + os.path.realpath(&amp;#39;test.html&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; webbrowser.open(file_url)
True
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/webbrowser.html&#34;&gt;https://docs.python.org/3/library/webbrowser.html&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Distributing Python programs in source code form can be problematic if there&amp;rsquo;s
some non-technical users at the receiving end. Even if they have Python installed
there can be lacking dependencies, version conflicts and other runtime issues.
To make this less terrible Python ships &lt;code&gt;zipapp&lt;/code&gt; module with CLI tool and Python
API to package Python code into single file packages. This provides an interesting
midpoint between using virtual environments and solutions like PyInstaller to
compile the app into platform-specific form - code remains portable, but
Python interpreter has to be there to run it.&lt;/p&gt;
&lt;p&gt;API reference: &lt;a href=&#34;https://docs.python.org/3/library/zipapp.html&#34;&gt;https://docs.python.org/3/library/zipapp.html&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Simple ways to find exposed sensitive information</title>
      <link>https://www.trickster.dev/post/simple-ways-to-find-exposed-sensitive-information/</link>
      <pubDate>Fri, 21 Jun 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/simple-ways-to-find-exposed-sensitive-information/</guid>
      <description>&lt;p&gt;Informational advantage is a form of power. On the flipside, exposure of sensitive
information about a person or organisation can be a privacy/security problem.
Sensitive Data Exposure is a type of vulnerability where software system (or
user) makes sensitive data (API keys, user information, private documents, etc.)
available to potential adversaries. For example, web app that lets users edit
potentially confidential documents may be storing them in S3 buckets without
any proper access controls. This results in information that should not be
public being potentially available to those who know how to look for it. In this
post we will go through some basic techniques of Sensitive Data Discovery -
the activity of hunting for accidental leaks of things that are best kept hidden.&lt;/p&gt;
&lt;p&gt;One way to look for potentially sensitive information is to do search engine
dorking - launching queries specifically crafted to narrow down on specific,
potentially sensitive things.&lt;/p&gt;
&lt;p&gt;For example, the following query (Google dork) would look for PDF documents
containing the word &amp;ldquo;confidential&amp;rdquo; and hosted under specific domain:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;filetype:pdf site:hackerone.com &amp;#34;confidential&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have used &lt;a href=&#34;https://developers.google.com/search/docs/crawling-indexing/indexable-file-types#search-by-file-type&#34;&gt;&lt;code&gt;filetype:&lt;/code&gt;&lt;/a&gt;
operator to limit result to specific file extension and
&lt;a href=&#34;https://developers.google.com/search/docs/monitor-debug/search-operators/all-search-site&#34;&gt;&lt;code&gt;site:&lt;/code&gt;&lt;/a&gt;
operator to limit them to specific domain.&lt;/p&gt;
&lt;p&gt;Of course, not every document containing word &amp;ldquo;confidential&amp;rdquo; is actually
confidential, but a query like this could be a starting point to check if
nothing sensitive is being leaked.&lt;/p&gt;
&lt;p&gt;Another, more realistic example is the following Google query:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;contractors&amp;#34; filetype:xls &amp;#34;@gmail.com&amp;#34; site:gov
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Automating Google dorking can be done via many of SERP scraper APIs available,
but at the end of the day you will need to review the results manually.&lt;/p&gt;
&lt;p&gt;On the very first page of search results I was able to find actual lists of
government agency (e.g. Illinois Dept. of Transportation) contractors with email
addresses. Since pretending to be a contractor is very viable social engineering
vector that can lead to unauthorised access (physical and/or digital) to the
sensitive infrastucture this sort of information exposure can have pretty serious
security implications. Furthermore, service-based businesses should take care
not to expose their lead list spreadsheet on the public web as they can
potentially be found by some growth hacker working for the competition.&lt;/p&gt;
&lt;p&gt;Real examples of sensitive, private information ending up findable via Google
dorking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H1 report &lt;a href=&#34;https://hackerone.com/reports/644358&#34;&gt;644358&lt;/a&gt; - PDF files with
SSNs and family data related to US military personnel.&lt;/li&gt;
&lt;li&gt;H1 report &lt;a href=&#34;https://hackerone.com/reports/672629&#34;&gt;672629&lt;/a&gt; - searching for
&lt;code&gt;site:*.mil ext:ppt intext:password&lt;/code&gt; would lead to US DoD training materials
with usernames/password being available.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another way to look for more technical sensitive data exposures is to search on
Github.&lt;/p&gt;
&lt;p&gt;For example, the following query (Github dork) looks for instances of Git
credentials file being accidentally commited to public repos:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;path:**/.git-credentials
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also look for API keys to paid or private services. Github search query
can be derived from sample code related to the API. Let me give you a real
example. Google maps iOS SDK sample code repo contains the following code
in &lt;a href=&#34;https://github.com/googlemaps-samples/maps-sdk-for-ios-samples/blob/main/MapsAndPlacesDemo/MapsAndPlacesDemo/ApiKeys.swift&#34;&gt;MapsAndPlacesDemo/MapsAndPlacesDemo/ApiKeys.swift&lt;/a&gt;
file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/// API keys needed for this project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;enum&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ApiKeys&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    #error(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Register for API keys and enter them below; then, delete this line&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; mapsAPI = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; placesAPI = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now there is a chance that some people trying out the GMaps iOS SDK filled in
the API keys and commited the code to public repository. Indeed, the following
Github dork gives us what seem to be some real API keys to Places API:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;static let placesAPI&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, it must be noted that Github has &lt;a href=&#34;https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning&#34;&gt;secret scanning automation&lt;/a&gt;
running to warn users about accidentally publishing API keys for a growing list
of partner services (and revoke leaked ones), which makes this trick less
viable as time goes on.&lt;/p&gt;
&lt;p&gt;But what if we want to find API keys and other sensitive pieces of information
on websites running in production? In this case &lt;a href=&#34;https://publicwww.com/&#34;&gt;PublicWWW&lt;/a&gt; -
a code-level search engine - can be helpful. To look for some API keys hardcoded
into client-side JS snippets we can run queries like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;api_key&amp;#34; depth:all
&amp;#34;apiKey&amp;#34; depth:all
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, not everything you will find like this is going to be sensitive data.
Some API keys are not meant to be kept secret.&lt;/p&gt;
&lt;p&gt;Some real examples of API key exposures being a security problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H1 report &lt;a href=&#34;https://hackerone.com/reports/1065041&#34;&gt;1065041&lt;/a&gt; - Google geocoding
API leak.&lt;/li&gt;
&lt;li&gt;H1 report &lt;a href=&#34;https://hackerone.com/reports/1066410&#34;&gt;1066410&lt;/a&gt; - leaking API keys
in client-side JS code lead to further security problems.&lt;/li&gt;
&lt;li&gt;H1 report &lt;a href=&#34;https://hackerone.com/reports/1218754&#34;&gt;1218754&lt;/a&gt; - leaked
API key lead to exposure of internal system statistics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like with Google, we can use &lt;code&gt;site:&lt;/code&gt; operator to limit the results to given
domain. However, PublicWWW results are limited to users without paid account
and one must pay up to get the full results. Users with paid plan can also
search for stuff via API.&lt;/p&gt;
&lt;p&gt;Some sites heavily reliant on client side JavaScript may use preloaded state - a
JSON document or JavaScript object with some initial data to be shown on the
first load of webpage. It may include data beyond what is visibly seen on the
screen. I have personally found and scraped email addresses from preloaded
state that were not seen on the rendered form of HTML, but could be found
by digging through the page source a little. In some cases, web pages have more
information in them than what is seen at the very surface.&lt;/p&gt;
&lt;p&gt;AWS S3 and compatible services (e.g. Digital Ocean Spaces, MinIO, and many
others) are commonly used to store files in the cloud. For example, many web
applications use it to store user-uploaded data that they are supposed to
keep private - one user should not access other users files. However, sometimes
due to misconfiguration S3 buckets happen to be wide open to anyone who finds
them, which may lead to sensitive data discovery.&lt;/p&gt;
&lt;p&gt;Some tools to look for misconfigured S3 buckets are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/sa7mon/S3Scanner&#34;&gt;S3Scanner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://buckets.grayhatwarfare.com/&#34;&gt;Grayhat Warfare&lt;/a&gt; - search with API.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.openbuckets.io/&#34;&gt;OpenBuckets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Last, but not least way the sensitive data discovery happens I want to mention
is people simply not keeping their mouths shut. See the following conference
talks about this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://media.defcon.org/DEF%20CON%2022/DEF%20CON%2022%20video%20and%20slides/DEF%20CON%2022%20-%20Michael%20Schrenk%20-%20You%27re%20Leaking%20Trade%20Secrets%20-%20Video%20and%20Slides.mp4&#34;&gt;DEF CON 22 - Michael Schrenk - You&amp;rsquo;re Leaking Trade Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://media.defcon.org/DEF%20CON%2031/DEF%20CON%2031%20villages/DEF%20CON%2031%20-%20WoS%20-%20Death%20by%201000%20Likes%20-%20How%20Much%20Do%20You%20Really%20Leak%20in%20Social%20Media%20-%20Will%20Kay.mp4&#34;&gt;DEF CON 31 Packet Hacking Village - Death by 1000 Likes - Will Kay&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember: once you tell something to the world you no longer control that
information. &amp;ldquo;Loose lips sink ships&amp;rdquo; says the ancient wisdom.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Understanding SOCKS protocol</title>
      <link>https://www.trickster.dev/post/understanding-socks-protocol/</link>
      <pubDate>Sat, 04 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-socks-protocol/</guid>
      <description>&lt;p&gt;In computer networking a proxy server is intermediate party (software that may or
may not be running on a separate server) to transfer application-level traffic
between client and another server - a real destination of the connection.
There are two major ways this can be done: message forwarding (largely limited
to plaintext HTTP) and connection tunneling. SOCKS is a widely utilised protocol
that is based on the latter approach. Proxy servers may be useful for caching,
load balancing, network traffic inspection, hiding real traffic source from
target servers, bypassing censorship/geo-blocking and filtering the traffic.
Today we will look how SOCKS protocol works to relay the traffic between client
and target server.&lt;/p&gt;
&lt;p&gt;There are two major versions of SOCKS protocol: legacy SOCKS4A and slightly
newer, more advanced SOCKS5. Since SOCKS5 has some proper RFCs published to be
used as specifications it will be our primary focus. At macro level, primary
purpose of SOCKS5 protocol is to let user connect to a proxy server and provide
a way to &amp;ldquo;outsource&amp;rdquo; a secondary connection originating from proxy server and
terminated at the true destination the user wants to connect to. The original
idea behind this was enabling network firewalls to provide a controlled access
to servers running within private networks.&lt;/p&gt;
&lt;p&gt;There are two document from IETF that we are most interested in:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1928&#34;&gt;RFC 1928&lt;/a&gt; - SOCKS Protocol
Version 5.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1929&#34;&gt;RFC 1929&lt;/a&gt; - Username/Password
Authentication for SOCKS V5.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Unlike many network protocol specifications, these are fairly short reads and
can be consumed in a single sitting.&lt;/p&gt;
&lt;p&gt;One key thing to notice here is that SOCKS5 is a binary protocol - unlike HTTP
it does not rely on human readable strings, but uses shorter binary messages.
The tradeoff here is that it uses bandwidth more efficiently at the expense of
making troubleshooting more difficult.&lt;/p&gt;
&lt;p&gt;When client establishes a connection to SOCKS5 server it first is supposed to
send a version ID/method selection message with the following wire format:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; (1 octet) - must be 0x05.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NMETHODS&lt;/code&gt; (1 octet) - how many auth methods will the client propose.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;METHODS&lt;/code&gt; - a list of methods, represented by one octet each.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are two auth methods that are of interest for us: no auth (0x00) and
username/password auth (0x02). There&amp;rsquo;s also GSSAPI: a legacy API that is
technically required by the spec to allow for secure cryptographic handshake,
but let&amp;rsquo;s not go there now.&lt;/p&gt;
&lt;p&gt;So the following are valid examples of this message:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;05 01 00&lt;/code&gt; - client proposes doing connection without auth.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;05 01 02&lt;/code&gt; - client proposes using username/password based auth.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;05 02 00 02&lt;/code&gt; - client proposes either of the two and letting the server
choose.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice the TLV structure being used here: first we have &amp;ldquo;tag&amp;rdquo; - protocol version
number; next we have length - how many methods will be provided next and last we
have a list of methods. Structuring the wire format this way enables parsing to
be done incrementally.&lt;/p&gt;
&lt;p&gt;Now the server has to make a choice if it wants to proceed with the connection
and respond with method selection consisting of two fields, one octet each:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; - must be 0x05 again.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;METHOD&lt;/code&gt; - either one of the proposed method values or 0xFF if all are
rejected (connection is closed in that case).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If no-auth (0x00) is chosen the client is now allowed to proceed with using the
proxying features of the server. But if username/password based auth is
negotiated there is one extra thing left to do - the credential-based
subnegotiation. Again, there are two messages being exchanged between client and
server. Client sends the username/password request of the following wire format:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; - must be 0x01.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ULEN&lt;/code&gt; - username length (without trailing NUL character).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNAME&lt;/code&gt; - username as ASCII string (&lt;code&gt;ULEN&lt;/code&gt; octets).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PLEN&lt;/code&gt; - password length.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PASSWD&lt;/code&gt; - password.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that credentials are transferred in plaintext. Thus care must be taken when
considering using SOCKS in potentially hostile networks. Don&amp;rsquo;t end up on Wall
of Sheep during DEFCON!&lt;/p&gt;
&lt;p&gt;Upon receipt of this message, server checks the credentials and responds with
this kind of response (fields are 1 byte each):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; - must be 0x01 again.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STATUS&lt;/code&gt; - 0 iff auth succeeded, some other value otherwise.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Server closes the connection if client does not provide credentials that check
out.&lt;/p&gt;
&lt;p&gt;Now we are ready to the real deal - establishing a connection to the remote via
proxy server. The client sends a request of the following wire format:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; (1 octet) - protocol version (5).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMD&lt;/code&gt; (1 octet) - command to the server (0x01 if we want to proxy a TCP
connection).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RSV&lt;/code&gt; (1 octet) - reserved, must be 0x00.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ATYP&lt;/code&gt; - address type:
&lt;ul&gt;
&lt;li&gt;0x01 - IPv4&lt;/li&gt;
&lt;li&gt;0x03 - DNS hostname.&lt;/li&gt;
&lt;li&gt;0x04 - IPv6&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DST.ADDR&lt;/code&gt; - destination address (in NBO); size depends on &lt;code&gt;ATYP&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DST.PORT&lt;/code&gt; (2 octets) - destination port (in NBO).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The server will attempt to service the request. In typical use case &lt;code&gt;CMD&lt;/code&gt; will
be 0x01 and that will be a CONNECT request asking the proxy to establish TCP
connection to remote host and bridge (splice) the TCP streams.&lt;/p&gt;
&lt;p&gt;When SOCKS server succeeds (or fails) to handle the request it will give one
last response:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VER&lt;/code&gt; - 0x05.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REP&lt;/code&gt; - reply field; 0x00 on success, error code on failure.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RSV&lt;/code&gt; - reserved, must be 0x00.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ATYP&lt;/code&gt; - address type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BND.ADDR&lt;/code&gt; - address the server is bound to (e.g. exit IP of outbound
connection).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BND.PORT&lt;/code&gt; - port of outbound connection.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If CONNECT request succeeded the client can now use the resulting two-legged
connection as if it was a regular TCP connection - for all the remote server
knows it just got a fresh connection from a client and is ready to talk TLS or
some other protocol that is based on TCP transport. Furthermore, if remote
server is another SOCKS server it is possible to do a new SOCKS handshake for
the purpose of proxy chaining - extending the connection via one more proxy.&lt;/p&gt;
&lt;p&gt;Besides the outbound TCP connection support, there are couple more slightly
esoteric features SOCKS protocol has. One is BIND command (0x02) that lets the
client use SOCKS server for a secondary incoming connection. In that case
&lt;code&gt;BND.ADDR&lt;/code&gt; and &lt;code&gt;BND.PORT&lt;/code&gt; fields in the reply tell the client where in the
network the port has been opened.&lt;/p&gt;
&lt;p&gt;It is meant to be used with legacy FTP servers that work in active mode by
establishing the second connection to transfer contents (files, directory
listings) while leaving the original connection user originated available for
new commands. Since the RFC requires the initial primary connection to be
present for the lifetime of secondary connection this feature is not very useful
for anything else.&lt;/p&gt;
&lt;p&gt;Second interesting, but not very used SOCKS feature is UDP support. If a client
sends UDP ASSOCIATE command (&lt;code&gt;CMD&lt;/code&gt; = 0x03) the server may bind UDP port and
setup relaying of UDP datagrams. Since there is no such thing as connection at
UDP level and because it is desirable to retain the original port numbers in UDP
datagrams they are encapsulated in a wrapper message (defined in Section 7 of
RFC 1928) when transferred between proxy and the client.&lt;/p&gt;
&lt;p&gt;Some interesting software projects supporting (subset of) SOCKS protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.torproject.org/&#34;&gt;Tor&lt;/a&gt; - overlay network for anonymous
communications.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.openssh.com/&#34;&gt;OpenSSH&lt;/a&gt; - SOCKS can be used over SSH tunnel. See
my previous post on SSH tips and tricks.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://curl.se/&#34;&gt;Curl&lt;/a&gt; - see &lt;code&gt;--socks5&lt;/code&gt;, &lt;code&gt;--proxy&lt;/code&gt;, &lt;code&gt;--socks5-basic&lt;/code&gt; CLI
options.&lt;/li&gt;
&lt;li&gt;Python requests module if &lt;a href=&#34;https://github.com/Anorov/PySocks&#34;&gt;pysocks&lt;/a&gt; is
installed.&lt;/li&gt;
&lt;li&gt;Mitmproxy can be used in SOCKS5 mode (e.g. &lt;code&gt;mitmdump --mode socks5&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://prospeo.io/api/linkedin-email-finder&#34;&gt;Proxychains-ng&lt;/a&gt; is Unix/Linux
tool to tunnel connections through one or more proxies.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Scraping Clutch for B2B company data</title>
      <link>https://www.trickster.dev/post/scraping-clutch-for-b2b-company-data/</link>
      <pubDate>Fri, 12 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-clutch-for-b2b-company-data/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://clutch.co/&#34;&gt;Clutch.co&lt;/a&gt; is a web portal serving as B2B service company
directory. One may want to scrape it to make a list of companies within a
service niche to target them with some form of outreach. Company name, website
URL, phone number, hourly rate, project size, social media URLs would
be fields of the dataset we would create by scraping this site. But the thing is,
Clutch is fighting scraping attempts by using Cloudflare antibot service -
naively fetching a page from this site gives us a 403 response with JS challenge.
So what do we do?&lt;/p&gt;
&lt;p&gt;Some would suggest using anti-detect browser as foundation for the scraper, but
let us not rush to that. Instead, let&amp;rsquo;s work out what makes or breaks a request
here. Open the site in Chrome, accept the cookies and let it pass the JS challenge.
Now, by using &amp;ldquo;Copy as cURL&amp;rdquo; feature in Chrome DevTools we can get something like
the following curl(1) command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://clutch.co/profile/testmatick&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority: clutch.co&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: CookieConsent={stamp:%27gy8cYhc5rqyD+4wboFTjYwRZ3kmocvJU5dWpISe25EAYM1scKDNvWg==%27%2Cnecessary:true%2Cpreferences:false%2Cstatistics:false%2Cmarketing:false%2Cmethod:%27explicit%27%2Cver:1%2Cutc:1695021963851%2Cregion:%27lt%27}; exp_new-ab-test_attribute=1; exp_primary-cta-ab-test_attribute=old-main-CTA; __cf_bm=1Fxjs7RHjDX7mJRgzYLLodoV34MLcm0wFywRiYuqI0A-1708179657-1.0-ARy825yNViMh5Y/1qk+ul7/3KcNwb3kyyLNo+euY/dSC3f0123QWFZicuEcWZHDvEI0PFFzGb2A3IH++XuIto7c=; cf_clearance=2vwV27CEWNrT84e_nLo96kgOoihjSA2iBGBHBJvehAs-1708179658-1.0-AUS2KK3KRx6wX63hAVmC5YVDwczAz6d90pitbdBM6BIixlgYM/x1QaBMkzbT34dS2Trav72zccQ/zlvmoyicerE=&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua: &amp;#34;Not A(Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;121&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;121&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile: ?0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform: &amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest: document&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode: navigate&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site: none&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-user: ?1&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;upgrade-insecure-requests: 1&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --compressed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this command in the shell does indeed give us a proper page, so it
seems neither TLS nor HTTP/2 fingerprinting is used. We see some browser-specific
headers here and we see some cookies being used (at least not to block cURL).
To narrow down what exactly is needed, we can remove some headers and relaunch
the request - we discover that only cookies and &lt;code&gt;user-agent&lt;/code&gt; header is necessary.
Now we can remove cookies one by one and repeat the same kind of experiment.
We find that both of &lt;code&gt;exp_&lt;/code&gt; cookies can be removed without triggering the antibot.&lt;/p&gt;
&lt;p&gt;So the cookies that are necessary seem to be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;CookieConsent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__cf_bm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cf_clearance&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The latter two are directly related to Cloudflare&amp;rsquo;s antibot products and are
of prime importance to our objective.&lt;/p&gt;
&lt;p&gt;We need to get these cookies somehow. Let us try using Bright Data&amp;rsquo;s
&lt;a href=&#34;https://help.brightdata.com/hc/en-us/sections/13350440041873-Scraping-Browser&#34;&gt;Scraping Browser&lt;/a&gt; -
a programmatically controllable web browser hosted for us in blocking-resistant
setup. Despite being hosted in the cloud it can be integrated with the usual
suspects of browser automation - Selenium, Playwright, Puppeteer. But we don&amp;rsquo;t
need to implement the entire scraping flow with browser automation - we merely
have to use it just enough to get the CF cookies.&lt;/p&gt;
&lt;p&gt;Assuming we have Scraping Browser configured on Bright Data customer UI let us
rework the sample code from
&lt;a href=&#34;https://docs.brightdata.com/scraping-automation/scraping-browser/configuration&#34;&gt;documentation page&lt;/a&gt;
to suit our objective:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; asyncio
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.async_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; async_playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# $ pip3 install playwright&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AUTH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SBR_WS_CDP &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wss://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;AUTH&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@brd.superproxy.io:9222&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(pw):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connecting to Scraping Browser...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; pw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect_over_cdp(SBR_WS_CDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connected! Navigating...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://clutch.co/profile/devfortress&amp;#34;&lt;/span&gt;, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pprint(cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; async_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; playwright:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; run(playwright)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    asyncio&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run(main())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can easily convert this snippet to use synchronous Playwright API:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.sync_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sync_playwright, Playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AUTH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SBR_WS_CDP &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wss://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;AUTH&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@brd.superproxy.io:9222&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(pw: Playwright):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect_over_cdp(SBR_WS_CDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://clutch.co&amp;#34;&lt;/span&gt;, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pprint(cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; sync_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; playwright:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        run(playwright)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once we got the cookies, we can proceed with scraping the data. There are three
levels to the scraping process we are going to implement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grabbing a list of business categories.&lt;/li&gt;
&lt;li&gt;Traversing this list will give us bunch of links for company pages (each
category URL points to a paged list of companies within a category - we have
to account for some overlap between categories).&lt;/li&gt;
&lt;li&gt;Scraping each company page will give us an item - a unit of data that is
to be saved into CSV file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since this involves cookies that we have to reuse between requests we want to
set up a requests session to use for scraping and recreate it when needed.
However there is a little complication - it turned out that TLS fingerprint is
actually being checked, thus we need to use TLS client library to reproduce a
fingerprint that looks realistic - like the one coming from real browser, not
from Python requests library (we got lucky with curl earlier). We thus use
Playwright together with open source TLS client (&lt;code&gt;tls-client&lt;/code&gt; from PIP) to
prepare an equivalent of &lt;code&gt;requests.Session&lt;/code&gt; object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; tls_client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.sync_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sync_playwright, Playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PROXY_URL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AUTH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SBR_WS_CDP &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wss://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;AUTH&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@brd.superproxy.io:9222&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_cookies&lt;/span&gt;(page_url):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; sync_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pw:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect_over_cdp(SBR_WS_CDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(page_url, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Got cookies:&amp;#34;&lt;/span&gt;, cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; cookies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;(page_url):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tls_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client_identifier&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chrome120&amp;#34;&lt;/span&gt;, random_tls_extension_order&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Google Chrome&amp;#34;;v=&amp;#34;123&amp;#34;, &amp;#34;Not:A-Brand&amp;#34;;v=&amp;#34;8&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;123&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;document&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;navigate&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upgrade-insecure-requests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.234 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_cookies(page_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; cookie &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; cookies:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            cookie&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;), cookie&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;), domain&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;cookie&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;proxies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: PROXY_URL, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;: PROXY_URL}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When creating session we also set browser-like HTTP headers and integrate a proxy
service - in this case DC proxies are sufficient as long as we have the proper
cookies. Nobody seems to care that CF challenge was solved from different IP than
the requests we generate later (up to a point - sometimes we need to redo the
session creation step to proceed with scraping).&lt;/p&gt;
&lt;p&gt;I elected to break down the scraping process into two scripts. The first one
sources list of companies by first scraping &lt;a href=&#34;https://clutch.co/sitemap&#34;&gt;Sitemap page&lt;/a&gt;,
then traversing the company lists to make a deduplicated list of profile URLs.
Not much to see there if you have read more of the posts on this blog. The second
script goes through profile URLs and scrapes some information from profile pages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; concurrent.futures &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ThreadPoolExecutor
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; unquote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; threading
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; tls_session &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; create_session, safe_get
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sessions &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prepare_thread_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sessions[threading&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;current_thread()] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://clutch.co&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_company_page&lt;/span&gt;(company_url):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sessions[threading&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;current_thread()]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; safe_get(session, company_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;403&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Retrying: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(company_url))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prepare_thread_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sessions[threading&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;current_thread()]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; safe_get(session, company_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    company_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//h1/a/text()&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(company_name) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; company_name[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;company_name&amp;#34;&lt;/span&gt;: company_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;company_name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    company_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lists.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_reader &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictReader(in_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; csv_reader:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_urls&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;company_url&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pages.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w+&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; ThreadPoolExecutor(&lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;, initializer&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;prepare_thread_session) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; executor:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; executor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;map(scrape_company_page, company_urls):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some things to note here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ThreadPoolExecutor&lt;/code&gt; object from &lt;code&gt;concurrent.futures&lt;/code&gt; was used to create and
run a thread pool to make the scraping concurrent.&lt;/li&gt;
&lt;li&gt;For each thread we need a session that we initially create in thread initializer
function and also recreate on as-needed basis. The &lt;code&gt;sessions&lt;/code&gt; dictionary is keyed
by the thread which allows each thread to unambiguously access the session it
created.&lt;/li&gt;
&lt;li&gt;Sometimes HTTPS requests fail with some exception (e.g. connection times out).
To prevent this from crashing the entire script we use &lt;code&gt;safe_get()&lt;/code&gt; helper
function.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using rather simple code I was able to scrape over 307 000 rows of company
data with pretty trivial opex and not much development effort. The scraping
can be done overnight in a small VPS despite the first script being
single-threaded and the second one using only 16 threads.&lt;/p&gt;
&lt;p&gt;Scraped data can be further enriched via automated Google dorking or enrichment
APIs to build contact lists for lead generation. One can also do some some
additional scraping to extract company emails from their websites.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SSH tips and tricks</title>
      <link>https://www.trickster.dev/post/ssh-tips-and-tricks/</link>
      <pubDate>Sun, 04 Feb 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/ssh-tips-and-tricks/</guid>
      <description>&lt;p&gt;SSH is well established protocol for securely accessing remote systems over the
network for administration and devops purposes. A widely deployed OpenSSH
software suite implements this protocol. But there is more to the SSH technology
than reaching a remote shell over ssh(1) or copying files via scp(1)/sftp(1).
We will go through some lesser known, somewhat advanced tricks and use cases
that could be valuable in your daily work.&lt;/p&gt;
&lt;h2 id=&#34;ssh-escape-sequences&#34;&gt;SSH escape sequences&lt;/h2&gt;
&lt;p&gt;Most people don&amp;rsquo;t know that ssh(1) provides somewhat secret control sequences
that can be used to manage in-progress connections. Make sure the last keystroke
you have typed is Enter, then type the tilde character and question mark (&lt;code&gt;~?&lt;/code&gt;).
You will be given the following message listing other sequences you can do in a
similar way:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&amp;amp;   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;~C&lt;/code&gt; gives you a secondary command prompt that can be used to manage port
forwarding/tunneling (more on this later).&lt;/p&gt;
&lt;h2 id=&#34;single-command-through-ssh&#34;&gt;Single command through SSH&lt;/h2&gt;
&lt;p&gt;Sometimes you want to run just a single OS shell command via SSH connection
and get the output. That can be done with a standard SSH client by launching it
with &lt;code&gt;-t&lt;/code&gt; and passing your command as last argument:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh -t root@167.172.24.25 &amp;#34;date -R&amp;#34;    
Sun, 14 Jan 2024 16:52:37 +0000
Connection to 167.172.24.25 closed.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;2fa-on-ssh-login&#34;&gt;2FA on SSH login&lt;/h2&gt;
&lt;p&gt;Generally speaking, it is a recommended practice to use SSH keys for
authentication and to avoid relying on passwords. Passwordless SSH effectively
prevents any successful brute force attack that could be done over the network.
However, if for some reason you still want to use SSH interface with
password you can make it more secure by adding Two Factor Authentication step.
We will go through the steps that can be taken to set it up on Debian 12 system.&lt;/p&gt;
&lt;p&gt;Make sure your APT cache is up to date and install &lt;code&gt;libpam-google-authenticator&lt;/code&gt;
package through APT:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# apt-get install libpam-google-authenticator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to edit /etc/pam.d/sshd file to add the following line there:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auth required pam_google_authenticator.so
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also edit /etc/ssh/sshd_config to make sure there&amp;rsquo;s following lines there
(watch out for conflicting directives - you may need to change &lt;code&gt;no&lt;/code&gt; to &lt;code&gt;yes&lt;/code&gt; on
some lines):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;UsePAM yes
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes
AuthenticationMethods keyboard-interactive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Restart the sshd daemon to force it to read the updated config file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# systemctl restart sshd.service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In your mobile device install Google Authenticator or a compatible app (e.g.
&lt;a href=&#34;https://freeotp.github.io/&#34;&gt;FreeOTP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re ready to run the &lt;code&gt;google-authenticator&lt;/code&gt; tool, answer some questions and
follow the instructions:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# google-authenticator 

Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
  https://www.google.com/chart?chs=200x200&amp;amp;chld=M|0&amp;amp;cht=qr&amp;amp;chl=otpauth://totp/root@debian-s-1vcpu-512mb-10gb-ams3-01%3Fsecret%3D[REDACTED]%26issuer%3Ddebian-s-1vcpu-512mb-10gb-ams3-01
                                                                                                          
[REDACTED]

Your new secret key is: [REDACTED]
Enter code from app (-1 to skip): 371342
Code confirmed
Your emergency scratch codes are:
  [REDACTED]

Do you want me to update your &amp;#34;/root/.google_authenticator&amp;#34; file? (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) y

If the computer that you are logging into isn&amp;#39;t hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We were given the option to either scan the QR code rendered on the terminal in
colored blocks or to have it displayed in the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2024-01-17_17.34.40.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once we do all of this, we will be asked both one time key and the password
when logging in via SSH:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh root@178.62.232.185
(root@178.62.232.185) Verification code: 
(root@178.62.232.185) Password: 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;ssh-tunneling-with--l&#34;&gt;SSH tunneling with &lt;code&gt;-L&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you have some networked software running on remote system with listener
socket being bound to localhost or otherwise restricted network interface.&lt;/p&gt;
&lt;p&gt;One example is the situation in
&lt;a href=&#34;https://marketplace.digitalocean.com/apps/jupyter-notebook&#34;&gt;Jupyter notebook VPS&lt;/a&gt;
on Digital Ocean. Once you instantiate the VPS image you can run notebook.sh
script in the home directory of &lt;code&gt;ubuntu&lt;/code&gt; user to start the Jupyter lab webapp
which will bind to localhost for security purposes. However, we are instructed
to use &lt;code&gt;ssh -L&lt;/code&gt; to access it through SSH tunnel:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ bash notebook.sh start
JupyterLab started.
See the logs under ~/notebook.log.

Use &amp;#39;notebook list&amp;#39; to view the URL.

Remember that you need to create a tunnel to access the notebook from your localhost.
On another terminal, use ssh root@&amp;lt;ip&amp;gt; -L 8888:localhost:8888 to create the tunnel. Then you can access the notebook from your local browser.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running the provided command on client-side shell gives another SSH session
with the added benefit of Jupyter web app being available locally at port 8888.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2024-01-17_18.40.09.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As another example, a Linux server running LAMP stack may allow HTTP and SSH
access on public network, but make MySQL listen for incoming connections on
127.0.0.1. That poses an inconvenience if we want to connect to the MySQL server
over the network. In cases like this, SSH tunneling - i.e. wrapping non-SSH
traffic in SSH connection can be quite useful.&lt;/p&gt;
&lt;h2 id=&#34;ssh-connection-as-local-socks-proxy&#34;&gt;SSH connection as local SOCKS proxy&lt;/h2&gt;
&lt;p&gt;SSH tunneling enables us to access a single network daemon that otherwise
would not be reached. But what if we want to reach everything that can be
reached from the server? In that case we can set up SSH connection as local
SOCKS proxy with command like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh -D 1080 root@64.23.163.194  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here 1080 is the TCP port on local network interface we want to open as SOCKS
proxy.&lt;/p&gt;
&lt;p&gt;By using curl, we can verify that it works as intended:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -s -x socks5://localhost:1080 https://lumtest.com/myip.json | jq
{
  &amp;#34;ip&amp;#34;: &amp;#34;64.23.163.194&amp;#34;,
  &amp;#34;country&amp;#34;: &amp;#34;US&amp;#34;,
  &amp;#34;asn&amp;#34;: {
    &amp;#34;asnum&amp;#34;: 14061,
    &amp;#34;org_name&amp;#34;: &amp;#34;DIGITALOCEAN-ASN&amp;#34;
  },
  &amp;#34;geo&amp;#34;: {
    &amp;#34;city&amp;#34;: &amp;#34;Santa Clara&amp;#34;,
    &amp;#34;region&amp;#34;: &amp;#34;CA&amp;#34;,
    &amp;#34;region_name&amp;#34;: &amp;#34;California&amp;#34;,
    &amp;#34;postal_code&amp;#34;: &amp;#34;95054&amp;#34;,
    &amp;#34;latitude&amp;#34;: 37.3931,
    &amp;#34;longitude&amp;#34;: -121.962,
    &amp;#34;tz&amp;#34;: &amp;#34;America/Los_Angeles&amp;#34;,
    &amp;#34;lum_city&amp;#34;: &amp;#34;santaclara&amp;#34;,
    &amp;#34;lum_region&amp;#34;: &amp;#34;ca&amp;#34;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can also done via the &amp;ldquo;secret&amp;rdquo; SSH command line (&lt;code&gt;~C&lt;/code&gt;) during an in-progress
SSH session:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ssh&amp;gt; -D 1080
Forwarding port.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;ssh-server-behind-nat-via-tor-onion-service&#34;&gt;SSH server behind NAT via Tor Onion Service&lt;/h2&gt;
&lt;p&gt;Tor is a well-known distributed system for anonymous communications that allows
for a convenient secondary use case: NAT traversal via Onion Services. Typically
this is done to expose a website or web app to be reachable via Tor Browser,
but since Tor works at TCP connection level nothing is stopping us from setting
up Onion Service for some TCP-based application other than HTTP - i.e. we
can also use it with SSH.&lt;/p&gt;
&lt;p&gt;For example, on Debian 12 server we could do as follows.&lt;/p&gt;
&lt;p&gt;We start by installing Tor:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# apt-get update
# apt-get install -y tor
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us create a directory somewhere in file system to store the cryptographic
keys and metadata:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# mkdir /var/onionservice
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Limit the access to this directory so that only owner can fully access it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# chmod 700 /var/onionservice/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Edit your torrc file (on Debian it&amp;rsquo;s available at /etc/tor/torrc) to add the
following directives:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;HiddenServiceDir /var/onionservice
HiddenServicePort 22 127.0.0.1:22
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Restart or launch the tor daemon (e.g &lt;code&gt;systemctl restart tor&lt;/code&gt;). If all is good
the Onion Service directory will have some files, including the one named
hostname:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# cat /var/onionservice/hostname 
iyqklkzlvieyehybr44yqyfqavcvhepdltumngxfje7x36ewx2li3hyd.onion
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to tunnel the SSH connection over Tor at client side. This can be
done via program named &lt;a href=&#34;https://github.com/dgoulet/torsocks&#34;&gt;torsocks&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ torsocks ssh root@iyqklkzlvieyehybr44yqyfqavcvhepdltumngxfje7x36ewx2li3hyd.onion
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The caveat of this approach it that the extra latency Tor introduces can make
the shell work very laggy and slow. This can be particulary bad for touch typers
who rely on visual feedback to fix typos as they happen. A possible improvement
is making your Onion Service use only one hop into Tor network by adding the
following directive into torrc:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;HiddenServiceNonAnonymousMode 1
HiddenServiceSingleHopMode 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However it must be noted that this essentially removes the server hiding aspect
(which is fine if NAT traversal is all you need). This is also incompatible with
SOCKS interface being open on Tor instance at your server.&lt;/p&gt;
&lt;p&gt;Torsocks is available via package managers on some Linux/Unix systems (e.g.
Debian Linux, macOS).&lt;/p&gt;
&lt;h2 id=&#34;mounting-directory-subtree-via-sshfs&#34;&gt;Mounting directory subtree via sshfs&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/libfuse/sshfs&#34;&gt;SSHFS&lt;/a&gt; is a program that mounts a remote
file system subtree to local one over SFTP connection. It is part of FUSE
project that ships a framework for developing custom file systems.&lt;/p&gt;
&lt;p&gt;The following is assuming you are using Debian 12 on client side of the connection.&lt;/p&gt;
&lt;p&gt;Install sshfs via APT:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# apt-get update
# apt-get install -y sshfs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On client side create the new directory to be used via sshfs:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mkdir /mnt/droplet
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To mount a remote FS subtree via sshfs now we can run a command like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sshfs root@209.97.131.201:/etc /mnt/droplet
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When we no longer need access to remove directory we can use umount(1) to unmount
the directory we have created:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ umount /mnt/droplet 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;SSHFS is mostly supported on Linux. For other platforms there are some alternative
solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://osxfuse.github.io/&#34;&gt;macFUSE&lt;/a&gt; project ships it&amp;rsquo;s own version of
sshfs for macOS.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/winfsp/sshfs-win&#34;&gt;sshfs-win&lt;/a&gt; is somewhat limited port of
sshfs for Windows that relies on &lt;a href=&#34;https://github.com/winfsp/winfsp&#34;&gt;WinFsp&lt;/a&gt; -
an equivalent of FUSE for Windows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your mileage may vary.&lt;/p&gt;
&lt;h2 id=&#34;using-ssh-programmatically-via-paramikofabric&#34;&gt;Using SSH programmatically via Paramiko/Fabric&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.paramiko.org/&#34;&gt;Paramiko&lt;/a&gt; is a Python module that implements
SSH protocol. It provides a somewhat low level API for automating SSH tasks.
For example, connecting to remote SSH server and running a command can be done
in few lines of code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; paramiko
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;hostname&amp;gt; &amp;lt;username&amp;gt; &amp;lt;password&amp;gt; &amp;lt;command&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hostname &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    password &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    command &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; paramiko&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;SSHClient()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_missing_host_key_policy(paramiko&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AutoAddPolicy())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect(hostname, username&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;username, password&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;password)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _, stdout, _ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exec_command(command)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    output &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stdout&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;decode(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(output)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.fabfile.org/&#34;&gt;Fabric&lt;/a&gt; is Python module that wraps Paramiko to
provide a more abstract, higher level API.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Retrospective scraping of historical data from Internet Archive snapshots</title>
      <link>https://www.trickster.dev/post/retrospective-scraping-of-historical-data-from-internet-archive-snapshots/</link>
      <pubDate>Wed, 27 Dec 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/retrospective-scraping-of-historical-data-from-internet-archive-snapshots/</guid>
      <description>&lt;p&gt;Some internet research activities are based not only on the present data, but also
on historical data that was, but no longer is posted online. As of late 2023,
Internet Archive has over 842 billion web page snapshots stored and available
for retrieval.  We will go through a simple example of how scraping pre-crawled
pages from Wayback Machine can be used to gather historical data for data science
purposes. To look into Iowa inmate population trends, we will be scraping historical
snapshots of Iowa Department of Corrections &lt;a href=&#34;https://doc-search.iowa.gov/dailystatistics&#34;&gt;inmate statistics page&lt;/a&gt;.
Data of interest is in &amp;ldquo;Current Count&amp;rdquo; and &amp;ldquo;Institution&amp;rdquo; columns of the table.
We will do data scraping and wrangling in Python with Pandas dataframe library.&lt;/p&gt;
&lt;p&gt;To quickly view the available snapshots for this page we search it through
&lt;a href=&#34;https://archive.org&#34;&gt;Archive.org front page&lt;/a&gt;. But there seems to be a little
complication. Only two snapshots are available, which would not be sufficient
for our purposes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-12-26_14.35.17.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thus we must backtrack a little. Searching for the domain - &lt;code&gt;doc-search.iowa.gov&lt;/code&gt; -
on Wayback Machine gives us only 5 snapshots. It seems this domain is pointing
to fairly new (sub)site that is primarily meant for looking up prisoners in
Iowa and only secondarily to provide overall inmate statistics. The primary
domain for Iowa Dept. of Corrections is doc.iowa.gov. Exploring snapshots
for this site reveals that until some time in 2023 it had a statistics page
at following URL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://doc.iowa.gov/daily-statistics&#34;&gt;https://doc.iowa.gov/daily-statistics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-12-26_14.39.23.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-12-26_14.42.49.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We are after data on all available snapshots of this page.&lt;/p&gt;
&lt;p&gt;To check for snapshot availability we can call the
&lt;a href=&#34;https://archive.org/developers/_static/test-wayback.html&#34;&gt;&lt;code&gt;/wayback/available&lt;/code&gt;&lt;/a&gt;
API endpoint with the URL of interest:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;https://archive.org/wayback/available?url=https://doc.iowa.gov/daily-statistics&amp;#34; | jq
{
  &amp;#34;url&amp;#34;: &amp;#34;https://doc.iowa.gov/daily-statistics&amp;#34;,
  &amp;#34;archived_snapshots&amp;#34;: {
    &amp;#34;closest&amp;#34;: {
      &amp;#34;status&amp;#34;: &amp;#34;200&amp;#34;,
      &amp;#34;available&amp;#34;: true,
      &amp;#34;url&amp;#34;: &amp;#34;http://web.archive.org/web/20230429064712/https://doc.iowa.gov/daily-statistics&amp;#34;,
      &amp;#34;timestamp&amp;#34;: &amp;#34;20230429064712&amp;#34;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;How do we get the list of snapshots? Internet Archive
&lt;a href=&#34;https://archive.org/developers/wayback-cdx-server.html&#34;&gt;CDX API&lt;/a&gt; is a flexible
way to search for snapshots by URL and secondary criteria: HTTP status code,
timeframe and MIME type of the content. By default this API provides output
in CDX table format that looks as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;http://web.archive.org/cdx/search/cdx?url=doc.iowa.gov&amp;amp;limit=5&amp;#34;
gov,iowa,doc)/ 20170323172420 https://doc.iowa.gov/ text/html 200 L4NPH6MSVTZBRCD6CUTTQ2Y66OKY5Y3S 6904
gov,iowa,doc)/ 20170323172435 https://doc.iowa.gov/ text/html 200 BPFQVM2VIW32BNVM7LJ23QTXN7TDTPIL 6906
gov,iowa,doc)/ 20170323172720 https://doc.iowa.gov/ text/html 200 76LUVXUPNL3NPGU246YM3YQ6VNOOVIVH 6907
gov,iowa,doc)/ 20170416221430 https://doc.iowa.gov/ text/html 200 6VMMWJRPN33DAGREX54WSLFG367NCCEF 7113
gov,iowa,doc)/ 20170417071150 https://doc.iowa.gov/ text/html 200 OZMIM56F6OM5IL3HXTWBJZQCMGVRBYTB 7119
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can get a JSONified version of this table by passing &lt;code&gt;output=json&lt;/code&gt; URL parameter:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;http://web.archive.org/cdx/search/cdx?url=doc.iowa.gov&amp;amp;limit=5&amp;amp;output=json&amp;#34;
[[&amp;#34;urlkey&amp;#34;,&amp;#34;timestamp&amp;#34;,&amp;#34;original&amp;#34;,&amp;#34;mimetype&amp;#34;,&amp;#34;statuscode&amp;#34;,&amp;#34;digest&amp;#34;,&amp;#34;length&amp;#34;],
[&amp;#34;gov,iowa,doc)/&amp;#34;, &amp;#34;20170323172420&amp;#34;, &amp;#34;https://doc.iowa.gov/&amp;#34;, &amp;#34;text/html&amp;#34;, &amp;#34;200&amp;#34;, &amp;#34;L4NPH6MSVTZBRCD6CUTTQ2Y66OKY5Y3S&amp;#34;, &amp;#34;6904&amp;#34;],
[&amp;#34;gov,iowa,doc)/&amp;#34;, &amp;#34;20170323172435&amp;#34;, &amp;#34;https://doc.iowa.gov/&amp;#34;, &amp;#34;text/html&amp;#34;, &amp;#34;200&amp;#34;, &amp;#34;BPFQVM2VIW32BNVM7LJ23QTXN7TDTPIL&amp;#34;, &amp;#34;6906&amp;#34;],
[&amp;#34;gov,iowa,doc)/&amp;#34;, &amp;#34;20170323172720&amp;#34;, &amp;#34;https://doc.iowa.gov/&amp;#34;, &amp;#34;text/html&amp;#34;, &amp;#34;200&amp;#34;, &amp;#34;76LUVXUPNL3NPGU246YM3YQ6VNOOVIVH&amp;#34;, &amp;#34;6907&amp;#34;],
[&amp;#34;gov,iowa,doc)/&amp;#34;, &amp;#34;20170416221430&amp;#34;, &amp;#34;https://doc.iowa.gov/&amp;#34;, &amp;#34;text/html&amp;#34;, &amp;#34;200&amp;#34;, &amp;#34;6VMMWJRPN33DAGREX54WSLFG367NCCEF&amp;#34;, &amp;#34;7113&amp;#34;],
[&amp;#34;gov,iowa,doc)/&amp;#34;, &amp;#34;20170417071150&amp;#34;, &amp;#34;https://doc.iowa.gov/&amp;#34;, &amp;#34;text/html&amp;#34;, &amp;#34;200&amp;#34;, &amp;#34;OZMIM56F6OM5IL3HXTWBJZQCMGVRBYTB&amp;#34;, &amp;#34;7119&amp;#34;]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is still not quite the shape of data that most modern JSON APIs provide,
but we can work with that by using &lt;a href=&#34;https://pandas.pydata.org/docs/reference/api/pandas.read_json.html&#34;&gt;&lt;code&gt;read_json()&lt;/code&gt; function&lt;/a&gt;
from Pandas:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.11.6 (main, Oct  2 2023, 20:46:14) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import pandas as pd
&amp;gt;&amp;gt;&amp;gt; df = pd.read_json(&amp;#34;http://web.archive.org/cdx/search/cdx?url=doc.iowa.gov&amp;amp;limit=5&amp;amp;output=json&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; df
                0               1                      2          3           4                                 5       6
0          urlkey       timestamp               original   mimetype  statuscode                            digest  length
1  gov,iowa,doc)/  20170323172420  https://doc.iowa.gov/  text/html         200  L4NPH6MSVTZBRCD6CUTTQ2Y66OKY5Y3S    6904
2  gov,iowa,doc)/  20170323172435  https://doc.iowa.gov/  text/html         200  BPFQVM2VIW32BNVM7LJ23QTXN7TDTPIL    6906
3  gov,iowa,doc)/  20170323172720  https://doc.iowa.gov/  text/html         200  76LUVXUPNL3NPGU246YM3YQ6VNOOVIVH    6907
4  gov,iowa,doc)/  20170416221430  https://doc.iowa.gov/  text/html         200  6VMMWJRPN33DAGREX54WSLFG367NCCEF    7113
5  gov,iowa,doc)/  20170417071150  https://doc.iowa.gov/  text/html         200  OZMIM56F6OM5IL3HXTWBJZQCMGVRBYTB    7119
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It misread column labels in the first line, but we can trivially fix it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; headers = df.iloc[0]
&amp;gt;&amp;gt;&amp;gt; df = pd.DataFrame(df.values[1:], columns=headers)
&amp;gt;&amp;gt;&amp;gt; df
0          urlkey       timestamp               original   mimetype statuscode                            digest length
0  gov,iowa,doc)/  20170323172420  https://doc.iowa.gov/  text/html        200  L4NPH6MSVTZBRCD6CUTTQ2Y66OKY5Y3S   6904
1  gov,iowa,doc)/  20170323172435  https://doc.iowa.gov/  text/html        200  BPFQVM2VIW32BNVM7LJ23QTXN7TDTPIL   6906
2  gov,iowa,doc)/  20170323172720  https://doc.iowa.gov/  text/html        200  76LUVXUPNL3NPGU246YM3YQ6VNOOVIVH   6907
3  gov,iowa,doc)/  20170416221430  https://doc.iowa.gov/  text/html        200  6VMMWJRPN33DAGREX54WSLFG367NCCEF   7113
4  gov,iowa,doc)/  20170417071150  https://doc.iowa.gov/  text/html        200  OZMIM56F6OM5IL3HXTWBJZQCMGVRBYTB   7119
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From the timestamp column we can derive the snapshot URL by treating the timestamp
as path component between &lt;code&gt;https://web.archive.org/web/&lt;/code&gt; and the original URL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; df[&amp;#39;snapshot_url&amp;#39;] = df[&amp;#39;timestamp&amp;#39;].apply(lambda ts: &amp;#39;https://web.archive.org/web/&amp;#39; + ts + &amp;#39;/https://doc.iowa.gov/&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; df[&amp;#39;snapshot_url&amp;#39;].to_list()
[&amp;#39;https://web.archive.org/web/20170323172420/https://doc.iowa.gov/&amp;#39;, &amp;#39;https://web.archive.org/web/20170323172435/https://doc.iowa.gov/&amp;#39;, &amp;#39;https://web.archive.org/web/20170323172720/https://doc.iowa.gov/&amp;#39;, &amp;#39;https://web.archive.org/web/20170416221430/https://doc.iowa.gov/&amp;#39;, &amp;#39;https://web.archive.org/web/20170417071150/https://doc.iowa.gov/&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each snapshot is the original HTML page with some extra stuff injected to help
us navigate the history of the page. Thus we can scrape it by using all the
usual web scraping techniques. Furthermore, the old pages we want to scrape
present the data in the basic HTML &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; element, which makes it easy
to scrape by using Pandas &lt;a href=&#34;https://pandas.pydata.org/docs/reference/api/pandas.read_html.html#pandas-read-html&#34;&gt;&lt;code&gt;read_html()&lt;/code&gt; function&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; dfs = pd.read_html(&amp;#34;https://web.archive.org/web/20210303210751/https://doc.iowa.gov/daily-statistics&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; stats_df = dfs[1]
&amp;gt;&amp;gt;&amp;gt; stats_df
                      Institution Current Count  Capacity  Medical/Segregation
0                         Anamosa           943     911.0                171.0
1                        Clarinda           962     750.0                 46.0
2                      Fort Dodge          1151    1162.0                 75.0
3                   Mitchellville           486     654.0                114.0
4                Minimum Live-Out            99     120.0                  0.0
5                         Oakdale           818     585.0                142.0
6   Forensic Psychiatric Hospital             9       0.0                 28.0
7                    Fort Madison           708     612.0                153.0
8                  Mount Pleasant           815     776.0                 80.0
9                   Newton-Medium           853     762.0                197.0
10                        Minimum           175     252.0                  6.0
11                  Rockwell City           432     245.0                 18.0
12          MPCF Minimum Live-Out            86     104.0                  0.0
13           INSTITUTIONAL TOTALS          7537    6933.0               1030.0
14               % overcrowded by         8.71%       NaN                  NaN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To clean up the data, let us drop the last two columns and last two rows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; stats_df = stats_df[[&amp;#39;Institution&amp;#39;, &amp;#39;Current Count&amp;#39;]]
&amp;gt;&amp;gt;&amp;gt; stats_df = stats_df[:-2]
&amp;gt;&amp;gt;&amp;gt; stats_df
                      Institution Current Count
0                         Anamosa           943
1                        Clarinda           962
2                      Fort Dodge          1151
3                   Mitchellville           486
4                Minimum Live-Out            99
5                         Oakdale           818
6   Forensic Psychiatric Hospital             9
7                    Fort Madison           708
8                  Mount Pleasant           815
9                   Newton-Medium           853
10                        Minimum           175
11                  Rockwell City           432
12          MPCF Minimum Live-Out            86
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lastly, let us tweak names of some sub-entries:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; stats_df.loc[stats_df[&amp;#39;Institution&amp;#39;] == &amp;#34;Forensic Psychiatric Hospital&amp;#34;, &amp;#39;Institution&amp;#39;] = &amp;#34;Oakdale - Forensic Psychiatric Hospital&amp;#34;
&amp;gt;&amp;gt;&amp;gt; stats_df.loc[stats_df[&amp;#39;Institution&amp;#39;] == &amp;#34;Minimum&amp;#34;, &amp;#39;Institution&amp;#39;] = &amp;#34;Newton-Minimum&amp;#34;
&amp;gt;&amp;gt;&amp;gt; stats_df.loc[stats_df[&amp;#39;Institution&amp;#39;] == &amp;#34;Minimum Live-Out&amp;#34;, &amp;#39;Institution&amp;#39;] = &amp;#34;Mitchellville - Minimum Live-Out&amp;#34;
&amp;gt;&amp;gt;&amp;gt; stats_df
                                Institution Current Count
0                                   Anamosa           943
1                                  Clarinda           962
2                                Fort Dodge          1151
3                             Mitchellville           486
4          Mitchellville - Minimum Live-Out            99
5                                   Oakdale           818
6   Oakdale - Forensic Psychiatric Hospital             9
7                              Fort Madison           708
8                            Mount Pleasant           815
9                             Newton-Medium           853
10                           Newton-Minimum           175
11                            Rockwell City           432
12                    MPCF Minimum Live-Out            86
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By now we have prototyped finding and parsing page snapshots in Python REPL
environment. Let us put everything together into a Python script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urlencode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pandas &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ORIG_URL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://doc.iowa.gov/daily-statistics&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_snapshot_urls&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: ORIG_URL, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;output&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;filter&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;statuscode:200&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cdx_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://web.archive.org/cdx/search/cdx&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(cdx_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_json(cdx_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;iloc[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DataFrame(df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;values[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:], columns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;snapshot_url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;apply(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; ts: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://web.archive.org/web/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ts &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/https://doc.iowa.gov/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;snapshot_url&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_list()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_stats_table&lt;/span&gt;(url):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dfs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_html(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dfs[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stats_df[[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Current Count&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stats_df[:&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loc[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stats_df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Forensic Psychiatric Hospital&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Oakdale - Forensic Psychiatric Hospital&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loc[stats_df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Minimum&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Newton-Minimum&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loc[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stats_df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Minimum Live-Out&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Institution&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mitchellville - Minimum Live-Out&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(stats_df)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; stats_df
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    snapshot_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_snapshot_urls()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Found &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; snapshots&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(len(snapshot_urls)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result_dfs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; snapshot_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            stats_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrape_stats_table(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyboardInterrupt&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exit(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                stats_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrape_stats_table(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyboardInterrupt&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exit(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        result_dfs&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(stats_df)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result_df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;concat(result_dfs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result_df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_csv(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;iowa_doc.csv&amp;#34;&lt;/span&gt;, index&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We don&amp;rsquo;t store the timestamp directly in the scraped data as we can trivially
recover it from the URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pandas &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;convert_timestamp&lt;/span&gt;(timestamp):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; datetime(year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;]), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;]), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    day&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;]), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    hour&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;]), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    minute&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;]), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    second&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;int(timestamp[&lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_csv(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iowa_doc.csv&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;apply(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; url: url&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; df[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;apply(convert_timestamp)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some open source projects relying on Internet Archive API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/lc/gau&#34;&gt;gau&lt;/a&gt; - a recon tool to fetch known URLs from several
sources, including Internet Archive.&lt;/li&gt;
&lt;li&gt;Bellingcat&amp;rsquo;s &lt;a href=&#34;https://github.com/bellingcat/wayback-google-analytics&#34;&gt;wayback-google-analytics&lt;/a&gt;
is OSINT tool to dig up Google Analytics identifiers for finding associations
between websites.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Obscure corners of a standard macOS installation</title>
      <link>https://www.trickster.dev/post/obscure-corners-of-a-standard-macos-installation/</link>
      <pubDate>Sat, 11 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/obscure-corners-of-a-standard-macos-installation/</guid>
      <description>&lt;h2 id=&#34;apache-httpd&#34;&gt;Apache httpd&lt;/h2&gt;
&lt;p&gt;A little known fact is that macOS ships with Apache httpd. You can launch it
by running &lt;code&gt;sudo apachectl start&lt;/code&gt;. Configuration files are available in
/etc/apache2 directory and default &lt;code&gt;DocumentRoot&lt;/code&gt; is /Library/WebServer/Documents.
Note however that macOS no longer ships PHP/Ruby/Perl/etc. so you will need to
install a scripting language separately to do any kind of dynamic web
development.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-11-11_11.20.37.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;say1-and-tts-in-edit-menu&#34;&gt;say(1) and TTS in Edit menu&lt;/h2&gt;
&lt;p&gt;macOS has built-in text-to-speech capabilities. To make your Mac speak out some
text, pipe it to the say(1) program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;colorless green ideas sleep furiously&amp;#34; | say
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also pass the text as via CLI arguments:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ say &amp;#34;hello world&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running it with &lt;code&gt;-v &amp;quot;?&amp;quot;&lt;/code&gt; prints a list of voices available. Multiple languages
are supported and there are some fun novelty voices. You can choose a
voice by passing voice name through &lt;code&gt;-v&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ say &amp;#34;hello world&amp;#34; -v Bells
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;say(1) program can be used as TTS component in your shell scripts, cronjobs
or other automations.&lt;/p&gt;
&lt;p&gt;For most of macOS GUI programs you can highlight some text on the screen and make
it spoken through Edit -&amp;gt; Speech -&amp;gt; Start Speaking.&lt;/p&gt;
&lt;h2 id=&#34;screen1&#34;&gt;screen(1)&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s no tmux in a standard macOS install, but older equivalent of tmux -
GNU screen - is shipped with macOS. It will be less convenient, but it still
gives you an option to make CLI tools run in the background without keeping an
active terminal session. Like tmux, it supports terminal session multiplexing
with pseudo-window manager features like splitting the terminal into multiple
panes. Read the screen(1) manpage for further information.&lt;/p&gt;
&lt;h2 id=&#34;grapher&#34;&gt;Grapher&lt;/h2&gt;
&lt;p&gt;Grapher is a simple GUI tool to draw 2D and 3D graphs of mathematical functions.
It supports various coordinate systems such as classical, spheral, logarithmic,
cylindrical. Both regular and differential equations are supported.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-11-07_10.41.24.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;digital-colour-meter&#34;&gt;Digital Colour Meter&lt;/h2&gt;
&lt;p&gt;Digital Colour Meter is a GUI app to measure the exact RGB color values of the
pixel under the cursor. This is helpful for GUI or frontend programming tasks
that require reproducing the exact colors from the design images. Command-L
makes it (un)lock on a point on the screen.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-11-07_10.38.58.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-book&#34;&gt;Font Book&lt;/h2&gt;
&lt;p&gt;Font Book is a viewer for all the fonts installed on your macOS system that also
allows installing optional or custom fonts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-11-07_10.48.54.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;usrsharecalendar-and-calendar1&#34;&gt;/usr/share/calendar and calendar(1)&lt;/h2&gt;
&lt;p&gt;Modern macOS is Unix system (officially and ancestrally). Many technical details
from BSD systems can be found in macOS. One example is /usr/share/calendar
directory that has human and machine readable files listing various notable
dates, such as national holidays:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ls -lh
total 248
-rw-r--r--  1 root  wheel   483B Sep  2 10:35 calendar.all
-rw-r--r--  1 root  wheel   1.4K Sep  2 10:35 calendar.australia
-rw-r--r--  1 root  wheel    13K Sep  2 10:35 calendar.birthday
-rw-r--r--  1 root  wheel   1.0K Sep  2 10:35 calendar.christian
-rw-r--r--  1 root  wheel   3.2K Sep  2 10:35 calendar.computer
-rw-r--r--  1 root  wheel   269B Sep  2 10:35 calendar.croatian
-rw-r--r--  1 root  wheel   1.7K Sep  2 10:35 calendar.dutch
-rw-r--r--  1 root  wheel    22K Sep  2 10:35 calendar.freebsd
-rw-r--r--  1 root  wheel   265B Sep  2 10:35 calendar.french
-rw-r--r--  1 root  wheel   261B Sep  2 10:35 calendar.german
-rw-r--r--  1 root  wheel    25K Sep  2 10:35 calendar.history
-rw-r--r--  1 root  wheel    21K Sep  2 10:35 calendar.holiday
-rw-r--r--  1 root  wheel   280B Sep  2 10:35 calendar.hungarian
-rw-r--r--  1 root  wheel   6.5K Sep  2 10:35 calendar.judaic
-rw-r--r--  1 root  wheel   1.4K Sep  2 10:35 calendar.lotr
-rw-r--r--  1 root  wheel    13K Sep  2 10:35 calendar.music
-rw-r--r--  1 root  wheel   674B Sep  2 10:35 calendar.newzealand
-rw-r--r--  1 root  wheel   264B Sep  2 10:35 calendar.russian
-rw-r--r--  1 root  wheel   843B Sep  2 10:35 calendar.southafrica
-rw-r--r--  1 root  wheel   269B Sep  2 10:35 calendar.ukrainian
-rw-r--r--  1 root  wheel   1.4K Sep  2 10:35 calendar.usholiday
-rw-r--r--  1 root  wheel   469B Sep  2 10:35 calendar.world
drwxr-xr-x  3 root  wheel    96B Sep  2 10:35 de_AT.ISO_8859-15
drwxr-xr-x  9 root  wheel   288B Sep  2 10:35 de_DE.ISO8859-1
drwxr-xr-x  2 root  wheel    64B Sep  2 10:35 de_DE.ISO8859-15
drwxr-xr-x  7 root  wheel   224B Sep  2 10:35 fr_FR.ISO8859-1
drwxr-xr-x  2 root  wheel    64B Sep  2 10:35 fr_FR.ISO8859-15
drwxr-xr-x  4 root  wheel   128B Sep  2 10:35 hr_HR.ISO8859-2
drwxr-xr-x  5 root  wheel   160B Sep  2 10:35 hu_HU.ISO8859-2
drwxr-xr-x  9 root  wheel   288B Sep  2 10:35 ru_RU.KOI8-R
drwxr-xr-x  6 root  wheel   192B Sep  2 10:35 uk_UA.KOI8-U
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For example, calendar.computer lists notable dates from computer history:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ head -n 20 /usr/share/calendar/calendar.computer
/*
 * Computer
 *
 * $FreeBSD: src/usr.bin/calendar/calendars/calendar.computer,v 1.11 2007/09/07 03:23:06 edwin Exp $
 */

#ifndef _calendar_computer_
#define _calendar_computer_

01/01	AT&amp;amp;T officially divests its local Bell companies, 1984
01/01	The Epoch (Time 0 for UNIX systems, Midnight GMT, 1970)
01/03	Apple Computer founded, 1977
01/08	American Telephone and Telegraph loses antitrust case, 1982
01/08	Herman Hollerith patents first data processing computer, 1889
01/08	Justice Dept. drops IBM suit, 1982
01/10	First CDC 1604 delivered to Navy, 1960
01/16	Set uid bit patent issued, to Dennis Ritchie, 1979
01/17	Justice Dept. begins IBM anti-trust suit, 1969 (drops it, January 8, 1982)
01/24	DG Nova introduced, 1969
01/25	First U.S. meeting of ALGOL definition committee, 1958
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is also calendar(1) - a CLI tool to help you remember some notable dates
based on the calendar file (the following output is from 2023 November 10):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ calendar -f /usr/share/calendar/calendar.usholiday 
Nov 11 	Veterans&amp;#39; Day
$ calendar -f /usr/share/calendar/calendar.world    
Nov 10 	Greg Lake is born in Bournemouth, England, 1948
Nov 10*	Parshas Toldos
Nov 10 	King&amp;#39;s Birthday in Bhutan
Nov 10 	Henry Stanley asks David Livingston, &amp;#34;Dr. Livingston, I presume?&amp;#34;, 1871
Nov 10 	Cpt. Wirz, commandant of Andersonville Prison hanged, 1865
Nov 10 	41 Women arrested in suffragette demonstrations near White House, 1917
Nov 10 	Soviet President Leonid Brezhnev dies at age 75, 1982
Nov 10 	Martin Luther born in Eisleben, Germany, 1483
Nov 11*	Rosh Chodesh Kislev (Beginning of the month of Kislev)
Nov 11 	Republic Day in Maldives
Nov 11 	Remembrance Day in Canada
Nov 11 	Independence of Cartagena in Colombia
Nov 11 	Independence Day in Angola
Nov 11 	Angola gains independence from Portugal, 1975
Nov 11 	Washington becomes the 42nd state, 1889
Nov 11 	Kurt Vonnegut, Jr, born in Indianapolis, 1922
Nov 12 	Neil Young is born in Toronto, 1945
Nov 12 	USA first exports oil to Europe, 1861
Nov 12 	Dr. Sun Yat-sen&amp;#39;s Birthday in Taiwan
Nov 13 	Paul Simon born, 1942
Nov 13 	St. Augustine of Hippo born in Numidia, Algeria, 354
Nov 13 	Robert Louis Stevenson born, 1850
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;pbcopy1-and-pbpaste1&#34;&gt;pbcopy(1) and pbpaste(1)&lt;/h2&gt;
&lt;p&gt;These two tools integrate macOS pasteboard into terminal workflows. Piping
text into &lt;code&gt;pbcopy&lt;/code&gt; copies it and running &lt;code&gt;pbpaste&lt;/code&gt; outputs text from pasteboard
into standard output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;crypto stands for cryptography&amp;#34; | pbcopy
$ pbpaste
crypto stands for cryptography
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;networkquality&#34;&gt;networkQuality&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;networkQuality&lt;/code&gt; is CLI tool to measure UL/DL bandwidth and latency of your
network connection.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping data from Google Places API</title>
      <link>https://www.trickster.dev/post/scraping-data-from-google-places-api/</link>
      <pubDate>Sun, 05 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-data-from-google-places-api/</guid>
      <description>&lt;p&gt;Google Maps Platform contains large amount of POI (Point Of Interest) data that
one might want to scrape for purposes such as lead generation, real estate
OSINT, academic research and so on. However Google Maps web/mobile apps
are highly complex pieces of software and may prove difficult to reverse engineer
for data extraction. Once could be doing some browser automation for scraping,
but the very same POI data can be extracted through scraping the official
&lt;a href=&#34;https://developers.google.com/maps/documentation/places/web-service/overview&#34;&gt;Google Places API&lt;/a&gt;
that is free to use at small scale (Google provides some free monthly credits)
and is billed at &lt;a href=&#34;https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#new-payg&#34;&gt;pay-as-you-go&lt;/a&gt;
model at non-trivial volumes. We will go through some basic examples of how this
data can be scraped with Python. If you retain the data for your own purposes
this still violates the &lt;a href=&#34;https://cloud.google.com/maps-platform/terms&#34;&gt;platform terms&lt;/a&gt;,
but it&amp;rsquo;s pretty much the least shady and most convenient way to extract some of
the data from Google Maps Platform as it relies on the official API.&lt;/p&gt;
&lt;p&gt;Let us review what we have here to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Place Search functionality is implemented via three endpoints:
&lt;ul&gt;
&lt;li&gt;Find Place (&lt;code&gt;/api/place/findplacefromtext/&lt;/code&gt;) to search by place name,
address, POI category or phone number. Fuzzy matching is applied when
searching.&lt;/li&gt;
&lt;li&gt;Nearby Search (&lt;code&gt;/api/place/nearbysearch&lt;/code&gt;) to search for places around given
coordinates with POI filtering criteria being applied. Can be used to
autocomplete search queries in mobile or web apps.&lt;/li&gt;
&lt;li&gt;Text Search (&lt;code&gt;/api/place/textsearch&lt;/code&gt;) to search for places by arbitrary
search queries. Can be given location and radius to bias (but not filter)
search results.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Place Details endpoint (&lt;code&gt;/api/place/details/&lt;/code&gt;) takes &lt;code&gt;place_id&lt;/code&gt; value from
Place Search result and provides more details about the POI including reviews,
opening hours, photos and so on. One can apply field masks to skip on some
fields to improve cost and performance.&lt;/li&gt;
&lt;li&gt;Place Photos endpoint (&lt;code&gt;/api/place/photo&lt;/code&gt;) takes a &lt;code&gt;photo_reference&lt;/code&gt; from
Place Search or Place Details response and gives you a photo rendered to given
size preference.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to that, there&amp;rsquo;s two helper endpoints to assist searches via
autocompletion.&lt;/p&gt;
&lt;p&gt;At the time of writing Google has launched a set of next generation APIs that
you may need to enable via Google Cloud console and switch to if the old API
gets deprecated.&lt;/p&gt;
&lt;p&gt;For a complete list of data fields one can get, see
&lt;a href=&#34;https://developers.google.com/maps/documentation/places/web-service/place-data-fields&#34;&gt;Place Data Fields&lt;/a&gt;
page. Note that business emails are not provided via Places API, but phone
numbers and website URLs are. If API scraping is done for lead generation
purposes, the gap could be bridged by doing opportunistic scraping of company
websites or by performing data enrichment via services like Hunter or PDL.&lt;/p&gt;
&lt;p&gt;Place Search endpoints have one limitation that must be noted and addressed. One
search query can return at most 60 results (3 pages, 20 results each). That may
pose a problem if we want to collect non-trivial amounts of data. The solution to
this is to compute a grid of locations and run many search queries to cover the
territory.&lt;/p&gt;
&lt;p&gt;But first we need to get the API key from Google. To proceed, you need a Google
Cloud account with billing setup completed. Follow the instructions on these two
Google Developer portal pages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/maps/documentation/places/web-service/cloud-setup&#34;&gt;Set up your Google Cloud project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/maps/documentation/places/web-service/get-api-key&#34;&gt;Use API Keys with Places API&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we have the API key. We can verify that everything works by doing a single
request with curl:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -s &amp;#34;https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJj61dQgK6j4AR4GeTYWZsKWw&amp;amp;fields=id,name,international_phone_number&amp;amp;key=[REDACTED]&amp;#34; | jq
{
  &amp;#34;html_attributions&amp;#34;: [],
  &amp;#34;result&amp;#34;: {
    &amp;#34;international_phone_number&amp;#34;: &amp;#34;+1 650-253-0000&amp;#34;,
    &amp;#34;name&amp;#34;: &amp;#34;Googleplex&amp;#34;
  },
  &amp;#34;status&amp;#34;: &amp;#34;OK&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We will use the official &lt;a href=&#34;https://github.com/googlemaps/google-maps-services-python&#34;&gt;Google Maps Python module&lt;/a&gt;
to benefit from the Pythonic API wrapper it provides. Furthermore, we will use the
&lt;a href=&#34;https://github.com/mapado/haversine&#34;&gt;&lt;code&gt;haversine&lt;/code&gt;&lt;/a&gt; module to help us
compute a grid (since Earth is not flat the geospatial software community uses
&lt;a href=&#34;https://en.wikipedia.org/wiki/Haversine_formula&#34;&gt;Haversine formula&lt;/a&gt;
that this module implements for computing distances between points on the globe).&lt;/p&gt;
&lt;p&gt;The Python script is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; configparser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; math
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; haversine &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; haversine
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; googlemaps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;latitude&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;longitude&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;make_grid&lt;/span&gt;(gmaps_client, grid_element_side, location):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Search for location and get bounding box.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# https://developers.google.com/maps/documentation/places/web-service/search-find-place&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;find_place(location, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;textquery&amp;#34;&lt;/span&gt;, language&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;candidates&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; len(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;candidates&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error: Location not found!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exit(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    place_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;candidates&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;place_id&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;place(place_id, language&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    viewport &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;geometry&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; viewport &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error: viewport not found!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exit(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    from_longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; viewport&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;southwest&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lng&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    from_latitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; viewport&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;southwest&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lat&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    to_longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; viewport&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;northeast&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lng&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    to_latitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; viewport&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;northeast&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lat&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Longitude range: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; to &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(from_longitude, to_longitude))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Latitude range: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; to &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(from_latitude, to_latitude))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    horiz_len_km &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; haversine(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (from_latitude, from_longitude), (from_latitude, to_longitude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vert_len_km &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; haversine(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (from_latitude, from_longitude), (to_latitude, to_longitude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_horiz &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; math&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ceil(horiz_len_km &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; grid_element_side)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_vert &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; math&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ceil(vert_len_km &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; grid_element_side)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    grid_elements &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    latitude_step &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (to_latitude &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; from_latitude) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; n_vert
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    longitude_step &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (to_longitude &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; from_longitude) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; n_horiz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(n_horiz):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(n_vert):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            south_lat &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; from_latitude &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; latitude_step
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            north_lat &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; south_lat &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; latitude_step
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            north_lat &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; min(north_lat, to_latitude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            west_lng &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; from_longitude &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; longitude_step
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            east_lng &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; west_lng &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; longitude_step
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            east_lng &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; min(east_lng, to_longitude)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            grid_elements&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rectangle:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;|&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(south_lat, west_lng, north_lat, east_lng)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(grid_elements)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; grid_elements, from_latitude, to_latitude, from_longitude, to_longitude
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chdir(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dirname(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;realpath(__file__)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; configparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ConfigParser()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gplaces.ini&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    google_api_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Google&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;APIKey&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    grid_element_side &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Google&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GridElementSideKM&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    grid_element_side &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; float(grid_element_side)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gplaces.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    gmaps_client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; googlemaps&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Client(key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;google_api_key)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Location: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Search for: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    grid_elements, from_latitude, to_latitude, from_longitude, to_longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; make_grid(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gmaps_client, grid_element_side, location
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seen_place_ids &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Iterate across each subregion and perform search with given query in that region&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; location_bias &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; grid_elements:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;find_place(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;textquery&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            fields&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;place_id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;geometry&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;formatted_address&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            location_bias&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;location_bias,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            language&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Iterate across search results and get contact info for each of them.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# https://developers.google.com/maps/documentation/places/web-service/details&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; result &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;candidates&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            place_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;place_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; place_id &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; seen_place_ids:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            latitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;geometry&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lat&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;geometry&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lng&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; latitude &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; from_latitude &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; latitude &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; to_latitude:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; longitude &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; from_longitude &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; longitude &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; to_longitude:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            seen_place_ids&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(place_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;place(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                place_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                fields&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;international_phone_number&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                language&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;: resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;international_phone_number&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;formatted_address&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;: resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;latitude&amp;#34;&lt;/span&gt;: latitude,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;longitude&amp;#34;&lt;/span&gt;: longitude,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When launched, this script asks to things to be entered into standard input:
location name and search query to be used with Text Search API endpoint. The
API key is found in the gplaces.ini file that is parsed with a
&lt;code&gt;configparser&lt;/code&gt; module. Location name is used to find the overall territory
and retrieve it&amp;rsquo;s bounding box in &lt;code&gt;make_grid()&lt;/code&gt; function, then split the bounding
box into list of grid elements by doing some basic geometric computations. The
&lt;code&gt;make_grid()&lt;/code&gt; function returns not only list of grid elements in a form that
can be used for Place Search &lt;code&gt;locationBias&lt;/code&gt; parameter, but also the boundaries
of the overall bounding box for some sanity checks to be done later.&lt;/p&gt;
&lt;p&gt;Once we have that, we can retrieve the POI data - one grid element at a time.
To remove duplicates, we keep &lt;code&gt;place_id&lt;/code&gt; value in &lt;code&gt;seen_place_ids&lt;/code&gt; set and
skip duplicate POIs. We also skip POIs with coordinates outside the bounding
box (remember that Text Search endpoint supports biasing, but not filtering
search results towards a given location). For each result that passes the
filtering, we call the Place Details API (via &lt;code&gt;.place()&lt;/code&gt; method of API client)
to get two additional fields: &lt;code&gt;international_phone_number&lt;/code&gt; and &lt;code&gt;website&lt;/code&gt;. The
resulting scraped data is written to a CSV file.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Extracting data from elasticsearch API</title>
      <link>https://www.trickster.dev/post/extracting-data-from-elasticsearch-api/</link>
      <pubDate>Mon, 16 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/extracting-data-from-elasticsearch-api/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/elastic/elasticsearch&#34;&gt;Elasticsearch&lt;/a&gt; is open
source server software that acts both as database and search engine, based on
&lt;a href=&#34;https://lucene.apache.org/&#34;&gt;Apache Lucene&lt;/a&gt; library. It can be used to build
distributed clusters for indexing and searching Enterprise-level amounts of data.
Some sites that present large, searchable, mostly-textual datasets to
the end user are based on elasticsearch as their backend data store and have
frontend code talking directly to the API of elasticsearch. If the frontend
can talk directly to elasticsearch, so can we as web scraper developers with all
the benefits the flexible data search engine can bring. A specific example is
the website of Carnegie Museum of Art. To see how the trick works, we will be
scraping this site today.&lt;/p&gt;
&lt;p&gt;If we go to &lt;a href=&#34;https://collection.carnegieart.org/&#34;&gt;collection.carnegieart.org&lt;/a&gt;
with web browser we see a list of of various objects being presented with search
icon on the upper right corner of the page. Loading this page relies on
elasticsearch API, as can be seen in Network tab of Chrome DevTools.&lt;/p&gt;
&lt;p&gt;For instance, the following request (represented by curl snippet) fetches
the first page worth of data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://collection.carnegieart.org/api/cmoa_objects/_msearch?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority: collection.carnegieart.org&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: application/json&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authorization: Basic Y29sbGVjdGlvbnM6bzQxS0chTW1KUSRBNjY=&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;content-type: application/x-ndjson&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin: https://collection.carnegieart.org&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer: https://collection.carnegieart.org/&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua: &amp;#34;Chromium&amp;#34;;v=&amp;#34;116&amp;#34;, &amp;#34;Not)A;Brand&amp;#34;;v=&amp;#34;24&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;116&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile: ?0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform: &amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest: empty&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode: cors&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site: same-origin&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --data-raw &lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#39;{&amp;#34;preference&amp;#34;:&amp;#34;results&amp;#34;}\n{&amp;#34;query&amp;#34;:{&amp;#34;bool&amp;#34;:{&amp;#34;must&amp;#34;:[{&amp;#34;bool&amp;#34;:{&amp;#34;must&amp;#34;:[{&amp;#34;bool&amp;#34;:{&amp;#34;must&amp;#34;:[{&amp;#34;term&amp;#34;:{&amp;#34;images.permitted&amp;#34;:true}},{&amp;#34;exists&amp;#34;:{&amp;#34;field&amp;#34;:&amp;#34;images.filename&amp;#34;}}]}}]}}]}},&amp;#34;size&amp;#34;:24,&amp;#34;sort&amp;#34;:[{&amp;#34;acquisition_date&amp;#34;:{&amp;#34;order&amp;#34;:&amp;#34;desc&amp;#34;}}]}\n&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --compressed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-10-04_18.16.04.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-10-04_18.16.15.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some things are notable here. A &lt;code&gt;content-type&lt;/code&gt; header is &lt;code&gt;application/x-ndjson&lt;/code&gt;
which means Newline-Delimited JSON is provided in the POST request payload.
That entails not a single JSON array or object, but a sequence of JSON objects -
one per line. That&amp;rsquo;s because the &lt;a href=&#34;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multi-search.html&#34;&gt;Multi search API&lt;/a&gt;
is used to populate the pages.  Each line (even the last one) must have a newline
at the end. The first line is a search header and provides basic parameters.
The second line provides a configuration for search query (pretty-printed for
readability):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;must&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;must&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;must&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;term&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;images.permitted&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;exists&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;field&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images.filename&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;acquisition_date&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;order&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;desc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At &lt;code&gt;query&lt;/code&gt; key we have some nested predicate that basically requires object
images to be available (&amp;ldquo;Has Images&amp;rdquo; checkbox is on by default). At &lt;code&gt;size&lt;/code&gt;
we have a page size and at &lt;code&gt;sort&lt;/code&gt; key we have sorting configuration.&lt;/p&gt;
&lt;p&gt;If we go to next page we start having another key here - &lt;code&gt;from&lt;/code&gt; that enables
pagination by search result offset.&lt;/p&gt;
&lt;p&gt;A regular JSON payload is available in the payload of response and has a great
deal of information on the museum objects. So the good news is that we don&amp;rsquo;t
need to parse any kind of HTML here and can fully rely on pre-structured
data that we can access through elasticsearch API. The entire dataset of
Carnegie Museum of Art object catalog can be gathered solely by doing API
scraping.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-10-04_18.16.34.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also the &lt;code&gt;authorization&lt;/code&gt; header here with Base64-encoded API
credentials. This does not add any security as we can trivally decode the
encoded part of the header and recover the credentials that frontend code
uses:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;Y29sbGVjdGlvbnM6bzQxS0chTW1KUSRBNjY=&amp;#34; | base64 -d
collections:o41KG!MmJQ$A66
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, if we uncheck the &amp;ldquo;Has Image&amp;rdquo; checkbox the second line in the request
payload becomes even simpler:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;match_all&amp;#34;&lt;/span&gt;: {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;acquisition_date&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;order&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;desc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-10-04_18.47.47.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now the query covers all the data (over 90 000 records!). But there&amp;rsquo;s a little
problem - the elasticsearch Multi Search API that is used here is not meant to
provide all the search results. For that purpose we will be using the
&lt;a href=&#34;https://www.elastic.co/guide/en/elasticsearch/reference/current/scroll-api.html&#34;&gt;Scroll API&lt;/a&gt;
that is designed to provide a paginated access to search results after the
first (regular, not multiple) Search API request is performed. But first let us
prepare a &lt;code&gt;requests.Session&lt;/code&gt; object with the proper headers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# elasticsearch API credentials:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;collections&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o41KG!MmJQ$A66&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authority&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;collection.carnegieart.org&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;referer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Chromium&amp;#34;;v=&amp;#34;116&amp;#34;, &amp;#34;Not)A;Brand&amp;#34;;v=&amp;#34;24&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;116&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;empty&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;same-origin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;auth &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (USERNAME, PASSWORD)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice that the content type we say we&amp;rsquo;re using is &lt;code&gt;application/json&lt;/code&gt; -
a regular JSON format, not the multiline variant. That&amp;rsquo;s because we will be
using slightly different APIs than what the site frontend relies on.&lt;/p&gt;
&lt;p&gt;When launching the first search request for the first page, we provide a &lt;code&gt;scroll&lt;/code&gt;
URL parameter with some duration that we want the search results to be retained
for between requests:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape&lt;/span&gt;(session):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;match_all&amp;#34;&lt;/span&gt;: dict()},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;from&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: [{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquisition_date&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;order&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;desc&amp;#34;&lt;/span&gt;}}],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scroll&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1m&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/api/cmoa_objects/_search&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_payload,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;yield from&lt;/span&gt; gen_rows_from_response_payload(resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here &lt;code&gt;1m&lt;/code&gt; stands for 1 minute. This value is known as Scroll Window.&lt;/p&gt;
&lt;p&gt;This gives us the first page of the dataset AND a &lt;code&gt;_scroll_id&lt;/code&gt; field in the
JSON response that we can use with Scroll API to get further data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_scroll_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/api/_search/scroll/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; scroll_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield from&lt;/span&gt; gen_rows_from_response_payload(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hits &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(hits) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_scroll_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The generator function &lt;code&gt;gen_rows_from_response_payload()&lt;/code&gt; we use here helps with
converting some of the stuff in API response to data rows we can write into
CSV file.&lt;/p&gt;
&lt;p&gt;The entire scraper script is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# elasticsearch API credentials:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;collections&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;o41KG!MmJQ$A66&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authority&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;collection.carnegieart.org&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;referer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Chromium&amp;#34;;v=&amp;#34;116&amp;#34;, &amp;#34;Not)A;Brand&amp;#34;;v=&amp;#34;24&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;116&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;empty&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;same-origin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;auth &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (USERNAME, PASSWORD)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gen_rows_from_response_payload&lt;/span&gt;(resp_json):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hits &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp_json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hit_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; hits:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        source_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hit_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_source&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;department&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;department&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;current_location&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;current_location&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;materials&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;medium&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;technique&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;medium_description&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;from_location&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;creation_address&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;date_description&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;creation_date&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;year_start&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;creation_date&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;year_end&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;creation_date&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        makers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;creators&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; makers &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; len(makers) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            maker_names &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; m: m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;), makers))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            maker_birth_years &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; m: str(m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;birth&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)), makers))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            maker_death_years &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; m: str(m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;death&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)), makers))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            maker_genders &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; m: str(m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gender&amp;#34;&lt;/span&gt;)), makers))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maker_full_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(maker_names)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maker_birth_year&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(maker_birth_years)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maker_death_year&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(maker_death_years)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maker_gender&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(maker_genders)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquired_year&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquisition_date&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-&amp;#34;&lt;/span&gt;)[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquired_from&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquisition_method&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accession_number&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accession_number&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;credit_line&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;credit_line&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/objects/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; source_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cmoa:objects/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape&lt;/span&gt;(session):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;match_all&amp;#34;&lt;/span&gt;: dict()},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;from&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: [{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acquisition_date&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;order&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;desc&amp;#34;&lt;/span&gt;}}],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scroll&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1m&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/api/cmoa_objects/_search&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_payload,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;yield from&lt;/span&gt; gen_rows_from_response_payload(resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp0&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_scroll_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://collection.carnegieart.org/api/_search/scroll/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; scroll_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield from&lt;/span&gt; gen_rows_from_response_payload(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hits &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hits&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(hits) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        scroll_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_scroll_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;carnegie.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape(session):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;keys()), lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may want to run this through automatically rotating DC proxy pool as there
seems to be a little IP-based rate-limiting being done on the server.&lt;/p&gt;
&lt;p&gt;Since we have access to the API of elasticsearch database, we can use it to
develop unofficial/adversarial integrations by either doing the API calls
directly as in above example of by using one of the elasticsearch client
libraries. There are two official options for Python:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/elastic/elasticsearch-py&#34;&gt;elasticsearch-py&lt;/a&gt; for bridging
the gap between ES API constructs and Python dictionaries.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/elastic/elasticsearch-dsl-py&#34;&gt;elasticsearch-dsl-py&lt;/a&gt; for a
higher order API that provides a more convenient way to work with database while
not straying too far from the JSON-based API.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Setting up mitmproxy with iOS 17.1</title>
      <link>https://www.trickster.dev/post/setting-up-mitmproxy-with-ios17.1/</link>
      <pubDate>Wed, 04 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/setting-up-mitmproxy-with-ios17.1/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; is open source, interactive proxy server
meant for interception, capture and analysis of network traffic for purposes such
as penetration testing, debugging, troubleshooting, reverse engineering and
privacy analysis. A common use case of this tool is to explore mobile application
communications (typically API calls over HTTPS) for API scraping, automation and
security testing. Earlier in the blog I have covered how to set up
mitmproxy with mobile device that run iOS 15. Today iOS 17.1 beta 2 is available
for end users and thus we will go through the process of setting it up with
mitmproxy.&lt;/p&gt;
&lt;p&gt;Let us start with installing mitmproxy itself. There are several ways to do
it depending on your needs and specifics of dev environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On macOS you can install it through Homebrew: &lt;code&gt;brew install mitmproxy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to develop mitmproxy extensions in Python it can be installed
via PIP: &lt;code&gt;pip3 install mitmproxy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to run it via Docker, the &lt;a href=&#34;https://hub.docker.com/r/mitmproxy/mitmproxy/&#34;&gt;official image&lt;/a&gt;
is available on Docker hub.&lt;/li&gt;
&lt;li&gt;Some Linux distributions (e.g. Debian, Arch, Kali) ship mitmproxy in their
package managers.&lt;/li&gt;
&lt;li&gt;Windows installer, source tarballs and Linux/macOS binaries are available from
the &lt;a href=&#34;https://mitmproxy.org/&#34;&gt;official website&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make sure you have it installed correctly, run it with &lt;code&gt;--version&lt;/code&gt; CLI
argument:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmproxy --version
Mitmproxy: 10.0.0
Python:    3.11.5
OpenSSL:   OpenSSL 3.1.3 19 Sep 2023
Platform:  macOS-13.5.2-arm64-arm-64bit
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now run the mitmproxy TUI program in terminal of your local machine (behind NAT).
Doing this on server is possible as well, but not recommended. Either way, you
need to make sure that your iPhone can reach the mitmproxy server via the network.
A typical setup is having them both in a single WiFi network.&lt;/p&gt;
&lt;p&gt;Now you need to find out the IP address of your server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On macoS run &lt;code&gt;ifconfig&lt;/code&gt; and look for IPv4 address under your primary network
interface (e.g. &lt;code&gt;en0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;On Linux it will depend on exact system specifics, but you can try using
&lt;code&gt;ifconfig&lt;/code&gt; if available, or running &lt;code&gt;ip addr&lt;/code&gt; or &lt;code&gt;hostname -I&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On Windows it will be available in the output of &lt;code&gt;ipconfig&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have desktop GUI environment running the IP address will be available
through network setting app as well.&lt;/p&gt;
&lt;p&gt;Let us do the setup on iPhone side. Make sure you are connected to the right
network and go to the Settings app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1695.PNG&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Press Wi-Fi cell in the table view and tap the &lt;code&gt;(i)&lt;/code&gt; button next to the name of
Wi-Fi network you are connected to.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1696.PNG&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the next screen go to the bottom of the table and tap &amp;ldquo;Configure Proxy&amp;rdquo; cell.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1697.PNG&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Choose &amp;ldquo;Manual&amp;rdquo; from the available options and fill the little form here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &amp;ldquo;Server&amp;rdquo; cell enter the IP address of your computer you have determined
earlier.&lt;/li&gt;
&lt;li&gt;In &amp;ldquo;Port&amp;rdquo; field enter proxy port number - 8080.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1698.PNG&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Press the &amp;ldquo;Save&amp;rdquo; button on the top of UI.&lt;/p&gt;
&lt;p&gt;To intercept the HTTPS traffic, we need to install an X.509 certificate. Open the
Safari (specifically Safari - not some other browser) and navigate to mitm.it.
Press the green button under &amp;ldquo;iOS&amp;rdquo; heading. Allow the browser to download the
file into your system.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1699.PNG&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/IMG_1700.PNG&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Install the certificate through Settings app: go to General, then to &amp;ldquo;VPN &amp;amp;
Device Management&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1701.PNG&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You will find &amp;ldquo;mitmproxy&amp;rdquo; entry under &amp;ldquo;Downloaded Profile&amp;rdquo;
label. Tap it and press Install button in the modal view. Since this is a
sensitive action, iOS will ask for PIN code and additional verification. If all
goes well, a &amp;ldquo;Verified&amp;rdquo; label with a checkmark will appear on the GUI.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1702.PNG&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But this is not enough. We installed the certificate, but we also need to trust
it. Go back to General section of settings, then go to About and choose
&amp;ldquo;Certificate Trust Settings&amp;rdquo; at the bottom. Turn on the switch for the
mitmproxy certificate.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1706.PNG&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now let us verify that it works correctly. Go back to Safari and open some
website that is protected by TLS (which most modern websites use). You should
see HTTPS requests related to the site displayed in mitmproxy TUI.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_1707.PNG&#34;&gt;Screenshot 10&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-10-04_13.22.21.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If this does not work right away you may want to set your iPhone to airplane
mode for a brief period of time and make it reconnect to Wi-Fi network. That
used to help in earlier versions of iOS.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t like the TUI version of mitmproxy you can try mitmweb. It also ships
with mitmproxy, but presents data in web interface.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-10-04_13.27.41.png&#34;&gt;Screenshot 12&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-10-04_13.28.28.png&#34;&gt;Screenshot 13&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another thing that ships with mitmproxy is mitmdump - a tool similar to
tcpdump, but based on mitmproxy machinery and made to primarily intercept
HTTP(S) traffic:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ mitmdump
[13:29:03.684] HTTP(S) proxy listening at *:8080.
[13:29:04.825][192.168.0.16:52306] client connect
[13:29:04.883][192.168.0.16:52306] server connect bam.nr-data.net:443 (162.247.243.29:443)
192.168.0.16:52306: POST https://bam.nr-data.net/jserrors/1/NRBR-26d48a12bc762b8fbd7?a=45012625&amp;amp;v=1.242.0&amp;amp;to=ZVdXbUtXXBIHVUNfXlwde1ZLW1MND0xSUmRAWxoT&amp;amp;rst=93147&amp;amp;ck=0&amp;amp;s=25398efdfbe5dc5f&amp;amp;ref=https://www.nike…
                 &amp;lt;&amp;lt; 200 OK 24b
[13:29:05.237][192.168.0.16:52307] client connect
[13:29:05.298][192.168.0.16:52307] server connect www.nike.com:443 (2.18.32.173:443)
192.168.0.16:52307: POST https://www.nike.com/Fj-Dfn/Fyj/7WK/PLInpt0t/7pEchtzQph7ruY/STdCAQ/SVJ3bWdc/bUgB HTTP/2.0
        &amp;lt;&amp;lt; HTTP/2.0 201 Created 18b
192.168.0.16:52307: POST https://www.nike.com/Fj-Dfn/Fyj/7WK/PLInpt0t/7pEchtzQph7ruY/STdCAQ/SVJ3bWdc/bUgB HTTP/2.0
        &amp;lt;&amp;lt; HTTP/2.0 201 Created 18b
192.168.0.16:52307: POST https://www.nike.com/Fj-Dfn/Fyj/7WK/PLInpt0t/7pEchtzQph7ruY/STdCAQ/SVJ3bWdc/bUgB HTTP/2.0
 &amp;lt;&amp;lt; stream reset by client (CANCEL)
192.168.0.16:52307: GET https://www.nike.com/w/air-force-1-shoes-5sj3yzy7ok HTTP/2.0
        &amp;lt;&amp;lt; HTTP/2.0 200 OK 115k
[13:29:06.554][192.168.0.16:52308] client connect
[13:29:06.556][192.168.0.16:52309] client connect
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you no longer want the traffic to be intercepted, go back to &amp;ldquo;Configure
Proxy&amp;rdquo; table in iPhone settings and disable the proxying there.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Parsing HTML documents with Beautiful Soup</title>
      <link>https://www.trickster.dev/post/parsing-html-documents-with-beautiful-soup/</link>
      <pubDate>Fri, 22 Sep 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/parsing-html-documents-with-beautiful-soup/</guid>
      <description>&lt;p&gt;Beautiful Soup (also known as &lt;code&gt;bs4&lt;/code&gt;) is a Python module for parsing HTML (and
also XML) documents. It is commonly used in web scraper development. Beautiful
Soup takes an HTML string and parses it into a tree structure that reflects
the structure of DOM tree, but has properties and methods related to
accessing data and running queries.&lt;/p&gt;
&lt;p&gt;We can install it via PIP as &lt;code&gt;beautifulsoup4&lt;/code&gt;. Furthermore, some operating
systems ship Beautiful Soup in their package managers. For example,
there&amp;rsquo;s &lt;code&gt;python3-bs4&lt;/code&gt; package on Debian APT.&lt;/p&gt;
&lt;p&gt;Beautiful Soup does not perform parsing on it&amp;rsquo;s own. Instead, it uses a lower
level HTML/XML parsing library and converts the results into it&amp;rsquo;s own data model.
As of September 2023, there are following options for the HTML parsing library:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python&amp;rsquo;s vanilla HTML parser (&lt;code&gt;html.parser&lt;/code&gt;) - middling performance and
average tolerance to malformed HTML.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lxml&lt;/code&gt; - Python wrapper around libxml2 C library - very good performance,
quite lenient for bad HTML.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;html5lib&lt;/code&gt; - Pure Python HTML5 parser that is slow, but can deal with some
pretty messed up pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since Beautiful Soup is an abstraction over these HTML/XML parsers we have
some flexibility when choosing the underlying parser while keeping the
same data model.&lt;/p&gt;
&lt;p&gt;Let us explore how to use Beautiful Soup for web scraping. We start by using
the lxml parser on a simple HTML document.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.11.5 (main, Aug 24 2023, 15:09:45) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; from bs4 import BeautifulSoup
&amp;gt;&amp;gt;&amp;gt; soup = BeautifulSoup(html_str, &amp;#39;lxml&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; soup
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Title!&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;The Title&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&amp;gt;&amp;gt;&amp;gt; type(soup)
&amp;lt;class &amp;#39;bs4.BeautifulSoup&amp;#39;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can regenerate the HTML document in a pretty-printed form:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; print(soup.prettify())
&amp;lt;html&amp;gt;
 &amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;
   Title!
  &amp;lt;/title&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;
   The Title
  &amp;lt;/h1&amp;gt;
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can use object properties to traverse across the tree structure and extract
stuff we may want to parse:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; soup.title
&amp;lt;title&amp;gt;Title!&amp;lt;/title&amp;gt;
&amp;gt;&amp;gt;&amp;gt; soup.title.text
&amp;#39;Title!&amp;#39;
&amp;gt;&amp;gt;&amp;gt; soup.title.name
&amp;#39;title&amp;#39;
&amp;gt;&amp;gt;&amp;gt; soup.body.h1.text
&amp;#39;The Title&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To provide an example on how to run queries, let us have a bigger HTML document
to parse:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt;&amp;gt;Product list&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;border&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;thead&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;Product name&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;Price&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;URL&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;thead&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tbody&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;productname&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;productname&amp;#34;&lt;/span&gt;&amp;gt;Jordan 4 Retro&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&amp;gt;$269&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pdpurl&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://stockx.com/air-jordan-4-retro-red-cement&amp;#34;&lt;/span&gt;&amp;gt;link&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;productname&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;productname&amp;#34;&lt;/span&gt;&amp;gt;adidas Yeezy Boost 350 V2 Static (Non-Reflective) (2018/2023)&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&amp;gt;$195&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pdpurl&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://stockx.com/adidas-yeezy-boost-350-v2-static&amp;#34;&lt;/span&gt;&amp;gt;link&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tbody&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can use &lt;code&gt;find()&lt;/code&gt; method to find a single result or &lt;code&gt;find_all()&lt;/code&gt; to find
multiple results:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; in_f = open(&amp;#34;test.html&amp;#34;, &amp;#34;r&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; html_str = in_f.read()
&amp;gt;&amp;gt;&amp;gt; in_f.close()
&amp;gt;&amp;gt;&amp;gt; soup = BeautifulSoup(html_str, &amp;#34;lxml&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; soup.find(&amp;#39;title&amp;#39;)
&amp;lt;title&amp;gt;Product list&amp;lt;/title&amp;gt;
&amp;gt;&amp;gt;&amp;gt; soup.find_all(class_=&amp;#39;productname&amp;#39;)
[&amp;lt;td class=&amp;#34;productname&amp;#34; id=&amp;#34;productname&amp;#34;&amp;gt;Jordan 4 Retro&amp;lt;/td&amp;gt;, &amp;lt;td class=&amp;#34;productname&amp;#34; id=&amp;#34;productname&amp;#34;&amp;gt;adidas Yeezy Boost 350 V2 Static (Non-Reflective) (2018/2023)&amp;lt;/td&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; soup.find_all(id=&amp;#39;price&amp;#39;)
[&amp;lt;td id=&amp;#34;price&amp;#34;&amp;gt;$269&amp;lt;/td&amp;gt;, &amp;lt;td id=&amp;#34;price&amp;#34;&amp;gt;$195&amp;lt;/td&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; soup.find_all(attrs={&amp;#39;class&amp;#39;:&amp;#39;productname&amp;#39;, &amp;#39;id&amp;#39;:&amp;#39;productname&amp;#39;})
[&amp;lt;td class=&amp;#34;productname&amp;#34; id=&amp;#34;productname&amp;#34;&amp;gt;Jordan 4 Retro&amp;lt;/td&amp;gt;, &amp;lt;td class=&amp;#34;productname&amp;#34; id=&amp;#34;productname&amp;#34;&amp;gt;adidas Yeezy Boost 350 V2 Static (Non-Reflective) (2018/2023)&amp;lt;/td&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To extract text recursively from a (sub)tree we can use &lt;code&gt;get_text()&lt;/code&gt; method:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; print(soup.get_text())


Product list





Product name
Price
URL




Jordan 4 Retro
$269
link


adidas Yeezy Boost 350 V2 Static (Non-Reflective) (2018/2023)
$195
link
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us fetch a more complex page for more advanced example (Bright Data Web
Unlocker is used here):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; proxy_url = &amp;#34;[REDACTED]&amp;#34;
&amp;gt;&amp;gt;&amp;gt; resp = requests.get(&amp;#34;https://clutch.co/directory/iphone-application-developers&amp;#34;, proxies={&amp;#39;https&amp;#39;: proxy_url})
&amp;gt;&amp;gt;&amp;gt; resp = requests.get(&amp;#34;https://clutch.co/directory/iphone-application-developers&amp;#34;, proxies={&amp;#39;https&amp;#39;: proxy_url}, verify=False)
/opt/homebrew/lib/python3.11/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host &amp;#39;brd.superproxy.io&amp;#39;. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By inspecting the page we see that list items are children of &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; element
with class &lt;code&gt;directory-list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-09-21_13.24.39.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-09-21_13.24.49.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-09-21_14.05.38.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, let us try to parse it and extract some data based on structure we see
when inspecting the page:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; soup = BeautifulSoup(resp.text, &amp;#39;html.parser&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; ul = soup.find(&amp;#39;ul&amp;#39;, class_=&amp;#39;directory-list&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; list_items = ul.find_all(&amp;#39;li&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; li = list_items[0]
&amp;gt;&amp;gt;&amp;gt; title = li.find(attrs={&amp;#39;data-link_text&amp;#39;: &amp;#39;Profile Title&amp;#39;})
&amp;gt;&amp;gt;&amp;gt; title
&amp;lt;a class=&amp;#34;company_title directory_profile&amp;#34; data-link_text=&amp;#34;Profile Title&amp;#34; href=&amp;#34;/profile/naked-development&amp;#34; target=&amp;#34;_blank&amp;#34;&amp;gt;
                            Naked Development
                        &amp;lt;/a&amp;gt;
&amp;gt;&amp;gt;&amp;gt; title = li.find(attrs={&amp;#39;data-link_text&amp;#39;: &amp;#39;Profile Title&amp;#39;}).text
&amp;gt;&amp;gt;&amp;gt; title
&amp;#39;\n                            Naked Development\n                        &amp;#39;
&amp;gt;&amp;gt;&amp;gt; title = title.strip()
&amp;gt;&amp;gt;&amp;gt; title
&amp;#39;Naked Development&amp;#39;
&amp;gt;&amp;gt;&amp;gt; li.find(class_=&amp;#34;company_info__wrap&amp;#34;).text
&amp;#39;Creative Development Agency&amp;#39;
&amp;gt;&amp;gt;&amp;gt; li.find(attrs={&amp;#39;data-link_text&amp;#39;: &amp;#39;Profile Title&amp;#39;}).attrs[&amp;#39;href&amp;#39;]
&amp;#39;/profile/naked-development&amp;#39;
&amp;gt;&amp;gt;&amp;gt; li.find(class_=&amp;#34;website-link__item&amp;#34;).attrs[&amp;#39;href&amp;#39;]
&amp;#39;https://app.nakeddev.com/application?utm_source=clutch.co&amp;amp;utm_medium=referral&amp;amp;utm_campaign=directory-iphone-application-developers&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Happy scraping!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Solving a simple JS challenge with sandboxing</title>
      <link>https://www.trickster.dev/post/solving-a-simple-js-challenge-with-sandboxing/</link>
      <pubDate>Fri, 15 Sep 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/solving-a-simple-js-challenge-with-sandboxing/</guid>
      <description>&lt;p&gt;Many antibot solutions involve client-side JavaScript challenges - performing
randomised computation, probing browser environment and so on. That may pose
an obstacle for web scraper and automation bot development. In some cases this
involves heavily obfuscated or even virtualised code that is hard to reverse
engineer. Ideally we want to fully understand and reproduce the challenge in
our code, but that is not always easy or worth the time and effort. A simpler
way to deal with JS challenges is technique known as sandboxing - creating
an artificial, isolated environment to run the challenge code with
little to no modifications and extracting the outputs from the code (e.g. HTTP
headers or cookies). In this post we will go through a simple example of this
technique.&lt;/p&gt;
&lt;p&gt;For the sake of example, we need a sample of JS challenge code. Some sites,
such as OpenCorporates provide us with a simple one to play with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl https://opencorporates.com
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;script type=&amp;#34;text/javascript&amp;#34;&amp;gt;&amp;lt;!--
function leastFactor(n) {
 if (isNaN(n) || !isFinite(n)) return NaN;
 if (typeof phantom !== &amp;#39;undefined&amp;#39;) return &amp;#39;phantom&amp;#39;;
 if (typeof module !== &amp;#39;undefined&amp;#39; &amp;amp;&amp;amp; module.exports) return &amp;#39;node&amp;#39;;
 if (n==0) return 0;
 if (n%1 || n*n&amp;lt;2) return 1;
 if (n%2==0) return 2;
 if (n%3==0) return 3;
 if (n%5==0) return 5;
 var m=Math.sqrt(n);
 for (var i=7;i&amp;lt;=m;i+=30) {
  if (n%i==0)      return i;
  if (n%(i+4)==0)  return i+4;
  if (n%(i+6)==0)  return i+6;
  if (n%(i+10)==0) return i+10;
  if (n%(i+12)==0) return i+12;
  if (n%(i+16)==0) return i+16;
  if (n%(i+22)==0) return i+22;
  if (n%(i+24)==0) return i+24;
 }
 return n;
}
function go() {
 var p=1612862479081; var s=3718246712; var n;
if ((s &amp;gt;&amp;gt; 12) &amp;amp; 1)/*
*13;
*/p+=120385074*15;
else 
p-=/*
else p-=
*/126371223* 13;	if ((s &amp;gt;&amp;gt; 6) &amp;amp; 1)/*
*13;
*/p+=159919203* 9;
else 	p-=
142123288*	7;if ((s &amp;gt;&amp;gt; 11) &amp;amp; 1)/*
*13;
*/p+=135381145*/*
p+= */12;
else /*
*13;
*/p-=/*
else p-=
*/79540378* 12;	if ((s &amp;gt;&amp;gt; 13) &amp;amp; 1) p+=/*
p+= */86227786*
14; else  p-=/* 120886108*
*/78525417*
14;/*
p+= */if ((s &amp;gt;&amp;gt; 14) &amp;amp; 1) p+=/* 120886108*
*/104707476*/*
else p-=
*/17;/*
else p-=
*/else 	p-= 23237263*
15;
 p-=3141693876;
 n=leastFactor(p);
{ document.cookie=&amp;#34;KEY=&amp;#34;+n+&amp;#34;*&amp;#34;+p/n+&amp;#34;:&amp;#34;+s+&amp;#34;:2460963898:1;path=/;&amp;#34;;
  document.location.reload(true); }
}
//--&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body onload=&amp;#34;go()&amp;#34;&amp;gt;
Loading...
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The HTML document calls the &lt;code&gt;go()&lt;/code&gt; function when it is loaded. The &lt;code&gt;go()&lt;/code&gt;
function calls &lt;code&gt;leastFactor()&lt;/code&gt; to perform some checks and computations. There
are some funny comments meant to throw off people who are using regular
expresions to clean up code, but we can get rid of them at AST level. Let
us copy-paste the JS code into &lt;a href=&#34;https://astexplorer.net/&#34;&gt;AST Explorer&lt;/a&gt; and
apply the following Babel transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Program|IfStatement|Expression|ExpressionStatement&amp;#34;&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;removeComments&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This yields the following code with junk comments removed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;leastFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (isNaN(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;isFinite(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;NaN&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;phantom&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phantom&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;node&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;sqrt&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;go&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1612862479081&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3718246712&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;120385074&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;126371223&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;159919203&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;142123288&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;135381145&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;79540378&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;86227786&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;78525417&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;104707476&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;17&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;23237263&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3141693876&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;leastFactor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    document.&lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;KEY=&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:2460963898:1;path=/;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    document.&lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;reload&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-09-09_19.05.00.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Aside from some random computations with random numbers, there are two things
to notice here. First are checks for symptoms of automation on the top of
&lt;code&gt;leastFactor()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;phantom&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phantom&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;node&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have one check for now obsolete &lt;a href=&#34;https://phantomjs.org/&#34;&gt;PhantomJS&lt;/a&gt;
browser automation software and another check for NodeJS environment. If any
of these fail the the main computation is not performed and correct result
is not achieved.&lt;/p&gt;
&lt;p&gt;Another thing to pay attention to is the following two statements at the
bottom of code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    document.&lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;KEY=&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:2460963898:1;path=/;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    document.&lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;reload&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first one sets the cookie based on initial hardcoded numbers and return
value of &lt;code&gt;leastFactor()&lt;/code&gt; function. Second one forces page reload, so that
web server could see the cookie and give the proper page instead of JS
challenge we got here.&lt;/p&gt;
&lt;p&gt;So if we want to scrape the site we need to manufacture the cookie based on
JS challenge code we&amp;rsquo;re getting without triggering checks for traces of
automation. Is it trivial to pass the first check as PhantomJS is dead as of
2023. We could tamper with the code bit more and remove the check for NodeJS
modules, but this would entail a bit of cheating, as generally JS challenges are
obfuscated in more advanced way than what we see here.&lt;/p&gt;
&lt;p&gt;Some options to create sandbox environment are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NodeJS &lt;code&gt;vm&lt;/code&gt; API&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/patriksimek/vm2&#34;&gt;&lt;code&gt;vm2&lt;/code&gt;&lt;/a&gt; module from NPM (recently discontinued)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/laverdet/isolated-vm&#34;&gt;&lt;code&gt;isolated-vm&lt;/code&gt;&lt;/a&gt; NPM module&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jsdom/jsdom&#34;&gt;JSDOM&lt;/a&gt; - a lightweight emulator for
browser-like JS environment.&lt;/li&gt;
&lt;li&gt;Using an actual browser and controlling it programmatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will keep it simple here and show a simple example based on the first option.
We save the challenge code into in.js. The following few lines of JS will read
it and execute the challenge code in a sandbox environment to get the cookie:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vm&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vm&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;in.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Script&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mapperContext&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { window&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {}, document&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;reload&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sandbox&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createContext&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;mapperContext&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;runInContext&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sandbox&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vm&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;runInContext&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;go();&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;sandbox&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sandbox&lt;/span&gt;.document.&lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We save this code into sandbox.js file.&lt;/p&gt;
&lt;p&gt;Note that when creating a VM context (a sandbox) we provide an object called
&lt;code&gt;mapperContext&lt;/code&gt; with some things that JS code expects to be there stubbed out.
For instance, to prevent the challenge code from crashing we provide an
empty implementation for &lt;code&gt;document.location.reload()&lt;/code&gt; function. This ensures
that prerequisites for challenge code execution are met.&lt;/p&gt;
&lt;p&gt;Next, there&amp;rsquo;s &lt;code&gt;script.runInContext(sandbox);&lt;/code&gt;. This statement &amp;ldquo;runs&amp;rdquo; the code
in VM, but since the original code contains only the two function declarations
without any call expression, we get it going in next line by calling &lt;code&gt;go()&lt;/code&gt;
function in the sandbox environment. Lastly, we retrieve and print the
&lt;code&gt;document.cookie&lt;/code&gt; value from the sandbox.&lt;/p&gt;
&lt;p&gt;It turned out modifying the original code in any way was not necessary and
that it can be sandboxed as-is.&lt;/p&gt;
&lt;p&gt;The following Bash script shows the entire steps to get the challenge, solve
it with sandboxing and using the cookie value to retrieve a proper page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set -x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://opencorporates.com&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    xmllint --html --xpath &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script/text()&amp;#39;&lt;/span&gt; - | &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    tail +2 &amp;gt; in.js &lt;span style=&#34;color:#75715e&#34;&gt;# Deleting &amp;#34;&amp;lt;![CDATA[&amp;lt;!--&amp;#34; line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://opencorporates.com&amp;#34;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cookie: &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;node sandbox.js&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More advanced examples of JS sandboxing from and outside grayhat space are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/FlareSolverr/FlareSolverr&#34;&gt;FlareSolverr&lt;/a&gt; - a CloudFlare
solver API server that relies on
&lt;a href=&#34;https://github.com/ultrafunkamsterdam/undetected-chromedriver&#34;&gt;undetected-chromedriver&lt;/a&gt;
to solve CF challenges in modified browser environment.&lt;/li&gt;
&lt;li&gt;Google allows running some subset of JS language in Google Tag Manager
templates to enable further customisation. See the
&lt;a href=&#34;https://developers.google.com/tag-platform/tag-manager/templates/sandboxed-javascript&#34;&gt;Sandboxed JavaScript&lt;/a&gt;
page.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Abusing GOAT mobile API for data scraping</title>
      <link>https://www.trickster.dev/post/abusing-goat-mobile-api-for-data-scraping/</link>
      <pubDate>Mon, 14 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/abusing-goat-mobile-api-for-data-scraping/</guid>
      <description>&lt;p&gt;GOAT is an online platform for retail sale and reselling of certain consumer
products - primarily sneakers, apparel and electronics. It is estimated to
have about 50 million active users that include some one million sellers. When
there&amp;rsquo;s a major userbase, there&amp;rsquo;s a potential to extract monetary benefit by
scraping the data and/or running some automations. But 1661 Inc., a company that
is developing GOAT don&amp;rsquo;t want us to do that. GOAT is one of the websites that
proactively fight automated traffic. For example, merely trying load the front
page in Scrapy shell gives us a Cloudflare error.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_14.31.05.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So we have to proceed differently. One trick from grayhat automation arsenal is
to MITM mobile app traffic to work out the exact API calls being done against
the backend system, then replay them programmatically. Our example will be
a simple API scraping to extract data.&lt;/p&gt;
&lt;p&gt;We will be using &lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; to intercept GOAT API
traffic. The setup in question is described in
&lt;a href=&#34;https://www.trickster.dev/post/setting-up-mitmproxy-with-ios15/&#34;&gt;earlier post&lt;/a&gt;, except that a newer iOS
version is used on the iPhone.&lt;/p&gt;
&lt;p&gt;When we run the app and press &amp;ldquo;Start browsing&amp;rdquo; button we see quite a bit of
traffic in mitmproxy TUI.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_14.52.12.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some things to notice here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Early in the app lifecycle it sends a request to PerimeterX API. This means
that PerimeterX is also used to fight automation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_15.00.16.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some requests have &lt;code&gt;x-px-authorization&lt;/code&gt; and &lt;code&gt;x-px-original-token&lt;/code&gt; headers
that are related to PerimeterX. Furthermore, &lt;code&gt;__cf_bm&lt;/code&gt; cookie is present on
some requests.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s &lt;code&gt;authorization&lt;/code&gt; header with empty value. We expect this value to
be set when app is being used after logging in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_15.17.22.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some requests go to goat.cnstrc.com domain and are not covered by PX/CF.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_15.19.17.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us start with the simple stuff first. Product data scraping is a common
thing that web scraper developers do. So let&amp;rsquo;s do that, but through the mobile
API. On the mobile GUI we access the Search tab, type in a search query, press
&lt;code&gt;z&lt;/code&gt; to clean the flows captured by mitmproxy and then press the search button.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_C85AF3496598-1.jpeg&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/IMG_60458540AC4D-1.jpeg&#34;&gt;Screesnhot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see one request to goat.cnstrc.com and some more requests to images.goat.com.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_15.31.10.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first one is of primary interest to us, as it seems to return some subset
of product data in the JSON payload. Last component of the URL path is the
search query that was typed into the app.&lt;/p&gt;
&lt;p&gt;We want to export this request as curl command. We position the curson on it
in the flow list view, press &lt;code&gt;e&lt;/code&gt; and choose &lt;code&gt;1) curl&lt;/code&gt; from list of options in
the little dialog box. Then we type the file path (e.g. &lt;code&gt;search.curl&lt;/code&gt;) into
bottom part of user interface.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: */*&amp;#39;&lt;/span&gt; --compressed -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: GOAT/19 CFNetwork/1410.0.3 Darwin/22.6.0&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en;q=0.9&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id: 7E2DEE62833C40A0B733085027D1A5BC&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://goat.cnstrc.com/search/gtx?key=key_XT7bjdbvjgECO5d8&amp;amp;page=1&amp;amp;fmt_options%5Bhidden_fields%5D=gp_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_facets%5D=gp_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_fields%5D=gp_instant_ship_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_facets%5D=gp_instant_ship_lowest_price_cents_2&amp;amp;features%5Bdisplay_variations%5D=true&amp;amp;feature_variants%5Bdisplay_variations%5D=matched&amp;amp;variations_map=%7B%22dtype%22:%22object%22,%22group_by%22:%5B%7B%22name%22:%22product_condition%22,%22field%22:%22data.product_condition%22%7D,%7B%22name%22:%22box_condition%22,%22field%22:%22data.box_condition%22%7D%5D,%22values%22:%7B%22min_regional_instant_ship_price%22:%7B%22field%22:%22data.gp_instant_ship_lowest_price_cents_2%22,%22aggregation%22:%22min%22%7D,%22min_regional_price%22:%7B%22field%22:%22data.gp_lowest_price_cents_2%22,%22aggregation%22:%22min%22%7D%7D%7D&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By putting this into &lt;a href=&#34;https://curlconverter.com/&#34;&gt;curlconverter&lt;/a&gt; we get the
following Python snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*/*&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GOAT/19 CFNetwork/1410.0.3 Darwin/22.6.0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;7E2DEE62833C40A0B733085027D1A5BC&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://goat.cnstrc.com/search/gtx?key=key_XT7bjdbvjgECO5d8&amp;amp;page=1&amp;amp;fmt_options%5Bhidden_fields%5D=gp_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_facets%5D=gp_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_fields%5D=gp_instant_ship_lowest_price_cents_2&amp;amp;fmt_options%5Bhidden_facets%5D=gp_instant_ship_lowest_price_cents_2&amp;amp;features%5Bdisplay_variations%5D=true&amp;amp;feature_variants%5Bdisplay_variations%5D=matched&amp;amp;variations_map=%7B&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;type%22:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22o&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;bject%22,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22g&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;roup_by%22:%5B%7B%22name%22:%22product_condition%22,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ield%22:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ata.product_condition&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22%&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;7D,%7B%22name%22:%22box_condition%22,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ield%22:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ata.box_condition&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22%&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;7D%5D,%22values%22:%7B%22min_regional_instant_ship_price%22:%7B&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ield%22:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ata.gp_instant_ship_lowest_price_cents_2%22,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22a&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ggregation%22:%22min&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22%&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;7D,%22min_regional_price%22:%7B&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ield%22:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ata.gp_lowest_price_cents_2%22,&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22a&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;ggregation%22:%22min&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%22%&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;7D%7D%7D&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t exactly like that it didn&amp;rsquo;t decode the URL parameters into Python &lt;code&gt;dict&lt;/code&gt;,
but this is a starting point for the script to be written.&lt;/p&gt;
&lt;p&gt;We see that there&amp;rsquo;s a &lt;code&gt;page&lt;/code&gt; parameter with values starting from one. This
covers the API pagination aspect.&lt;/p&gt;
&lt;p&gt;In case you are wondering about &lt;code&gt;x-emb-id&lt;/code&gt; header it is related to
&lt;a href=&#34;https://embrace.io/docs/ios/faq/#can-trace-ids-for-network-requests-be-captured&#34;&gt;mobile analytics solution&lt;/a&gt;
provided by Embrace Mobile, Inc.&lt;/p&gt;
&lt;p&gt;The next step is getting some product details via API. When we tap on one of the
search results we see a bunch of HTTPS requests, most of them being made to
subdomains of goat.com domain.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-13_16.41.08.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us explore what we have here. There&amp;rsquo;s a request to product template
endpoint &lt;code&gt;/api/v1/product_templates/.../show_v2&lt;/code&gt; that takes product URL slug
(e.g. &lt;code&gt;terrex-swift-r2-mid-gtx-triple-black-cm7500&lt;/code&gt;) as part of URL path
and gives the app a numeric product template ID to be used in some of the
further API calls. It also provides some data is common across all product
variants. To scrape it, our code will reproduce this API call with slug value
it got from search API.&lt;/p&gt;
&lt;p&gt;Like we did with search API request, we export this one to curl snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-authorization: 3&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: application/json&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authorization: Token token=&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt; --compressed -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en;q=0.9&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-st: 1691934124434&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: GOAT/2.62.0 (iPhone; iOS 16.6; Scale/2.00) Locale/en&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id: A131256965044D838D97E9AEC3CC32DE&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-original-token: 3:7b9f8feffc454bb265869bb69319201a10c0733ded5f64415904867ca6015448:V3kOFKugd0IYEzhYfgTK4QOh8dWCzZH04C4uoGYEfOekVmjMvCYLle7yVImUv8bSOoVChlY3FPELVmFZLboPxA==:1000:V6naWWAGfhIA54bPIFXyWPSpd7e9WmoWghqXoB1xwiAb0TVePEULt5nHoZFhWkpg1E4ZjMtwt1N9yfV2HCYOklHUqUy+oaAlYkACXQLwqsD21d70W55yb0UY9qHQHxY9zQcr6th//3ckUVLU/v1yWhZt/GV9jNyf6EesLG9fw+gqMWPhrpi8bDT1j5eeTR9BLmWMqrY3hmQSYRc9C7K5pQ==&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: __cf_bm=SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: currency=EUR&amp;#39;&lt;/span&gt; https://www.goat.com/api/v1/product_templates/terrex-swift-r2-mid-gtx-triple-black-cm7500/show_v2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also convert this snippet to Python:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__cf_bm&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currency&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;EUR&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-authorization&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authorization&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Token token=&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-st&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1691934124434&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GOAT/2.62.0 (iPhone; iOS 16.6; Scale/2.00) Locale/en&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;A131256965044D838D97E9AEC3CC32DE&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-original-token&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3:7b9f8feffc454bb265869bb69319201a10c0733ded5f64415904867ca6015448:V3kOFKugd0IYEzhYfgTK4QOh8dWCzZH04C4uoGYEfOekVmjMvCYLle7yVImUv8bSOoVChlY3FPELVmFZLboPxA==:1000:V6naWWAGfhIA54bPIFXyWPSpd7e9WmoWghqXoB1xwiAb0TVePEULt5nHoZFhWkpg1E4ZjMtwt1N9yfV2HCYOklHUqUy+oaAlYkACXQLwqsD21d70W55yb0UY9qHQHxY9zQcr6th//3ckUVLU/v1yWhZt/GV9jNyf6EesLG9fw+gqMWPhrpi8bDT1j5eeTR9BLmWMqrY3hmQSYRc9C7K5pQ==&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;#39;cookie&amp;#39;: &amp;#39;__cf_bm=SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=; currency=EUR&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.goat.com/api/v1/product_templates/terrex-swift-r2-mid-gtx-triple-black-cm7500/show_v2&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;cookies,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the sake of example, we will save product name, main image URL and SKU from
the API response here.&lt;/p&gt;
&lt;p&gt;We also want to save some price data and availability data. The part of UI that
shows prices and last sale amounts is populated by the &lt;code&gt;/api/v2/product_variants/buy_bar_data&lt;/code&gt;
endpoint. We extract the curl snippet from mitmproxy again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-authorization: 3&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: application/json&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authorization: Token token=&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt; --compressed -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en;q=0.9&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-st: 1691934124855&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: GOAT/2.62.0 (iPhone; iOS 16.6; Scale/2.00) Locale/en&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id: 9E8BF79CF66F4912A122C3C38F872E0E&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-original-token: 3:7b9f8feffc454bb265869bb69319201a10c0733ded5f64415904867ca6015448:V3kOFKugd0IYEzhYfgTK4QOh8dWCzZH04C4uoGYEfOekVmjMvCYLle7yVImUv8bSOoVChlY3FPELVmFZLboPxA==:1000:V6naWWAGfhIA54bPIFXyWPSpd7e9WmoWghqXoB1xwiAb0TVePEULt5nHoZFhWkpg1E4ZjMtwt1N9yfV2HCYOklHUqUy+oaAlYkACXQLwqsD21d70W55yb0UY9qHQHxY9zQcr6th//3ckUVLU/v1yWhZt/GV9jNyf6EesLG9fw+gqMWPhrpi8bDT1j5eeTR9BLmWMqrY3hmQSYRc9C7K5pQ==&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: __cf_bm=SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: currency=EUR&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.goat.com/api/v1/product_variants/buy_bar_data?countryCode=US&amp;amp;productTemplateId=terrex-swift-r2-mid-gtx-triple-black-cm7500&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_0893.PNG&#34;&gt;Screenshot 9&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-08-13_17.31.33.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The corresponding Python snippet is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__cf_bm&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currency&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;EUR&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-authorization&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authorization&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Token token=&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-st&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1691934124855&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GOAT/2.62.0 (iPhone; iOS 16.6; Scale/2.00) Locale/en&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-emb-id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;9E8BF79CF66F4912A122C3C38F872E0E&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-px-original-token&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3:7b9f8feffc454bb265869bb69319201a10c0733ded5f64415904867ca6015448:V3kOFKugd0IYEzhYfgTK4QOh8dWCzZH04C4uoGYEfOekVmjMvCYLle7yVImUv8bSOoVChlY3FPELVmFZLboPxA==:1000:V6naWWAGfhIA54bPIFXyWPSpd7e9WmoWghqXoB1xwiAb0TVePEULt5nHoZFhWkpg1E4ZjMtwt1N9yfV2HCYOklHUqUy+oaAlYkACXQLwqsD21d70W55yb0UY9qHQHxY9zQcr6th//3ckUVLU/v1yWhZt/GV9jNyf6EesLG9fw+gqMWPhrpi8bDT1j5eeTR9BLmWMqrY3hmQSYRc9C7K5pQ==&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;#39;cookie&amp;#39;: &amp;#39;__cf_bm=SHyG5WOKr777DofsXNbK3U29rw2zT0.FAfklx3N4xlA-1691934028-0-AUJcAbBlN7VoM9Sv8KU4ADRc7kRMROE5dN8u/rsVpl+cxdtwaLQTX/D/tIGlWKDEjaZfMbkE+PaMCzCrlGheIbI=; currency=EUR&amp;#39;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;countryCode&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;LT&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;productTemplateId&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;terrex-swift-r2-mid-gtx-triple-black-cm7500&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.goat.com/api/v1/product_variants/buy_bar_data&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;cookies,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quick experiment shows that removing &lt;code&gt;__cf_bm&lt;/code&gt; cookie does not cause the requests
to be blocked. However a default currency is set via another cookie, which we
probably want to keep.&lt;/p&gt;
&lt;p&gt;Since PerimeterX blocks requests based on source autonomous systems and IP
reputation we need to route the requests through a proxy pool. I find that
ISP proxies from Bright Data are good enough for this.&lt;/p&gt;
&lt;p&gt;One last thing to note is that some APIs are meant to be used after login
and thus don&amp;rsquo;t provide any data at this point.&lt;/p&gt;
&lt;p&gt;A simple Python script to implement GOAT mobile API scraping is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; quote
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ISP proxy URL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PROXY_URL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COUNTRY_CODE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CURRENCY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;USD&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;template_id&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;img_url&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brand&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;box_condition&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shoe_condition&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock_status&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lowest_price&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RETRIES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-px-authorization&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authorization&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Token token=&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en;q=0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-emb-st&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1691934124434&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GOAT/2.62.0 (iPhone; iOS 16.6; Scale/2.00) Locale/en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-emb-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;A131256965044D838D97E9AEC3CC32DE&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-px-original-token&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3:7b9f8feffc454bb265869bb69319201a10c0733ded5f64415904867ca6015448:V3kOFKugd0IYEzhYfgTK4QOh8dWCzZH04C4uoGYEfOekVmjMvCYLle7yVImUv8bSOoVChlY3FPELVmFZLboPxA==:1000:V6naWWAGfhIA54bPIFXyWPSpd7e9WmoWghqXoB1xwiAb0TVePEULt5nHoZFhWkpg1E4ZjMtwt1N9yfV2HCYOklHUqUy+oaAlYkACXQLwqsD21d70W55yb0UY9qHQHxY9zQcr6th//3ckUVLU/v1yWhZt/GV9jNyf6EesLG9fw+gqMWPhrpi8bDT1j5eeTR9BLmWMqrY3hmQSYRc9C7K5pQ==&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;proxies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: PROXY_URL, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;: PROXY_URL}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;, CURRENCY)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_product_template_data&lt;/span&gt;(session, product_slug):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.goat.com/api/v1/product_templates/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/show_v2&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(product_slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_buy_bar_data&lt;/span&gt;(session, product_slug):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.goat.com/api/v1/product_variants/buy_bar_data&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;countryCode&amp;#39;&lt;/span&gt;: COUNTRY_CODE,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;productTemplateId&amp;#39;&lt;/span&gt;: product_slug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tries_left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; RETRIES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; tries_left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tries_left &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_search&lt;/span&gt;(session, search_query):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://goat.cnstrc.com/search/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; quote(search_query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;key_XT7bjdbvjgECO5d8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;: str(page),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt_options[hidden_fields]&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gp_instant_ship_lowest_price_cents_2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt_options[hidden_facets]&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gp_instant_ship_lowest_price_cents_2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;features[display_variations]&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;feature_variants[display_variations]&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;matched&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;variations_map&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;dtype&amp;#34;:&amp;#34;object&amp;#34;,&amp;#34;group_by&amp;#34;:[{&amp;#34;name&amp;#34;:&amp;#34;product_condition&amp;#34;,&amp;#34;field&amp;#34;:&amp;#34;data.product_condition&amp;#34;},{&amp;#34;name&amp;#34;:&amp;#34;box_condition&amp;#34;,&amp;#34;field&amp;#34;:&amp;#34;data.box_condition&amp;#34;}],&amp;#34;values&amp;#34;:{&amp;#34;min_regional_instant_ship_price&amp;#34;:{&amp;#34;field&amp;#34;:&amp;#34;data.gp_instant_ship_lowest_price_cents_2&amp;#34;,&amp;#34;aggregation&amp;#34;:&amp;#34;min&amp;#34;},&amp;#34;min_regional_price&amp;#34;:{&amp;#34;field&amp;#34;:&amp;#34;data.gp_lowest_price_cents_2&amp;#34;,&amp;#34;aggregation&amp;#34;:&amp;#34;min&amp;#34;}}}&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        per_page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;num_results_per_page&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        results &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;response&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;results&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; result_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; results:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            slug &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; slug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(results) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; per_page:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;page&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;query&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;results.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; slug &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape_search(session, query):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        template_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_product_template_data(session, slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        template_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; template_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; template_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        img_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; template_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mainPictureUrl&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        brand &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; template_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sizeBrand&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sku &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; template_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        buy_bar_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_buy_bar_data(session, slug)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; buy_bar_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; buy_bar_data:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            box_condition &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buy_bar_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;boxCondition&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            shoe_condition &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buy_bar_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shoeCondition&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buy_bar_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sizeOption&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            stock_status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buy_bar_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stockStatus&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            lowest_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buy_bar_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lowestPriceCents&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; type(lowest_price) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lowest_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lowest_price &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;template_id&amp;#34;&lt;/span&gt;: template_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;: slug,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;img_url&amp;#34;&lt;/span&gt;: img_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brand&amp;#34;&lt;/span&gt;: brand,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku&amp;#34;&lt;/span&gt;: sku,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;box_condition&amp;#34;&lt;/span&gt;: box_condition,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shoe_condition&amp;#34;&lt;/span&gt;: shoe_condition,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: size,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock_status&amp;#34;&lt;/span&gt;: stock_status,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lowest_price&amp;#34;&lt;/span&gt;: lowest_price
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us go through the code. The script gets the search query via CLI parameter
(&lt;code&gt;sys.argv[1]&lt;/code&gt;) and uses that to scrape URL slugs from the search API. This
is done in &lt;code&gt;scrape_search()&lt;/code&gt; function. The code here is based on the first
Python snippet we got earlier. We implement page traversal by incrementing
&lt;code&gt;page&lt;/code&gt; parameter until the current page has less results that a full page
would (threshold value is in &lt;code&gt;request.num_results_per_page&lt;/code&gt;). This function is
a generator function - by using &lt;code&gt;yield&lt;/code&gt; instead of &lt;code&gt;return&lt;/code&gt; we make it so
that search API requests are done on as-needed basis and the function call
can be iterated upon (which we do in &lt;code&gt;main()&lt;/code&gt;, as technically the return
value is an iterable &lt;a href=&#34;https://www.trickster.dev/post/understanding-python-generators-for-scraping-and-automation&#34;&gt;generator object&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;When we get the slug, scrape the two other endpoints for product template and
pricing/availability/product condition data (a &lt;code&gt;requests.Session&lt;/code&gt; object
is used to keep the HTTP headers, cookies and proxy settings between requests).
Since GOAT is a two-sided marketplace that allows reselling of used or defective
shoes we also grab the fields about shoe and box condition. Data is merged into
Python &lt;code&gt;dict&lt;/code&gt; and written out into CSV file.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-14_17.45.23.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, there is PerimeterX SDK being integrated into GOAT mobile app. But as long
we generate our traffic through good-enough proxies and reproduce the original
headers we can scrape the data just fine, more or less. One problem is that
sometimes 5 retries are not enough to get the data from &lt;code&gt;buy_bar_data&lt;/code&gt; endpoint
when PerimeterX starts asking for captcha solution. Modifying &lt;code&gt;get_buy_bar_data()&lt;/code&gt;
function to force proxy rotation on failed request is left as an exercise for
the reader. But the larger point here is that anti-automation security measures
on mobile app APIs are often not very tight and can be easily defeated even for
prominent, well funded targets such as GOAT. To be fair it is harder to scrape
behind login, but that&amp;rsquo;s a topic for another post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scrapy by example: scraping company data from Yelp</title>
      <link>https://www.trickster.dev/post/scrapy-by-example-scraping-company-data-from-yelp/</link>
      <pubDate>Sat, 12 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scrapy-by-example-scraping-company-data-from-yelp/</guid>
      <description>&lt;p&gt;Yelp is a major yellow pages portal in USA and some other countries. It provides
large amount of data on various businesses - phone numbers, addresses,
descriptions, reviews, working hours and so on. Yelp provides a
&lt;a href=&#34;https://docs.developer.yelp.com/reference/v3_business_search&#34;&gt;public API&lt;/a&gt;, but
it is &lt;a href=&#34;https://docs.developer.yelp.com/docs/fusion-rate-limiting&#34;&gt;rate-limited&lt;/a&gt;
to 500 requests per 24 hours. Thus we will be doing some web scraping to
extract data from Yelp web pages.&lt;/p&gt;
&lt;p&gt;Let us do some planning and strategy work first. This entails browsing the page
with Chrome DevTools open, taking some notes and also loading some pages via
Scrapy shell, e.g:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy shell &amp;#34;https://www.yelp.com/search?find_desc=Pizza&amp;amp;find_loc=San+Francisco%2C+CA%2C+United+States&amp;#34;
2023-08-11 14:06:14 [scrapy.utils.log] INFO: Scrapy 2.8.0 started (bot: scrapybot)
2023-08-11 14:06:14 [scrapy.utils.log] INFO: Versions: lxml 4.9.2.0, libxml2 2.9.13, cssselect 1.2.0, parsel 1.7.0, w3lib 2.1.1, Twisted 22.10.0, Python 3.11.4 (main, Jun 20 2023, 17:23:00) [Clang 14.0.3 (clang-1403.0.22.14.1)], pyOpenSSL 23.1.1 (OpenSSL 3.1.0 14 Mar 2023), cryptography 40.0.1, Platform macOS-13.3.1-arm64-arm-64bit
2023-08-11 14:06:14 [scrapy.crawler] INFO: Overridden settings:
{&amp;#39;DUPEFILTER_CLASS&amp;#39;: &amp;#39;scrapy.dupefilters.BaseDupeFilter&amp;#39;,
 &amp;#39;LOGSTATS_INTERVAL&amp;#39;: 0}
2023-08-11 14:06:14 [py.warnings] WARNING: /opt/homebrew/lib/python3.11/site-packages/scrapy/utils/request.py:232: ScrapyDeprecationWarning: &amp;#39;2.6&amp;#39; is a deprecated value for the &amp;#39;REQUEST_FINGERPRINTER_IMPLEMENTATION&amp;#39; setting.

It is also the default value. In other words, it is normal to get this warning if you have not defined a value for the &amp;#39;REQUEST_FINGERPRINTER_IMPLEMENTATION&amp;#39; setting. This is so for backward compatibility reasons, but it will change in a future version of Scrapy.

See the documentation of the &amp;#39;REQUEST_FINGERPRINTER_IMPLEMENTATION&amp;#39; setting for information on how to handle this deprecation.
  return cls(crawler)

2023-08-11 14:06:14 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor
2023-08-11 14:06:14 [scrapy.extensions.telnet] INFO: Telnet Password: 20481848a3d75456
2023-08-11 14:06:14 [scrapy.middleware] INFO: Enabled extensions:
[&amp;#39;scrapy.extensions.corestats.CoreStats&amp;#39;,
 &amp;#39;scrapy.extensions.telnet.TelnetConsole&amp;#39;,
 &amp;#39;scrapy.extensions.memusage.MemoryUsage&amp;#39;]
2023-08-11 14:06:15 [scrapy.middleware] INFO: Enabled downloader middlewares:
[&amp;#39;scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.useragent.UserAgentMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.retry.RetryMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.RedirectMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.cookies.CookiesMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.stats.DownloaderStats&amp;#39;]
2023-08-11 14:06:15 [scrapy.middleware] INFO: Enabled spider middlewares:
[&amp;#39;scrapy.spidermiddlewares.httperror.HttpErrorMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.offsite.OffsiteMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.referer.RefererMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.urllength.UrlLengthMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.depth.DepthMiddleware&amp;#39;]
2023-08-11 14:06:15 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2023-08-11 14:06:15 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2023-08-11 14:06:15 [scrapy.core.engine] INFO: Spider opened
2023-08-11 14:06:17 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET https://www.yelp.com/search?find_desc=Pizza&amp;amp;find_loc=San+Francisco%2C+CA%2C+United+States&amp;gt; (referer: None)
2023-08-11 14:06:17 [asyncio] DEBUG: Using selector: KqueueSelector
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    &amp;lt;scrapy.crawler.Crawler object at 0x107083e10&amp;gt;
[s]   item       {}
[s]   request    &amp;lt;GET https://www.yelp.com/search?find_desc=Pizza&amp;amp;find_loc=San+Francisco%2C+CA%2C+United+States&amp;gt;
[s]   response   &amp;lt;200 https://www.yelp.com/search?find_desc=Pizza&amp;amp;find_loc=San+Francisco%2C+CA%2C+United+States&amp;gt;
[s]   settings   &amp;lt;scrapy.settings.Settings object at 0x106213ed0&amp;gt;
[s]   spider     &amp;lt;DefaultSpider &amp;#39;default&amp;#39; at 0x108044bd0&amp;gt;
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects 
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
2023-08-11 14:06:17 [asyncio] DEBUG: Using selector: KqueueSelector
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the sucessfull request here - it means we&amp;rsquo;re not blocked by any security
mechanism (yet).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-11_13.54.33.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Like many other websites in yellow pages world, Yelp has search feature with
pagination and links to company detail pages.  There&amp;rsquo;s also a map on he right
hand side. If we wanted to get the map annotation coordinates we could extract
them from JSON that is cooked into the page source. Yelp frontend is API-driven
to a degree, but we can load the full HTML for each page. In this example,
we will be running XPath queries on the pages downloaded by Scrapy.&lt;/p&gt;
&lt;p&gt;For example, we can extract links to company detail pages like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [1]: response.xpath(&amp;#39;//a[starts-with(@href, &amp;#34;/biz/&amp;#34;)]/@href&amp;#39;).getall()
Out[1]: 
[&amp;#39;/biz/golden-boy-pizza-san-francisco?osq=Pizza&amp;#39;,
 &amp;#39;/biz/golden-boy-pizza-san-francisco?osq=Pizza&amp;#39;,
 &amp;#39;/biz/golden-boy-pizza-san-francisco?hrid=QySEJtXN3DGb0KEVAmxF6w&amp;amp;osq=Pizza&amp;#39;,
 &amp;#39;/biz/golden-boy-pizza-san-francisco?hrid=QySEJtXN3DGb0KEVAmxF6w&amp;amp;osq=Pizza&amp;#39;,
 &amp;#39;/biz/golden-boy-pizza-san-francisco?hrid=QySEJtXN3DGb0KEVAmxF6w&amp;amp;osq=Pizza&amp;#39;,
 &amp;#39;/biz/golden-boy-pizza-san-francisco?hrid=QySEJtXN3DGb0KEVAmxF6w&amp;amp;osq=Pizza&amp;#39;,
 &amp;#39;/biz/PTFxtXS47ZVRCdZIrEWvGw?show_platform_modal=True&amp;#39;,
 &amp;#39;/biz/tonys-pizza-napoletana-san-francisco?osq=Pizza&amp;#39;,
 &amp;#39;/biz/tonys-pizza-napoletana-san-francisco?osq=Pizza&amp;#39;,

...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The XPath query here is based on company page URL path always starting with
&lt;code&gt;/biz/&lt;/code&gt;. We see some duplication here, but that&amp;rsquo;s not going to be a problem
as Scrapy will deduplicate the requests for us.&lt;/p&gt;
&lt;p&gt;We also need a link to next page of search results. That can be extracted from
&amp;ldquo;&amp;gt;&amp;rdquo; element that we can match by &lt;code&gt;aria-label&lt;/code&gt; being set to &lt;code&gt;Next&lt;/code&gt;. The XPath
query here is also rather simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [2]: response.xpath(&amp;#39;//a[@aria-label=&amp;#34;Next&amp;#34;]/@href&amp;#39;).get()
Out[2]: &amp;#39;https://www.yelp.com/search?find_desc=Pizza&amp;amp;find_loc=San%20Francisco%2C%20CA%2C%20United%20States&amp;amp;start=10&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-11_14.16.47.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we can explore the company details page. Let us also load it in Scrapy shell:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [3]: fetch(&amp;#34;https://www.yelp.com/biz/flour-water-pizza-san-francisco?osq=Pizza&amp;#34;)
2023-08-11 14:24:22 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET https://www.yelp.com/biz/flour-water-pizza-san-francisco?osq=Pizza&amp;gt; (referer: None)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, we get a fairly simple case of HTML being created on the server with not
much done on the client side to render the data. Thus we can also run some
simple XPath queries here to extract some information on the company:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [4]: response.xpath(&amp;#34;//h1/text()&amp;#34;).get()
Out[4]: &amp;#39;Flour + Water Pizza&amp;#39;

In [5]: response.xpath(&amp;#39;//div[./p/a[text()=&amp;#34;Get Directions&amp;#34;]]/p[last()]/text()&amp;#39;).get()
Out[5]: &amp;#39;532 Columbus Ave San Francisco, CA 94133&amp;#39;

In [6]: response.xpath(&amp;#39;//img[contains(@src, &amp;#34;maps.googleapis.com&amp;#34;)]/@src&amp;#39;).get()
Out[6]: &amp;#39;https://maps.googleapis.com/maps/api/staticmap?size=315x150&amp;amp;sensor=false&amp;amp;client=gme-yelp&amp;amp;language=en&amp;amp;scale=1&amp;amp;zoom=15&amp;amp;center=37.800084%2C-122.409429&amp;amp;markers=scale%3A1%7Cicon%3Ahttps%3A%2F%2Fyelp-images.s3.amazonaws.com%2Fassets%2Fmap-markers%2Fannotation_32x43.png%7C37.800084%2C-122.409429&amp;amp;signature=IGF-J6T8eel638VQaoUG3pY1BtA=&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-11_14.25.44.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-08-11_14.26.27.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The little map from Google Maps is not interactive. It&amp;rsquo;s a static image generated
by Google Maps API. The URL usually (but not always) has a &lt;code&gt;center&lt;/code&gt; parameter
with company location coordinates. In some cases this little map shows the
service coverage area. We will be extracting company coordinates from the image
source URL when possible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-11_14.34.31.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We run the following two commands to generate a Scrapy project with spider
boilerplate:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy startproject recon .
$ scrapy genspider yelp yelp.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We edit items.py file to create an item class for data we scrape from Yelp
company page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define here the models for your scraped items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See documentation in:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# https://docs.scrapy.org/en/latest/topics/items.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YelpItem&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# define the fields for your item here like:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    yelp_biz_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    category &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    phone_number &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    website &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    latitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    gmaps_img_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    yelp_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We got the usual fields here - company name, address, phone number, address
website URL and so on. We also include the URL to page we scrape to enable
checks on the scraped data.&lt;/p&gt;
&lt;p&gt;The Scrapy spider code in yelp.py file is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urlencode, urlparse, parse_qsl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; recon.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; YelpItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YelpSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yelp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yelp.com&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://yelp.com/&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    locations &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queries &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, locations&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;United States&amp;#34;&lt;/span&gt;, queries&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Pizza|Coffee&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locations &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; locations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;queries &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; queries&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; location &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locations:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; query &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;queries:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.yelp.com/search&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;find_desc&amp;#34;&lt;/span&gt;: query, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;find_loc&amp;#34;&lt;/span&gt;: location}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_company_list, meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;: query}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_company_list&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        category &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_links &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[starts-with(@href, &amp;#34;/biz/&amp;#34;)]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_links &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; cl: cl&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], company_links))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_links &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(set(company_links))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; company_link &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; company_links:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                company_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_company_details,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;: category},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_page_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[@aria-label=&amp;#34;Next&amp;#34;]/@href&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_url &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                next_page_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_company_list,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;: category},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_company_details&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        category &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; YelpItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;category&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yelp_biz_id&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//meta[@name=&amp;#34;yelp-biz-id&amp;#34;]/@content&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//h1/text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//meta[@property=&amp;#34;og:description&amp;#34;]/@content&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone_number&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[./p[text()=&amp;#34;Phone number&amp;#34;]]/p[last()]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[./p[text()=&amp;#34;Business website&amp;#34;]]/p[last()]/a/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/biz_redir&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            o &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qsl(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; params&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[./p/a[text()=&amp;#34;Get Directions&amp;#34;]]/p[last()]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gmaps_img_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//img[contains(@src, &amp;#34;maps.googleapis.com&amp;#34;)]/@src&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        o &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(gmaps_img_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gmaps_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qsl(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gmaps_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(gmaps_params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(gmaps_params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        center &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_params&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;center&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; center &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                center &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_params&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;markers&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; center &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            latitude, longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; center&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;latitude&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; latitude
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;longitude&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; longitude
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gmaps_img_url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gmaps_img_url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yelp_url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This spider is parametrised by location(s) and search queries that can be
passed via CLI. Since both can include commas in their values we expect the
user to use pipe symbol as separator. This aspect is accomplished in the
constructor method (&lt;code&gt;__init__&lt;/code&gt;). Note that Yelp limits the amount of search
results per query-location pair, which makes search space sharding potentially
applicable here.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;start_requests()&lt;/code&gt;, a standard Scrapy spider method, we iterate across
both values of both parameters in nested loops to generate requests to
initial search pages. We pass &lt;code&gt;parse_company_list()&lt;/code&gt; as callback to request.
When Scrapy fetches the page, it calls this method to parse it. At this
callback, we do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We extract company page links (relative URLs) and generate requests to get
them. This time callback is another method - &lt;code&gt;parse_company_details&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We extract the URL of next search page if there is one and generate a request
to it. Since it will be the same kind of page with company links, the callback
is the same.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We use the &lt;code&gt;meta&lt;/code&gt; dictionary to pass the original search query along the
request flow, as we want to preserve it in the scraped data item. Commonly
the query is a business category. That&amp;rsquo;s why we name it as such.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;parse_company_details&lt;/code&gt; method we apply XPath queries based on the
page structure to extract the data we need. We already discussed a small
complication with coordinates, but there&amp;rsquo;s also something we need to do
about the website URL. To track clicks to the company website, Yelp links it
through a redirect. Thus we use &lt;code&gt;urlparse&lt;/code&gt; and &lt;code&gt;parse_qsl&lt;/code&gt; functions from
&lt;a href=&#34;https://docs.python.org/3/library/urllib.parse.html&#34;&gt;&lt;code&gt;urllib.parse&lt;/code&gt;&lt;/a&gt;
module to get a clean, direct URL. The code to extract rest of the fields is
rather straightforward and trivially based on DOM structure. If the XPath
queries look puzzling to you please read an earlier post on
&lt;a href=&#34;https://www.trickster.dev/post/the-very-basics-of-xpath/&#34;&gt;the very basics of XPath&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s a little problem we have not addressed yet. Yelp does not have
any agressive countermeasures deployed and it does not use any commercial
solutions against scraping, but it does one annoying thing - temporary IP
bans on excessive traffic. Thus we need some proxies here. I find that DC
proxies are enough, but it&amp;rsquo;s best to force exit IP randomisation (e.g. rotating
the proxy pool exit on every request). This is implemented in the following
Scrapy middleware:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define here the models for your spider middleware&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See documentation in:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# https://docs.scrapy.org/en/latest/topics/spider-middleware.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; signals
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; recon.settings &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ENABLED,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ZONE_USERNAME,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ZONE_PASSWORD,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; w3lib.http &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; basic_auth_header
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; random
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BrightDataDownloaderMiddleware&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@classmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from_crawler&lt;/span&gt;(cls, crawler):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; cls()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_request&lt;/span&gt;(self, request, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; BRIGHT_DATA_ENABLED:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://brd.superproxy.io:22225&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; BRIGHT_DATA_ZONE_USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-session-&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; str(random&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;random())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Proxy-Authorization&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; basic_auth_header(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            username, BRIGHT_DATA_ZONE_PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since I use Bright Data, this entails a little vendor lock-in. This is updated
version of middleware code from the
&lt;a href=&#34;https://www.trickster.dev/post/writing-custom-scrapy-middleware-for-proxy-pool-integration/&#34;&gt;previous post&lt;/a&gt;.
Note that &lt;code&gt;-session-&lt;/code&gt; string and a random number is appended to proxy zone
username. This approach is based on sample code from Bright Data.&lt;/p&gt;
&lt;p&gt;To integrate this middleware into the Scrapy project, we have to edit the
settings.py file as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BRIGHT_DATA_ENABLED &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BRIGHT_DATA_ZONE_USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BRIGHT_DATA_ZONE_PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DOWNLOADER_MIDDLEWARES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;recon.middlewares.BrightDataDownloaderMiddleware&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I also decided to raise &lt;code&gt;CONCURRENT_REQUESTS&lt;/code&gt; and &lt;code&gt;RETRY_TIMES&lt;/code&gt; to make the
scraping more agressive:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Configure maximum concurrent requests performed by Scrapy (default: 16)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONCURRENT_REQUESTS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RETRY_TIMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This covers the possibility of some exit IPs getting blocked by allowing for
more retries to be made. It also speeds up the scraping by doing more requests
concurrently.&lt;/p&gt;
&lt;p&gt;Lastly, I disabled cookie handling, because we don&amp;rsquo;t need cookies to scrape
this site:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Disable cookies (enabled by default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COOKIES_ENABLED &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I could probably have left this on, but now I eliminate the possibility of
cookies being used to cross-correlate requests to thwart or hinder the scraping.&lt;/p&gt;
&lt;p&gt;So if we wanted to scrape data about coffee shops and pizza places in both
San Francisco, CA and New York City we could run the Scrapy project like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy runspider -a &amp;#34;locations=San Francisco, CA|New York, NY&amp;#34; \
  -a &amp;#34;queries=pizza|coffee&amp;#34; recon/spiders/yelp.py -O yelp.csv
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>Back to the terminal: the new era of CLI and TUI software</title>
      <link>https://www.trickster.dev/post/back-to-the-terminal-the-new-era-of-cli-and-tui-software/</link>
      <pubDate>Wed, 09 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/back-to-the-terminal-the-new-era-of-cli-and-tui-software/</guid>
      <description>&lt;p&gt;With desktop GUI apps becoming generally becoming increasingly bloated and
sluggish over time (largely thanks to growing popularity of Electron.js) some
technical users are turning back to sofware using the simpler and leaner user
interfaces - command line and pseudo-GUIs being rendered in ASCII/Unicode (text
user interfaces). CLI tools have the additional benefit of being relatively easy
to be used via subprocceses from automation scripts. We will take a look at
some modern software that is meant be used via terminal.&lt;/p&gt;
&lt;h2 id=&#34;carbonyl-21st-century-lynx&#34;&gt;Carbonyl: 21st Century Lynx&lt;/h2&gt;
&lt;p&gt;Remember &lt;a href=&#34;https://lynx.invisible-island.net/&#34;&gt;Lynx&lt;/a&gt;, a text-only web browser
to render web pages in simplified, text only form? Funny thing is, Lynx is still
being maintained to some degree, but lack of support for client-side JavaScript
largely makes it obsolete for browsing the modern web, even if we consider the
lack of image rendering to be a feature.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/fathyb/carbonyl&#34;&gt;Carbonyl&lt;/a&gt; is a Chromium fork that is a
modern take on TUI-based web browser idea. If you have Docker installed you can
easily try it out:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ docker run --rm -ti fathyb/carbonyl https://trickster.dev
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-08_14.36.27.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-08-08_14.41.51.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It works quite well with simpler sites, but it can run into issues when you
trip something up in anti-automation solutions. Another limitation is that you
cannot watch videos with sound.&lt;/p&gt;
&lt;h2 id=&#34;ag-ripgrep---the-new-grep1&#34;&gt;ag, ripgrep - the new grep(1)&lt;/h2&gt;
&lt;p&gt;You use grep(1) when you want to search something in source code or text file(s).
&lt;a href=&#34;https://github.com/ggreer/the_silver_searcher&#34;&gt;The Silver Search&lt;/a&gt; (&lt;code&gt;ag&lt;/code&gt;) is
a tool similar to grep, but designed to search for substrings in source code
and optimised for that end.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/BurntSushi/ripgrep&#34;&gt;Ripgrep&lt;/a&gt; is another grep-like tool to
run fast searches for text in a directory tree (with regex support), while
also respecting your .gitignore file.&lt;/p&gt;
&lt;h2 id=&#34;bottom-btop-glances---better-kind-of-top1&#34;&gt;bottom, btop, glances - better kind of top(1)&lt;/h2&gt;
&lt;p&gt;In Linux/Unix systems top(1) is a TUI tool that renders a list of running
processes with their resource consumption numbers - similar to Activity Monitor
in macOS or Task Manager in Windows. Many tech people are aware of
&lt;a href=&#34;https://htop.dev/&#34;&gt;htop&lt;/a&gt; - an alternative tool with enhanced interactivity.&lt;/p&gt;
&lt;p&gt;But we can go even further. &lt;a href=&#34;https://github.com/ClementTsang/bottom&#34;&gt;Bottom&lt;/a&gt;
(&lt;code&gt;btm&lt;/code&gt;) is a Rust program that renders resource consumption graphs in Unicode
pseudo-graphics with a process list.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-08_14.56.57.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/aristocratos/btop&#34;&gt;btop&lt;/a&gt; and
&lt;a href=&#34;https://github.com/nicolargo/glances&#34;&gt;glances&lt;/a&gt; are another programs similar to
Bottom.&lt;/p&gt;
&lt;h2 id=&#34;terminal-image-viewer-tiv&#34;&gt;Terminal Image Viewer (tiv)&lt;/h2&gt;
&lt;p&gt;But what if you want to take a quick glance at pictures without having a GUI
environment? Perhaps you have some web scraping project that involves downloading
images and it&amp;rsquo;s bit of pain to use scp(1) or sftp(1) to download them from
the server in order to preview them. &lt;a href=&#34;https://github.com/stefanhaustein/TerminalImageViewer&#34;&gt;Terminal Image Viewer&lt;/a&gt;
is a C++ program that relies on features of modern terminals for actually
quite good image rendering.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-09_10.32.36.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note however that this does not work on default Terminal app on macOS - you
will need iTerm for this.&lt;/p&gt;
&lt;h2 id=&#34;httpie---command-line-api-client&#34;&gt;HTTPie - command line API client&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://httpie.io/&#34;&gt;HTTPie&lt;/a&gt; is a CLI tool designed specifically for launching
requests against RESTful APIs. Unline curl, which is more general tool, HTTPie
provides a simple CLI that directly maps into aspects of HTTP requests you want
to generate:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ http GET https://ifconfig.me/all.json
HTTP/1.1 200 OK
Alt-Svc: h3=&amp;#34;:443&amp;#34;; ma=2592000,h3-29=&amp;#34;:443&amp;#34;; ma=2592000
Via: 1.1 google
access-control-allow-origin: *
content-length: 268
content-type: application/json; charset=utf-8
date: Tue, 08 Aug 2023 12:17:07 GMT
server: istio-envoy
strict-transport-security: max-age=2592000; includeSubDomains
x-envoy-upstream-service-time: 1

{
    &amp;#34;encoding&amp;#34;: &amp;#34;gzip, deflate, br&amp;#34;,
    &amp;#34;forwarded&amp;#34;: &amp;#34;[REDACTED], 34.160.111.145,35.191.2.1&amp;#34;,
    &amp;#34;ip_addr&amp;#34;: &amp;#34;[REDACTED]&amp;#34;,
    &amp;#34;method&amp;#34;: &amp;#34;GET&amp;#34;,
    &amp;#34;mime&amp;#34;: &amp;#34;*/*&amp;#34;,
    &amp;#34;port&amp;#34;: 60392,
    &amp;#34;remote_host&amp;#34;: &amp;#34;unavailable&amp;#34;,
    &amp;#34;user_agent&amp;#34;: &amp;#34;HTTPie/3.2.2&amp;#34;,
    &amp;#34;via&amp;#34;: &amp;#34;1.1 google&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see it also pretty-prints the JSON payload in the response.&lt;/p&gt;
&lt;h2 id=&#34;jq---cli-tool-and-dsl-for-json-processing&#34;&gt;jq - CLI tool and DSL for JSON processing&lt;/h2&gt;
&lt;p&gt;If pretty-printing the JSON is not enough, you can use &lt;a href=&#34;https://jqlang.github.io/jq/&#34;&gt;jq&lt;/a&gt; -
a CLI tool that provides an entire Domain Specific Language for JSON wrangling.&lt;/p&gt;
&lt;h2 id=&#34;tshark---cli-equivalent-of-wireshark&#34;&gt;tshark - CLI equivalent of Wireshark&lt;/h2&gt;
&lt;p&gt;Wireshark is a prominent packet sniffer used by network engineers, security
people and software developers. &lt;a href=&#34;https://www.wireshark.org/docs/man-pages/tshark.html&#34;&gt;TShark&lt;/a&gt;
is a relatively obscure sibling of Wireshark that is CLI-based. Think of it
as a tcpdump(1), but better. It can be used to not only capture network traffic,
but to run various filters and statistical techniques for traffic analysis
purposes.&lt;/p&gt;
&lt;h2 id=&#34;termshark&#34;&gt;TermShark&lt;/h2&gt;
&lt;p&gt;But what if you want something like Wireshark GUI in your terminal environment?
&lt;a href=&#34;https://termshark.io/&#34;&gt;TermShark&lt;/a&gt; is a TUI wrapper for TShark that renders
a user interface similar to that of Wireshark.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-09_14.40.29.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;mitmproxy&#34;&gt;mitmproxy&lt;/h2&gt;
&lt;p&gt;When doing exploration of web and mobile apps for security research or
automation one may want to intercept API traffic to see what conversations
between the app and remote servers. &lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; is
an interactive TUI program that works as HTTP(S) proxy server meant to capture
and inspect the HTTP(S) requests. It&amp;rsquo;s a simpler alternative to Burp Suite GUI
app that you can also customise with your own plugin code.&lt;/p&gt;
&lt;h2 id=&#34;neovim&#34;&gt;neovim&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://neovim.io/&#34;&gt;Neovim&lt;/a&gt; is a modernised and more extensible addition to
the vi/VIM text editor lineage with features like Language Server Protocol
support, Lua scripting interface, improved usability, RPC API, system clipboard
integration, built-in terminal emulator and so on.&lt;/p&gt;
&lt;h2 id=&#34;github-cli-tool&#34;&gt;GitHub CLI tool&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s git(1), and there&amp;rsquo;s GitHub. The former is CLI tool and the latter is
web-based platform. To bridge the gap between the two there&amp;rsquo;s a Github
&lt;a href=&#34;https://cli.github.com/&#34;&gt;CLI tool&lt;/a&gt; for doing tasks like working with tickets,
managing pull requests and configuring the CI system. This can be helpful
when doing devops work to automate various aspects of software development
lifecycle.&lt;/p&gt;
&lt;h2 id=&#34;mapscii&#34;&gt;mapscii&lt;/h2&gt;
&lt;p&gt;But what if you want to see some maps in your terminal? You can do that with
&lt;a href=&#34;https://github.com/rastapasta/mapscii&#34;&gt;mapscii&lt;/a&gt; - a Node.JS program that
renders OpenStreetMap data in terminal environment with mouse support. If you
don&amp;rsquo;t want to install it via NPM you can access a test instance via telnet
protocol:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ telnet mapscii.me  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-09_13.27.37.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;visidata&#34;&gt;Visidata&lt;/h2&gt;
&lt;p&gt;In scraping and automation it is very common to deal with tabular data in formats
such as CSV files, Excel spreadsheets, SQLite databases and so on.
&lt;a href=&#34;https://www.visidata.org/&#34;&gt;Visidata&lt;/a&gt; is TUI application for doing exactly that.
It does a good subset of what Excel can do - data filtering, rendering some
charts (for example, Shift-F quickly renders a histogram of values in the current
column), lettting you edit cells and so on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-09_13.39.44.png&#34;&gt;Screenshot 7&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-08-09_13.40.02.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since Visidata is developed in Python you can install it via PIP. It is also
available on packet managers of some Linux systems.&lt;/p&gt;
&lt;h2 id=&#34;radare2&#34;&gt;Radare2&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://rada.re/n/&#34;&gt;Radare2&lt;/a&gt; is a programmable binary reverse engineering
toolkit for low-level hackers. Besides the main TUI program it also ships
&lt;code&gt;r2pipe&lt;/code&gt; - a Python module for scripting binary analysis workflows (disassembly,
binary code analysis, runtime debugging).&lt;/p&gt;
&lt;h2 id=&#34;rich-and-textual&#34;&gt;Rich and Textual&lt;/h2&gt;
&lt;p&gt;These are all nice examples, but what if you want to develop something like
this yourself? Writing ncurses-based C programs is not the most productive
way to spend time in 2023. When it comes to Python CLI/TUI programming, there
are two inter-related projects of interest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Textualize/rich&#34;&gt;Rich&lt;/a&gt; - a library for enhanced text
formatting.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Textualize/textual&#34;&gt;Textual&lt;/a&gt; - a TUI programming framework,
based on Rich (with CSS support).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get a glimpse of what it can do, you can run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 -m textual
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-08-09_14.15.42.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Restringer: modular JavaScript deobfuscator</title>
      <link>https://www.trickster.dev/post/restringer-modular-javascript-deobfuscator/</link>
      <pubDate>Sun, 30 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/restringer-modular-javascript-deobfuscator/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/PerimeterX/restringer&#34;&gt;Restringer&lt;/a&gt; is a modular Javascript
deobfuscation tool that attempts to autodetect and undo some common JS
obfuscation techniques. This tool was developed by PerimeterX for malware
analysis purposes. Besides CLI tool and a NPM module (for custom deobfuscator
development), Restringer is also available as &lt;a href=&#34;https://restringer.tech/&#34;&gt;web app&lt;/a&gt;.
We will be taking a look into the inner workings of this tool for educational
purposes.&lt;/p&gt;
&lt;p&gt;The underlying data model that Restringer operates on is not based on Babel
flavour of Abstract Syntax Tree. Instead, it uses a library called
&lt;a href=&#34;https://github.com/PerimeterX/flast&#34;&gt;flAST&lt;/a&gt; (also developed by PerimeterX)
to parse the code into &lt;em&gt;flattened&lt;/em&gt; form of AST, modify it and regenerate the
code. AST flattening is a key concept in the Restringer project. Unlike in Babel,
ASTs are not represented as tree structures with references between nodes.
Instead, it is represented as an array of nodes that point to related nodes
via references:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v20.4.0.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; const {generateFlatAST} = require(&amp;#39;flast&amp;#39;);
undefined
&amp;gt; const ast = generateFlatAST(`console.log(1 + 2 + 3);`);
undefined
&amp;gt; ast
[
  &amp;lt;ref *1&amp;gt; Node {
    type: &amp;#39;Program&amp;#39;,
    start: 0,
    end: 23,
    range: [ 0, 23 ],
    body: [ [Node] ],
    sourceType: &amp;#39;module&amp;#39;,
    comments: [],
    srcClosure: [Function (anonymous)],
    nodeId: 0,
    childNodes: [ [Node] ],
    parentNode: null,
    parentKey: &amp;#39;&amp;#39;,
    lineage: [],
    isScopeBlock: true,
    allScopes: { &amp;#39;0&amp;#39;: [GlobalScope] },
    scope: &amp;lt;ref *2&amp;gt; GlobalScope {
      type: &amp;#39;global&amp;#39;,
      set: Map(0) {},
      taints: Map(0) {},
      dynamic: true,
      block: [Circular *1],
      through: [Array],
      variables: [],
      references: [Array],
      variableScope: [Circular *2],
      functionExpressionScope: false,
      directCallToEvalScope: false,
      thisFound: false,
      __left: null,
      upper: null,
      isStrict: false,
      childScopes: [],
      __declaredVariables: [WeakMap],
      implicit: [Object]
    }
  },
  &amp;lt;ref *3&amp;gt; Node {
    type: &amp;#39;ExpressionStatement&amp;#39;,
    start: 0,
    end: 23,
    range: [ 0, 23 ],
    expression: Node {
      type: &amp;#39;CallExpression&amp;#39;,
      start: 0,
      end: 22,
      range: [Array],
      callee: [Node],
      arguments: [Array],
      optional: false,
      nodeId: 2,
      childNodes: [Array],
      parentNode: [Circular *3],
      parentKey: &amp;#39;expression&amp;#39;,
      lineage: [Array],
      scope: [GlobalScope]
    },
    ...
  &amp;lt;ref *12&amp;gt; Node {
    type: &amp;#39;Literal&amp;#39;,
    start: 20,
    end: 21,
    range: [ 20, 21 ],
    value: 3,
    raw: &amp;#39;3&amp;#39;,
    nodeId: 10,
    childNodes: [],
    parentNode: &amp;lt;ref *8&amp;gt; Node {
      type: &amp;#39;BinaryExpression&amp;#39;,
      start: 12,
      end: 21,
      range: [Array],
      left: [Node],
      operator: &amp;#39;+&amp;#39;,
      right: [Circular *12],
      nodeId: 6,
      childNodes: [Array],
      parentNode: [Node],
      parentKey: &amp;#39;arguments&amp;#39;,
      lineage: [Array],
      scope: [GlobalScope]
    },
    parentKey: &amp;#39;right&amp;#39;,
    lineage: [ 0, 1, 2, 6 ],
    scope: &amp;lt;ref *2&amp;gt; GlobalScope {
      type: &amp;#39;global&amp;#39;,
      set: Map(0) {},
      taints: Map(0) {},
      dynamic: true,
      block: [Node],
      through: [Array],
      variables: [],
      references: [Array],
      variableScope: [Circular *2],
      functionExpressionScope: false,
      directCallToEvalScope: false,
      thisFound: false,
      __left: null,
      upper: null,
      isStrict: false,
      childScopes: [],
      __declaredVariables: [WeakMap],
      implicit: [Object]
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Furthermore, flAST data model tracks variable scopes to associate symbol
declarations with their uses.&lt;/p&gt;
&lt;p&gt;To let us modify the code in this flat AST form, flAST provides us with another
abstraction - the &lt;code&gt;Arborist&lt;/code&gt; object. There are two steps to making a code
change with flAST - we first mark the nodes we want to replace or delete,
then call &lt;code&gt;applyChanges()&lt;/code&gt; method. The README file provides a simple
example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;generateFlatAST&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;generateCode&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;Arborist&lt;/span&gt;} &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;flast&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generateFlatAST&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`console.log(&amp;#39;Hello&amp;#39; + &amp;#39; &amp;#39; + &amp;#39;there!&amp;#39;);`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;replacements&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;General&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;there!&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Kenobi&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arborist&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Arborist&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Mark all relevant nodes for replacement.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Literal&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;replacements&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;]).&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arborist&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;markNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Literal&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;replacements&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;replacements&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;`&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;numberOfChangesMade&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arborist&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;applyChanges&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;generateCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arborist&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]));  &lt;span style=&#34;color:#75715e&#34;&gt;// console.log(&amp;#39;General&amp;#39; + &amp;#39; &amp;#39; + &amp;#39;Kenobi&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the key conceptual difference in how the code modification is done here vs.
in Babel-based code: instead of traversing the AST recursively and applying the
visitor function we would need to use an &lt;code&gt;filter()&lt;/code&gt; API that is based on a
functional programming approach.&lt;/p&gt;
&lt;p&gt;Another library that Restringer depends on is
&lt;a href=&#34;https://github.com/PerimeterX/obfuscation-detector/tree/main&#34;&gt;Obfuscation Detector&lt;/a&gt; -
also from PerimeterX. This library applies pattern matching on flattened ASTs
to detect several obfuscation techniques. For example,
&lt;a href=&#34;https://github.com/PerimeterX/obfuscation-detector/blob/main/src/detectors/arrayReplacements.js&#34;&gt;src/detectors/arrayReplacements.js&lt;/a&gt; file
contains the following code to detect &lt;a href=&#34;https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-defeating-string-array-mapping/&#34;&gt;string array mapping&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayHasMinimumRequiredReferences&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;findArrayDeclarationCandidates&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/sharedDetectionMethods&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;obfuscationName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;array_replacements&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Array Replacements obfuscation type has the following characteristics:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * - An array (A) is with many strings is defined.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * - There are many member expression references where the object is array A.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {ASTNode[]} flatTree
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @return {string} The obfuscation name if detected; empty string otherwise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;detectArrayReplacements&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;candidates&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;findArrayDeclarationCandidates&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;candidates&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;references&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayHasMinimumRequiredReferences&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;refs&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;obfuscationName&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;detectArrayReplacements&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;detectArrayReplacements()&lt;/code&gt; is a detector function that relies on two other
functions: &lt;code&gt;findArrayDeclarationCandidates&lt;/code&gt; and &lt;code&gt;arrayHasMinimumRequiredReferences&lt;/code&gt;
from &lt;a href=&#34;https://github.com/PerimeterX/obfuscation-detector/blob/main/src/detectors/sharedDetectionMethods.js&#34;&gt;sharedDetectionMethods.js&lt;/a&gt;
file. The former is looking for the array expressions that look like they are
used to do string array mapping by applying some checks within predicate of
a &lt;code&gt;filter()&lt;/code&gt; call:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {ASTNode[]} flatTree
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @return {ASTNode[]} Candidates matching the target profile of an array with more than a few items, all literals.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;findArrayDeclarationCandidates&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;VariableDeclarator&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ArrayExpression&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayHasMeaningfulContentLength&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;some&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;el&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;el&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Literal&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us unpack the predicate here. First, it checks if node type is &lt;code&gt;VariableDeclarator&lt;/code&gt;.
Then, it checks if node points to another node of type &lt;code&gt;ArrayExpression&lt;/code&gt; at it&amp;rsquo;s
&lt;code&gt;init&lt;/code&gt; property. Next, it calls &lt;code&gt;arrayHasMeaningfulContentLength()&lt;/code&gt; function
to check if array length is at least 2% of the entire flat AST. Lastly, it
performs a check to reject arrays containing members that are not literals.&lt;/p&gt;
&lt;p&gt;The other function, &lt;code&gt;arrayHasMinimumRequiredReferences()&lt;/code&gt; checks for high
enough reference count to array in question:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {ASTNode[]} references
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {string} targetArrayName
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {ASTNode[]} flatTree
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @return {boolean} true if the target array has at least the minimum required references; false otherwise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayHasMinimumRequiredReferences&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;references&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;targetArrayName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;references&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MemberExpression&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;targetArrayName&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;flatTree&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;minMeaningfulPercentageOfReferences&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if a candidate array has above 2%
(&lt;code&gt;const minMeaningfulPercentageOfReferences = 2; // 2%&lt;/code&gt;) of it&amp;rsquo;s members
referenced via member expressions, the detector function consider it to be used
for string array mapping.&lt;/p&gt;
&lt;p&gt;At this point we are familiar with the building blocks for Restringer, so
let us take a look into Restringer itself. The code base exposes a number
of modules that can be used for custom deobfucation development. The very
same modules are into &lt;a href=&#34;https://github.com/PerimeterX/restringer/blob/main/src/restringer.js&#34;&gt;src/restringer.js&lt;/a&gt;
file that implements the CLI tool. There are two kind of modules in Restringer:
unsafe ones that rely on code evaluation inside isolated Node.JS VM sandbox
and safe ones that do not need that.&lt;/p&gt;
&lt;p&gt;One simple module implements dead code removal in
&lt;a href=&#34;https://github.com/PerimeterX/restringer/blob/main/src/modules/safe/removeDeadNodes.js&#34;&gt;src/modules/safe/removeDeadNodes.js&lt;/a&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;relevantParents&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;VariableDeclarator&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;AssignmentExpression&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;FunctionDeclaration&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ClassDeclaration&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Remove nodes code which is only declared but never used.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * NOTE: This is a dangerous operation which shouldn&amp;#39;t run by default, invokations of the so-called dead code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * may be dynamically built during execution. Handle with care.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {Arborist} arb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @param {Function} candidateFilter (optional) a filter to apply on the candidates list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @return {Arborist}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;removeDeadNodes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arb&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;candidateFilter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Identifier&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;relevantParents&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;includes&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;declNode&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;references&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;references&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;candidateFilter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#75715e&#34;&gt;// Do not remove root nodes as they might be referenced in another script
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Program&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;arb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;markNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ExpressionStatement&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;removeDeadNodes&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This merely marks the AST nodes for removal. Applying the changes is left to
the caller.&lt;/p&gt;
&lt;p&gt;Each module is like a building block for deobfuscator development. However,
PerimeterX also provides bigger constructs called processors, that
rely on at least one module to simplify code from a specific obfuscation solution
(e.g. self-defending code from Obfuscator.io). There is not many of them -
Restringer should primarily be considered a framework to develop custom deobfuscators
instead of ready-made general purpose deobfuscator itself.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;Restringer&lt;/code&gt; class there&amp;rsquo;s a &lt;code&gt;deobfuscate()&lt;/code&gt; method that pretty much
runs everything:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Entry point for this class.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Determine obfuscation type and run the pre- and post- processors accordingly.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Run the deobfuscation methods in a loop until nothing more is changed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Normalize script to make it more readable.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * @param {boolean} clean (optional) Remove dead nodes after deobfuscation. Defaults to false.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * @return {boolean} true if the script was modified during deobfuscation; false otherwise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;deobfuscate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;determineObfuscationType&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_runProcessors&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_preprocessors&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_loopSafeAndUnsafeDeobfuscationMethods&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_runProcessors&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_postprocessors&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;normalize&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;normalizeScript&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;runLoop&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;, [&lt;span style=&#34;color:#a6e22e&#34;&gt;removeDeadNodes&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The private method &lt;code&gt;_loopSafeAndUnsafeDeobfuscationMethods()&lt;/code&gt; is of particular
interest, as this is where deobfuscation modules are applied. The implementation
of this method is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Make all changes which don&amp;#39;t involve eval first in order to avoid running eval on probelmatic values
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * which can only be detected once part of the script is deobfuscated. Once all the safe changes are made,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * continue to the unsafe changes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 * Since the unsafe modification may be overreaching, run them only once and try the safe methods again.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;	 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;_loopSafeAndUnsafeDeobfuscationMethods&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;runLoop&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_safeDeobfuscationMethods&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;runLoop&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_unsafeDeobfuscationMethods&lt;/span&gt;(), &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;script&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		} &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// Run this loop until the deobfuscation methods stop being effective.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;modified&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We see that deobfuscation methods are being applied until they no longer change
the code. Safe methods are prioritised over unsafe ones. The
&lt;a href=&#34;https://github.com/PerimeterX/restringer/blob/main/src/modules/utils/runLoop.js#L23&#34;&gt;&lt;code&gt;runLoop()&lt;/code&gt; function in another file&lt;/a&gt;
does the heavy lifting at flat AST level while also making sure that it does
not get into infinite loop.&lt;/p&gt;
&lt;p&gt;So how good is Restringer is in practice? Well, it is able to deobfuscate JSFuck
output, but does not go much further without any customisations.&lt;/p&gt;
&lt;p&gt;For example, it fails to completely cleanup the output of Obfuscator.io on the
&amp;ldquo;Low&amp;rdquo; preset:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ restringer obfuscated.js 
[+] Obfuscation type is Generic
	[+] resolveProxyReferences committed 1 new changes!
	[+] resolveFunctionConstructorCalls committed 1 new changes!
[+] ==&amp;gt; Cycle 1 completed in 0.023 seconds with 2 changes (241 nodes)
[+] ==&amp;gt; Cycle 2 completed in 0.001 seconds with 2 changes (241 nodes)
	[+] resolveMinimalAlphabet committed 6 new changes!
[+] ==&amp;gt; Cycle 3 completed in 5.058 seconds with 6 changes (233 nodes)
	[+] resolveFunctionConstructorCalls committed 2 new changes!
[+] ==&amp;gt; Cycle 4 completed in 0.006 seconds with 2 changes (224 nodes)
[+] ==&amp;gt; Cycle 5 completed in 0 seconds with 2 changes (224 nodes)
	[+] resolveMinimalAlphabet committed 1 new changes!
[+] ==&amp;gt; Cycle 6 completed in 0.026 seconds with 1 changes (223 nodes)
[+] ==&amp;gt; Cycle 7 completed in 0.001 seconds with no changes (223 nodes)
[+] ==&amp;gt; Cycle 8 completed in 0.001 seconds with no changes (223 nodes)
	[+] normalizeComputed committed 12 new changes!
[+] ==&amp;gt; Cycle 9 completed in 0.007 seconds with 12 changes (223 nodes)
[+] ==&amp;gt; Cycle 10 completed in 0 seconds with 12 changes (223 nodes)
[+] Saved obfuscated.js-deob.js
[!] Deobfuscation took 5.184 seconds.
const _0x5d3fc2 = function () {
  let _0x5e1813 = true;
  return function (_0x4b42c2, _0x1f2986) {
    const _0xab51ad = _0x5e1813 ? function () {
      if (_0x1f2986) {
        const _0x18ac9b = _0x1f2986.apply(_0x4b42c2, arguments);
        _0x1f2986 = null;
        return _0x18ac9b;
      }
    } : function () {
    };
    _0x5e1813 = false;
    return _0xab51ad;
  };
}();
(function () {
  _0x5d3fc2(this, function () {
    const _0x1c8f0b = new RegExp(&amp;#39;function *\\( *\\)&amp;#39;);
    const _0x6cbb3 = new RegExp(&amp;#39;\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)&amp;#39;, &amp;#39;i&amp;#39;);
    const _0x496fd6 = _0x4aeb94(&amp;#39;init&amp;#39;);
    if (!_0x1c8f0b.test(_0x496fd6 + &amp;#39;chain&amp;#39;) || !_0x6cbb3.test(_0x496fd6 + &amp;#39;input&amp;#39;)) {
      _0x496fd6(&amp;#39;0&amp;#39;);
    } else {
      _0x4aeb94();
    }
  })();
}());
const verseChoose = document.querySelector(&amp;#39;select&amp;#39;);
const poemDisplay = document.querySelector(&amp;#39;pre&amp;#39;);
verseChoose.addEventListener(&amp;#39;change&amp;#39;, () =&amp;gt; {
  const _0x4f5b1d = verseChoose.value;
  updateDisplay(verseChoose.value);
});
function _0x4aeb94(_0x662129) {
  function _0x9c1238(_0x2dfb67) {
    if (typeof _0x2dfb67 === &amp;#39;string&amp;#39;) {
      return function () {
        while (true) {
        }
      }.apply(&amp;#39;counter&amp;#39;);
    } else {
      if ((&amp;#39;&amp;#39; + _0x2dfb67 / _0x2dfb67).length !== 1 || _0x2dfb67 % 20 === 0) {
        (function () {
          debugge_;
        }.call(&amp;#39;action&amp;#39;));
      } else {
        (function () {
          debugge_;
        }.apply(&amp;#39;stateObject&amp;#39;));
      }
    }
    _0x9c1238(++_0x2dfb67);
  }
  try {
    if (_0x662129) {
      return _0x9c1238;
    } else {
      _0x9c1238(0);
    }
  } catch (_0x45a2e1) {
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compare and contrast the output here with the original snippet before it was
obfuscated:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;verseChoose&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;select&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;poemDisplay&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pre&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;verseChoose&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;change&amp;#34;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;verse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;verseChoose&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;updateDisplay&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;verse&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case Restringer really did not do much to undo what Obfuscator.io did.&lt;/p&gt;
&lt;p&gt;This can perhaps be explained by Restringer being a rather specialised piece of
software that is primarily developed for malware analysis. Furthermore,
undoing code obfuscations is a problem of high inherent complexity. There&amp;rsquo;s so
many ways one can make the code confusing to read and an ideal deobfuscator
would need to address all of them, which is a practical impossibility.&lt;/p&gt;
&lt;p&gt;However in my opinion using flat ASTs is interesting idea that deserves some
attention. When developing software, it is of great importance to work out
how exactly the underlying data structures will be organised, as it will
determine what kind of algorithms will be applicable to process the data.
That will have its own ramifications on how hard will it be to write the
code and how fast it will run. In this particular case, choosing flat ASTs
enabled using elements of functional programming that would not be applicable
for regular ASTs that Babel parses the code into.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cs.cornell.edu/~asampson/blog/flattening.html&#34;&gt;Flattening ASTs (and Other Compiler Data Structures)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.humansecurity.com/tech-engineering-blog/defeating-javascript-obfuscation&#34;&gt;Defeating Javascript Obfuscation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Using Scrapy-playwright for integrating browser automation with Scrapy</title>
      <link>https://www.trickster.dev/post/using-scrapy-playwright-for-integrating-browser-automation-with-scrapy/</link>
      <pubDate>Sat, 22 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/using-scrapy-playwright-for-integrating-browser-automation-with-scrapy/</guid>
      <description>&lt;p&gt;Scrapy framework provides a great deal of machinery for developing and operating
web scrapers that is based on launching requests and parsing responses. However,
sometimes it is desirable to introduce browser automation into a web scraping
project. One may want to have code as general as possible across many target
sites, address certain kinds of blocking (e.g. Javascript challenges) or simply
take screenshots of pages being scraped. &lt;a href=&#34;https://github.com/scrapy-plugins/scrapy-playwright&#34;&gt;Scrapy-playwright&lt;/a&gt;
is Scrapy plugin that connects Scrapy with MS Playwright browser automation
software. In this post we will get familiar with it and go through some code
examples that demonstrate the integration between the two.&lt;/p&gt;
&lt;p&gt;The following steps require Scrapy, Playwright and Scrapy-playwright to be
installed in your system.&lt;/p&gt;
&lt;p&gt;To enable Scrapy-playwright in the Scrapy project, we have to override
the &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/settings.html#std-setting-DOWNLOAD_HANDLERS&#34;&gt;&lt;code&gt;DOWNLOADER_HANDLERS&lt;/code&gt;&lt;/a&gt;
configuration parameter to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DOWNLOAD_HANDLERS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Furthermore, we must also override &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/settings.html?highlight=TWISTED_REACTOR#twisted-reactor&#34;&gt;&lt;code&gt;TWISTED_REACTOR&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TWISTED_REACTOR &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;twisted.internet.asyncioreactor.AsyncioSelectorReactor&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This replaces a class for reactor object class that is used by Scrapy. Reactor
object is where network event loop is running for the purposes of Twisted
network programming framework that Scrapy is based upon.&lt;/p&gt;
&lt;p&gt;Now we can use Playwright integration for launching some or all of the requests
through the browser. If we want the request to be performed through browser,
we add &lt;code&gt;&amp;quot;playwright&amp;quot;: True&lt;/code&gt; to the &lt;code&gt;meta&lt;/code&gt; dictionary for that request.&lt;/p&gt;
&lt;p&gt;Let us go through a simple example of Scrapy-based script that relies on
Playwright to achieve greater scraper generality when checking for presence
of user-provided keyword on the pages.&lt;/p&gt;
&lt;p&gt;Take a look at this code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; argparse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urlparse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.crawler &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; CrawlerProcess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;KeywordSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kwchecker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    keywords &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    use_playwright &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, start_urls, keywords, use_playwright, &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(set(start_urls))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; start_url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            domain &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(start_url)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;netloc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            allowed_domains&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(domain)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(set(allowed_domains))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;keywords &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(set(keywords))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;use_playwright &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; use_playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;playwright&amp;#34;&lt;/span&gt;: self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;use_playwright}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; start_url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(start_url, meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;playwright&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;playwright&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;playwright&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*//text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; keyword &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;keywords:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; keyword&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;lower() &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; text&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;lower():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;keyword&amp;#34;&lt;/span&gt;: keyword,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;seen_at&amp;#34;&lt;/span&gt;: datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;now()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        links &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//a/@href&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; link &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; links:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            o &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(link)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;scheme &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;scheme &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;scheme &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(link, meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;read_lines&lt;/span&gt;(file_path):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(file_path, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lines &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lines &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; line: line&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip(), lines))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lines &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(set(lines))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; lines
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chdir(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dirname(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;realpath(__file__)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; argparse&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ArgumentParser()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--start-urls&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;List of start URLs, one per line&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start_urls.txt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--keywords&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;List of keywords, one per line&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;keywords.txt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--use-playwright&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Use Playwright&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;store_true&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--output-csv-file&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Output CSV file&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;output.csv&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parsed_args &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; vars(parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_args(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Parsed CLI args: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(parsed_args))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; read_lines(parsed_args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start_urls&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    keywords &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; read_lines(parsed_args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;keywords&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    output_csv_file &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parsed_args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;output_csv_file&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    use_playwright &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parsed_args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;use_playwright&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isfile(output_csv_file):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;unlink(output_csv_file)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Google Chrome&amp;#34;;v=&amp;#34;111&amp;#34;, &amp;#34;Not(A:Brand&amp;#34;;v=&amp;#34;8&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;111&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;document&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;navigate&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upgrade-insecure-requests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    settings &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;USER_AGENT&amp;#34;&lt;/span&gt;: headers&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEFAULT_REQUEST_HEADERS&amp;#34;&lt;/span&gt;: headers,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FEEDS&amp;#34;&lt;/span&gt;: {output_csv_file: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;format&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;csv&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;REQUEST_FINGERPRINTER_IMPLEMENTATION&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; use_playwright:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DOWNLOAD_HANDLERS&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TWISTED_REACTOR&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;twisted.internet.asyncioreactor.AsyncioSelectorReactor&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; CrawlerProcess(settings&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;settings)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;crawl(KeywordSpider, start_urls, keywords, use_playwright)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the sake of simplicity, we use &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/practices.html?highlight=CrawlerProcess#run-scrapy-from-a-script&#34;&gt;&lt;code&gt;CrawlerProcess&lt;/code&gt;&lt;/a&gt;
to run a single Scrapy spider for a script instead of creating a Scrapy project
that has far more boilerplate. We override the aforementioned settings, as
well as &lt;code&gt;USER_AGENT&lt;/code&gt; and &lt;code&gt;DEFAULT_REQUEST_HEADERS&lt;/code&gt; to make our scraper look
more like a normal browser. Note that the user of this script is allowed to
choose not to use browser automation by not passing in &lt;code&gt;--use-playwright&lt;/code&gt;
CLI option. Scrapy spider works pretty much the same, except that value for
&lt;code&gt;playwright&lt;/code&gt; key in &lt;code&gt;meta&lt;/code&gt; dictionary is set to &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When running this script we can see some output that is a little different
than what we regulary see from Scrapy:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ kw_checker.py --use-playwright --start-urls start_urls.txt --keywords keywords.txt --output-csv-file results.csv
Parsed CLI args: {&amp;#39;start_urls&amp;#39;: &amp;#39;start_urls.txt&amp;#39;, &amp;#39;keywords&amp;#39;: &amp;#39;keywords.txt&amp;#39;, &amp;#39;use_playwright&amp;#39;: True, &amp;#39;output_csv_file&amp;#39;: &amp;#39;results.csv&amp;#39;}
2023-07-21 16:26:09 [scrapy.utils.log] INFO: Scrapy 2.8.0 started (bot: scrapybot)
2023-07-21 16:26:09 [scrapy.utils.log] INFO: Versions: lxml 4.9.2.0, libxml2 2.9.13, cssselect 1.2.0, parsel 1.7.0, w3lib 2.1.1, Twisted 22.10.0, Python 3.11.4 (main, Jun 20 2023, 17:23:00) [Clang 14.0.3 (clang-1403.0.22.14.1)], pyOpenSSL 23.1.1 (OpenSSL 3.1.0 14 Mar 2023), cryptography 40.0.1, Platform macOS-13.3.1-arm64-arm-64bit
2023-07-21 16:26:09 [scrapy.crawler] INFO: Overridden settings:
{&amp;#39;REQUEST_FINGERPRINTER_IMPLEMENTATION&amp;#39;: &amp;#39;2.7&amp;#39;,
 &amp;#39;TWISTED_REACTOR&amp;#39;: &amp;#39;twisted.internet.asyncioreactor.AsyncioSelectorReactor&amp;#39;,
 &amp;#39;USER_AGENT&amp;#39;: &amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) &amp;#39;
               &amp;#39;AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 &amp;#39;
               &amp;#39;Safari/537.36&amp;#39;}
2023-07-21 16:26:09 [asyncio] DEBUG: Using selector: KqueueSelector
2023-07-21 16:26:09 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor
2023-07-21 16:26:09 [scrapy.utils.log] DEBUG: Using asyncio event loop: asyncio.unix_events._UnixSelectorEventLoop
2023-07-21 16:26:09 [scrapy.extensions.telnet] INFO: Telnet Password: 8ed078a7779ff379
2023-07-21 16:26:09 [scrapy.middleware] INFO: Enabled extensions:
[&amp;#39;scrapy.extensions.corestats.CoreStats&amp;#39;,
 &amp;#39;scrapy.extensions.telnet.TelnetConsole&amp;#39;,
 &amp;#39;scrapy.extensions.memusage.MemoryUsage&amp;#39;,
 &amp;#39;scrapy.extensions.feedexport.FeedExporter&amp;#39;,
 &amp;#39;scrapy.extensions.logstats.LogStats&amp;#39;]
2023-07-21 16:26:10 [scrapy.middleware] INFO: Enabled downloader middlewares:
[&amp;#39;scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.useragent.UserAgentMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.retry.RetryMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.RedirectMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.cookies.CookiesMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.stats.DownloaderStats&amp;#39;]
2023-07-21 16:26:10 [scrapy.middleware] INFO: Enabled spider middlewares:
[&amp;#39;scrapy.spidermiddlewares.httperror.HttpErrorMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.offsite.OffsiteMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.referer.RefererMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.urllength.UrlLengthMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.depth.DepthMiddleware&amp;#39;]
2023-07-21 16:26:10 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2023-07-21 16:26:10 [scrapy.core.engine] INFO: Spider opened
2023-07-21 16:26:10 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2023-07-21 16:26:10 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2023-07-21 16:26:10 [scrapy-playwright] INFO: Starting download handler
2023-07-21 16:26:10 [scrapy-playwright] INFO: Starting download handler
2023-07-21 16:26:15 [scrapy-playwright] INFO: Launching browser chromium
2023-07-21 16:26:15 [scrapy-playwright] INFO: Browser chromium launched
2023-07-21 16:26:15 [scrapy-playwright] DEBUG: Browser context started: &amp;#39;default&amp;#39; (persistent=False)
2023-07-21 16:26:15 [scrapy-playwright] DEBUG: [Context=default] New page created, page count is 1 (1 for all contexts)
2023-07-21 16:26:15 [scrapy-playwright] DEBUG: [Context=default] New page created, page count is 2 (2 for all contexts)
2023-07-21 16:26:15 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://quotes.toscrape.com/js/&amp;gt; (resource type: document, referrer: None)
2023-07-21 16:26:15 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://books.toscrape.com/&amp;gt; (resource type: document, referrer: None)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Response: &amp;lt;200 http://books.toscrape.com/&amp;gt; (referrer: None)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Response: &amp;lt;200 http://quotes.toscrape.com/js/&amp;gt; (referrer: None)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://books.toscrape.com/static/oscar/css/styles.css&amp;gt; (resource type: stylesheet, referrer: http://books.toscrape.com/)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/bootstrap-datetimepicker.css&amp;gt; (resource type: stylesheet, referrer: http://books.toscrape.com/)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://books.toscrape.com/static/oscar/css/datetimepicker.css&amp;gt; (resource type: stylesheet, referrer: http://books.toscrape.com/)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://quotes.toscrape.com/static/bootstrap.min.css&amp;gt; (resource type: stylesheet, referrer: http://quotes.toscrape.com/js/)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://quotes.toscrape.com/static/main.css&amp;gt; (resource type: stylesheet, referrer: http://quotes.toscrape.com/js/)
2023-07-21 16:26:16 [scrapy-playwright] DEBUG: [Context=default] Request: &amp;lt;GET http://quotes.toscrape.com/static/jquery.js&amp;gt; (resource type: script, referrer: http://quotes.toscrape.com/js/)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that a headless Chromium browser is launched and that a browser
context is being mentioned. A browser context is essentially a session within
a browser that is controlled by Playwright.&lt;/p&gt;
&lt;p&gt;As we would expect, images and JavaScript files are downloaded by the
browser without spider explicitly asking for that:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2023-07-21 16:32:53 [scrapy-playwright] DEBUG: [Context=default] Response: &amp;lt;200 http://books.toscrape.com/static/oscar/js/bootstrap-datetimepicker/locales/bootstrap-datetimepicker.all.js&amp;gt; (referrer: None)
2023-07-21 16:32:53 [scrapy-playwright] DEBUG: [Context=default] Response: &amp;lt;200 http://books.toscrape.com/media/cache/ee/a9/eea9e831f8964b4dc0190c84a1f9a1f6.jpg&amp;gt; (referrer: None)
2023-07-21 16:32:53 [scrapy-playwright] DEBUG: [Context=default] Response: &amp;lt;200 http://books.toscrape.com/media/cache/dc/4d/dc4d070e33813a07a4e02f069e6d482f.jpg&amp;gt; (referrer: None)
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>Self-defending JS code and debugger traps</title>
      <link>https://www.trickster.dev/post/self-defending-js-code-and-debugger-traps/</link>
      <pubDate>Sun, 02 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/self-defending-js-code-and-debugger-traps/</guid>
      <description>&lt;p&gt;Sometimes programmers really don&amp;rsquo;t want their code to tampered with during runtime.
Code obfuscation makes it harder to read and understand, but by itself does not
do anything against modification and dynamic analysis with a debugger. This is
particularly applicable to client-side JavaScript code that is meant to be
running in web browser environment. After all, the code is largely under users
control after it is fetched from the server. We will look into two kinds of
techniques to make this kind of tampering harder: self-defending code to hinder
code modifications and debugger traps are meant to mess with debugger if someone
tries to dynamically step through the code during runtime. Whitehat developers
at security or anti-fraud software companies would be interested in these
techniques for the same reason as the blackhat malware writers: to hinder
their adversaries from discovering the inner working of the code and developing
attacks or defenses against it.&lt;/p&gt;
&lt;p&gt;We can use &lt;a href=&#34;https://obfuscator.io/&#34;&gt;obfuscator.io&lt;/a&gt; to get simple examples of
both techniques. Let us start with the following very basic unprotected snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Paste your JavaScript code here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On the obfuscator web UI we uncheck all checkboxes except &amp;ldquo;Self Defending&amp;rdquo; and
&amp;ldquo;Compact&amp;rdquo;. We get the following obfuscated snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;(){&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3298fd&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c6e04&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=!!&lt;/span&gt;[];&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x471481&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2bd7a3&lt;/span&gt;){&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x14b95c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c6e04&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2bd7a3&lt;/span&gt;){&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xc92697&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2bd7a3&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apply&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x471481&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2bd7a3&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xc92697&lt;/span&gt;;}}&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){};&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c6e04&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=!&lt;/span&gt;[];&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x14b95c&lt;/span&gt;;};}());&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3a8b12&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3298fd&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;,&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3a8b12&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;]()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(((.+)+)+)+$&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;]()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructor&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3a8b12&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(((.+)+)+)+$&amp;#39;&lt;/span&gt;);});&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3a8b12&lt;/span&gt;();&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);}&lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-07-01_13.38.21.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Undoing the minification makes it bit cleaner:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xd9587a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x505420&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1ae0dc&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c054&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1c2bf6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x505420&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c054&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6b34ef&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c054&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apply&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1ae0dc&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c054&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6b34ef&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x505420&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1c2bf6&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23cc70&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xd9587a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23cc70&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;]()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(((.+)+)+)+$&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;]()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructor&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23cc70&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(((.+)+)+)+$&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23cc70&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We see some weird obfuscated code injected in here. Let us explore it a bit
with Node.JS debugger:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node inspect self_defending_edited.js
&amp;lt; Debugger listening on ws://127.0.0.1:9229/d6b71c95-1217-41e4-b188-e30a7a822b47
&amp;lt; For help, see: https://nodejs.org/en/docs/inspector
&amp;lt; 
&amp;lt; Debugger attached.
&amp;lt; 
 ok
Break on start in self_defending_edited.js:28
 26   console[&amp;#39;log&amp;#39;](&amp;#39;Hello\x20World!&amp;#39;);
 27 }
&amp;gt;28 hi();
 29 
debug&amp;gt; s
break in self_defending_edited.js:2
  1 function hi() {
&amp;gt; 2   var _0xd9587a = (function() {
  3     var _0x505420 = !![];
  4     return function(_0x1ae0dc, _0x3c054) {
debug&amp;gt; s

...

debug&amp;gt; s
break in self_defending_edited.js:17
 15   }());
 16   var _0x23cc70 = _0xd9587a(this, function() {
&amp;gt;17     return _0x23cc70[&amp;#39;toString&amp;#39;]()[&amp;#39;search&amp;#39;](&amp;#39;(((.+)+)+)+$&amp;#39;)[&amp;#39;toString&amp;#39;]()[&amp;#39;constructor&amp;#39;](_0x23cc70)[&amp;#39;search&amp;#39;](&amp;#39;(((.+)+)+)+$&amp;#39;);
 18   });
 19   _0x23cc70();
debug&amp;gt; exec _0x23cc70
[Function: function]
debug&amp;gt; exec _0x23cc70.toString()
&amp;#39;function() {\n&amp;#39; +
  &amp;#39;        if (_0x3c054) {\n&amp;#39; +
  &amp;#34;          var _0x6b34ef = _0x3c054[&amp;#39;apply&amp;#39;](_0x1ae0dc, arguments);\n&amp;#34; +
  &amp;#39;          _0x3c054 = null;\n&amp;#39; +
  &amp;#39;          return _0x6b34ef;\n&amp;#39; +
  &amp;#39;        }\n&amp;#39; +
  &amp;#39;      }&amp;#39;
debug&amp;gt; s
debug&amp;gt; s
Uncaught Error [ERR_DEBUGGER_ERROR]: Can only perform operation while paused.
    at _pending.&amp;lt;computed&amp;gt; (node:internal/debugger/inspect_client:247:27)
    at Client._handleChunk (node:internal/debugger/inspect_client:214:11)
    at Socket.emit (node:events:511:28)
    at Socket.emit (node:domain:489:12)
    at addChunk (node:internal/streams/readable:332:12)
    at readableAddChunk (node:internal/streams/readable:305:9)
    at Readable.push (node:internal/streams/readable:242:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
    at TCP.callbackTrampoline (node:internal/async_hooks:130:17) {
  code: -32000
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that it crashed when attempting to match regular expression &lt;code&gt;(((.+)+)+)+$&lt;/code&gt;
on source code string of one of the nested functions. Since we undid the
minification and introduced white space characters into the code, this operation
overwhelms the regular expression engine by causing &lt;a href=&#34;https://stackoverflow.com/questions/64583150/how-does-javascript-self-defending-work-and-how-does-it-manage-to-enter-an-infin&#34;&gt;catastrophic
backtracking&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More generally, self-defending code involves self-introspection, integrity
checking and triggering anti-tampering countermeasures. In this case the latter
two were done together in the weird regex statement, but they can be separate.
Integrity checking may involve hashing key parts of the code and checking against
a precomputed hash value. Anti-tampering countermeasures may involve throwing
an expection to crash the code or redirecting the user to error page.&lt;/p&gt;
&lt;p&gt;To get an example of a debugger trap, we rerun the obfuscation with only a
&amp;ldquo;Debug Protection&amp;rdquo; feature being activated.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-07-01_13.41.34.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We get the following snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x20b38b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x36b5cf&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x56f748&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b111c&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x37844a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x36b5cf&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b111c&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4a1f4c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b111c&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apply&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x56f748&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b111c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4a1f4c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x36b5cf&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x37844a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x20b38b&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ac2e9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RegExp(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;function\x20*\x5c(\x20*\x5c)&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3b4dbb&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RegExp(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x5c+\x5c+\x20*(?:[a-zA-Z_$][0-9a-zA-Z_$]*)&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;i&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x43549a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4c804e&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;init&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ac2e9&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;test&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x43549a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;chain&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3b4dbb&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;test&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x43549a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input&amp;#39;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x43549a&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4c804e&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello\x20World!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hi&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4c804e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x203e97&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x21f924&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x194661&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructor&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;while\x20(true)\x20{}&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apply&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;counter&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ((&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;length&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x14&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructor&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;debu&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;gger&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;call&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructor&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;debu&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;gger&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apply&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stateObject&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x21f924&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x6c4ed&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x203e97&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x21f924&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x21f924&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5a4b0c&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The injected code is somewhat obfuscated, but we have a small snippet here and
can get an idea of what happens here. The obfuscator injected a code that
performs a classical anti-debugging trick - invoking &lt;code&gt;debugger&lt;/code&gt; JS statement
periodically. This will disrupt an attempt to step through the code one line at
a time like in web browser dev tools panel and may even crash the entire browser
on a debugging attempt.&lt;/p&gt;
&lt;p&gt;None of the above techniques are silver bullets against reverse engineer looking
to work out what exactly a malware sample does or how to defeat security
mechanisms of automation-hostile website. They merely make it incrementally
harder and can be dealt with by removing the injected code at AST level or even
by manually editing the source.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/weizman/awesome-javascript-anti-debugging&#34;&gt;Awesome JavaScript Anti Debugging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://x-c3ll.github.io/posts/javascript-antidebugging/&#34;&gt;JavaScript AntiDebugging Tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Abusing Ahrefs Backlink Checker</title>
      <link>https://www.trickster.dev/post/abusing-ahrefs-backlink-checker/</link>
      <pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/abusing-ahrefs-backlink-checker/</guid>
      <description>&lt;p&gt;World Wide Web is a network of HTML documents (pages) with hyperlinks between
them. Consider a directed graph that consists of vertices representing pages
and edges representing links between pages. For a given page, links from other
pages to that page are known as backlinks. Backlinks are of significance to
search engine ranking of the site, thus making them of importance to SEO people,
digital marketers and growth hackers. Furthermore, OSINT practitioners and web
scraper developers may want to find sites/pages linking to certain pages/files
on the web as it would help traversing the data landscape. Major SEO SaaS apps
(Ahrefs, SEMRush, Moz, etc.) are sourcing their data via large-scale web
crawling operations that build up a fairly complete maps of the web and thus
can be leveraged for this purpose. In this post we will go through an example
of abusing a free &lt;a href=&#34;https://ahrefs.com/backlink-checker&#34;&gt;Ahrefs Backlink Checker&lt;/a&gt;
to show how a list of backlinks can be scraped from a SEO tool.&lt;/p&gt;
&lt;p&gt;Let us explore the request flow that happens when the user pastes an URL into
the form and presses the &amp;ldquo;Check backlinks&amp;rdquo; button. We are dealing with
&lt;a href=&#34;https://developers.cloudflare.com/turnstile/&#34;&gt;Cloudflare Turnstile&lt;/a&gt; - a drop-in
alternative to Google reCaptcha that may ask for checkbox to be clicked, but
largely tries to separate bots from humans by non-interactive means - probing
the JS environment in the browser, JavaScript challenges, gathering a sample
of user activity and so on. Since it is kind of captcha, we can use
&lt;a href=&#34;https://anti-captcha.com/&#34;&gt;Anti-captcha&lt;/a&gt; service to solve it. For $2 we can
buy a thousand solutions. In Chrome DevTools we can see a GET request to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://challenges.cloudflare.com/cdn-cgi/challenge-platform/h/g/turnstile/if/ov2/av0/rcv0/0/dy268/0x4AAAAAAAAzi9ITzSN9xKMi/light/normal&#34;&gt;https://challenges.cloudflare.com/cdn-cgi/challenge-platform/h/g/turnstile/if/ov2/av0/rcv0/0/dy268/0x4AAAAAAAAzi9ITzSN9xKMi/light/normal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Value &lt;code&gt;0x4AAAAAAAAzi9ITzSN9xKMi&lt;/code&gt; is a site key - a unique value that identifies
this site in API interactions with Cloudflare. We will be passing it into the
Anti-captcha API.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-06-26_14.50.16.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the site gets a captcha token from Cloudflare it launches two HTTP POST
requests to the API. We extract both of them as curl snippets to be converted
into Python code later.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-06-27_11.48.40.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Curl command for the first request is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://ahrefs.com/v4/ftBacklinkCheckerPrimary&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority: ahrefs.com&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: */*&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;content-type: application/json; charset=utf-8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: __cf_bm=n2OYqOmarAhgWDxD3Tad6v1BAgLYFTtI3J8RxY9yapc-1687855654-0-AaF8nuZhwZT9A7zWj+Ojn1N58AhPC9jyUi+3Mfbpk/3pXYXEfdmpahcomXy/Bjv/TF6Qu3agnbRSquDnmjFC9RvKXWdwH50W/qipBYlv9DsD&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin: https://ahrefs.com&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer: https://ahrefs.com/backlink-checker&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua: &amp;#34;Not.A/Brand&amp;#34;;v=&amp;#34;8&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;114&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;114&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile: ?0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform: &amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest: empty&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode: cors&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site: same-origin&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --data-raw &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;captcha&amp;#34;:&amp;#34;0.wsyuoTAQbPdempTdrWisjoNqxj6LRJwGyP4nnYteWF9mHnSQZ-v5ofEyoc1MVEMuUz-N04E5CpdhwBaY9tPpAbDxNjnLKqdvF54VxppzncFjDg7JEbS3KSFxL-9Zkzk_lBx9DiuID2Ojn8s5N7X2BUCMtITXLqHt5kdJhgYphmtRHB4F8w6zXXaqi7830p1pKD17lRxiKIqCTJowy6Q5J-0vx-OqWtN1NN6sv6uGLBvOhyWVcxPO3GGSjnxisXwF5Y2Tzjbr7gGjnOrePZH2adWbJlxFbgQ0gyT-qul6dR7xq6-2IYUPp8ud0ZJ9qomq2xJinE5d_TCF_4is-5kpW4ovGBFbreotGh5sR_6vAEn2nq-SjovnxMya0fA9XreX5lUtU3yxymOa7BWWYZoQJg.s2MS2XLcbogodDf16-NyPA.1b163e522a4349cf7556af8134812e0f3e9d53c0dca934b6a60010e558162db8&amp;#34;,&amp;#34;mode&amp;#34;:&amp;#34;exact&amp;#34;,&amp;#34;url&amp;#34;:&amp;#34;https://ahrefs.com/backlink-checker&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --compressed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In HTTP request payload there&amp;rsquo;s JSON object with captcha token and URL the user
provided.&lt;/p&gt;
&lt;p&gt;The JSON string in a response is of following form (reformatted for readability):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ok&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;domainRating&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;91.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlRating&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;41&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;backlinks&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;29409&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;refdomains&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;4917&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;dofollowBacklinks&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;49&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;dofollowRefdomains&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;67&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;signedInput&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ahrefs.com/backlink-checker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;exact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;validUntil&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2023-06-27T20:48:03Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;signature&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ed9ead2b3e58fc3f45374cd562c51c5ba563d560206d9870b4d7695ff5a14652&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This provides a status (&lt;code&gt;Ok&lt;/code&gt;) and some SEO-related numbers about the URL/domain,
including a number of backlinks. If it was zero we could quit the API flow at
this point.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;signedInput&lt;/code&gt; object goes into the second API request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://ahrefs.com/v4/ftBacklinkCheckerSecondary&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority: ahrefs.com&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: */*&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;content-type: application/json; charset=utf-8&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie: __cf_bm=n2OYqOmarAhgWDxD3Tad6v1BAgLYFTtI3J8RxY9yapc-1687855654-0-AaF8nuZhwZT9A7zWj+Ojn1N58AhPC9jyUi+3Mfbpk/3pXYXEfdmpahcomXy/Bjv/TF6Qu3agnbRSquDnmjFC9RvKXWdwH50W/qipBYlv9DsD&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin: https://ahrefs.com&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma: no-cache&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer: https://ahrefs.com/backlink-checker&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua: &amp;#34;Not.A/Brand&amp;#34;;v=&amp;#34;8&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;114&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;114&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile: ?0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform: &amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest: empty&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode: cors&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site: same-origin&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --data-raw &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;reportType&amp;#34;:&amp;#34;TopBacklinks&amp;#34;,&amp;#34;signedInput&amp;#34;:{&amp;#34;signature&amp;#34;:&amp;#34;ed9ead2b3e58fc3f45374cd562c51c5ba563d560206d9870b4d7695ff5a14652&amp;#34;,&amp;#34;input&amp;#34;:{&amp;#34;validUntil&amp;#34;:&amp;#34;2023-06-27T20:48:03.000Z&amp;#34;,&amp;#34;mode&amp;#34;:&amp;#34;exact&amp;#34;,&amp;#34;url&amp;#34;:&amp;#34;ahrefs.com/backlink-checker&amp;#34;}}}&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --compressed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The latter API call yields the data we can extract and save somewhere. The JSON
string in the response is something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TopBacklinks&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;topBacklinks&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;backlinks&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;anchor&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ahrefs’ Backlink Checker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;domainRating&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;91&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;edu&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;gov&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;httpCode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;redirectChain&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/backlink-checker?ref=buffer.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlNew&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;redirectCode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mustBeGray&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;httpCodeCross&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;chainDestinationChanged&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;hasDelReason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delReasonTitle&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delReasonKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lostRedirectReason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;isLost&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;textPost&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;textPre&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;6.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;27 Simple and Free SEO Tools [Updated for 2023]&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlFrom&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://buffer.com/library/free-seo-tools/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlTo&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/backlink-checker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;inRendered&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;inRaw&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;redirectCode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;original&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToMustBeGray&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToHttpCodeCross&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToNew&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToChainDestinationChanged&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToDelReasonTitle&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToDelReasonKey&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;None&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;urlToHasDelReason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lost_redirect_reason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;del_reason&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lost_redirect_source&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lost_redirect_new_target&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;isLost&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;total&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-06-26_14.51.36.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also notice that API calls made from client side JS code have
&lt;a href=&#34;https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/#__cf_bm-cookie-for-cloudflare-bot-products&#34;&gt;&lt;code&gt;__cf_bm&lt;/code&gt; cookie&lt;/a&gt;
added to them. This suggests that at least one of &lt;a href=&#34;https://developers.cloudflare.com/bots/&#34;&gt;Cloudflare&amp;rsquo;s antibot
products&lt;/a&gt; aside from Turnstile is
deployed.&lt;/p&gt;
&lt;p&gt;Since Ahrefs Backlink Checker is a &lt;a href=&#34;https://trends.vc/trends-0016-growth-tools/&#34;&gt;growth tool&lt;/a&gt;
meant to provide a potential user of Ahrefs a taste of what would be possible
upon becoming a paying customer, the result list is limited to 100 entries.
But that is not a problem for use cases involving exploration of obscure
corners of the web.&lt;/p&gt;
&lt;p&gt;So we want to develop the script now. But Ahrefs is using at least one feature of
Cloudflare CDN that is meant to prevent or at least hinder the exact thing we
want to do now. So I did some experimentation to get the details right:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For Turnstile part, both &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/TurnstileTask&#34;&gt;proxied&lt;/a&gt;
and &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/TurnstileTaskProxyless&#34;&gt;proxyless&lt;/a&gt;
solutions from Anti-captcha work fine. Since proxy is not needed in this case,
I chose a proxyless solution.&lt;/li&gt;
&lt;li&gt;There is no need to talk HTTP/2, but TLS fingerprint has to be normalized to
match the one from real browser.&lt;/li&gt;
&lt;li&gt;We don&amp;rsquo;t need to worry about &lt;code&gt;__cf_bm&lt;/code&gt; cookie, as it does not seem to matter
much. First step of API flow works properly once we have TLS fingerprint
normalised and captcha solved.&lt;/li&gt;
&lt;li&gt;No proxies are needed as generating requests from DC IP did not cause any
problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete script that does backlink scraping for a given list of pages is
as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; anticaptchaofficial.turnstileproxyless &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; tls_client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backlink_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;anchor&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page_title&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_solver&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; turnstileProxyless()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_verbose(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_key(os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getenv(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ANTICAPTCHA_KEY&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_website_url(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/backlink-checker&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_website_key(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0x4AAAAAAAAzi9ITzSN9xKMi&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; solver
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_turnstile_token&lt;/span&gt;(solver):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;solve_and_return_solution()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; token &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; token
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;task finished with error &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error_code)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tls_client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client_identifier&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chrome112&amp;#34;&lt;/span&gt;, random_tls_extension_order&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authority&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ahrefs.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*/*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json; charset=utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;referer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/backlink-checker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Google Chrome&amp;#34;;v=&amp;#34;113&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;113&amp;#34;, &amp;#34;Not-A.Brand&amp;#34;;v=&amp;#34;24&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;empty&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;same-origin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/backlink-checker&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_backlinks&lt;/span&gt;(session, solver, url):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_turnstile_token(solver)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; token &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to get turnstile token - retrying&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_turnstile_token(solver)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; token &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;captcha&amp;#34;&lt;/span&gt;: token, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;exact&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/v4/ftBacklinkCheckerPrimary&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_arr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(json_arr) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_backlinks &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_arr[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backlinks&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; n_backlinks &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; n_backlinks &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signed_input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_arr[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;signedInput&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_data2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reportType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TopBacklinks&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;signedInput&amp;#34;&lt;/span&gt;: signed_input}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ahrefs.com/v4/ftBacklinkCheckerSecondary&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_data2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topBacklinks&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backlinks&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;url_list_file&amp;gt; &amp;lt;out_file&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url_list_file &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_file &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_solver()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(url_list_file, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(out_file, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            raw_backlinks &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_backlinks(session, solver, url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; raw_backlinks &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; backlink_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; raw_backlinks:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            backlink_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; backlink_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;urlFrom&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            anchor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; backlink_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;anchor&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page_title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; backlink_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backlink_url&amp;#34;&lt;/span&gt;: backlink_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;anchor&amp;#34;&lt;/span&gt;: anchor,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page_title&amp;#34;&lt;/span&gt;: page_title,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Results are saved into CSV file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ head res.csv 
url,backlink_url,anchor,page_title
https://www.trickster.dev/post/dont-jsfuck-with-me-part-3/,https://geek.ds3783.com/2023/06/%E5%88%AB%E8%B7%9F%E6%88%91%E6%9D%A5jsfuck%EF%BC%9A%E7%AC%AC%E4%B8%89%E9%83%A8%E5%88%86/,详情参考,别跟我来JSFuck：第三部分 - 偏执的码农
https://www.trickster.dev/post/prompting-the-llm-is-no-substitute-for-knowledge-work/,https://www.theteams.kr/stack/news_view/TrackJS/10,Prompting the LLM is no substitute for knowledge work 2023-05-31,TrackJS 해외 소식 | 더팀스
https://www.trickster.dev/post/prompting-the-llm-is-no-substitute-for-knowledge-work/,https://hacker.martintudor.com/user?id=rl1987,Prompting the LLM is no substitute for knowledge work,Hacker news
https://www.trickster.dev/post/prompting-the-llm-is-no-substitute-for-knowledge-work/,https://geek.ds3783.com/2023/05/%E6%BF%80%E5%8A%B1llm%E4%B8%8D%E8%83%BD%E6%9B%BF%E4%BB%A3%E7%9F%A5%E8%AF%86%E5%B7%A5%E4%BD%9C%E3%80%82/,详情参考,激励LLM不能替代知识工作。 - 偏执的码农
https://www.trickster.dev/post/understanding-javascript-packers/,https://www.theteams.kr/stack/news_view/Packet/4,Understanding JavaScript Packers 2023-05-30,Packet 해외 소식 | 더팀스
https://www.trickster.dev/post/understanding-javascript-packers/,https://geek.ds3783.com/2023/05/%E7%90%86%E8%A7%A3javascript%E6%89%93%E5%8C%85%E5%99%A8/,详情参考,理解JavaScript打包器 - 偏执的码农
https://www.trickster.dev/post/understanding-javascript-packers/,https://hacker.martintudor.com/user?id=rl1987,Understanding JavaScript Packers,Hacker news
https://www.trickster.dev/post/understanding-javascript-packers/,https://lampfull.com/news.php,Understanding JavaScript Packers,News - Gliath
https://www.trickster.dev/post/understanding-javascript-packers/,https://lr.relyma.com/r/javascript,trickster.dev,𝚓𝚊𝚟𝚊𝚜𝚌𝚛𝚒𝚙𝚝
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-06-27_13.10.17.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t JSFuck with me: Part 3</title>
      <link>https://www.trickster.dev/post/dont-jsfuck-with-me-part-3/</link>
      <pubDate>Sun, 25 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/dont-jsfuck-with-me-part-3/</guid>
      <description>&lt;p&gt;Previously on Trickster Dev:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.trickster.dev/post/dont-jsfuck-with-me-part-1/&#34;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.trickster.dev/post/dont-jsfuck-with-me-part-2/&#34;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using AST transforms developed so far, JSFuck output for character &lt;code&gt;@&lt;/code&gt; can
be simplified to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;)()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What about other characters that have &lt;code&gt;null&lt;/code&gt; value in the &lt;code&gt;MAPPING&lt;/code&gt; object in
jsfuck.js?&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;H&lt;/code&gt; we have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;)()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For &lt;code&gt;J&lt;/code&gt; we have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;)()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For &lt;code&gt;K&lt;/code&gt; we have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;)()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For &lt;code&gt;~&lt;/code&gt; we have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;)()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code we end up is very similar and follows a clear pattern.&lt;/p&gt;
&lt;p&gt;Again, we can use NodeJS REPL to verify that this crazy shebang evaluates back
to the original character:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + ([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1] + [1] + [7] + [6] + &amp;#34;\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us try to pick this stuff apart.&lt;/p&gt;
&lt;p&gt;Due to type coercion, &lt;code&gt;[1] + [7] + [6]&lt;/code&gt; part can be simplified to a single
numeric string:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [1] + [7] + [6]
&amp;#39;176&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now what can we learn about code deeper in the statement?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()
/false/
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;]
[Function: RegExp]
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;)
/\//
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + []
&amp;#39;/\\//&amp;#39;
&amp;gt; ([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1]
&amp;#39;\\&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This statement creates a regular expression object (&lt;code&gt;/false/&lt;/code&gt;), accesses
it&amp;rsquo;s &lt;code&gt;contructor&lt;/code&gt; property to get a corresponding constructor function object
(&lt;code&gt;RegExp()&lt;/code&gt;), calls it with parameter &lt;code&gt;/&lt;/code&gt; (this results in new regular expression
object), then add an empty array to a result to stringify it through type
coercion. Resulting string is indexed to to extract a single backslash character.&lt;/p&gt;
&lt;p&gt;Based on this, we can perform some manual simplifications to this code:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + ([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1] + &amp;#39;176&amp;#39; + &amp;#34;\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + (RegExp(&amp;#34;/&amp;#34;) + [])[1] + &amp;#39;176&amp;#39; + &amp;#34;\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#39;\\&amp;#39; + &amp;#39;176&amp;#39; + &amp;#34;\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;\\176\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What is the number 176 doing here? In the ASCII standard, octal 176 corresponds
to character &lt;code&gt;~&lt;/code&gt; (see ascii(7) manpage). After undoing this octal-encoding, code
gets even simpler:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;~\&amp;#34;&amp;#34;)();
&amp;#39;~&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But what about this &lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;]&lt;/code&gt; part we have here? JSFuck web app
front page notes it is equivalent to calling &lt;code&gt;eval()&lt;/code&gt;. Let us verify that for
ourselves.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;]
[Function: flat]
&amp;gt; typeof [][&amp;#34;flat&amp;#34;]
&amp;#39;function&amp;#39;
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]
[Function: Function]
&amp;gt; typeof [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]
&amp;#39;function&amp;#39;
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;console.log(1)&amp;#34;)
[Function: anonymous]
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;console.log(1)&amp;#34;)()
1
undefined
&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;] == Function
true
&amp;gt; Function(&amp;#34;console.log(1)&amp;#34;)()
1
undefined
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So let us try to refactor this code bit by bit using Babel AST transformations.&lt;/p&gt;
&lt;p&gt;First transformation is for converting &lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return/false/&amp;quot;)()&lt;/code&gt;
into &lt;code&gt;/false/&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;simplify-false-regex&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;memberExpr2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;memberExpr2&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayObj&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;memberExpr2&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayObj&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayObj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;memberExpr2&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;property1&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;property1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;property2&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;property2&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argStrLiteral&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;argStrLiteral&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;argStrLiteral&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return/false/&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;      
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;regExpLiteral&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;false&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need to deal with regexp constructor thing that accepts a string argument.
There&amp;rsquo;s another transform for that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;refactor-regex-constr&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argument&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;argument&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isRegExpLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeMemberExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;regExpLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;argument&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\/&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we deal with addition of regular expression with empty array to stringify
the regular expression object. Current code from part 1 deals with similar
stuff, but is unable to cover what we have now. Thus we write one more
small transformation to only cover this case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regex-str&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;BinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isRegExpLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;regexStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; String(RegExp(&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pattern&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;regexStr&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To convert &lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;]()&lt;/code&gt; into &lt;code&gt;eval()&lt;/code&gt; calls we develop the
following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fix-eval&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;key1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;key2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;filter&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evalCallExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;eval&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;evalCallExpr&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To convert string-returning &lt;code&gt;eval()&lt;/code&gt; calls into string literals we do one more
transformation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eval-return-str&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eval&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;endsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;substr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return\&amp;#34;&amp;#34;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;substr&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;argStr&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;During this deobfuscation exercise, some more AST transforms were developed to
cover new edge cases as they emerge. It would be excessive to cover them all
in this post, as they all follow the pattern of matching the subtree for code
we want to change and modifying to simplify it.&lt;/p&gt;
&lt;p&gt;At this point, we can talk about unifying all the code we have into deobfuscator
program that can be launched from a command line. Each AST transform we
prototyped in AST Explorer can be trivially converted into Babel plugin by
changing couple lines in the code. For example, the &lt;code&gt;undo-string-trick&lt;/code&gt;
transform becomes the following Babel plugin in file plugin_undo_string_trick.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;logASTChange&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./debug.js&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;logASTChange&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-string-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(String));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;logASTChange&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-string-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;newNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice that &lt;code&gt;logASTChange()&lt;/code&gt; function is being imported from debug.js
file. This function is meant to show us how the code is being changed as the
transform is applied, so that we could see a step-by-step progress in the
standard output. The code in debug.js file is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;logASTChange&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pluginName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ast1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ast2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast1&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast2&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PLUGIN NAME:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;pluginName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FROM CODE:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;js1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TO CODE:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;js2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;logASTChange&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Code before and after the modification is being regenerated from AST form and
printed into standard output with the Babel plugin name.&lt;/p&gt;
&lt;p&gt;In app.js file we have code that unites all the plugins into single program
with basic CLI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;process&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;argv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node app.js &amp;lt;input_js&amp;gt; &amp;lt;output_js&amp;gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;input_js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;argv&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;output_js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;argv&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;input_js&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-----------------------------------------------------------------&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;transformSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;plugins&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_eval_expr.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_flat_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_entries_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_concat_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_slice_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_boolean_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_fontcolor_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_italics_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_function_escape_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_escape_call.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_function_date_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_regexp_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_string_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_number_tostring_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_undo_object_tostring_trick.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_eval_return_str.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_eval_return_fncall.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_fix_eval.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_constructor_str.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_refactor_regex_constr.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_regex_str.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_simplify_false_regex.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_string_array_join.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_string_constructor_name.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_string_split.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_eval_program.js&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-----------------------------------------------------------------&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;output_js&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Like we did manually with AST Explorer, this program modifies the input code
until it no longer changes.&lt;/p&gt;
&lt;p&gt;As a sidenote, &lt;code&gt;undo-entries-trick&lt;/code&gt; transform from earlier had to be modified
to prevent it from breaking the code:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-          path.replaceWith(t.stringLiteral(String(Array.prototype.entries)));
+          path.replaceWith(t.stringLiteral(String(String([][&amp;#34;entries&amp;#34;]()))));
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Furthermore, code in &lt;code&gt;eval-return-str&lt;/code&gt; transform was extended to support
escaped characters in octal/hexadecimal form.&lt;/p&gt;
&lt;p&gt;The following listing provides an example of debug output from deobfuscator
(most lines were removed for brevity):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node app.js examples/in1.js examples/out1.js
[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]])[(![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]]((!![]+[])[+[]])[([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]](([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+![]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])()[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])+[])[+!+[]])+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])())()
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +[]
TO CODE: 0
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: !+[] + !+[]
TO CODE: 2
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +!+[]
TO CODE: 1
PLUGIN NAME: eval-expr
FROM CODE: !![] + []
TO CODE: &amp;#34;true&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +[]
TO CODE: 0
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +[]
TO CODE: 0
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: !+[] + !+[]
TO CODE: 2
PLUGIN NAME: eval-expr
FROM CODE: ![] + []
TO CODE: &amp;#34;false&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +!+[]
TO CODE: 1
PLUGIN NAME: eval-expr
FROM CODE: !![] + []
TO CODE: &amp;#34;true&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: +[]
TO CODE: 0
PLUGIN NAME: eval-expr
FROM CODE: !+[] + !+[] + !+[]
TO CODE: 3
...
-----------------------------------------------------------------
PLUGIN NAME: undo-fontcolor-trick
FROM CODE: &amp;#34;&amp;#34;[&amp;#34;fontcolor&amp;#34;]()
TO CODE: &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;
PLUGIN NAME: constructor-str
FROM CODE: 0[&amp;#34;constructor&amp;#34;]
TO CODE: &amp;#34;function Number() { [native code] }&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;j&amp;#34; + &amp;#34;o&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;n&amp;#34;
TO CODE: &amp;#34;join&amp;#34;
PLUGIN NAME: undo-italics-trick
FROM CODE: &amp;#34;false0&amp;#34;[&amp;#34;italics&amp;#34;]()
TO CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;
PLUGIN NAME: undo-italics-trick
FROM CODE: &amp;#34;false0&amp;#34;[&amp;#34;italics&amp;#34;]()
TO CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;
PLUGIN NAME: undo-italics-trick
FROM CODE: &amp;#34;false0&amp;#34;[&amp;#34;italics&amp;#34;]()
TO CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;
PLUGIN NAME: undo-fontcolor-trick
FROM CODE: &amp;#34;&amp;#34;[&amp;#34;fontcolor&amp;#34;]()
TO CODE: &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return&amp;#34; + &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;[12] + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;to&amp;#34; + &amp;#34;&amp;#34;[&amp;#34;constructor&amp;#34;][&amp;#34;na&amp;#34; + (&amp;#34;function Number() { [native code] }&amp;#34; + [])[11] + &amp;#34;e&amp;#34;]](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return&amp;#34; + &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10] + false + &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10])()[&amp;#34;constructor&amp;#34;](&amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10]) + [])[1]) + &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;[12])())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;[12]
TO CODE: &amp;#34;\&amp;#34;&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;function Number() { [native code] }&amp;#34; + []
TO CODE: &amp;#34;function Number() { [native code] }&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10]
TO CODE: &amp;#34;/&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10]
TO CODE: &amp;#34;/&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;#34;[10]
TO CODE: &amp;#34;/&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;&amp;lt;font color=\&amp;#34;undefined\&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#34;[12]
TO CODE: &amp;#34;\&amp;#34;&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return&amp;#34; + &amp;#34;\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;to&amp;#34; + &amp;#34;&amp;#34;[&amp;#34;constructor&amp;#34;][&amp;#34;na&amp;#34; + &amp;#34;function Number() { [native code] }&amp;#34;[11] + &amp;#34;e&amp;#34;]](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return&amp;#34; + &amp;#34;/&amp;#34; + false + &amp;#34;/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1]) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;return&amp;#34; + &amp;#34;\&amp;#34;&amp;#34;
TO CODE: &amp;#34;return\&amp;#34;&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;function Number() { [native code] }&amp;#34;[11]
TO CODE: &amp;#34;m&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;return&amp;#34; + &amp;#34;/&amp;#34; + false + &amp;#34;/&amp;#34;
TO CODE: &amp;#34;return/false/&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;to&amp;#34; + &amp;#34;&amp;#34;[&amp;#34;constructor&amp;#34;][&amp;#34;na&amp;#34; + &amp;#34;m&amp;#34; + &amp;#34;e&amp;#34;]](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1]) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;na&amp;#34; + &amp;#34;m&amp;#34; + &amp;#34;e&amp;#34;
TO CODE: &amp;#34;name&amp;#34;
PLUGIN NAME: simplify-false-regex
FROM CODE: [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()
TO CODE: /false/
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;to&amp;#34; + &amp;#34;&amp;#34;[&amp;#34;constructor&amp;#34;][&amp;#34;name&amp;#34;]](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;]((/false/[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1]) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: string-constructor-name
FROM CODE: &amp;#34;&amp;#34;[&amp;#34;constructor&amp;#34;][&amp;#34;name&amp;#34;]
TO CODE: &amp;#34;String&amp;#34;
PLUGIN NAME: refactor-regex-constr
FROM CODE: /false/[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;)
TO CODE: /\//
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;to&amp;#34; + &amp;#34;String&amp;#34;](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;]((/\// + [])[1]) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;to&amp;#34; + &amp;#34;String&amp;#34;
TO CODE: &amp;#34;toString&amp;#34;
PLUGIN NAME: regex-str
FROM CODE: /\// + []
TO CODE: &amp;#34;/\\//&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + 211[&amp;#34;toString&amp;#34;](&amp;#34;31&amp;#34;)[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](&amp;#34;/\\//&amp;#34;[1]) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: undo-number-tostring-trick
FROM CODE: 211[&amp;#34;toString&amp;#34;](&amp;#34;31&amp;#34;)
TO CODE: &amp;#34;6p&amp;#34;
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;/\\//&amp;#34;[1]
TO CODE: &amp;#34;\\&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + &amp;#34;6p&amp;#34;[1] + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](&amp;#34;\\&amp;#34;) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;6p&amp;#34;[1]
TO CODE: &amp;#34;p&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;s&amp;#34; + &amp;#34;p&amp;#34; + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](&amp;#34;\\&amp;#34;) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;s&amp;#34; + &amp;#34;p&amp;#34; + &amp;#34;l&amp;#34; + &amp;#34;i&amp;#34; + &amp;#34;t&amp;#34;
TO CODE: &amp;#34;split&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;split&amp;#34;](&amp;#34;t&amp;#34;)[&amp;#34;join&amp;#34;](&amp;#34;\\&amp;#34;) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: string-split
FROM CODE: &amp;#34;alert164t50t42t110t112t113t114t120t121t126t127t130t131t132t41t134t47t41t43t44t52t100t136t137t140t174t176t42t51t73&amp;#34;[&amp;#34;split&amp;#34;](&amp;#34;t&amp;#34;)
TO CODE: [&amp;#34;aler&amp;#34;, &amp;#34;164&amp;#34;, &amp;#34;50&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;110&amp;#34;, &amp;#34;112&amp;#34;, &amp;#34;113&amp;#34;, &amp;#34;114&amp;#34;, &amp;#34;120&amp;#34;, &amp;#34;121&amp;#34;, &amp;#34;126&amp;#34;, &amp;#34;127&amp;#34;, &amp;#34;130&amp;#34;, &amp;#34;131&amp;#34;, &amp;#34;132&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;134&amp;#34;, &amp;#34;47&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;43&amp;#34;, &amp;#34;44&amp;#34;, &amp;#34;52&amp;#34;, &amp;#34;100&amp;#34;, &amp;#34;136&amp;#34;, &amp;#34;137&amp;#34;, &amp;#34;140&amp;#34;, &amp;#34;174&amp;#34;, &amp;#34;176&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;51&amp;#34;, &amp;#34;73&amp;#34;]
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + [&amp;#34;aler&amp;#34;, &amp;#34;164&amp;#34;, &amp;#34;50&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;110&amp;#34;, &amp;#34;112&amp;#34;, &amp;#34;113&amp;#34;, &amp;#34;114&amp;#34;, &amp;#34;120&amp;#34;, &amp;#34;121&amp;#34;, &amp;#34;126&amp;#34;, &amp;#34;127&amp;#34;, &amp;#34;130&amp;#34;, &amp;#34;131&amp;#34;, &amp;#34;132&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;134&amp;#34;, &amp;#34;47&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;43&amp;#34;, &amp;#34;44&amp;#34;, &amp;#34;52&amp;#34;, &amp;#34;100&amp;#34;, &amp;#34;136&amp;#34;, &amp;#34;137&amp;#34;, &amp;#34;140&amp;#34;, &amp;#34;174&amp;#34;, &amp;#34;176&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;51&amp;#34;, &amp;#34;73&amp;#34;][&amp;#34;join&amp;#34;](&amp;#34;\\&amp;#34;) + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: string-array-join
FROM CODE: [&amp;#34;aler&amp;#34;, &amp;#34;164&amp;#34;, &amp;#34;50&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;110&amp;#34;, &amp;#34;112&amp;#34;, &amp;#34;113&amp;#34;, &amp;#34;114&amp;#34;, &amp;#34;120&amp;#34;, &amp;#34;121&amp;#34;, &amp;#34;126&amp;#34;, &amp;#34;127&amp;#34;, &amp;#34;130&amp;#34;, &amp;#34;131&amp;#34;, &amp;#34;132&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;134&amp;#34;, &amp;#34;47&amp;#34;, &amp;#34;41&amp;#34;, &amp;#34;43&amp;#34;, &amp;#34;44&amp;#34;, &amp;#34;52&amp;#34;, &amp;#34;100&amp;#34;, &amp;#34;136&amp;#34;, &amp;#34;137&amp;#34;, &amp;#34;140&amp;#34;, &amp;#34;174&amp;#34;, &amp;#34;176&amp;#34;, &amp;#34;42&amp;#34;, &amp;#34;51&amp;#34;, &amp;#34;73&amp;#34;][&amp;#34;join&amp;#34;](&amp;#34;\\&amp;#34;)
TO CODE: &amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73&amp;#34; + &amp;#34;\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: eval-expr
FROM CODE: &amp;#34;return\&amp;#34;&amp;#34; + &amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73&amp;#34; + &amp;#34;\&amp;#34;&amp;#34;
TO CODE: &amp;#34;return\&amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73\&amp;#34;&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;]([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73\&amp;#34;&amp;#34;)())();
-----------------------------------------------------------------
PLUGIN NAME: fix-eval
FROM CODE: [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73\&amp;#34;&amp;#34;)
TO CODE: eval(&amp;#34;return\&amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73\&amp;#34;&amp;#34;)
PLUGIN NAME: eval-return-str
FROM CODE: eval(&amp;#34;return\&amp;#34;aler\\164\\50\\42\\110\\112\\113\\114\\120\\121\\126\\127\\130\\131\\132\\41\\134\\47\\41\\43\\44\\52\\100\\136\\137\\140\\174\\176\\42\\51\\73\&amp;#34;&amp;#34;)
TO CODE: &amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;
[][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;)();
-----------------------------------------------------------------
PLUGIN NAME: fix-eval
FROM CODE: [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;)
TO CODE: eval(&amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;)
eval(&amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;);
-----------------------------------------------------------------
PLUGIN NAME: eval-program
FROM CODE: eval(&amp;#34;alert(\&amp;#34;HJKLPQVWXYZ!\\&amp;#39;!#$*@^_`|~\&amp;#34;);&amp;#34;);
TO CODE: alert(&amp;#34;HJKLPQVWXYZ!\&amp;#39;!#$*@^_`|~&amp;#34;);
alert(&amp;#34;HJKLPQVWXYZ!\&amp;#39;!#$*@^_`|~&amp;#34;);
-----------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So the deobfuscator works for small snippets. The deobfuscated line is the same
that I entered into JSFuck web app. However for bigger snippet there
is a problem. During the AST traversal Babel runs out of stack space and crashes
with the the following error message on the first iteration of the loop:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:77
  visitSingle(node, key) {
             ^

RangeError: Maximum call stack size exceeded
    at TraversalContext.visitSingle (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:77:14)
    at TraversalContext.visit (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:133:19)
    at traverseNode (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/traverse-node.js:24:17)
    at NodePath.visit (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/path/context.js:107:52)
    at TraversalContext.visitQueue (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:105:16)
    at TraversalContext.visitSingle (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:79:19)
    at TraversalContext.visit (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:133:19)
    at traverseNode (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/traverse-node.js:24:17)
    at NodePath.visit (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/path/context.js:107:52)
    at TraversalContext.visitQueue (/Users/rl/src/trickster.dev-code/jsfuck_wip/jsfuck_deobfuscator/node_modules/@babel/traverse/lib/context.js:105:16)

Node.js v20.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice function calls repeating in the stack trace. This means that we have
recursion going on in here. JSFuck makes the AST excessively bloated
and thus difficult to programmatically simplify with Babel.&lt;/p&gt;
&lt;p&gt;I think this is the final point of my JSFuck deobfuscation journey. The original
objective for doing this was to learn more about Babel and AST-level deobfuscation
and that was achieved. The current deobfuscation approach was of some success
in a way that it is able to simplify sufficiently small and simple snippets,
but ran into a fundamental limitation of Babel (if you know how to solve this -
kindly let me know).&lt;/p&gt;
&lt;p&gt;Does that mean JSFuck output cannot be deobfuscated? No. There are open source
examples that treat JS code as string to undo the changes that JSFuck is
making:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/dnetguru/JSUNFuck&#34;&gt;JSUNFUck&lt;/a&gt; - C#/.NET program&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/VeNoMouS/cloudscraper/blob/master/cloudscraper/interpreters/jsunfuck.py&#34;&gt;jsunfuck.py&lt;/a&gt; file in cloudscraper Python module.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/enkhee-Osiris/Decoder-JSFuck&#34;&gt;JSFuck Decoder&lt;/a&gt; - simple web app developed in vanilla JS/HTML.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-06-25_15.39.24.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-06-25_15.40.10.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is indeed an easier and better way to deal with JSFuck being used to
obfuscate client-side JavaScript code. But one cannot become stronger and
smarter by always taking an easy way. We learned about JS type coercion and about
various weird runtime tricks that JSFuck is relying on. Martin Kleppe, the
developer of JSFuck is a smart, creative person and that shows in his work.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Prompting the LLM is no substitute for knowledge work</title>
      <link>https://www.trickster.dev/post/prompting-the-llm-is-no-substitute-for-knowledge-work/</link>
      <pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/prompting-the-llm-is-no-substitute-for-knowledge-work/</guid>
      <description>&lt;p&gt;Artificial intelligence, especially large language models such as ChatGPT are all
the rage now. ChatGPT has the distinction of being the fastest growing product
in the history of technology - it reached the first one million users in just a
few days. As such, it is the talk of the global village now. There is no shortage
of overly excited people posting their takes on how it will change the world
for the better and what you should be doing right now to benefit from that.
On the other hand, some are worried about ending up jobless with no place to go
in the dystopian reality of 2029. Who are right? We cannot tell without knowing
the future, but I don&amp;rsquo;t think any of the extreme predictions are reasonable. In
many cases they are not even intellectually honest (more on that later). I am
not saying that LLMs are not a big deal. Building the computational machinery
to make human language tractable by software has long been a Holy Grail of
research fields such as computational linguistics and natural language
processing. Making that work is a great technical achievement that should not
be taken lightly. However I don&amp;rsquo;t believe in black-and-white thinking. I don&amp;rsquo;t
even believe in grayscale thinking. There is way too much complexity in the
current social, technical, political and cultural realities to be sufficiently
captured by mere notion of &amp;ldquo;goodness&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Can the LLMs make you jobless by text-generating your job position away? Such
question was posed to sci-fi author Cory Doctorow in 2020, during the Remote
Chaos Experience conference. Back then, ChatGPT was not released, but GPT-3 was
already available. His response was that only people who have very boring,
terrible jobs should be worried about GPT-3. That answer should not have been
surprising to people who actually played a bit with GPT-3. It can certainly be
guided into generating text that compares to what a human could write, but if
left unattended it can easily go off the rails and start generating complete
garbage.&lt;/p&gt;
&lt;p&gt;ChatGPT (a.k.a. GPT-3.5-turbo) is not exactly immune to generating nonsense
either. Let us do a quick experiment with &lt;a href=&#34;https://github.com/sigoden/aichat&#34;&gt;aichat&lt;/a&gt; -
one of the OpenAI API clients.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ aichat
Welcome to aichat 0.8.0
Type &amp;#34;.help&amp;#34; for more information.
〉.model gpt-3.5-turbo

〉.prompt What online courses could be taken to learn JavaScript deobfuscation?

Ｐ〉
Ｐ〉;
There are a few online courses that could help you learn JavaScript deobfuscation:

1. &amp;#34;Advanced JavaScript Concepts&amp;#34; by Udemy: This course covers advanced JavaScript topics, including deobfuscation techniques.

2. &amp;#34;JavaScript Deobfuscation and Malware Analysis&amp;#34; by Pluralsight: This course focuses specifically on JavaScript deobfuscation and how to analyze malware that uses obfuscated JavaScript.

3. &amp;#34;Practical Malware Analysis&amp;#34; by the Malware Unicorn: This course covers malware analysis techniques, including how to deobfuscate JavaScript used in malware.

4. &amp;#34;JavaScript Deobfuscation for Web Developers&amp;#34; by Packt: This course focuses on deobfuscation techniques for JavaScript used in web development.

5. &amp;#34;Reverse Engineering Malware&amp;#34; by OpenSecurityTraining: This course covers a variety of reverse engineering techniques, including deobfuscating JavaScript used in malware.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see a factually wrong output here. Courses 2 and 4 don&amp;rsquo;t exist. Courses
1, 3 and 5 do exist, but they are not very relevant to JS deobfuscation. So we
got an answer that is factually wrong, but written in a very confident wording.
We got a problem of trust here. If we rely on ChatGPT to produce a text that
is factually correct we cannot accept the possibility of some percentage of
output being simply wrong. Thus it cannot be trusted to do all the work for us -
to benefit from ChatGPT and similar systems, we have to work &lt;em&gt;with them&lt;/em&gt; to
achieve a desired result. That includes checking the generated text for factual
correctness.&lt;/p&gt;
&lt;p&gt;But we&amp;rsquo;re talking fairly obscure, niche stuff here. In fact, I kind of did it
on purpose to demonstrate a major failure mode of LLMs. But what if ChatGPT was used
to help with something more relevant to general society, such as preparing a
lawsuit against an airline? One Steven Schwartz, an attorney with some three
decades of experience, was using ChatGPT for (scary quotes) &amp;ldquo;research&amp;rdquo; when
preparing paperwork to sue a certain airline over an injury allegedly sustained
by a customer. That went horribly, scandalously wrong. Like in our quick
experiment above, ChatGPT hallucinated references to prior legal cases and
court decisions that simply did not happen. Schwartz himself got in trouble
for submitting legal paperwork with false data and the story made it&amp;rsquo;s rounds
across the press outlets. You know you really fuck up your work when your oopsie
&lt;a href=&#34;https://web.archive.org/web/20230530005520/https://www.nytimes.com/2023/05/27/nyregion/avianca-airline-lawsuit-chatgpt.html&#34;&gt;makes it&amp;rsquo;s way to New York Times&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;GPT-4 is more advanced than ChatGPT, but is susceptible to hallucinating stuff
as well (just ask the same question about deobfuscation courses). But what if
there was a sufficiently advanced LLM that did not hallucinate and only
generated text that was factually correct (unless specifically instructed to
write fiction)? Realistically that is not even possible, as it relies on
perfectly correct information being sourced for training of such model. However,
for the sake of argument, let us assume that there is such a system running. If
we have a perfect knowledge machine, can we use it to replace a human work?
Well, the thing is, we still need to tell it what to do. Even a perfect LLM is
GIGO: garbage in, garbage out. When it comes to non-trivial things (such as
developing large software systems), describing them in sufficient detail in a
way that captures all nuances and edge cases is quite difficult type of work.
This can be seen in real world software projects, especially when they involve
non-technical stakeholders. It&amp;rsquo;s naive for manager or entrepreneur to assume that
soon enough they will be able to get rid of those pesky developers, as they will
just tell a chatbot what code it should write. Quite likely the activity
of getting an LLM to do things (generate or edit blog post, develop code,
write marketing copy, etc.) will be new kind of work - prompt design/engineering.&lt;/p&gt;
&lt;p&gt;Sure, you can use ChatGPT or GPT-4 to assist in software development work -
generate code snippets, do some refactoring, get advice on improvements and so
on. But at the end of the day LLM is merely a tool AI. It does not have it&amp;rsquo;s
own judgement, values or will that human possesses. Even worse, it is debatable
to what extent it &lt;em&gt;knows&lt;/em&gt; things, as the fundamental way it operates is
predicting what text should be generated next. It is the user that drives the
LLM who is supposed to know what they are doing. For example, ChatGPT may
generate a code snippet that is mostly correct in the light of provided
requirements. It&amp;rsquo;s up to the user to make it completely correct. Now, what if
the user was a non-technical entrepreneur? How well would it work out for them?&lt;/p&gt;
&lt;p&gt;By amplifying, not replacing, human effort LLMs can be said to provide a
leverage. As the leverage increases, the value of sound judgement increases as
well. When it&amp;rsquo;s easier to turn your decision into a reality, the quality of
these decisions starts to matter a whole lot more.&lt;/p&gt;
&lt;p&gt;But the AI hype wave is in full swing now. There&amp;rsquo;s all kinds of social media
influencers and content creators telling you to do this or that to benefit
from the AI revolution to get rich. A lot of these people are digital snake oil
merchants who are jumping from trend to trend. Some propose completely silly,
unrealistic money making schemes just to get your attention. It&amp;rsquo;s all part of
make-money-online content creation playbook. The truth is, these people mostly
don&amp;rsquo;t make money from the thing they promote. Instead, they make money by
promoting the thing they are promoting through ad revenue (esp. on Youtube),
affiliate offers and their own info products. During the Bitcoin bull
run and NFT craze there was no shortage of characters talking loudly about how
you have to &amp;ldquo;invest&amp;rdquo; into this or that thingy on the blockchain. I suspect the
vast majority of them could not tell the difference between Base64 and AES. Now
you have great deal of people talking the same way about AI (mostly LLMs). How
many of these newly minted &amp;ldquo;AI experts&amp;rdquo; even know what the fuck a linear
regression is?&lt;/p&gt;
&lt;p&gt;That is not to say that nobody ever gets rich during a gold rush. Some people
swiftly grab a transient opportunity and milk it for what it&amp;rsquo;s worth. Others do
some equivalent of selling shovels and pick axes during the Wild West period.
Some simply gamble and happen to be winning. Speaking about LLM-based SaaS apps
specifically, there&amp;rsquo;s a certain problem. If you have some SaaS app that has
OpenAI API as a hard, critical dependency that means your SaaS business is
essentially at the mercy of OpenAI. Right now, there are some important
questions that pertain to generative AI. What exactly should and
should not it be allowed to generate? If some data is scraped to train
the LLM what happens to related copyrights? What will be done against
gray hat and black hat usage of LLMs, such as fake product review generation,
harassment, phishing, influence operations? Depending on how things play
out, your entire SaaS business could die overnight by being cut off from API
access as it may turn out not be kosher anymore in the light of new legislation
or newly introduced corporate policy. Remember that Twitter API was free and open
in the early years. There were many companion apps for Twitter users built on
that API - unofficial clients, automation tools and the like. But over time
Twitter introduced many restrictions into the official API and most of them
had to die. This is something that indie hackers who rely on the goodwill
and interest alignment from OpenAI (or Anthropic, or Google&amp;hellip;) should keep in
mind.&lt;/p&gt;
&lt;p&gt;Furthermore, using an LLM that you don&amp;rsquo;t self-host can be problematic from data
security and privacy perspective, as you are literally telling some other
entity the specifics of your work. There was already a scandal of Samsung
employees leaking trade secrets to OpenAI by using ChatGPT to help with their
regularly scheduled programming. That is the reason why the usage of
ChatGPT is explicitly banned in some corporations. A fundamental problem with
information society is that many things are data driven, but we mostly don&amp;rsquo;t have
any control how data about people and companies is processed, stored, transferred.
Information functions by being always in motion and largely has a life of it&amp;rsquo;s
own. If you tell your trade secrets to ChatGPT, can you trust ChatGPT not to
tell them to your competitor?&lt;/p&gt;
&lt;p&gt;Times are difficult now. We&amp;rsquo;re only 3.5 years into 2020s and so far we had
a global pandemic, energy crisis, murderous Taliban psychos winning the
Afghan war, a new major conflict in Ukraine, inflation wave causing cost of
living crisis, tech downturn and banks failing. But even now I advice everyone
to keep their heads cool. You probably cannot prompt your way into quick wealth,
especially if you&amp;rsquo;re about average on coming up with LLM prompts. In economics
there&amp;rsquo;s a concept of alpha. It&amp;rsquo;s a number quantifying how well an asset
outperforms the overall market. Dumb luck aside, to achieve exceptional results
that greatly outperform most of the competition one must posses or do something
exceptional. The problem with doing easy things (e.g. generating basic ad copy)
is that they don&amp;rsquo;t stay easy for very long in a competitive environment. It is
unlikely that ChatGPT, GPT-4 and other AI systems will make everyone jobless
due to considerations I have discussed. These systems are merely tools subservient to
a human will and there will be a need for people working with them. Perhaps
the daily reality of many occupations will change, perhaps some people will need
to learn how to do something else, but I don&amp;rsquo;t expect the fully AI-driven world
to happen anytime soon.&lt;/p&gt;
&lt;p&gt;We cannot know what the future will be like. In a sense, we don&amp;rsquo;t even have a
(predictable) future when things are so crazy, volatile and fucked up. All we
can do is hedge our bets and roll the dice.&lt;/p&gt;
&lt;p&gt;Keep calm and seek your alpha.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Understanding JavaScript packers</title>
      <link>https://www.trickster.dev/post/understanding-javascript-packers/</link>
      <pubDate>Tue, 30 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-javascript-packers/</guid>
      <description>&lt;p&gt;In the world of desktop software, the concept of packer is not new. A packer is
a tool that takes a binary executable file as input, applies transformations
(e.g. compression, encryption, introducing anti-debugging tricks) and outputs a
new, modified executable file that is different at binary level, but retains the
functionality of original program. Some packers, such as &lt;a href=&#34;https://upx.github.io/&#34;&gt;UPX&lt;/a&gt;
are only meant to make the executable files smaller. More advanced packers
apply machine code encryption to make reverse engineering harder. The key to
decrypt the machine code is hidden in a binary file and applied automatically
when program is launched, but the encryption layer introduces a barrier for
disassebling or decompiling the machine code, which increases the effort
needed to reverse engineer the inner workings of a program.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s old news. Nowadays a lot of software is web-based. But the thing is,
the concept of packer is also applicable in JavaScript world. There are tools
and techniques to transform JS code in such a way that it becomes smaller and/or
less readable. Thus JS packing can be used as obfuscation technique. Note that
I am not talking about simple code minification here. Instead, we&amp;rsquo;re going to
see some techniques that are similar to executable packing from the compiled
software world.&lt;/p&gt;
&lt;p&gt;Depending on what kind of hat the programmer is wearing, there might be different
motivations to apply JS packing. One is simply to make JS code smaller, which
helps performance. Lighter pages load faster, which leads to better user
experience and may have a positive impact on search engine ranking. JS packing
can also be applied to make things harder for web scraper developers, esp. if
there&amp;rsquo;s some non-trivial client side functionality involved. For instance,
Imperva (formely known as Incapsula) antibot solution is known to have used
&lt;a href=&#34;https://nerodesu017.github.io/antibots/programming/2021/05/07/antibots-part-3.html&#34;&gt;a simple JS packing technique&lt;/a&gt;
as one layer of JS SDK code obfuscation. On the black hat side, JS packing is
used to evade detection of malicious code that is placed on compromised or
fraudulent websites for cybercrime purposes.&lt;/p&gt;
&lt;p&gt;There are two aspects to JavaScript packing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Transforming the original code into a new (e.g. compressed, encoded, encrypted)
form. This will be hardcoded into output code.&lt;/li&gt;
&lt;li&gt;Introducing a small amount of helper code that will unpack and execute the
original code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;eval(unescape())&lt;/code&gt; is the minimum viable JS packing technique. Original code
is converted into URL-encoded form, then put as an argument to &lt;code&gt;eval(unescape())&lt;/code&gt;
functions calls.&lt;/p&gt;
&lt;p&gt;To find an example, we can &lt;a href=&#34;https://publicwww.com/websites/%22eval%28unescape%28%22/&#34;&gt;search for &lt;code&gt;&amp;quot;eval(unescape(&amp;quot;&lt;/code&gt; on PublicWWW&lt;/a&gt;.
On one of the pages we find, there&amp;rsquo;s a following JS snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;myText&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Пишите нам&amp;#39;&lt;/span&gt;; eval(&lt;span style=&#34;color:#a6e22e&#34;&gt;unescape&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%76%69%74%40%61%75%64%69%74%2d%69%74%2e%72%75%3f%73%75%62%6a%65%63%74%3d%25%44%30%25%41%31%25%44%30%25%42%31%25%44%30%25%42%35%25%44%31%25%38%30%25%44%30%25%42%45%25%44%30%25%42%43%25%44%30%25%42%35%25%44%31%25%38%32%25%44%31%25%38%30%22%3e%27%2b%28%6d%79%54%65%78%74%20%3f%20%6d%79%54%65%78%74%20%3a%20%27%76%69%74%40%61%75%64%69%74%2d%69%74%2e%72%75%27%29%2b%27%3c%2f%61%3e%27%29%3b&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-29_14.09.39.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can trivially recover the original source code of this snippet by copy-pasting
the &lt;code&gt;unescape()&lt;/code&gt; call into JS REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v20.1.0.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; unescape(&amp;#39;%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%76%69%74%40%61%75%64%69%74%2d%69%74%2e%72%75%3f%73%75%62%6a%65%63%74%3d%25%44%30%25%41%31%25%44%30%25%42%31%25%44%30%25%42%35%25%44%31%25%38%30%25%44%30%25%42%45%25%44%30%25%42%43%25%44%30%25%42%35%25%44%31%25%38%32%25%44%31%25%38%30%22%3e%27%2b%28%6d%79%54%65%78%74%20%3f%20%6d%79%54%65%78%74%20%3a%20%27%76%69%74%40%61%75%64%69%74%2d%69%74%2e%72%75%27%29%2b%27%3c%2f%61%3e%27%29%3b&amp;#39;)
`document.write(&amp;#39;&amp;lt;a href=&amp;#34;mailto:[REDACTED]?subject=%D0%A1%D0%B1%D0%B5%D1%80%D0%BE%D0%BC%D0%B5%D1%82%D1%80&amp;#34;&amp;gt;&amp;#39;+(myText ? myText : &amp;#39;[REDACTED]&amp;#39;)+&amp;#39;&amp;lt;/a&amp;gt;&amp;#39;);`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This turned out to be a simple trick to hide owner&amp;rsquo;s email address from web
scrapers that don&amp;rsquo;t execute JavaScript and are not tailored to this specific
site.&lt;/p&gt;
&lt;p&gt;A more complex example would be &lt;a href=&#34;http://dean.edwards.name/packer/&#34;&gt;Dean Edwards packer&lt;/a&gt;.
Let us pack the following JavaScript snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hello&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;hello&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After checking both checkboxes and pressing &amp;ldquo;Pack&amp;rdquo; button we get the following
packed code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;eval(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;){&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;String;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/^/&lt;/span&gt;,String)){&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;){&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;]}];&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\w+&amp;#39;&lt;/span&gt;};&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;};&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;)&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;])&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RegExp(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\b&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\b&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;),&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;]);&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;}(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1 0=&amp;#34;0 2&amp;#34;;3.4(0);&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hello|let|world|console|log&amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;),&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,{}))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using &lt;a href=&#34;https://beautifier.io/&#34;&gt;Beautifier.io&lt;/a&gt; to prettify (but not unpack at
the moment - make sure all checkboxes are unchecked) this code makes it slightly
more readable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;eval(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; String;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/^/&lt;/span&gt;, String)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\w+&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;]) &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RegExp(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\b&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\b&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1 0=&amp;#34;0 2&amp;#34;;3.4(0);&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hello|let|world|console|log&amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, {}))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-29_15.10.30.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the outermost layer we see that &lt;code&gt;eval()&lt;/code&gt; function takes a JS code string
returned by immediately invoked function with five parameters - &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;,
&lt;code&gt;k&lt;/code&gt;, &lt;code&gt;e&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt;. The values for these arguments are related to original code
snippet and in aggregate represent a packed representation. The innermost
function applies some regular expression magic to regenerate what the code
was originally, so that &lt;code&gt;eval()&lt;/code&gt; could execute it.&lt;/p&gt;
&lt;p&gt;Like with the previous example, we can skip the &lt;code&gt;eval()&lt;/code&gt; part and perform only
the unpacking:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; let unpack = function(p,a,c,k,e,r){e=String;if(!&amp;#39;&amp;#39;.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return&amp;#39;\\w+&amp;#39;};c=1};while(c--)if(k[c])p=p.replace(new RegExp(&amp;#39;\\b&amp;#39;+e(c)+&amp;#39;\\b&amp;#39;,&amp;#39;g&amp;#39;),k[c]);return p};
undefined
&amp;gt; unpack(&amp;#39;1 0=&amp;#34;0 2&amp;#34;;3.4(0);&amp;#39;,5,5,&amp;#39;hello|let|world|console|log&amp;#39;.split(&amp;#39;|&amp;#39;),0,{});
&amp;#39;let hello=&amp;#34;hello world&amp;#34;;console.log(hello);&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We got the original code without newline in the middle. Like with other
obfuscation techniques, we are not necessarily able to recover the exact
original code when reverse engineering.&lt;/p&gt;
&lt;p&gt;A lot of the online JS packer tools are some variations of Dean Edwards packer
that was originally developed in C#. Thus it is useful to know how to revert
what it does to the code.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping product data from Nike.com</title>
      <link>https://www.trickster.dev/post/scraping-product-data-from-nike/</link>
      <pubDate>Mon, 15 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-product-data-from-nike/</guid>
      <description>&lt;p&gt;Suppose you are looking to collect pricing data on male footwear from the
official website of one of the industry leaders - Nike.com. There&amp;rsquo;s a
&lt;a href=&#34;https://www.nike.com/w/mens-shoes-nik1zy7ok&#34;&gt;product list page&lt;/a&gt; that seems
like a good place to start, but the infinite scroll feature might seem puzzling
to budding web scraper developers. In this post, we will go through developing
a Scrapy project for scraping sneaker price data from this website. Some
familiarity with Scrapy and web scraping in general is assumed.&lt;/p&gt;
&lt;p&gt;Before we proceed with writing the code, let us explore the site a little.
Open the source code of product list page and try searching for a product
title that we can see rendered on the page. The purpose of doing this is to
gain insight of how data is represented in the original HTML that is fetched
on the first load of the page. By searching for &amp;ldquo;Nike Air Max Pulse&amp;rdquo; we get
some hits with this string being part of normal HTML, but more importantly
we get a hit in the big JSON document that is embedded in the page. We can see
that data on products (titles, prices, image URLs) is encoded into this JSON
string as well.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-15_14.50.53.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By scrolling up, we find that the JSON string is wrapped in a script element
(due to frontend programming reasons):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__NEXT_DATA__&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-15_14.51.15.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is good for us, as we can extract the data we need from big JSON string and
use it for our purposes. However, how does the infinite scroll work? How is the
new data loaded? This can be explored in Network tab of Chrome DevTools.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-15_14.58.45.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-05-15_14.59.29.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can see that site launched an API call to api.nike.com every time new page of
product list data is needed (i.e. you scroll to the bottom and trigger the
fetching of new data). The JSON response to this API request contains product
data (largely in the same structure as we found in the page source) and a section
for pagination that contains some relative URLs (starting with &lt;code&gt;/product_feed/rollup_threads/v2&lt;/code&gt;)
that are passed into &lt;code&gt;endpoint&lt;/code&gt; URL parameter when further pages of data are
being loaded. The initial value for &lt;code&gt;endpoint&lt;/code&gt; parameter that matches second
page of data can also be found in the big JSON in the page source:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;pageData&amp;#34;:{&amp;#34;prev&amp;#34;:&amp;#34;&amp;#34;,&amp;#34;next&amp;#34;:&amp;#34;/product_feed/rollup_threads/v2?filter=marketplace%28US%29\u0026filter=language%28en%29\u0026filter=employeePrice%28true%29\u0026filter=attributeIds%2816633190-45e5-4830-a068-232ac7aea82c%2C0f64ecc7-d624-4e91-b171-b83a03dd8550%29\u0026anchor=24\u0026consumerChannelId=d9a5bc42-4b9c-4976-858a-f159cf99c647&amp;amp;count=24
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-15_15.03.12.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This informs our general strategy for scraping:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch the product list page HTML with nested JSON. Use it to extract some
initial product data and the first value for &lt;code&gt;endpoint&lt;/code&gt; parameter to start API
scraping.&lt;/li&gt;
&lt;li&gt;Scrape product feed API by traversing all pages. This gives product titles
and pricing info.&lt;/li&gt;
&lt;li&gt;Augment data from step 2 by scraping product detail pages to add some more
data on products.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 3 is technically not needed if we only want pricing information, but I&amp;rsquo;m
including it here as many web scraping project for ecommerce industry involve
scraping the PDP (product detail page) as well.&lt;/p&gt;
&lt;p&gt;After new project boilerplate is generated with &lt;code&gt;scrapy startproject&lt;/code&gt; and
&lt;code&gt;genspider&lt;/code&gt; commands let us start by editing items.py to define a class for
an item (a unit of scraped data):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define here the models for your scraped items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See documentation in:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# https://docs.scrapy.org/en/latest/topics/items.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NikeProductItem&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subtitle &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    current_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    empl_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    full_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_stock &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Scrapy spider class I generated is named &lt;code&gt;NikecomSpider&lt;/code&gt; and resides in
nikecom.py file. Let&amp;rsquo;s make this spider load the product list page and extract
some initial product data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NikecomSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nikecom&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nike.com&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;www.nike.com&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;api.nike.com&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.nike.com/w/mens-shoes-nik1zy7ok&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_first_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;convert_product_dict_into_item&lt;/span&gt;(self, product):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Removed for brevity.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_first_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_data_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@id=&amp;#34;__NEXT_DATA__&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(next_data_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        props &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;props&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page_props &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; props&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pageProps&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        initial_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page_props&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;initialState&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        wall &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; initial_state&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wall&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; wall&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; product &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; products:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;convert_product_dict_into_item(product)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# TODO: yield request to product page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I have created method &lt;code&gt;convert_product_dict_into_item()&lt;/code&gt; that converts data from
original JSON to Scrapy item object, but for now this code is not shown for the
sake of brevity. Just keep in mind that it will be reused when we do API
scraping.&lt;/p&gt;
&lt;p&gt;Next thing we want to do in &lt;code&gt;parse_first_page()&lt;/code&gt; method is to launch the first
API request. We do this based on initial &lt;code&gt;endpoint&lt;/code&gt; parameter value that
we extract from JSON string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; wall&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pageData&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; page_data &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_feed_api_request(next_page_link)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we rely on another helper method to create the API request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_feed_api_request&lt;/span&gt;(self, next_page_link):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryid&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;anonymousId&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;7CC266B713D36CCC7275B33B6E4F9206&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#XXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;country&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;us&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;endpoint&amp;#39;&lt;/span&gt;: next_page_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;localizedRangeStr&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{lowestPrice}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; — &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{highestPrice}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_product_feed)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In &lt;code&gt;parse_product_feed()&lt;/code&gt; method we process the API response, create item objects
and generate requests to product detail pages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_product_feed&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; product &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; products:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;convert_product_dict_into_item(product)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;], meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;item&amp;#39;&lt;/span&gt;: item}, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_product_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;next&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_feed_api_request(next_page_link)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that partially-filled item object is passed to the next callback via the
&lt;code&gt;meta&lt;/code&gt; dictionary. We modify the &lt;code&gt;parse_first_page()&lt;/code&gt; method to do this as well,
instead of just generating item object.&lt;/p&gt;
&lt;p&gt;In this sample project product detail page parsing is limited to extracting
only a couple of fields from the HTML:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_product_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;item&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[contains(@class, &amp;#34;description-preview&amp;#34;)]/p/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;image_urls&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//picture/source/@srcset&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This now generates the final item object, with product description and image
URLs:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{&amp;#39;current_price&amp;#39;: 130,
 &amp;#39;description&amp;#39;: &amp;#39;Nothing as fly, nothing as comfortable, nothing as proven. &amp;#39;
                &amp;#39;The Nike Air Max 90 stays true to its OG running roots with &amp;#39;
                &amp;#39;the iconic Waffle sole, stitched overlays and classic TPU &amp;#39;
                &amp;#39;details. Classic colors celebrate your fresh look while Max &amp;#39;
                &amp;#39;Air cushioning adds comfort to the journey.&amp;#39;,
 &amp;#39;empl_price&amp;#39;: 78,
 &amp;#39;full_price&amp;#39;: 130,
 &amp;#39;image_urls&amp;#39;: [&amp;#39;https://static.nike.com/a/images/t_PDP_1280_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_1280_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_640/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_440/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_440/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_1280_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_1280_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_640/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_440/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/t_PDP_864_v1/f_auto,b_rgb:f5f5f5/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;,
                &amp;#39;https://static.nike.com/a/images/f_auto,b_rgb:f5f5f5,w_440/wzitsrb4oucx9jukxsmc/air-max-90-mens-shoes-6n3vKB.png&amp;#39;],
 &amp;#39;in_stock&amp;#39;: True,
 &amp;#39;pid&amp;#39;: &amp;#39;12788919&amp;#39;,
 &amp;#39;product_url&amp;#39;: &amp;#39;https://www.nike.com/t/air-max-90-mens-shoes-6n3vKB/CN8490-002&amp;#39;,
 &amp;#39;subtitle&amp;#39;: &amp;#34;Men&amp;#39;s Shoes&amp;#34;,
 &amp;#39;title&amp;#39;: &amp;#39;Nike Air Max 90&amp;#39;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The final Scrapy spider code is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urlencode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; nike.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; NikeProductItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NikecomSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nikecom&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nike.com&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;www.nike.com&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;api.nike.com&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.nike.com/w/mens-shoes-nik1zy7ok&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_first_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;convert_product_dict_into_item&lt;/span&gt;(self, product):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; NikeProductItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;subtitle&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subtitle&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pid&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pid&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        price_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; price_dict &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;current_price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; price_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;currentPrice&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;empl_price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; price_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;employeePrice&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;full_price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; price_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fullPrice&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;in_stock&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;inStock&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{countryLang}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.nike.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_feed_api_request&lt;/span&gt;(self, next_page_link):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryid&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;anonymousId&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;7CC266B713D36CCC7275B33B6E4F9206&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#XXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;country&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;us&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;endpoint&amp;#39;&lt;/span&gt;: next_page_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;localizedRangeStr&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{lowestPrice}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; — &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{highestPrice}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_product_feed)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_first_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_data_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@id=&amp;#34;__NEXT_DATA__&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(next_data_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        props &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;props&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page_props &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; props&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pageProps&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        initial_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page_props&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;initialState&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        wall &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; initial_state&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wall&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; wall&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; product &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; products:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;convert_product_dict_into_item(product)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;], meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;item&amp;#39;&lt;/span&gt;: item}, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_product_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; wall&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pageData&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; page_data &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page_data&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_feed_api_request(next_page_link)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_product_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;item&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[contains(@class, &amp;#34;description-preview&amp;#34;)]/p/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;image_urls&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//picture/source/@srcset&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_product_feed&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; product &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; products:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;convert_product_dict_into_item(product)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;product_url&amp;#39;&lt;/span&gt;], meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;item&amp;#39;&lt;/span&gt;: item}, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_product_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;products&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;next&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_feed_api_request(next_page_link)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this requires no proxies or anything to deal with antibots despite
Nike being a customer of not one, but two antibot solutions - Kasada and Akamai
Bot Manager. This provides an interesting example of how there are multiple levels
to protection against automation. Automatically creating Nike accounts and/or
placing orders is known to be challenging thing to do, yet Nike allows a simple
form of product data scraping without imposing any difficulty on doing that.
What we developed here was a quite simple Scrapy project that was based on
some quite basic forms of reverse engineering.&lt;/p&gt;
&lt;p&gt;One last thing to note is the discounted employee price that we were able
to scrape from API. This is a slightly hidden piece of information that regular
web user is not supposed to see, but one can scrape it by knowing how to tap
into the API.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How do they (try to) block bots: a broad review of automation countermeasures</title>
      <link>https://www.trickster.dev/post/how-do-they-try-to-block-bots-a-broad-review-of-automation-countermeasures/</link>
      <pubDate>Sun, 14 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-do-they-try-to-block-bots-a-broad-review-of-automation-countermeasures/</guid>
      <description>&lt;p&gt;Not every website wants to let the data to be scraped and not every app wants
to allow automation of user activity. If you work in scraping and automation
at any capacity you certainly have dealt with sites that work just fine when
accessed through normal browser throwing captchas or error pages at your bot.
There are multiple security mechanisms that can cause this to happen. Today
we will do a broad review of automation countermeasures that can be implemented
at various levels.&lt;/p&gt;
&lt;h1 id=&#34;ip-level&#34;&gt;IP level&lt;/h1&gt;
&lt;p&gt;The following techniques work at Internet Protocol level by allowing or blocking
traffic based on source IP address it is coming from.&lt;/p&gt;
&lt;h2 id=&#34;geofencing&#34;&gt;Geofencing&lt;/h2&gt;
&lt;p&gt;Geofencing (or geo-restricting/geo-blocking) is blocking/allowing requests
based on source geographic location (typically a country). This relies on GeoIP
data provided by vendors such as MaxMind or IP2Location to perform lookups.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-11_17.10.10.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As web scraper developer you can trivially bypass this using proxies.&lt;/p&gt;
&lt;h2 id=&#34;rate-limiting&#34;&gt;Rate limiting&lt;/h2&gt;
&lt;p&gt;Web sites may impose upper bounds on on how many requests are allowed per some
timeframe from a single IP and start blocking incoming traffic if traffic
intensity exceeds that threshold. This is typically implemented with a leaky
bucket algorithm. Suppose the site allows 60 requests per minute from a single
IP (i.e. one per seconds). It keeps a counter of how many requests it received,
decreasing it by one every second. If the counter exceeds the threshold value
(60) the site refuses further requests until it is below the threshold again.
This limits how aggressive we can be when scraping, but typically can be defeated
by spreading the traffic around a set of IP addresses by routing it through proxy
pool.&lt;/p&gt;
&lt;h2 id=&#34;filtering-by-asisp&#34;&gt;Filtering by AS/ISP&lt;/h2&gt;
&lt;p&gt;This can be seen as a variation of geofencing, but blocking can also be performed
based on source ISP or Autonomous System by performing Autonomous System
Lookups. For example, sites or antibot vendors may explicitly disallow traffic
that comes from data centers or major cloud vendors (AWS, Digital Ocean, Azure
and so on).&lt;/p&gt;
&lt;h2 id=&#34;filtering-by-ip-reputation&#34;&gt;Filtering by IP reputation&lt;/h2&gt;
&lt;p&gt;Proxies can be used to bypass the above ways to block traffic, but blocking
can also be performed based on IP address reputation data from threat
intelligence vendors like IPQualityScore. So if your proxy provider is a
sketchy one that is built on foundation of botnet it&amp;rsquo;s proxies may be listed
in some blacklist, which makes the trust score bad. One must take care to use
a reputable proxy provider.&lt;/p&gt;
&lt;h1 id=&#34;https-level&#34;&gt;HTTPS level&lt;/h1&gt;
&lt;p&gt;The following techniques work at HTTP and TLS protocol levels.&lt;/p&gt;
&lt;h2 id=&#34;http-headers-and-cookies&#34;&gt;HTTP headers and cookies&lt;/h2&gt;
&lt;p&gt;The very simplest way to know that traffic is automated is to check User-Agent
header in HTTP request. For example, curl puts something like &lt;code&gt;curl/8.0.1&lt;/code&gt; in
there and some headless browsers actually let the server to know they are
running in headless mode (unless configured otherwise). But that&amp;rsquo;s trivial
to defeat by just reproducing the header value programmatically.
One baby step further is to not only check the User-Agent header,
but also other request headers to make sure like the ones in the browser in
aggregate. This is also easy to defeat by reproducing all the headers in a
request. In fact, Chrome DevTools Network tab has a handy feature of letting you
conveniently copy a request as a curl command or JavaScript (or even PowerShell!)
snippet.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-11_17.05.44.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cookies also matter when it comes to dealing with sites that are hostile to
automation, as cookieless requests might get rejected. Depending on exact
specifics of the site, getting the cookies might be as easy as just loading
the front page with &lt;code&gt;requests.Session&lt;/code&gt; or it might involve more elaborate
trickery if antibot vendor or captcha service is issuing them based on browser
assesment and user activity (more on that later).&lt;/p&gt;
&lt;h2 id=&#34;http2-fingerprinting&#34;&gt;HTTP/2 fingerprinting&lt;/h2&gt;
&lt;p&gt;We covered the simple stuff, now lets get to something more complicated -
HTTP/2 fingerprinting. HTTP/2 is quite complex binary protocol that leaves quite
a bit of wiggle room to implementor. The way it is implemented in curl is
different than implementation in Google Chrome (altough both are RFC-compliant).
Traffic generated by each implementation can reliably betray what kind of
implementation it is to the server, CDN or WAF. I have covered this in greated
detail in &lt;a href=&#34;https://www.trickster.dev/post/understanding-http2-fingerprinting/&#34;&gt;another post&lt;/a&gt; earlier.&lt;/p&gt;
&lt;h2 id=&#34;tls-fingerprinting&#34;&gt;TLS fingerprinting&lt;/h2&gt;
&lt;p&gt;HTTP/2 is pretty much never used in plaintext form. After all, you cannot let the
dirty people from NSA to just watch the unencrypted traffic without any problems.
However, TLS protocol is also very complex. It involves a great deal
of technical details in the client traffic (protocol version, list of extensions,
proposed cryptographic protocols, etc.) that betray what kind of client
software it comes from. In fact, a single ClientHello message can be used to
quite reliably predict the OS and browser that generated it. There&amp;rsquo;s also
&lt;a href=&#34;https://www.trickster.dev/post/understanding-tls-fingerprinting/&#34;&gt;post&lt;/a&gt; earlier in this blog to cover
this topic in greater detail.&lt;/p&gt;
&lt;h1 id=&#34;application-level&#34;&gt;Application level&lt;/h1&gt;
&lt;p&gt;The following techniques deal with probing the client-side environment (typically
a web browser) to detect and/or prevent automation.&lt;/p&gt;
&lt;h2 id=&#34;js-environment-checking&#34;&gt;JS environment checking&lt;/h2&gt;
&lt;p&gt;Modern web browsers are extremely complex software systems. They&amp;rsquo;re not just
user-facing applications to load some pages from remote servers anymore. As of
2023, Chrome or Firefox contains an entire programmable environment for client
side software development that is primarily done in JS or variants thereof
(that end up being transpiled into JS anyway). Naturally this means that
there&amp;rsquo;s quite a bit of stuff in the JavaScript environment that sites can
check for tell-tale signs of browser being controlled programmatically by
automation software like like Selenium, Playwright or Puppetteer.&lt;/p&gt;
&lt;p&gt;Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;navigator.webdriver=true&lt;/code&gt; generally betrays browser automation.&lt;/li&gt;
&lt;li&gt;Presence of &lt;code&gt;document.$cdc_asdjflasutopfhvcZLmcfl_&lt;/code&gt; object betrays Selenium.&lt;/li&gt;
&lt;li&gt;On Chromium-based browsers, &lt;code&gt;window.chrome&lt;/code&gt; object not being available means
the browser is running in headless mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;browser-and-device-fingerprinting&#34;&gt;Browser and device fingerprinting&lt;/h2&gt;
&lt;p&gt;Other than aforementioned tell-tale signs of automation, client side JS code
can gather a great deal of information on the system it is running on: installed
fonts, screen resolution, supported browser features, hardware components and so
on. Each of them is a trait of hardware-software setup the code is running on.
Furthermore, exact rendering of graphics (through HTML canvas or WebGL API) is
dependent on hardware/software setup, i.e. Chrome on macOS/Apple Silicon will not
render the exact same image as Firefox on Linux/x86 - some pixels will be
slightly, but consistently off, which is another piece of information that
can be kept track of. A collection of these traits is referred to as fingerprint.&lt;/p&gt;
&lt;p&gt;Fingerprinting is part of anti-automation toolkit. For example, social media
apps are able to detect when login session is launched from a new devide vs.
the same device being used for new user session. Furthermore, it can be quite
telling when a cluster of accounts share the same fingerprint (in the case of
e.g. device farm or fully virtualised botting operation).&lt;/p&gt;
&lt;h2 id=&#34;javascript-challenges&#34;&gt;Javascript challenges&lt;/h2&gt;
&lt;p&gt;To make requests-based bots harder to implement, site may want to check if
client is capable of running Javascript before giving the data. For example,
Cloudflare has an &lt;a href=&#34;https://developers.cloudflare.com/support/more-dashboard-apps/cloudflare-scrape-shield/what-is-email-address-obfuscation/&#34;&gt;Email Address Obfuscation&lt;/a&gt;
feature that relies on client-side JS code running in the browser to convert
the email address from encoded form in the HTML document your browser gets
from CDN to the normal form. Notice the difference between what you see in
Elements tab of Chrome DevTools and in the original page source for the
page of site using this feature.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-05-11_18.17.50.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-05-11_18.19.24.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For this particular example of data obfuscation, there&amp;rsquo;s a
&lt;a href=&#34;https://stackoverflow.com/questions/48878687/using-python-to-scrape-information-from-a-cloudflare-site&#34;&gt;Stack Overflow answer&lt;/a&gt;
providing a small Python snippet to decode it back. However, if you are doing
requests-based scraping and rely on regular expressions to extract email
addresses this sort of stuff can already cause at least some difficulty.&lt;/p&gt;
&lt;p&gt;Furthermore, JS challenges can be more elaborate than this. Depending on
how Cloudflare&amp;rsquo;s antibot features are configured browser may be required to run
some JS code with cryptographic proof of work stuff, provide the result of the
computation to CF and only then get the cookie that allows it to properly access
anything on the site.&lt;/p&gt;
&lt;h1 id=&#34;user-level&#34;&gt;User level&lt;/h1&gt;
&lt;p&gt;The following techniques involve behavioural tracking of user activity, checking
that the user is actually human and leveraging user identitities (accounts) to
fight automation.&lt;/p&gt;
&lt;h2 id=&#34;captcha&#34;&gt;CAPTCHA&lt;/h2&gt;
&lt;p&gt;A captcha is often (but not always) an incovenient silly puzzle that you have
to solve to prove you&amp;rsquo;re not a robot by picking the segments containing certain
object or something like that. That&amp;rsquo;s a visible captcha. Some captchas
(e.g. &lt;a href=&#34;https://developers.google.com/recaptcha/docs/v3&#34;&gt;reCaptcha v3&lt;/a&gt;) are
invisible - they work in background tracking user activity (mouse movement, etc.)
to assess if it looks human or robotic. The puzzle UI is displayed only when
there is a suspicion that user is a bot.&lt;/p&gt;
&lt;p&gt;The underlying idea for this is that certain tasks are difficult to do
programmatically, but easy to do for a person.&lt;/p&gt;
&lt;h2 id=&#34;account-level-throttling&#34;&gt;Account-level throttling&lt;/h2&gt;
&lt;p&gt;A simple way to hinder automation is to rate-limit user activity per account.
This is commonly done by social media platforms. If user activity exceeds the
rate limits the site/app can simply reject the requests or even ban the account.&lt;/p&gt;
&lt;h2 id=&#34;account-banning&#34;&gt;Account banning&lt;/h2&gt;
&lt;p&gt;No suprise here - ban hammer is part of anti-automation arsenal as well.
Misbehaving accounts can be banned. This is of particular importance to social
media automation - an account ban can mean a setback or even outright failure
of social media growth hacking efforts.&lt;/p&gt;
&lt;h2 id=&#34;making-account-creation-difficult&#34;&gt;Making account creation difficult&lt;/h2&gt;
&lt;p&gt;Banning and throttling user accounts would not help much if it&amp;rsquo;s possible to
create a lot of them quickly. Thus making account creation difficult to automate
(by requiring phone verification, integrating third party antibot solutions,
making request flows hard to reproduce) is one more thing that sites/apps
can do to fight automation.&lt;/p&gt;
&lt;p&gt;Realistically, it is near impossible to completely prevent botting if there&amp;rsquo;s an
a sufficient incentive for people to engage in automation. However, the economic
feasibility of automation can be weakened by putting barriers that make it more
expensive such as phone or ID verification for new accounts and
rate-limiting/restricting/banning existing accounts for sketchy, anomalous
activity.&lt;/p&gt;
&lt;h2 id=&#34;mouse-activity-monitoring&#34;&gt;Mouse activity monitoring&lt;/h2&gt;
&lt;p&gt;Client-side JS code can monitor mouse movements and submit them to some neural
network running on the backend to assess if they look human or not. This is
performed not only by some captcha solutions, but also antibot vendors as well.
For example, Akamai Bot Manager phones back a sample of user&amp;rsquo;s mouse movement
as part of clearance cookie issuance flow.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>CrawlSpider and link extractors for rule-based crawling in Scrapy</title>
      <link>https://www.trickster.dev/post/crawlspider-and-link-extractors-for-rule-based-crawling-in-scrapy/</link>
      <pubDate>Tue, 02 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/crawlspider-and-link-extractors-for-rule-based-crawling-in-scrapy/</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;https://scrapy.org/&#34;&gt;front page of Scrapy project&lt;/a&gt; provides a basic example
of Scrapy spider:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BlogSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;blogspider&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zyte.com/blog/&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; title &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.oxy-post-title&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;: title&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;::text&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; next_page &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a.next&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(next_page, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this code snippet, crawling is performed imperatively - link to next page
is extracted and from it a new request is generated. However, Scrapy also
supports another, declarative, approach of crawling that is based on
setting crawling rules for the spider and letting it follow links without
explicit request generation. This abstracts away the link traversal and
enables us to focus on data extraction aspect.&lt;/p&gt;
&lt;p&gt;To use this approach in our code, we must be familiar with &lt;code&gt;LinkExtractor&lt;/code&gt;
objects and &lt;code&gt;CrawlSpider&lt;/code&gt; class. Let us start with the former. For the sake
of the example, we will be doing a little scraping of
&lt;a href=&#34;http://books.toscrape.com/index.html&#34;&gt;books.toscrape.com&lt;/a&gt; - a simple site that
is made to be scraped.&lt;/p&gt;
&lt;p&gt;A link extractor is an object that processes a Scrapy &lt;code&gt;Response&lt;/code&gt; object and
extracts a list of links according to some criteria (URL substrings, regexes,
XPaths, CSS selectors, subdomains). It returns the extracted links as a list
of &lt;code&gt;Link&lt;/code&gt; objects. Each &lt;code&gt;Link&lt;/code&gt; object contains not only the absolute URL,
but also some additional information: anchor text, URL fragment, &lt;code&gt;nofollow&lt;/code&gt;
flag.&lt;/p&gt;
&lt;p&gt;Let us use Scrapy shell to see how link extractors can be created and used:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [1]: fetch(&amp;#34;https://books.toscrape.com/&amp;#34;)
2023-05-02 13:05:41 [scrapy.core.engine] INFO: Spider opened
2023-05-02 13:05:42 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET https://books.toscrape.com/&amp;gt; (referer: None)

In [2]: from scrapy.linkextractors import LinkExtractor

In [3]: book_link_extractor = LinkExtractor(allow=&amp;#39;index.html&amp;#39;, deny=[&amp;#39;catalogue/page&amp;#39;, &amp;#39;catalogue/category/books&amp;#39;, &amp;#39;https://books.toscrape.com/index.html&amp;#39;])

In [4]: for link in book_link_extractor.extract_links(response):
   ...:     print(link)
   ...: 
Link(url=&amp;#39;https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/soumission_998/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/sharp-objects_997/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/the-requiem-red_995/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/the-dirty-little-secrets-of-getting-your-dream-job_994/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/the-coming-woman-a-novel-based-on-the-life-of-the-infamous-feminist-victoria-woodhull_993/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/the-boys-in-the-boat-nine-americans-and-their-epic-quest-for-gold-at-the-1936-berlin-olympics_992/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/the-black-maria_991/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/starving-hearts-triangular-trade-trilogy-1_990/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/set-me-free_988/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/scott-pilgrims-precious-little-life-scott-pilgrim-1_987/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/rip-it-up-and-start-again_986/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/our-band-could-be-your-life-scenes-from-the-american-indie-underground-1981-1991_985/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/olio_984/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/mesaerion-the-best-science-fiction-stories-1800-1849_983/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/libertarianism-for-beginners_982/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
Link(url=&amp;#39;https://books.toscrape.com/catalogue/its-only-the-himalayas_981/index.html&amp;#39;, text=&amp;#39;&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We fetched the front page of the site, imported &lt;code&gt;LinkExtractor&lt;/code&gt; class and
initialised an extractor for links pointing to what eCommerce people would
call Product Detail Pages (PDPs). The &lt;code&gt;allow&lt;/code&gt; and &lt;code&gt;deny&lt;/code&gt; values of
&lt;code&gt;LinkExtractor&lt;/code&gt; constructor arguments are written so that they narrow down
the exact links we need. Note that we can allow or deny links based on a single
substring or a list of them. Once we have the link extractor created, we
call it&amp;rsquo;s &lt;code&gt;extract_links()&lt;/code&gt; method on a response object we got from fetching
the page, which gives us a list of &lt;code&gt;Link&lt;/code&gt; objects. We can see that all the links
point to book details pages.&lt;/p&gt;
&lt;p&gt;But we also need a link to the next Product List Page. Extracting it is also
quite simple with another link extractor:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [5]: next_page_link_extractor = LinkExtractor(allow=&amp;#39;catalogue/page&amp;#39;)

In [6]: next_page_link_extractor.extract_links(response)
Out[6]: [Link(url=&amp;#39;https://books.toscrape.com/catalogue/page-2.html&amp;#39;, text=&amp;#39;next&amp;#39;, fragment=&amp;#39;&amp;#39;, nofollow=False)]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we are familiar with link extractors, let us learn about the
&lt;code&gt;CrawlSpider&lt;/code&gt;. Let&amp;rsquo;s use the Scrapy CLI to generate all the boilerplate for
Scrapy project and a new spider class from the &lt;code&gt;crawl&lt;/code&gt; template:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy startproject sample .
$ scrapy genspider -t crawl books books.toscrape.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In sample/spiders/books.py file we have the following starter code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.linkextractors &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LinkExtractor
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.spiders &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; CrawlSpider, Rule
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksSpider&lt;/span&gt;(CrawlSpider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://books.toscrape.com/&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rules &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (Rule(LinkExtractor(allow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Items/&amp;#34;&lt;/span&gt;), callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;parse_item&amp;#34;&lt;/span&gt;, follow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_item&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;#item[&amp;#34;domain_id&amp;#34;] = response.xpath(&amp;#39;//input[@id=&amp;#34;sid&amp;#34;]/@value&amp;#39;).get()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;#item[&amp;#34;name&amp;#34;] = response.xpath(&amp;#39;//div[@id=&amp;#34;name&amp;#34;]&amp;#39;).get()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;#item[&amp;#34;description&amp;#34;] = response.xpath(&amp;#39;//div[@id=&amp;#34;description&amp;#34;]&amp;#39;).get()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a bit different than the default starter code you get from the
default template. &lt;code&gt;BooksSpider&lt;/code&gt; class is inheriting from
&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/spiders.html?highlight=CrawlSpider#scrapy.spiders.CrawlSpider&#34;&gt;&lt;code&gt;CrawlSpider&lt;/code&gt;&lt;/a&gt; class
(which is a subclass of &lt;code&gt;scrapy.Spider&lt;/code&gt;). It has a &lt;code&gt;rules&lt;/code&gt; property with a tuple
of &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/spiders.html?highlight=CrawlSpider#scrapy.spiders.Rule&#34;&gt;&lt;code&gt;Rule&lt;/code&gt;&lt;/a&gt;
objects (just a single entry at this point) and no &lt;code&gt;parse()&lt;/code&gt; method,
as the &lt;code&gt;parse()&lt;/code&gt; method is overriden in the &lt;code&gt;CrawlSpider&lt;/code&gt; class.
We also got one callback method that is referenced in the rule by name.&lt;/p&gt;
&lt;p&gt;Let us use this code as starting point to try out the rule-based crawling
on our target site:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.linkextractors &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; LinkExtractor
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.spiders &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; CrawlSpider, Rule
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksSpider&lt;/span&gt;(CrawlSpider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://books.toscrape.com/&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rules &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Rule(LinkExtractor(allow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           deny&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;catalogue/page&amp;#39;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;catalogue/category/books&amp;#39;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                               &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://books.toscrape.com/index.html&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           ), callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parse_item&amp;#39;&lt;/span&gt;, follow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Rule(LinkExtractor(allow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;catalogue/page&amp;#39;&lt;/span&gt;), follow&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_item&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//h1/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[contains(@class, &amp;#34;product_main&amp;#34;)]/p[@class=&amp;#34;price_color&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &lt;code&gt;rules&lt;/code&gt; tuple we got two &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/spiders.html?highlight=CrawlSpider#scrapy.spiders.Rule&#34;&gt;&lt;code&gt;Rule&lt;/code&gt;&lt;/a&gt;
objects that are based on link extractors
we tried out earlier. The first rule is based on PDP link extractor and
references the &lt;code&gt;parse_item()&lt;/code&gt; callback method. The second one is merely for
traversing to next page of the book list and is not directly associated with
data extraction. Thus it references no callback method. Since the objective
here is to demonstrate rule-based crawling we only extract a couple of fields
from the book details page.&lt;/p&gt;
&lt;p&gt;Running this spider scrapes the entire site without us having to explicitly
generate requests to progress the crawling further. We see log lines like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/page-2.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/its-only-the-himalayas_981/index.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/libertarianism-for-beginners_982/index.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/mesaerion-the-best-science-fiction-stories-1800-1849_983/index.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/olio_984/index.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/our-band-could-be-your-life-scenes-from-the-american-indie-underground-1981-1991_985/index.html&amp;gt; (referer: http://books.toscrape.com/)
2023-05-02 13:23:20 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/its-only-the-himalayas_981/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#34;It&amp;#39;s Only the Himalayas&amp;#34;, &amp;#39;price&amp;#39;: &amp;#39;£45.17&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;http://books.toscrape.com/catalogue/its-only-the-himalayas_981/index.html&amp;#39;}
2023-05-02 13:23:20 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/libertarianism-for-beginners_982/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#39;Libertarianism for Beginners&amp;#39;, &amp;#39;price&amp;#39;: &amp;#39;£51.33&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;http://books.toscrape.com/catalogue/libertarianism-for-beginners_982/index.html&amp;#39;}
2023-05-02 13:23:20 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/mesaerion-the-best-science-fiction-stories-1800-1849_983/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#39;Mesaerion: The Best Science Fiction Stories 1800-1849&amp;#39;, &amp;#39;price&amp;#39;: &amp;#39;£37.59&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;http://books.toscrape.com/catalogue/mesaerion-the-best-science-fiction-stories-1800-1849_983/index.html&amp;#39;}
2023-05-02 13:23:20 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/olio_984/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#39;Olio&amp;#39;, &amp;#39;price&amp;#39;: &amp;#39;£23.88&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;http://books.toscrape.com/catalogue/olio_984/index.html&amp;#39;}
2023-05-02 13:23:20 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/our-band-could-be-your-life-scenes-from-the-american-indie-underground-1981-1991_985/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#39;Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991&amp;#39;, &amp;#39;price&amp;#39;: &amp;#39;£57.25&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;http://books.toscrape.com/catalogue/our-band-could-be-your-life-scenes-from-the-american-indie-underground-1981-1991_985/index.html&amp;#39;}
2023-05-02 13:23:21 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/how-music-works_979/index.html&amp;gt; (referer: http://books.toscrape.com/catalogue/page-2.html)
2023-05-02 13:23:21 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/chase-me-paris-nights-2_977/index.html&amp;gt; (referer: http://books.toscrape.com/catalogue/page-2.html)
2023-05-02 13:23:21 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/in-her-wake_980/index.html&amp;gt; (referer: http://books.toscrape.com/catalogue/page-2.html)
2023-05-02 13:23:21 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/aladdin-and-his-wonderful-lamp_973/index.html&amp;gt; (referer: http://books.toscrape.com/catalogue/page-2.html)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To learn more about link extractors and rule-based crawling, read the following
pages of Scrapy documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/link-extractors.html#link-extractors&#34;&gt;Link Extractors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/spiders.html&#34;&gt;Spiders&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Intro to Bash scripting for scraping and automation</title>
      <link>https://www.trickster.dev/post/intro-to-bash-scripting-for-scraping-and-automation/</link>
      <pubDate>Tue, 04 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/intro-to-bash-scripting-for-scraping-and-automation/</guid>
      <description>&lt;p&gt;Bash is a Linux/UNIX program that reads users commands from the users, parses
them and executes the appropriate programs through OS-specific APIs. Since
it covers these APIs and provides some extra features on top of them this kind
of program is called a shell. Bash is not merely an interface between keyboard
and exec(2) et. al. It is also a scripting language and interpreter.&lt;/p&gt;
&lt;p&gt;Today we are going to explore various scripting facilities in Bash to learn
about programmable nature of this shell. The objective here is not
to give a tutorial on writing Bash scripts. Instead, I aim to show what
scripting capabilities it has and how does it fit into the broader reality,
with emphasis on scraping data and automating stuff.&lt;/p&gt;
&lt;p&gt;The following text assumes a basic familiarity with the world of Linux/Unix
software.&lt;/p&gt;
&lt;p&gt;In this world, each program has three communication channels available by
default:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard input (file descriptor 0)&lt;/li&gt;
&lt;li&gt;Standard output (FD 1)&lt;/li&gt;
&lt;li&gt;Standard error (FD 2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you run a CLI program in a terminal it uses standard input to read stuff
you type into it and standard output to print text for you to read. Standard
error is typically used for error messages and in some cases for a log output.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;&amp;gt;&lt;/code&gt; to redirect standard output, &lt;code&gt;2&amp;gt;&lt;/code&gt; to redirect standard error and &lt;code&gt;&amp;lt;&lt;/code&gt;
to redirect standard input, as seen in the following example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ls /etc &amp;gt; etc_list.txt
$ ls /notfound 2&amp;gt; err.msg
$ wc -l &amp;lt; err.msg 
       1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To daisy-chain two or more programs into pipeline such that standard output of
one program feeds into standard input of another you can use the pipe operator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ fortune | cowsay
 ____________________________ 
/ Sturgeon&amp;#39;s Law:            \
|                            |
\ 90% of everything is crud. /
 ---------------------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This entails a first baby-step towards using Bash as programming language. But
to do programming, we need variables. We can indeed declare them in Bash:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ x=1
$ echo &amp;#34;$x&amp;#34;
1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To declare a variable, we just did an assign statement. Note that there are no
whitespace characters around the &lt;code&gt;=&lt;/code&gt; sign - Bash would not be able to correctly
parse it otherwise. To see what a value the variable has, we prepended the
variable name with a dollar sign and used an in-build &lt;code&gt;echo&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;All variables in Bash are global to the script or user session unless declared
with a &lt;code&gt;local&lt;/code&gt; keyword within a funcion (e.g. &lt;code&gt;local b=2&lt;/code&gt;). Bash does not have
any real type system. Pretty much all data is treated as strings with exception
of some numeric operations on integers. On it&amp;rsquo;s own, Bash does not even support
floating point operations - you may need to use bc(1) or something else for
that.&lt;/p&gt;
&lt;p&gt;One way to perform integer arithmetic is to use built-in &lt;code&gt;expr&lt;/code&gt; command (make
sure that you put whitespaces around parts of expression as otherwise it will
be treated as string):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ expr 5 + 11
16
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another way is to use a &lt;code&gt;let&lt;/code&gt; command that will assign the result to a variable:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ let x=1+3
$ echo &amp;#34;$x&amp;#34;
4
$ let four=2*2
$ echo &amp;#34;$four&amp;#34;
4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;let&lt;/code&gt; command also supports increments and shorthand (&lt;code&gt;+=&lt;/code&gt; / &lt;code&gt;-=&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ let x++
$ echo &amp;#34;$x&amp;#34;
5
$ let x+=20
$ echo &amp;#34;$x&amp;#34;
25
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also reference another variable in the computation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ let &amp;#34;y=2*x&amp;#34;
$ echo &amp;#34;$y&amp;#34;
10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yet another way to do integer arithmetic operations is to use the double
parenthese notation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ two=$((1+1))
$ echo &amp;#34;$two&amp;#34;
2
$ (( three = two + 1 ))
$ echo &amp;#34;$three&amp;#34;
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like the &lt;code&gt;let&lt;/code&gt; command, this notation also supports increments/decrements and
shorthand operations:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ z=11
$ ((z++))
$ echo &amp;#34;$z&amp;#34;
12
$ ((z+=55))
$ echo &amp;#34;$z&amp;#34;
67
$ ((z--))
$ echo &amp;#34;$z&amp;#34;
66
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To compute some floating point numbers, we can use &lt;code&gt;printf&lt;/code&gt; command to generate
a command for bc(1) and read the result into variable by using a command
substitution syntax (&lt;code&gt;$( ... )&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ a=3
$ b=&amp;#34;2.1111111111111111&amp;#34;
$ result=$(printf &amp;#34;scale=5\n%d / %f\n&amp;#34; &amp;#34;$a&amp;#34; &amp;#34;$b&amp;#34; | bc -l)
$ echo &amp;#34;$result&amp;#34;
1.42105
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bash supports basic string manipulation functionality, such as substitutions and
slicing.&lt;/p&gt;
&lt;p&gt;For a string manipulation syntax, see the following examples:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ msg=&amp;#34;cypherpunks write code&amp;#34;
$ echo &amp;#34;${#msg}&amp;#34; # Get length.
22
$ echo &amp;#34;${msg:0:6}&amp;#34; # Get substring by starting index and length.
cypher
$ echo &amp;#34;${msg:(-4):4}&amp;#34; # Index can also count to count backwards from end of string.
code
$ echo &amp;#34;${msg%code}&amp;#34; # Remove suffix.
cypherpunks write 
$ echo &amp;#34;${msg%cypher}&amp;#34; # Remove prefix.
cypherpunks write code
$ echo &amp;#34;${msg/write/develop}&amp;#34; # Replace string (single match).
cypherpunks develop code
$ echo &amp;#34;${msg//c/C}&amp;#34; # Replace string (multiple matches).
Cypherpunks write Code
$ echo &amp;#34;${msg/%code/software}&amp;#34; # Replace suffix.
cypherpunks write software
$ echo &amp;#34;${msg/#cypher/crypto}&amp;#34; # Replace prefix.
cryptopunks write code
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For conditional logic, Bash supports if-statements and &lt;code&gt;case&lt;/code&gt; (switch) statements.&lt;/p&gt;
&lt;p&gt;In simple cases, if-statements can have one line form:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ if [[ 1 == 1 ]]; then echo &amp;#34;yes&amp;#34;; fi
yes
$ if [[ 1 == 2 ]]; then echo &amp;#34;yes&amp;#34;; else echo &amp;#34;no&amp;#34;; fi
no
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, so this is getting a rather dry and boring, so let&amp;rsquo;s provide an example
from real world software - &lt;a href=&#34;https://www.trickster.dev/post/axiom-just-in-time-dynamic-infra-for-offensive-security-operations/&#34;&gt;Axiom&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are following lines of code in file
&lt;a href=&#34;https://github.com/pry0cc/axiom/blob/master/interact/axiom-wait#L11&#34;&gt;interact/axiom-wait&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; ! -z &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$2&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    instance&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$2&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We see a bit different conditional syntax here. &lt;code&gt;$2&lt;/code&gt; is the second CLI argument
to the script and here it is being checked for having a defined value. &lt;code&gt;-z&lt;/code&gt;
stands for &amp;ldquo;is empty/undefined&amp;rdquo; and &lt;code&gt;!&lt;/code&gt; is a logical NOT operation.&lt;/p&gt;
&lt;p&gt;How does the &lt;code&gt;case&lt;/code&gt; statement look like? We can find an example in file
&lt;a href=&#34;https://github.com/pry0cc/axiom/blob/master/interact/axiom-scan#L12&#34;&gt;interact/axiom-scan&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BASEOS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;uname&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; $BASEOS in
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Darwin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;brew --prefix coreutils&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/libexec/gnubin:&lt;/span&gt;$PATH&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here it reads the output of uname(1) into variable &lt;code&gt;BASEOS&lt;/code&gt;. If the value is
&lt;code&gt;Darwin&lt;/code&gt; is it updates the &lt;code&gt;PATH&lt;/code&gt; environment variable with one more entry.
In the default case it does nothing (see line &lt;code&gt;*) ;;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Like many programming languages, Bash enables iteratative code through for and
while loops.&lt;/p&gt;
&lt;p&gt;The following example shows how for loop can be used to iterate across output
of some command, one line at a time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; f in &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;find &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$tmp&lt;span style=&#34;color:#e6db74&#34;&gt;/split/&amp;#34;&lt;/span&gt; -type f | tr &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt; | awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{ print $NF }&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        instance&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;echo $instances | awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{ print \$&lt;/span&gt;$i&lt;span style=&#34;color:#e6db74&#34;&gt; }&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        i&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;i+1&lt;span style=&#34;color:#66d9ef&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        mv &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$tmp&lt;span style=&#34;color:#e6db74&#34;&gt;/split/&lt;/span&gt;$f&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$tmp&lt;span style=&#34;color:#e6db74&#34;&gt;/input/&lt;/span&gt;$instance&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$i
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Example of while loop can be seen in file
&lt;a href=&#34;https://github.com/pry0cc/axiom/blob/641dba34424b0c8fe5afa6b8473a79cce7a39c3e/interact/account-helpers/aws.sh&#34;&gt;interact/account-helpers/aws.sh&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo -e -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Green&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Please enter your AWS Access Key ID (required): \n&amp;gt;&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Color_Off&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;read ACCESS_KEY
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ACCESS_KEY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	echo -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;BRed&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Please provide a AWS Access KEY ID, your entry contained no input.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Color_Off&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	echo -e -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Green&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Please enter your token (required): \n&amp;gt;&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Color_Off&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	read ACCESS_KEY
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case the loop would iterate until the user would provide a non-empty
input (&lt;code&gt;read&lt;/code&gt; command reads text from the user and saves it into a variable).&lt;/p&gt;
&lt;p&gt;Like many programming languages, Bash provides two basic built-in data structures:
arrays and dictionaries.&lt;/p&gt;
&lt;p&gt;Bash array can be declared and used as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ numbers=(1 2 3 4 5) # Declaring an array.
$ echo &amp;#34;${numbers[0]}&amp;#34; # Array indexing - getting an element
1
$ echo &amp;#34;${#numbers[@]}&amp;#34; # Array length
5
$ echo &amp;#34;${numbers[@]}&amp;#34; # Get all elements
1 2 3 4 5
$ numbers[0]=0 # Array indexing - setting an element
$ echo &amp;#34;${numbers[@]}&amp;#34; # Get all elements
0 2 3 4 5
$ numbers+=(&amp;#34;six&amp;#34;) # Append to array
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The following listing shows how Bash dictionaries can be used in practice:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ declare -A people # Declare empty dictionary.
$ people=([&amp;#34;John&amp;#34;]: &amp;#34;555-1212&amp;#34; [&amp;#34;Steve&amp;#34;]: &amp;#34;555-1313&amp;#34;) # Initialise the dictionary
$ echo &amp;#34;${people[&amp;#39;John&amp;#39;]}&amp;#34; # Get value by key.
555-1212
$ echo &amp;#34;${people[&amp;#39;Alice&amp;#39;]}&amp;#34;

$ echo &amp;#34;${people[@]}&amp;#34; # Get all keys.
555-1313 555-1212
$ echo &amp;#34;${!people[@]}&amp;#34; # Get all values.
Steve John
$ for key in &amp;#34;${!people[@]}&amp;#34;; do value=&amp;#34;${people[$key]}&amp;#34;; echo &amp;#34;$key - $value&amp;#34;; done # Iterate across dictionary.
Steve - 555-1313
John - 555-1212
$ people[&amp;#39;Alice&amp;#39;]=&amp;#39;555-0000&amp;#39; # Set value for key.
$ echo &amp;#34;${people[&amp;#39;Alice&amp;#39;]}&amp;#34;
555-0000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note, however, that Bash dictionaries are not supported in legacy (pre-4.0)
version of Bash. If you are using macOS then you may need to update your Bash
via Homebrew, as Apple does not ship modern Bash versions due to license issue.&lt;/p&gt;
&lt;p&gt;Bash also supports functions. One example would be the following simple
function from Axiom codebase, file
&lt;a href=&#34;https://github.com/pry0cc/axiom/blob/825570cf0387c8dc2c0489e594aa196abf71784b/interact/includes/system-notification.sh&#34;&gt;interact/includes/system-notification.sh&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# method issuing notifications in place of notify-send on OSX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# expects a title as first and a notification as second argument&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; notifyOSX &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  osascript -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;display notification \&amp;#34;&lt;/span&gt;$2&lt;span style=&#34;color:#e6db74&#34;&gt;\&amp;#34; with title \&amp;#34;&lt;/span&gt;$1&lt;span style=&#34;color:#e6db74&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function generates macOS system notification with title from first argument
(&lt;code&gt;$1&lt;/code&gt;) and description from second argument (&lt;code&gt;$2&lt;/code&gt;). It can be called as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;notifyOSX &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Warning&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The end is near&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we can see, the function call expression has the same syntax as a regular
Bash command. After, CLI is API when you&amp;rsquo;re developing shell scripts.&lt;/p&gt;
&lt;p&gt;The following CLI tools might be of interest when developing Bash scripts
in web scraping/automation/security fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/benibela/xidel&#34;&gt;xidel&lt;/a&gt; for introducing web scraping
functionality into your script. Fetches pages and runs XPath queries or CSS
selectors for data extraction.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/stedolan/jq&#34;&gt;jq&lt;/a&gt; - JSON processing tool with it&amp;rsquo;s own
domain-specific language.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ericchiang/pup&#34;&gt;pup&lt;/a&gt; for running CSS selectors on HTML
pages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/koalaman/shellcheck&#34;&gt;Shellcheck&lt;/a&gt; is a shell script
static analysis tool for finding potential problems with your code. Highly
recommended, as Bash has it&amp;rsquo;s own share of footguns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have surveyed the various features of Bash and now can consider the value
proposition that Bash brings to the table for someone doing web scraper
development, automation engineering, bounty hunting or even old school kind
of system administration.&lt;/p&gt;
&lt;p&gt;The way I see it, the value proposition can be viewed like this&amp;hellip;&lt;/p&gt;
&lt;p&gt;First, it provides a common denominator scripting language for Linux/Unix
that also serves as a power tool for users working in command line environment.
For example, instead of manually running 100 commands to import 100 CSV files
into a SQLite database you could do a quick for loop right in your shell. Due
to availability of Bash in many server and development environments this will
most likely be possible without installing any additional dependencies.&lt;/p&gt;
&lt;p&gt;Second, Bash is a glue language for uniting multiple CLI tools into greater
workflow. For example, you may have a Scrapy project that you run through
Scrapy CLI and also some helper scripts to postprocess your data before uploading
it to some remote system. Due to Bash statements being pretty much the same as
Unix/Linux commands (with some extra features) you have a low friction way
to chain the invocations of all your tools into single flow that is launched
from a Bash script.&lt;/p&gt;
&lt;p&gt;Axiom project demonstrates that one can develop quite extensive piece of software
by mainly relying on Bash. However, I would not recommend doing so as there
are better languages for this kind of work. The value of Bash scripting lies in
introducing programmability and automation capabilities into command line
workflows.&lt;/p&gt;
&lt;p&gt;To learn more about Bash scripting, see the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://wiki.bash-hackers.org/&#34;&gt;Bash Hackers Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html&#34;&gt;Official Bash Manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://linux.die.net/man/1/bash&#34;&gt;Bash manpage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t JSFuck with me: Part 2</title>
      <link>https://www.trickster.dev/post/dont-jsfuck-with-me-part-2/</link>
      <pubDate>Tue, 14 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/dont-jsfuck-with-me-part-2/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://www.trickster.dev/post/dont-jsfuck-with-me-part-1/&#34;&gt;previous post&lt;/a&gt; we went through using
Babel AST transforms to simplify JSFuck-generated unary and binary expressions.
This was shown to undo a lot, but not all of the obfuscation. What we still
have to do is to deal with certain API and runtime hacks that JSFuck leverages
to obfuscate some of the characters that are not covered just by abusing type
coercion and atomic components of JS (string and array indexing, logical/arithmetic
operations).&lt;/p&gt;
&lt;p&gt;Suppose we want to encode a &lt;code&gt;{&lt;/code&gt; character with JSFuck. Putting this into
&lt;a href=&#34;http://www.jsfuck.com&#34;&gt;JSFuck web app&lt;/a&gt; (make sure that checkboxes for additional
features are not checked) yields the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-03-12_11.10.16.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we put it into &lt;a href=&#34;https://astexplorer.net&#34;&gt;ASTExplorer&lt;/a&gt; we get pretty much
the same situation as before - a lot of &lt;code&gt;BinaryExpression&lt;/code&gt;, &lt;code&gt;UnaryExpression&lt;/code&gt;,
&lt;code&gt;ArrayExpression&lt;/code&gt; and &lt;code&gt;MemberExpression&lt;/code&gt; nodes making up the tree.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-03-12_11.11.59.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can try using our &lt;a href=&#34;https://github.com/rl1987/trickster.dev-code/blob/main/2023-02-24-dont-jsfuck-with-me-part-1/ast_transform.js&#34;&gt;previously developed transform&lt;/a&gt; to simplify the code.&lt;/p&gt;
&lt;p&gt;Applying it three times gives us the following results:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;(true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;20&amp;quot;];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[20];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(true + [][&amp;quot;flat&amp;quot;])[20];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The transform we have from earlier cannot simplify the code any further. What
we got here matches &lt;a href=&#34;https://github.com/aemkei/jsfuck/blob/master/jsfuck.js#L108&#34;&gt;line 108 in jsfuck.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The above trick works by coercing &lt;code&gt;Array.prototype.flat()&lt;/code&gt; to string by adding
&lt;code&gt;true&lt;/code&gt; to it (addition operation prefers to convert stuff into strings) and
indexing the resulting string:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v19.0.1.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; [][&amp;#34;flat&amp;#34;]
[Function: flat]
&amp;gt; String([][&amp;#34;flat&amp;#34;])
&amp;#39;function flat() { [native code] }&amp;#39;
&amp;gt; true + [][&amp;#34;flat&amp;#34;]
&amp;#39;truefunction flat() { [native code] }&amp;#39;
&amp;gt; (true + [][&amp;#34;flat&amp;#34;])[20]
&amp;#39;{&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So what we can do at AST level is pattern-match on &lt;code&gt;[][&amp;quot;flat&amp;quot;]&lt;/code&gt; part, convert
it to string equivalent and replace it with equivalent &lt;code&gt;StringLiteral&lt;/code&gt; node
in the AST so that expression evaluation code can simplify it further. We
also should check if &lt;code&gt;[][&amp;quot;flat&amp;quot;]&lt;/code&gt; is part of binary expression. This can be
done with a following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-flat-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(String(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;flat&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It converts the snippet we had to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function flat() { [native code] }&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our previous transform can pick it up from here, as we only have addition and
string indexing.&lt;/p&gt;
&lt;p&gt;Similarly to the &lt;code&gt;flat()&lt;/code&gt; trick, JSFuck abuses &lt;code&gt;entries()&lt;/code&gt; method, as can
be seen in the following &lt;code&gt;MAPPING&lt;/code&gt; object key-value pairs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;([][&amp;#34;entries&amp;#34;]()+&amp;#34;&amp;#34;)[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;j&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;([][&amp;#34;entries&amp;#34;]()+&amp;#34;&amp;#34;)[3]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(NaN+[][&amp;#34;entries&amp;#34;]())[11]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;([][&amp;#34;entries&amp;#34;]()+&amp;#34;&amp;#34;)[0]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;]&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;([][&amp;#34;entries&amp;#34;]()+&amp;#34;&amp;#34;)[22]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The way to undo this is equivalent to dealing with &lt;code&gt;flat()&lt;/code&gt; trick:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-entries-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;entries&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(String(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;entries&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another thing that JSFuck does is abusing array&amp;rsquo;s &lt;code&gt;concat()&lt;/code&gt; method to obfuscate
a comma character:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[[]][&amp;#34;concat&amp;#34;]([[]])+&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This abuses how array string representation is computed, as evident by a quick
experiment in Node.JS REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [[]][&amp;#34;concat&amp;#34;]([[]])
[ [], [] ]
&amp;gt; String([[]][&amp;#34;concat&amp;#34;]([[]]))
&amp;#39;,&amp;#39;
&amp;gt; String([ [], [] ])
&amp;#39;,&amp;#39;
&amp;gt; [[]][&amp;#34;concat&amp;#34;]([[]]) + &amp;#34;&amp;#34;
&amp;#39;,&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once again, we pattern-match the part that is to be converted to string due
to type coercion, create a &lt;code&gt;StringLiteral&lt;/code&gt; out of it and replace it into
the AST:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-concat-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;concat&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String([ [], [] ])));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In AST Explorer we can verify that &lt;code&gt;[[]][&amp;quot;concat&amp;quot;]([[]])+&amp;quot;&amp;quot;&lt;/code&gt; is converted to
&lt;code&gt;&amp;quot;,&amp;quot; + &amp;quot;&amp;quot;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;String.prototype.slice()&lt;/code&gt; method is also leveraged by JSFuck:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    &amp;#39;D&amp;#39;:   &amp;#39;Function(&amp;#34;return escape&amp;#34;)()([][&amp;#34;flat&amp;#34;])[&amp;#34;slice&amp;#34;](&amp;#34;-1&amp;#34;)&amp;#39;,
    &amp;#39;}&amp;#39;:   &amp;#39;([][&amp;#34;flat&amp;#34;]+&amp;#34;&amp;#34;)[&amp;#34;slice&amp;#34;](&amp;#34;-1&amp;#34;)&amp;#39;,
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that these cases also rely on some further trickery. Since we already
know how to deal with &lt;code&gt;flat()&lt;/code&gt; trick, we will focus on the latter on for now.&lt;/p&gt;
&lt;p&gt;Running the transform to undo &lt;code&gt;flat()&lt;/code&gt; trick converts it to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function flat() { [native code] }&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slice&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice&#34;&gt;According to the documentation&lt;/a&gt;,
calling &lt;code&gt;slice()&lt;/code&gt; on a string with single negative number returns a trailing
substring. In this case it will be single-character substring, i.e. just the
last character.&lt;/p&gt;
&lt;p&gt;We develop another AST transform to address the &lt;code&gt;slice()&lt;/code&gt; trick:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-slice-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slice&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             Number(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fromStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fromStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing particulary different here, except that the subtree matching part
had to check for some other things when matching.&lt;/p&gt;
&lt;p&gt;But before running this transform we have to deal with string concatenation,
as the matching is based on &lt;code&gt;StringLiteral&lt;/code&gt; node being at &lt;code&gt;callee&lt;/code&gt; property
of a &lt;code&gt;CallExpression&lt;/code&gt;. Applying the
&lt;a href=&#34;https://github.com/rl1987/trickster.dev-code/blob/main/2023-02-24-dont-jsfuck-with-me-part-1/ast_transform.js&#34;&gt;expression evaluation code&lt;/a&gt; simplifies
it to the form that is required by the new transform we just wrote:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function flat() { [native code] }&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slice&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, when we apply the new transform we get the original character.&lt;/p&gt;
&lt;p&gt;Letter &lt;code&gt;B&lt;/code&gt; is obfuscated by leveraging &lt;code&gt;Boolean&lt;/code&gt; type name:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+[]+Boolean)[10]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running the expression evaluation code on the obfuscated snippet converts it to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Boolean)[&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can undo the &lt;code&gt;Boolean&lt;/code&gt; trick, which is quite trivial:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-boolean-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Boolean&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(Boolean)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this one gives us the following snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function Boolean() { [native code] }&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running expression evaluation transform twice simplifies this to &lt;code&gt;B&lt;/code&gt; character:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;0function Boolean() { [native code] }&amp;quot;[10];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;B&amp;quot;;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JSFuck also abuses a &lt;code&gt;fontcolor()&lt;/code&gt; function to encode some characters:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;q&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]([0]+false+&amp;#34;)[20]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]()[12]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;](&amp;#34;)[13]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;](NaN+&amp;#34;)[21]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]()[11]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This trick actually relies on the output of &lt;code&gt;fontcolor()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]()[11]
&amp;#39;=&amp;#39;
&amp;gt; (&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]()
&amp;#39;&amp;lt;font color=&amp;#34;undefined&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#39;
&amp;gt; String.prototype.fontcolor(&amp;#34;&amp;#34;)
&amp;#39;&amp;lt;font color=&amp;#34;&amp;#34;&amp;gt;&amp;lt;/font&amp;gt;&amp;#39;
&amp;gt; String.prototype.fontcolor()[11]
&amp;#39;=&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;AST transform to undo this would be as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-fontcolor-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fontcolor&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fontcolor&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fontcolor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;(&amp;quot;&amp;quot;)[&amp;quot;fontcolor&amp;quot;]()[12]&lt;/code&gt; (snippet for double-quote character) is simplified
to &lt;code&gt;&amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12];&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go through a somewhat bigger example.&lt;/p&gt;
&lt;p&gt;Obfuscating a semicolon character yields the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]](&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can apply the expression evaluation transform until we hit the &lt;code&gt;flat()&lt;/code&gt; trick:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;false&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;true&amp;quot;[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;false&amp;quot;[2] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]](0 / 0 + &amp;quot;&amp;quot;[&amp;quot;false&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;true&amp;quot;[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;false&amp;quot;[2] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]]()[&amp;quot;12&amp;quot;])[&amp;quot;21&amp;quot;];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;t&amp;quot; + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;t&amp;quot; + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Applying the previous transform to undo this provides us with path to further
apply expression evaluation:&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;l&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;l&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;l&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;l&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;](NaN + &amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;]()[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now it&amp;rsquo;s time to apply the &lt;code&gt;undo-fontcolor-trick&lt;/code&gt; transform:&lt;/p&gt;
&lt;ol start=&#34;8&#34;&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;](NaN + &amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12])[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We do two more iterations of expression evaluation:&lt;/p&gt;
&lt;ol start=&#34;9&#34;&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;](NaN + &amp;quot;\&amp;quot;&amp;quot;)[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;](&amp;quot;NaN\&amp;quot;&amp;quot;)[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;hellip; and apply &lt;code&gt;undo-fontcolor-trick&lt;/code&gt; transform once again:&lt;/p&gt;
&lt;ol start=&#34;10&#34;&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;&amp;lt;font color=\&amp;quot;NaN&amp;amp;quot;\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Applying expression evaluation one more time gives us the original character:&lt;/p&gt;
&lt;ol start=&#34;11&#34;&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;;&amp;quot;;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JSFuck abuses &lt;code&gt;italics()&lt;/code&gt; in a similar way that is also based on indexing the
output string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(false+[0])[&amp;#34;italics&amp;#34;]()[10]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]()[0]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]()[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Function(&amp;#34;return escape&amp;#34;)()((&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]())[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The transform to undo the trick is similar to the one we did for &lt;code&gt;fontcolor()&lt;/code&gt;
case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-italics-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;italics&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fromStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fromStr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;italics&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quick check on AST Explorer shows that &lt;code&gt;(&amp;quot;&amp;quot;)[&amp;quot;italics&amp;quot;]()[0]&lt;/code&gt; is simplified to
&lt;code&gt;&amp;quot;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;quot;[0];&lt;/code&gt;, which is something we can process further with the other code.&lt;/p&gt;
&lt;p&gt;When encoding character &lt;code&gt;C&lt;/code&gt;, JSFuck also does another trick that relies on
using &lt;code&gt;escape()&lt;/code&gt; function to URL-encode some string, then indexing the output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; Function(&amp;#34;return escape&amp;#34;)()((&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]())[2]
&amp;#39;C&amp;#39;
&amp;gt; Function(&amp;#34;return escape&amp;#34;)()((&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]())
&amp;#39;%3Ci%3E%3C/i%3E&amp;#39;
&amp;gt; (&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]()
&amp;#39;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#39;
&amp;gt; Function(&amp;#34;return escape&amp;#34;)()(&amp;#39;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#39;)
&amp;#39;%3Ci%3E%3C/i%3E&amp;#39;
&amp;gt; Function(&amp;#34;return escape&amp;#34;)()(&amp;#39;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#39;)[2]
&amp;#39;C&amp;#39;
&amp;gt; escape(&amp;#39;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#39;)
&amp;#39;%3Ci%3E%3C/i%3E&amp;#39;
&amp;gt; escape(&amp;#39;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#39;)[2]
&amp;#39;C&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Besides &lt;code&gt;C&lt;/code&gt; character this trick is also used to obfuscated &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;%&lt;/code&gt;
characters (in combination with some other tricks that we covered already):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Function(&amp;#34;return escape&amp;#34;)()((&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]())[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;D&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Function(&amp;#34;return escape&amp;#34;)()([][&amp;#34;flat&amp;#34;])[&amp;#34;slice&amp;#34;](&amp;#34;-1&amp;#34;)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Function(&amp;#34;return escape&amp;#34;)()([][&amp;#34;flat&amp;#34;])[21]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Applying the &lt;code&gt;undo-italics-trick&lt;/code&gt; to the snippet from the first case turns
it into:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Function(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return escape&amp;#34;&lt;/span&gt;)()(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need an AST transform that calls &lt;code&gt;escape()&lt;/code&gt; function and substitutes
a URL-encoded string into the AST. We also need to deal with a special case
of the &lt;code&gt;flat()&lt;/code&gt; trick. The previous transform will not work as-is due to being
developed for cases with binary expressions.&lt;/p&gt;
&lt;p&gt;So the AST transform to deal with this stuff would be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-function-escape-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;flat&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Function&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return escape&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;escape&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us try it on the snippet for &lt;code&gt;%&lt;/code&gt; character - &lt;code&gt;Function(&amp;quot;return escape&amp;quot;)()([][&amp;quot;flat&amp;quot;])[21]&lt;/code&gt;.
We need to do it twice.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Function(&amp;quot;return escape&amp;quot;)()(&amp;quot;function flat() { [native code] }&amp;quot;)[21];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;function%20flat%28%29%20%7B%20%5Bnative%20code%5D%20%7D&amp;quot;[21];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expression evaluation transform pulls out the &lt;code&gt;%&lt;/code&gt; character from this.&lt;/p&gt;
&lt;p&gt;For 3 of the characters, JSFuck relies on calling &lt;code&gt;Date()&lt;/code&gt; to obfuscate them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;G&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(false+Function(&amp;#34;return Date&amp;#34;)()())[30]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(true+Function(&amp;#34;return Date&amp;#34;)()())[30]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;T&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(NaN+Function(&amp;#34;return Date&amp;#34;)()())[30]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This relies on characters &lt;code&gt;G&lt;/code&gt;, &lt;code&gt;M&lt;/code&gt; and &lt;code&gt;T&lt;/code&gt; being always present in the output
of &lt;code&gt;Date()&lt;/code&gt; function at predictable indices. Some dummy values are added to
tweak the output string a little, then indexing it to pull out the needed
character.&lt;/p&gt;
&lt;p&gt;The transform code to undo this is similar to the ones we wrote before:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-function-date-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Function&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return Date&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(Date()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This converts &lt;code&gt;Function(&amp;quot;return Date&amp;quot;)()()&lt;/code&gt; (equivalent to &lt;code&gt;Date()&lt;/code&gt;) part to
string literal with datetime string, which can be further dealt with by
the expression evaluation transform.&lt;/p&gt;
&lt;p&gt;JSFuck can also leverage &lt;code&gt;RegExp&lt;/code&gt; API for obfuscation, as evident in the
following cases:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;E&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp+&amp;#34;&amp;#34;)[12]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;R&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+[]+RegExp)[10]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp()+&amp;#34;&amp;#34;)[3]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp()+&amp;#34;&amp;#34;)[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp(&amp;#34;/&amp;#34;)+&amp;#34;&amp;#34;)[1]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, in some cases &lt;code&gt;RegExp&lt;/code&gt; identifier is used in addition operation
but in other cases, a return value from &lt;code&gt;RegExp()&lt;/code&gt; is used instead. We should
cover both forms when deobfuscating.&lt;/p&gt;
&lt;p&gt;These can be addressed with the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-regexp-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RegExp&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(RegExp)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RegExp&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(RegExp())));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(RegExp(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;))));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are few more string-based tricks that JSFuck does. One is by leveraging
the type name of &lt;code&gt;String&lt;/code&gt; in the following cases:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;g&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(false+[0]+String)[20]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;S&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+[]+String)[10]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Getting these cases to the point of tractability by expression evaluation
transform is easy and can be done with a transform very similar to the
one for &lt;code&gt;Boolean&lt;/code&gt; case earlier in this post:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-string-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;operator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(String(String)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another trick we need to address is abusing &lt;code&gt;toString()&lt;/code&gt; method as seen in
the following lines from jsfuck.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;h&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(101))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](21)[1]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;k&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(20))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](21)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;p&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(211))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](31)[1]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;v&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(31))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](32)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(32))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](33)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(101))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](34)[1]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;z&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(+(35))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](36)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;U&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(NaN+Object()[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]][&amp;#34;call&amp;#34;]())[11]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the exception of last case, all of these rely on calling
&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString&#34;&gt;&lt;code&gt;toString()&lt;/code&gt; method&lt;/a&gt;
on a number to covert it to numeric string with non-standard base. In some
cases this numeric string is indexed to pull out the needed character.&lt;/p&gt;
&lt;p&gt;Let us unpack the case for &lt;code&gt;h&lt;/code&gt; in Node.JS REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; (+(101))[&amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]](21)[1]
&amp;#39;h&amp;#39;
&amp;gt; String[&amp;#34;name&amp;#34;]
&amp;#39;String&amp;#39;
&amp;gt; (+(101))
101
&amp;gt; &amp;#34;to&amp;#34;+String[&amp;#34;name&amp;#34;]
&amp;#39;toString&amp;#39;
&amp;gt; 101[&amp;#34;toString&amp;#34;](21)
&amp;#39;4h&amp;#39;
&amp;gt; 101[&amp;#34;toString&amp;#34;](21)[1]
&amp;#39;h&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Applying the expression evaluation transform to the &lt;code&gt;h&lt;/code&gt; case only simplifies
the unary expression:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;101&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;]](&lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before we proceed with number-to-string conversion here, let us write a
quick helper transform that simplifies &lt;code&gt;String[&amp;quot;name&amp;quot;]&lt;/code&gt; part:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;simplify-string-name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This simplifies the snippet to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;101&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Applying expression evaluation transform on this simplifies it bit further:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;101&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once again, we pattern match on the relevant subtree and apply the change on it.&lt;/p&gt;
&lt;p&gt;See the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-number-tostring-trick&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;numericStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;numericStr&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This turns our current snippet to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4h&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let us deal with the snippet for character &lt;code&gt;U&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;NaN&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;Object()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;String[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;]][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;]())[&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Applying our helper transform simplifies it to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;NaN&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Object()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;String&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;]())[&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can further simplify it by applying expression evaluation transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;NaN&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Object()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;]())[&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need another transform written that matches an exact subtree and
simplifies it further. It would look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo-object-tostring-call&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;calleeObj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Object&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(Object()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;toString&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;]()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By applying this transform, we get the following snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;NaN&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[object Undefined]&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This can be further simplified to character &lt;code&gt;U&lt;/code&gt; by applying expression evaluation
transform two more times:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;NaN[object Undefined]&amp;quot;[11];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;U&amp;quot;;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&amp;rsquo;re getting close to the end, but there&amp;rsquo;s still some uncovered ground left.
In the &lt;code&gt;MAPPING&lt;/code&gt; object we see that some characters don&amp;rsquo;t have matching
snippet assignmed to them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;H&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;J&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;K&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;V&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;W&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;X&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Y&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Z&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;!&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;#&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;$&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;^&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;`&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s try to find out what happens when one of these characters are being
obfuscated. Can we deobfuscate them back?&lt;/p&gt;
&lt;p&gt;Obfuscating &lt;code&gt;@&lt;/code&gt; with JSFuck gives us the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]][([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]][([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])()[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]])()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We put it into ASTExplorer and apply expression evaluation transform several
times:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]][([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;false&amp;quot;[3] + &amp;quot;true&amp;quot;[0] + &amp;quot;true&amp;quot;[1] + (undefined + [])[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;true&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]](&amp;quot;true&amp;quot;[1] + &amp;quot;true&amp;quot;[3] + &amp;quot;true&amp;quot;[0] + (undefined + [])[0] + &amp;quot;true&amp;quot;[1] + (undefined + [])[1] + &amp;quot;&amp;quot;[&amp;quot;false&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;true&amp;quot;[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;false&amp;quot;[2] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]]()[&amp;quot;12&amp;quot;] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]][([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;false&amp;quot;[3] + &amp;quot;true&amp;quot;[0] + &amp;quot;true&amp;quot;[1] + (undefined + [])[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;true&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]](&amp;quot;true&amp;quot;[1] + &amp;quot;true&amp;quot;[3] + &amp;quot;true&amp;quot;[0] + (undefined + [])[0] + &amp;quot;true&amp;quot;[1] + (undefined + [])[1] + &amp;quot;false0&amp;quot;[([false] + undefined)[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[0] + &amp;quot;false&amp;quot;[1] + &amp;quot;false&amp;quot;[2] + ([false] + undefined)[&amp;quot;10&amp;quot;] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;false&amp;quot;[3]]()[&amp;quot;10&amp;quot;] + false + &amp;quot;false0&amp;quot;[([false] + undefined)[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[0] + &amp;quot;false&amp;quot;[1] + &amp;quot;false&amp;quot;[2] + ([false] + undefined)[&amp;quot;10&amp;quot;] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;false&amp;quot;[3]]()[&amp;quot;10&amp;quot;])()[([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;false&amp;quot;[3] + &amp;quot;true&amp;quot;[0] + &amp;quot;true&amp;quot;[1] + (undefined + [])[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;true&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]](&amp;quot;false0&amp;quot;[([false] + undefined)[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[0] + &amp;quot;false&amp;quot;[1] + &amp;quot;false&amp;quot;[2] + ([false] + undefined)[&amp;quot;10&amp;quot;] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + &amp;quot;false&amp;quot;[3]]()[&amp;quot;10&amp;quot;]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;false&amp;quot;[0] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + (undefined + [])[1] + &amp;quot;true&amp;quot;[0] + ([][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]] + [])[3] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;false&amp;quot;[2] + (true + [][&amp;quot;false&amp;quot;[0] + &amp;quot;false&amp;quot;[2] + &amp;quot;false&amp;quot;[1] + &amp;quot;true&amp;quot;[0]])[&amp;quot;10&amp;quot;] + &amp;quot;true&amp;quot;[1]]()[&amp;quot;12&amp;quot;])();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;][([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;undefined&amp;quot;[0] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;r&amp;quot; + &amp;quot;e&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;undefined&amp;quot;[0] + &amp;quot;r&amp;quot; + &amp;quot;undefined&amp;quot;[1] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;t&amp;quot; + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;][([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;undefined&amp;quot;[0] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;r&amp;quot; + &amp;quot;e&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;undefined&amp;quot;[0] + &amp;quot;r&amp;quot; + &amp;quot;undefined&amp;quot;[1] + &amp;quot;false0&amp;quot;[&amp;quot;falseundefined&amp;quot;[10] + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;falseundefined&amp;quot;[10] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;falseundefined&amp;quot;[10] + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;falseundefined&amp;quot;[10] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10])()[([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;undefined&amp;quot;[0] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;falseundefined&amp;quot;[10] + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;falseundefined&amp;quot;[10] + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;undefined&amp;quot;[1] + &amp;quot;t&amp;quot; + ([][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;] + [])[3] + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;f&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;t&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;ret&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;ret&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;i&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;i&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;i&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;i&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10])()[([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;i&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;a&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;i&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10])()[([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;t&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + ([][&amp;quot;flat&amp;quot;] + [])[3] + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;l&amp;quot; + (true + [][&amp;quot;flat&amp;quot;])[10] + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we apply &lt;code&gt;undo-flat-trick&lt;/code&gt; transform:&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][(&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;t&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;l&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][(&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;t&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;s&amp;quot;]()[10])()[(&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;t&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + (&amp;quot;function flat() { [native code] }&amp;quot; + [])[3] + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;l&amp;quot; + (true + &amp;quot;function flat() { [native code] }&amp;quot;)[10] + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Continuing with expression evaluation:&lt;/p&gt;
&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;t&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;l&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][&amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;t&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;s&amp;quot;]()[10])()[&amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;t&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;function flat() { [native code] }&amp;quot;[3] + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;l&amp;quot; + &amp;quot;truefunction flat() { [native code] }&amp;quot;[10] + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][&amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;s&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;s&amp;quot;]()[10])()[&amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;s&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;r&amp;quot; + &amp;quot;u&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;itali&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;s&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;f&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;n&amp;quot; + &amp;quot;t&amp;quot; + &amp;quot;c&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;l&amp;quot; + &amp;quot;o&amp;quot; + &amp;quot;r&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;]()[12] + ([][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10])()[&amp;quot;constructor&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;quot;[&amp;quot;fontcolor&amp;quot;]()[12])();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We see &lt;code&gt;fontcolor&lt;/code&gt; in the code now, so let us apply &lt;code&gt;undo-fontcolor-trick&lt;/code&gt;
transform:&lt;/p&gt;
&lt;ol start=&#34;9&#34;&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12] + ([][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10] + false + &amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10])()[&amp;quot;constructor&amp;quot;](&amp;quot;false0&amp;quot;[&amp;quot;italics&amp;quot;]()[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12])();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can also see that &lt;code&gt;undo-italics-trick&lt;/code&gt; transform is applicable, so let us apply
that as well:&lt;/p&gt;
&lt;ol start=&#34;10&#34;&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12] + ([][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;quot;[10] + false + &amp;quot;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;quot;[10])()[&amp;quot;constructor&amp;quot;](&amp;quot;&amp;lt;i&amp;gt;false0&amp;lt;/i&amp;gt;&amp;quot;[10]) + [])[1] + [1] + [0] + [0] + &amp;quot;&amp;lt;font color=\&amp;quot;undefined\&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&amp;quot;[12])();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Further simplifications with expression evaluation transform:&lt;/p&gt;
&lt;ol start=&#34;11&#34;&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;\&amp;quot;&amp;quot; + ([][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return&amp;quot; + &amp;quot;/&amp;quot; + false + &amp;quot;/&amp;quot;)()[&amp;quot;constructor&amp;quot;](&amp;quot;/&amp;quot;) + [])[1] + [1] + [0] + [0] + &amp;quot;\&amp;quot;&amp;quot;)();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return\&amp;quot;&amp;quot; + ([][&amp;quot;flat&amp;quot;][&amp;quot;constructor&amp;quot;](&amp;quot;return/false/&amp;quot;)()[&amp;quot;constructor&amp;quot;](&amp;quot;/&amp;quot;) + [])[1] + [1] + [0] + [0] + &amp;quot;\&amp;quot;&amp;quot;)();&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, what the fuck is this? We can verify that this stuff does indeed evaluate
back to the original character:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return\&amp;#34;&amp;#34; + ([][&amp;#34;flat&amp;#34;][&amp;#34;constructor&amp;#34;](&amp;#34;return/false/&amp;#34;)()[&amp;#34;constructor&amp;#34;](&amp;#34;/&amp;#34;) + [])[1] + [1] + [0] + [0] + &amp;#34;\&amp;#34;&amp;#34;)();
&amp;#39;@&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It also seems to be related to &lt;code&gt;eval()&lt;/code&gt; function, as we see the following
in jsfuck.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;wrapWithEval&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;runInParentScope&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[][&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;return eval&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)()&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[][&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flat&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;constructor&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;]&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;)()&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However completely untangling this at AST level is a matter of future research
that I will hopefully finish in the upcoming weeks. Researching how to undo
JSFuck obfuscation has been fun and interesting, but there are other
priorities now.&lt;/p&gt;
&lt;p&gt;To be continued&amp;hellip;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t JSFuck with me: Part 1</title>
      <link>https://www.trickster.dev/post/dont-jsfuck-with-me-part-1/</link>
      <pubDate>Fri, 24 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/dont-jsfuck-with-me-part-1/</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://jsfuck.com&#34;&gt;JSFuck&lt;/a&gt; is a prominent JavaScript obfuscation tool that
converts JS code into a rather weird looking form with only six characters:
&lt;code&gt;()+[]!&lt;/code&gt;. For example, &lt;code&gt;console.log(1)&lt;/code&gt; is turned into this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-24_17.33.21.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Code is made to be practically unreadable, but still retains it&amp;rsquo;s
original functionality. What&amp;rsquo;s the trick here? How can this possibly work?&lt;/p&gt;
&lt;p&gt;Fortunately JSFuck is open source and we can take a look into the
&lt;a href=&#34;https://github.com/aemkei/jsfuck/blob/master/jsfuck.js&#34;&gt;source code&lt;/a&gt;. We
see that no AST level transformation are performed and that source code from
the user is being treated as a string. One thing it does is changing substrings
from the original source code into a weird form, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; becomes &lt;code&gt;![]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; becomes &lt;code&gt;!![]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Single &lt;code&gt;e&lt;/code&gt; character becomes &lt;code&gt;(true+&amp;quot;&amp;quot;)[3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;hellip; and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we copy-paste these weird expressions into JavaScript REPL they do indeed
get evaluated to their original form:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v19.0.1.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; ![]
false
&amp;gt; !![]
true
&amp;gt; (true+&amp;#34;&amp;#34;)[3]
&amp;#39;e&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To understand what is going on here we must understand the concept of type
coercion. JavaScript is a weakly typed language, which means it provides
some leeway for developers using not exactly correct types. For example, &lt;code&gt;==&lt;/code&gt;
operator allows us to compare between numeric string and actual number:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; &amp;#34;1&amp;#34; == 1
true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we wanted to be more strict with the comparison here, we could use &lt;code&gt;===&lt;/code&gt;
operator which also checks that both types are the same.&lt;/p&gt;
&lt;p&gt;Type coercion is JavaScript language feature that entails applying type
conversion implicity on as needed basis. For example, we may have a numeric
string that we may want to apply some math on. JS runtime would perform the
type conversion for us:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; &amp;#34;100&amp;#34; / 3.14
31.84713375796178
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However we must not rely too much on this, as sometimes the results may not
be what you would expect:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; &amp;#34;100&amp;#34; + 50
&amp;#39;10050&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Complete discussion of type coercion logic is outside the scope of this text,
but some big picture rules are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Addition operation prefers converting stuff to strings, esp. if one of the
sides is a string. As fallback, it tries to convert it to numbers.&lt;/li&gt;
&lt;li&gt;Subtraction operation tries to convert operands to numbers before subtracting
them.&lt;/li&gt;
&lt;li&gt;There are truthy values that can be converted to &lt;code&gt;true&lt;/code&gt; (this is most of all
values) and some falsy values that can be converted to &lt;code&gt;false&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;,
&lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;NaN&lt;/code&gt; and empty string). This applies when doing binary logic,
e.g. logical NOT operation (&lt;code&gt;![]&lt;/code&gt; is false because empty array &lt;code&gt;[]&lt;/code&gt; is truthy).&lt;/li&gt;
&lt;li&gt;Unary operations (single &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt;) attempt conversion to numeric value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more on this, see the following pages on Mozilla developer portal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Addition&#34;&gt;Addition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Subtraction&#34;&gt;Subtraction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/Truthy&#34;&gt;Truthy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/Falsy&#34;&gt;Falsy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_plus&#34;&gt;Unary Plus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_negation&#34;&gt;Unary Negation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So essentially what happens inside JSFuck is converting each character into
these weird JS expressions that evaluate it back to the original value by
abusing JavaScript type coercion functionality. But not all characters
can be conjured from empty data by applying basic computational operations.
Sometimes JSFuck has to play tricks on implementation details of
JavaScript engine to obfuscate stuff (more on this later).&lt;/p&gt;
&lt;h2 id=&#34;enhanced-constant-folding&#34;&gt;Enhanced constant folding&lt;/h2&gt;
&lt;p&gt;So we want to apply AST-level transformations on code obfuscated with JSFuck
to turn it back to it&amp;rsquo;s original form (or something close enough). We have
previously tried applying
&lt;a href=&#34;https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-constant-folding-and-propagation/&#34;&gt;constant folding&lt;/a&gt;
for deobfuscation, so perhaps it can be used here as well? After all, we have
a lot of &lt;code&gt;BinaryExpression&lt;/code&gt; and &lt;code&gt;UnaryExpression&lt;/code&gt; nodes in the AST.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-24_17.37.58.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;From the previous article we got the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;BinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confident&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us try applying this on the obfuscated snippet in AST Explorer.&lt;/p&gt;
&lt;p&gt;The code becomes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;11&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;[([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ([][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;13&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0false&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-24_17.48.38.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Re-applying the transform again does not simplify it any further. Clearly the
AST transform code we have from before is not sufficient to undo JSFuck
obfuscation. Let us see how it can be improved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We only target &lt;code&gt;BinaryExpression&lt;/code&gt; nodes, but &lt;code&gt;UnaryExpression&lt;/code&gt; nodes also
need to be visited. The fix is simple: change &lt;code&gt;BinaryExpression(path)&lt;/code&gt; to
&lt;code&gt;&amp;quot;BinaryExpression|UnaryExpression&amp;quot;(path)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;JSFuck also abuses string indexing to pull out a character from larger string
(notice &lt;code&gt;&amp;quot;false&amp;quot;[2]&lt;/code&gt;). Babel does not know how to simplify it without our help.
We need to target &lt;code&gt;MemberExpression&lt;/code&gt; nodes to recognise when this is done
and replace the entire &lt;code&gt;MemberExpression&lt;/code&gt; with a &lt;code&gt;StringLiteral&lt;/code&gt; containing
a resulting character.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More generally, we tried to make a leap and it didn&amp;rsquo;t work out. Let&amp;rsquo;s take
another look at JSFuck source code and re-approach it in smaller steps.&lt;/p&gt;
&lt;p&gt;Consider the following mapping from the top of jsfuck.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SIMPLE&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;false&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;![]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;!![]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[][[]]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;NaN&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;+[![]]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Infinity&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// +&amp;#34;1e1000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us try to deobfuscate the values in this objects back to the original form.&lt;/p&gt;
&lt;p&gt;We fixed the node targeting, so &lt;code&gt;![]&lt;/code&gt; is now converted back into &lt;code&gt;false&lt;/code&gt; and
&lt;code&gt;!![]&lt;/code&gt; is converted back into &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However &lt;code&gt;[][[]]&lt;/code&gt; is not simplified any further. That&amp;rsquo;s because this is neither
binary not unary expression. It is empty array being indexed by empty array.&lt;/p&gt;
&lt;p&gt;We can confirm in the JS REPL that this does resolve into &lt;code&gt;undefined&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][[]]
undefined
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since at AST level this is &lt;code&gt;MemberExpression&lt;/code&gt; with empty &lt;code&gt;ArrayExpression&lt;/code&gt;s
at &lt;code&gt;object&lt;/code&gt; and &lt;code&gt;property&lt;/code&gt; fields we can target a &lt;code&gt;MemberExpression&lt;/code&gt; nodes
in our visitor, check both fields for empty arrays and replace it with a
node representing value &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We add the following code to our AST transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we want &lt;code&gt;+[![]]&lt;/code&gt; to be converted to &lt;code&gt;NaN&lt;/code&gt;, but our current code only
converts it to &lt;code&gt;+[false];&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It might not be immediately obvious, but Babel does not generate &lt;code&gt;NumericLiteral&lt;/code&gt;
node for &lt;code&gt;NaN&lt;/code&gt; value when running &lt;code&gt;NodePath.evaluate()&lt;/code&gt;/&lt;code&gt;valueWithNode()&lt;/code&gt;
on code like this. Instead, it generates a &lt;code&gt;BinaryExpression&lt;/code&gt; representing
division of zero by zero (&lt;code&gt;0 / 0&lt;/code&gt;). Since we explicitly check if the value we
got is a literal it cannot proceed any further with this. But we also don&amp;rsquo;t
want our code to run into infinite loop or bottomless recursion by trying to
reduce an irreducible expression. So what we do is we replace the
&lt;code&gt;BinaryExpression&lt;/code&gt; with the result anyway, but if the result node is not
a &lt;code&gt;NumericLiteral&lt;/code&gt; we call &lt;code&gt;NodePath.skip()&lt;/code&gt; to cut off traversal on the new
node.&lt;/p&gt;
&lt;p&gt;We modify constant folding code into the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;UnaryExpression|BinaryExpression&amp;#34;&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confident&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;skip&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we got &lt;code&gt;0 / 0;&lt;/code&gt; in the output, but we still want &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So we have to replace this binary expression with the appropriate node in the
AST. We preempt the expression evaluation with the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;NaN&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the early return here - it&amp;rsquo;s needed so that code further in the transform
is not attempted, as we are overriding the Babel&amp;rsquo;s &lt;code&gt;NodePath.evaluate()&lt;/code&gt; stuff
and injecting our own answer. We don&amp;rsquo;t want it to revert it back to &lt;code&gt;0 / 0&lt;/code&gt; for
us.&lt;/p&gt;
&lt;p&gt;So we got &lt;code&gt;NaN;&lt;/code&gt; in the output now.&lt;/p&gt;
&lt;p&gt;The following should be evaluated into &lt;code&gt;Infinity&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead, the current transform turns it into:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We see something we did not address yet - string indexing operation. We further
improve our &lt;code&gt;MemberExpression&lt;/code&gt; handling code to pull out the right character
for us and replace the &lt;code&gt;MemberExpression&lt;/code&gt; with &lt;code&gt;StringLiteral&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// https://stackoverflow.com/a/52986361/1044147
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isNumeric&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  	      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;isNaN(parseFloat(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; isFinite(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumeric&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(Number(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	  &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	  &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that we also allow index to be a numeric string (I noticed this sometimes
happens in the intermediate results when I was experimenting).&lt;/p&gt;
&lt;p&gt;Since we have more complex JS expression to deobfuscate, we now apply the
transform multiple times with the following results between steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;+(1 + &amp;quot;true&amp;quot;[3] + [1] + [0] + [0] + [0]);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+(1 + &amp;quot;e&amp;quot; + [1] + [0] + [0] + [0]);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 / 0;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last expression cannot be simplified by the AST transform as it is, but
a fix is trivial with one more evaluation override that further improves what
we did in the &lt;code&gt;NaN&lt;/code&gt; case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Infinity&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;NaN&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this change, &lt;code&gt;1 / 0&lt;/code&gt; is turned into &lt;code&gt;Infinity&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now let us try some obfuscated values from &lt;code&gt;MAPPING&lt;/code&gt; object further down
below in jsfuck.js. &lt;code&gt;(false+&amp;quot;&amp;quot;)[1]&lt;/code&gt; should turn into &lt;code&gt;a&lt;/code&gt;. Indeed our code
is able to deobfuscate it with &lt;code&gt;&amp;quot;false&amp;quot;[1];&lt;/code&gt; as intermediate value. Likewise,
&lt;code&gt;(undefined+&amp;quot;&amp;quot;)[2]&lt;/code&gt; is turned into &lt;code&gt;&amp;quot;d&amp;quot;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point our AST transform is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;UnaryExpression|BinaryExpression&amp;#34;&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Infinity&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;NaN&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confident&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;skip&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// https://stackoverflow.com/a/52986361/1044147
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isNumeric&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  	      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;isNaN(parseFloat(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; isFinite(&lt;span style=&#34;color:#a6e22e&#34;&gt;n&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumeric&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(Number(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	  &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      	  &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isArrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is able to undo a lot of stuff that JSFuck is doing and works on great
fraction of &lt;code&gt;MAPPING&lt;/code&gt; values.&lt;/p&gt;
&lt;h2 id=&#34;-but-this-is-not-enough-further-work&#34;&gt;&amp;hellip; but this is not enough: further work&lt;/h2&gt;
&lt;p&gt;However, what about the &lt;code&gt;(true+[][&amp;quot;flat&amp;quot;])[20]&lt;/code&gt; that should evaluate to a
single &lt;code&gt;{&lt;/code&gt; character? This one is not simplified any futher by our transform.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s unpack this expression in JS REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; [][&amp;#34;flat&amp;#34;]
[Function: flat]
&amp;gt; true + [][&amp;#34;flat&amp;#34;]
&amp;#39;truefunction flat() { [native code] }&amp;#39;
&amp;gt; (true+[][&amp;#34;flat&amp;#34;])[20]
&amp;#39;{&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What we have here is that &lt;code&gt;[][&amp;quot;flat&amp;quot;]&lt;/code&gt; gives us &lt;code&gt;flat()&lt;/code&gt; function as object,
that is stringified by adding &lt;code&gt;true&lt;/code&gt; to it and a character is extracted from
the resulting string. This kind of hack relies on JS runtime implementation
detail and our transform does not support this yet. It also does not support
the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(false+[0])[&amp;#34;italics&amp;#34;]()[10]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp()+&amp;#34;&amp;#34;)[3]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;](NaN+&amp;#34;)[21]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]()[0]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;fontcolor&amp;#34;]()[11]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(&amp;#34;&amp;#34;)[&amp;#34;italics&amp;#34;]()[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(RegExp()+&amp;#34;&amp;#34;)[2]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To fully deobfuscate code from JSFuck we have to further improve our AST
transform to cover all the weird hacks JSFuck is doing with JS functions.
That is the objective for the future work.&lt;/p&gt;
&lt;p&gt;The plan is to have a trilogy of posts working towards complete reversal of
JSFuck&amp;rsquo;ed code. Second part will deal with JS function hacks. The third part
will be about tying up loose ends and developing a standalone deobfuscator
with some basic CLI.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Understanding HTTP/2 fingerprinting</title>
      <link>https://www.trickster.dev/post/understanding-http2-fingerprinting/</link>
      <pubDate>Thu, 23 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-http2-fingerprinting/</guid>
      <description>&lt;p&gt;Incoming HTTPS traffic can be fingerprinted by server-side systems to derive
technical characteristics of client side systems. One way to do this
is TLS fingerprinting that we have covered before on this blog and that
is commonly done by antibot vendors as part of automation countermeasures suite.
But that&amp;rsquo;s not all they do. Fingerprinting can be done at HTTP/2 level as well.
Let us discuss HTTP/2 and how HTTP/2 fingerprinting works. More specifically,
we will look into HTTP/2 fingerprinting technique that was
&lt;a href=&#34;https://www.blackhat.com/docs/eu-17/materials/eu-17-Shuster-Passive-Fingerprinting-Of-HTTP2-Clients-wp.pdf&#34;&gt;proposed by Akamai&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unlike HTTP/1.1, HTTP/2 is a binary protocol, meaning it has binary wire format.
HTTP/1.1 requires a single TCP connection per request (with possibility for
connection reuse), but HTTP/2 supports request-response flow multiplexing across
a single connection. Both of these things makes HTTP/2 more performant, less
resource intensive protocol. Formal specification of HTTP/2 is
&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc7540&#34;&gt;RFC 7540&lt;/a&gt;. There is also something
called HPACK, which is HTTP/2 header compression technique, described in
&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc7541&#34;&gt;RFC 7541&lt;/a&gt;. These two RFCs are primary
specs for HTTP/2. Theorethically HTTP/2 can be used for plaintext communications,
but in practice it is pretty much always wrapped in TLS connection. When TLS
connection is established, TLS ALPN (Application Level Protocol Negotiation)
extension is used to negotiate whether to use HTTP/1.1 or HTTP/2 in the
encrypted channel.&lt;/p&gt;
&lt;p&gt;Conceptually speaking, HTTP/2 connection is a TCP/TLS connection that is further
divided into subconnections - streams. Streams are numbered logical
bidirectional conversations between client and server. Stream 0 deals with
parameters for entire connection. Odd numbered streams should be initiated by
the client and even numbered streams are started by the server. Each stream
in itself is a sequence of frames that it sent across a connection. HTTP/2
frame is a binary protocol data unit that contains stream ID, stream type,
some Boolean flags and payload. There are ten possible frame types.
Most significant are &lt;code&gt;HEADERS&lt;/code&gt; and &lt;code&gt;DATA&lt;/code&gt;. In HTTP/2, a message consists of
consists of a &lt;code&gt;HEADERS&lt;/code&gt; frame and possibly some DATA frames with payload (there
are no payload frames for GET request). Other types of frames deal with stream
priorities, flow control and so on. After the TLS connection is established,
SETTINGS frames are exchanged to negotiate some initial parameters for data
exchange.&lt;/p&gt;
&lt;p&gt;HTTP/2 fingerprinting entails observing behaviour of the client to infer various
attributes about software (OS, browser, etc.) on the client side system. It
does not uniquely identify the end user. HTTP/2 fingerprinting can be
implemented at web server or content delivery network. Client behaviour is
observed when connection is being established. Fingerprinting solution gathers
data on initial connection settings, flow control, stream priorities and
pseudo header ordering to deduce things from that.&lt;/p&gt;
&lt;p&gt;First component of fingerprint comes from SETTINGS frame that client has sent for
the zeroth stream. Default values of various parameters that client proposes
vary between HTTP/2 client implementations - Firefox consistently sends one
set of parameter, Chrome another set and so on. Furthermore, these setting may
vary between browser versions, which is potentially another piece of infromation
that can be inferred. More specifically, &lt;code&gt;SETTINGS&lt;/code&gt; frame sets the following
parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_HEADER_TABLE_SIZE&lt;/code&gt; (1) - size of header compression table (in bytes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_ENABLE_PUSH&lt;/code&gt; (2) - allow or disallow data pushing from the server
(disabled in modern Chrome).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_MAX_CONCURRENT_STREAMS&lt;/code&gt; (3) - maximum number of concurrent streams.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_INITIAL_WINDOW_SIZE&lt;/code&gt; (4) - initial stream window size (e.g. how many
bytes can be in-flight between client and server).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_MAX_FRAME_SIZE&lt;/code&gt; (5) - how big the frames are allowed to get (in bytes).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETTINGS_MAX_HEADER_LIST_SIZE&lt;/code&gt; (6) - advisory upper limit for header size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make a part of fingerprint, &lt;code&gt;SETTINGS&lt;/code&gt; frame is parsed and settings ID-value
pairs are extracted.&lt;/p&gt;
&lt;p&gt;Second part comes from &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; frame that is commonly sent by many clients
when starting a connection. If this frame was sent, the window size value is
appended to the fingerprint. If not, 0 is appended instead.&lt;/p&gt;
&lt;p&gt;Third component comes from &lt;code&gt;PRIORITY&lt;/code&gt; frames. An interesting feature of HTTP/2 is
that it allows setting not only stream priorities, but also dependencies between
streams. Streams are arranged in a tree structure, stream 0 being the root node.
Child nodes from node 0 are streams that are dependent on it and they can have
their own dependent streams down the tree. This provides some additional
entropy for fingerprinting. All of this allow client to communicate it&amp;rsquo;s
preferences for resource allocation across streams. Some HTTP/2 clients start
sending the &lt;code&gt;PRIORITY&lt;/code&gt; frames to do so early in the connection. Each &lt;code&gt;PRIORITY&lt;/code&gt;
frame has two stream IDs (for parent and child streams), priority weight and
an exclusivity bit. These numbers are also included into the fingerprint.&lt;/p&gt;
&lt;p&gt;Fourth and the last component of fingerprint is pseudo header ordering.
Pseudo headers are binary, non optional parts of &lt;code&gt;HEADERS&lt;/code&gt; frame - &lt;code&gt;:method&lt;/code&gt;,
&lt;code&gt;:authority&lt;/code&gt;, &lt;code&gt;:scheme&lt;/code&gt;, &lt;code&gt;:path&lt;/code&gt; that have their own equivalents in HTTP/1.1
headers and ordering that is implementation-specific, thus providing a signal
of what kind of software sent the request. For instance, Chrome has one
particular ordering hardcoded, Firefox another one and so forth.&lt;/p&gt;
&lt;p&gt;The final fingerprint string comprises of pipe-separated four substrings,
each representing one of the above components. The overall format is
&lt;code&gt;S[;]|WU|P[,]#|PS[,]&lt;/code&gt; where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;S&lt;/code&gt; is a settings ID - value pair; they are separated by semicolon if there&amp;rsquo;s
two or more of them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WU&lt;/code&gt; is window size value from &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; frame (0 if it was not sent
by the client).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt; is colon-separated tuple string representing &lt;code&gt;PRIORITY&lt;/code&gt; frame field
values in the following format: &lt;code&gt;StreamID:Exclusivity_Bit:Dependant_StreamID:Weight&lt;/code&gt;.
If there was two or more &lt;code&gt;PRIORITY&lt;/code&gt; frames from the user comma character is used
for separation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PS&lt;/code&gt; - string representing request pseudo-header order (&lt;code&gt;m&lt;/code&gt; for &lt;code&gt;:method&lt;/code&gt;,
&lt;code&gt;p&lt;/code&gt; for &lt;code&gt;:path&lt;/code&gt; and so on).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This string can be further passed into a hash function. You can check your
own HTTP/2 (and TLS) fingerprints at &lt;a href=&#34;https://tls.peet.ws&#34;&gt;tls.peet.ws&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This technique is commonly used for security purposes, such as fighting
automation. It is part of automation countermeasures arsenal that
companies such as Akamai are selling to their customers.&lt;/p&gt;
&lt;p&gt;To see an example of this kind of fingerprinting you may want to check Xetera&amp;rsquo;s
modified version of nginx, especially the &lt;a href=&#34;https://github.com/Xetera/nginx-http2-fingerprint/blob/master/src/http/v2/ngx_http_v2_module.c#L243&#34;&gt;&lt;code&gt;calculate_fingerprint()&lt;/code&gt; function
in src/http/v2/ngx_http_v2_module.c&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As scraper/bot developers, how do we deal with something like this? After all,
if we simply use curl or some request library to generate requests for scraping
or some other kind of automation the HTTP/2 fingerprints will betray the
automated nature of the traffic, as the fingerprint will not match the user
agent our code claims to be (e.g. browser of site-specific mobile app). Well,
the key is to make sure that HTTP/2 traffic we generate has all the
aforementioned traits to be exactly the same as one of the mainstream browsers.
There are some open source project that help with that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/lwthiker/curl-impersonate&#34;&gt;curl-impersonate&lt;/a&gt; is a
modified version of curl that pretends to be a browser by emitting TLS/HTTP2
traffic consistent with browser fingerprints.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/useflyent/fhttp&#34;&gt;fhttp&lt;/a&gt; is a Golang HTTP client library
that can help with this as well (there&amp;rsquo;s some forks for this repo that
introduce further work).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/bogdanfinn/tls-client&#34;&gt;tls-client&lt;/a&gt; - Golang library that
addresses TLS and HTTP/2 client fingerprinting (based on fhttp).&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: reducing nestedness, unflattening the CFG</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-reducing-nestedness-unflattening-the-cfg/</link>
      <pubDate>Sat, 18 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-reducing-nestedness-unflattening-the-cfg/</guid>
      <description>&lt;p&gt;In computer science, control flow graph is a graph that represents order of
code block execution and transitions between blocks. For the purposes of todays
topic, vertices in such graph are basic blocks of code that are to be executed
consequentially and have no branching logic. Each basic block has an entry point
and exit point. Edges in control graph represent transitions between basic
blocks.&lt;/p&gt;
&lt;p&gt;Control flow graph flattening is an obfuscation technique that introduces
large switch statements within while-loops with basic blocks at each switch
case. Switch condition contains some logic to choose a switch case on runtime
and each switch case does something at the end of basic block to switch to
next one.&lt;/p&gt;
&lt;p&gt;Consider the following sample code from &lt;a href=&#34;https://obfuscator.io&#34;&gt;obfuscator.io&lt;/a&gt;
documentation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;foo&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;foo&lt;/span&gt;()();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we run obfuscation with &amp;ldquo;Control Flow Flattening&amp;rdquo; checkbox being on, we get
the following obfuscated version of this code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HThGl&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2|4|1|3|5|6|0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ssbzl&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pWydz&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HwgBi&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HThGl&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YnoJz&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ssbzl&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HwgBi&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;split&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c58ad&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YnoJz&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;5&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pWydz&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-18_11.27.18.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We put this code into &lt;a href=&#34;https://astexplorer.net&#34;&gt;ASTExplorer&lt;/a&gt; where we will
be learning how to deobfuscate it.&lt;/p&gt;
&lt;p&gt;We see that the innermost block of input code has been split into smaller
basic blocks and distributed across switch-cases in the obfuscated version.
Furthermore, obfuscator.io introduces some supporting obfuscations into the
code to make the primary obfuscation technique harder to reverse. Our strategy
here is to chip away these secondary techniques until we get to the point
of attacking the CFG flattening, then simplify the code to something close
to original form.&lt;/p&gt;
&lt;p&gt;Since we also want to make the code less nested for easier readability, let&amp;rsquo;s
start by replacing the outermost IIFE with all the code inside it. This is
accomplished with the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;ExpressionStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isProgram&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;expr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expression&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;expr&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;expr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isFunctionExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;innerStatements&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWithMultiple&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;innerStatements&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We target &lt;code&gt;ExpressionStatement&lt;/code&gt; as that is the object type for IIFE. In the visitor
we also check for some other conditions to make sure we are visiting the
exact part of the code we want to change: parent node has to be &lt;code&gt;Program&lt;/code&gt; and
there has to be a &lt;code&gt;CallExpression&lt;/code&gt; node at &lt;code&gt;expression&lt;/code&gt; field that points to
&lt;code&gt;FunctionExpression&lt;/code&gt; node in it&amp;rsquo;s &lt;code&gt;callee&lt;/code&gt; field. If that is the case, we take
all the statement nodes from body of &lt;code&gt;callee&lt;/code&gt; and replace our currently visited
node with them by calling &lt;code&gt;replaceWithMultiple&lt;/code&gt; out the &lt;code&gt;NodePath&lt;/code&gt; object. To
sum it up, we replace one node high up in the AST with multiple nodes from
subtree below it. This refactors away the outermost IIFE.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-18_12.34.35.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the while loop we see that the condition for looping is quite confusing: &lt;code&gt;!![]&lt;/code&gt;.
When we try it in the JS REPL, we get that this merely is another way for
representing &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v19.0.1.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; !![]
true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At AST level we have two nested &lt;code&gt;UnaryExpression&lt;/code&gt; nodes with &lt;code&gt;ArrayExpression&lt;/code&gt;
in the innermost level.&lt;/p&gt;
&lt;p&gt;So if this stuff evaluates to a Boolean constant, perhaps Babel can compute
it for us? Turns out it can with &lt;code&gt;NodePath.evaluate()&lt;/code&gt; method that we use in
our next transformation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;UnaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confident&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-18_12.59.38.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We must take care to check &lt;code&gt;confident&lt;/code&gt; flag to make sure Babel is confident of
the result. We replace the &lt;code&gt;UnaryExpression&lt;/code&gt;s - both of them - with the
resulting values. There is no need to worry about re-traversing the AST nodes
after tree modification, as Babel does that for us automatically.&lt;/p&gt;
&lt;p&gt;So now we have &lt;code&gt;while(true)&lt;/code&gt; in the code. Another thing we want to get rid is
bracket notation, so that we have &lt;code&gt;console.log()&lt;/code&gt; in the code, not
&lt;code&gt;console[&amp;quot;log&amp;quot;]()&lt;/code&gt;. This was covered earlier in the blog and can be done with
the following AST transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;computed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We did some easy ones and now the code looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HThGl&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2|4|1|3|5|6|0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ssbzl&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pWydz&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HwgBi&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HThGl&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YnoJz&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Ssbzl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HwgBi&amp;#39;&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c58ad&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x134393&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;YnoJz&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;5&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5b0ba5&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pWydz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the thing is, we still have these JS objects introducing indirection in the
code. In one case, a pipe-separated case order list is being referenced. In
other cases, we have some auxillary functions being indirectly referenced.
To clean this up, we must further improve code from the
&lt;a href=&#34;https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-reducing-indirection-undoing-string-concealing/&#34;&gt;previous post&lt;/a&gt;. In this
case, we need to do three things: 1) replace references to string literal with
actual value, 2) move out these nested functions out of the objects and
give them some names, so that they could be called directly without referencing
these objects and 3) fix the function calls so that the new functions are called.
After that is done, we can remove the objects &lt;code&gt;_0x134393&lt;/code&gt; and
&lt;code&gt;_0x5b0ba5&lt;/code&gt; as they won&amp;rsquo;t be needed anymore.&lt;/p&gt;
&lt;p&gt;So the code to do this is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isObjectExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;constant&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Object();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isFunctionExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isFunctionExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fnName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// https://babeljs.io/docs/en/babel-types#functiondeclaration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;functionDecl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;functionDeclaration&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;params&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;generator&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentOfDecl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;parentOfDecl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;unshiftContainer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;functionDecl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fnName&lt;/span&gt;)); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As in previous post, we use scopes and bindings for replacing the references.
The new part is function creation and insertion into &lt;code&gt;body&lt;/code&gt; of AST node two
levels up from the &lt;code&gt;VariableDeclarator&lt;/code&gt; that we are visiting. This target node
will be either &lt;code&gt;Program&lt;/code&gt; or &lt;code&gt;FunctionExpression&lt;/code&gt;, thus it is safe to assume
it will have a &lt;code&gt;body&lt;/code&gt; property with array of AST nodes. We also make sure
that indirect function calls now are pointed to the new function that we
reconstructed from the code nested in the object.&lt;/p&gt;
&lt;p&gt;We are really getting ready to our final strike. The code now looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pWydz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Ssbzl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YnoJz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Ssbzl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2|4|1|3|5|6|0&amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c58ad&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YnoJz&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;5&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pWydz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have &lt;code&gt;_0x143f46&lt;/code&gt; variable starting at 0 and getting incremented by one per
each iteration of while loop. This counter is used to pick which switch case is
to be executed at each iteration. &lt;code&gt;_0x11ba88&lt;/code&gt; is an array with case indices
as numeric strings. But we have one inconvenience here. This array is not
being hardcoded directly (like &lt;code&gt;[&amp;quot;2&amp;quot;, &amp;quot;4&amp;quot;, &amp;quot;1&amp;quot;, &amp;quot;3&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;6&amp;quot;, &amp;quot;0&amp;quot;]&lt;/code&gt;), but
computed by splitting a pipe-separated string. Let us do one more AST transform
to convert this line into a form we need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isCallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;obj&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;obj&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;split&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strToSplit&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;obj&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;separator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;components&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strToSplit&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;separator&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;components&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-18_15.38.19.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We do our work inside visitor in a similar way as before. We pick the right
AST node by unwrapping few levels below it and checking for conditions to make
sure we are at right point in the tree. Then we extract the string value, split
it and create an array with final values (represented by &lt;code&gt;StringLiteral&lt;/code&gt; nodes)
that gets assigned to &lt;code&gt;init&lt;/code&gt; property of relevant &lt;code&gt;VariableDeclarator&lt;/code&gt; node.
Since AST requires node objects, not JS strings we convert values to nodes
with a little trick from functional programming - a
&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map&#34;&gt;&lt;code&gt;map()&lt;/code&gt; function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As we wanted, the line that declares case order array now is like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11ba88&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;6&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re ready to unflatten the CFG now. We got the directly hardcoded array of
case numbers in the order they will be executed and we got the switch statement
nested in a while loop that executes them until it runs out of cases in the
list. So we got to replace that while loop with all the little basic blocks
joined together in the order they would be executed at runtime.&lt;/p&gt;
&lt;p&gt;This is accomplished by the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;WhileStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;whileStmt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentBody&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bodyBlock&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;whileStmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bodyParts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bodyBlock&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;bodyParts&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isSwitchStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bodyParts&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;])) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchStmt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bodyParts&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchCases&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchStmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;cases&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;basicBlocksByCase&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchCase&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchCases&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchCase&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;basicBlocksByCase&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchCase&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;consequent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;switchStmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;discriminant&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentScope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentScope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arrayName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;order&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayExpr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stuffInOrder&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;caseNum&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;order&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;basicBlock&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;basicBlocksByCase&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;caseNum&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;stuffInOrder&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;basicBlock&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// https://stackoverflow.com/a/1374131/1044147
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWithMultiple&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;stuffInOrder&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;ContinueStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two stages of code reconstruction: 1) we gather all the basic blocks
into object, keyed by their case numbers and 2) we unite them into list of nodes
in the order of cases that we extracted from &lt;code&gt;ArrayExpression&lt;/code&gt; object.
&lt;code&gt;WhileStatement&lt;/code&gt; is replaced with this array of nodes, resulting in the
original order of statements. Array that lists case numbers is removed
afterwards as it&amp;rsquo;s no longer needed. We also remove &lt;code&gt;ContinueStatement&lt;/code&gt; nodes
as they were only needed in the while-switch structure that we had in obfuscated
code.&lt;/p&gt;
&lt;p&gt;The deobfuscated code is now as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pWydz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x237610&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Ssbzl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x113c5a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b9ca8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YnoJz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Ssbzl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5ad206&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2b2bb5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x143f46&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3c58ad&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;YnoJz&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pWydz&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x11c3ac&lt;/span&gt;)();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We still have a bit of nestedness and indirection, but this code does print
numbers 1 to 6 in the same way that original code did and is quite readable.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: reducing indirection, undoing string concealing</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-reducing-indirection-undoing-string-concealing/</link>
      <pubDate>Mon, 13 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-reducing-indirection-undoing-string-concealing/</guid>
      <description>&lt;p&gt;String concealing is a code obfuscation technique that involves some sort of
string constant recomputation (e.g. Base64 encoding or encryption with symmetric
ciphers) being introduced into code. Furthermore, obfuscation solutions may
introduce some variable and function indirection to further thwart reverse
engineering. In this post we will be learning how to deal with both of these
obstacles on the way to untold riches.&lt;/p&gt;
&lt;p&gt;Let us consider the following JS snippet that we are going to obfuscate with
&lt;a href=&#34;https://obfuscator.io&#34;&gt;obfuscator.io&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;api.nike.com&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*/*&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.nike.com&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.nike.com/&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Not_A Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;109&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;109&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;empty&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cors&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our objective is to obfuscate this code with &amp;ldquo;String Array&amp;rdquo;, &amp;ldquo;String Array
Calls Transform&amp;rdquo; and &amp;ldquo;String Array Encoding&amp;rdquo; options, then do AST-level
transformation to undo the obfuscation and get the code as close as possible
to what we started with.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_14.33.44.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Obfuscation tool converts the above snippet into this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3d5a8f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;yxbPlM5PA2uUy29T&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;kI8Q&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw4Tr0iSzw4Tvvm7Ct0WlJKSzw47Ct0WlJG&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20V&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iM1Hy09tiG&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw1WDhK&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;y29YCW&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ANnVBG&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Bg9N&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3d5a8f&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4a74f2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4d32cc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;); &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x40&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; String[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fromCharCode&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0xff&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4d32cc&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;indexOf&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;length&amp;#39;&lt;/span&gt;]; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charCodeAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x10&lt;/span&gt;))[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;slice&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; decodeURIComponent(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3da25d&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3544fd&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1e529f&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xc4fc60&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x16f93c&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x58c8a8&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x449635&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2fa582&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x7&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2e8261&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x56169a&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3da25d&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3544fd&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1e529f&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xc4fc60&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x16f93c&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22Not_A\x20Brand\x22;v=\x2299\x22,\x20\x22Google\x20Chrome\x22;v=\x22109\x22,\x20\x22Chromium\x22;v=\x22109\x22&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x58c8a8&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x449635&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2fa582&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/109.0.0.0\x20Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2e8261&lt;/span&gt;)]();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xdc848&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x56169a&lt;/span&gt;)](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us copy-paste this code into &lt;a href=&#34;https://astexplorer.net&#34;&gt;AST Explorer&lt;/a&gt; and
broadly review what we got here.&lt;/p&gt;
&lt;p&gt;At topmost level of the program, there are three major parts (two
&lt;code&gt;FunctionDeclaration&lt;/code&gt; objects and one &lt;code&gt;ExpressionStatement&lt;/code&gt;):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A small function that returns a hardcoded array with encoded string literals.&lt;/li&gt;
&lt;li&gt;A bigger function that seems to do string decoding given what seems to be an
index within string array.&lt;/li&gt;
&lt;li&gt;An IIFE that relies on the above two functions to get some of the string
values, but is otherwise quite similar to the code we started with.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_14.51.15.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-11_14.51.25.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-11_14.51.37.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What is going in this middle function? A quick Google search of the large
constant suggests that it has something to do with Base64 encoding, which
is consistent with obfuscation settings we used.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_14.58.55.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However it also seems to be something beyond just Base64 as the strings in the
encoded array do not yield anything readable when decoded with Base64 algorithm.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;yxbPlM5PA2uUy29T&amp;#34; | base64 -d | hexdump -C
00000000  cb 16 cf 94 ce 4f 03 6b  94 cb 6f 53              |?.?.?O.k.?oS|
0000000c
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point we don&amp;rsquo;t quite know what it does exactly, but let us put that
question aside.&lt;/p&gt;
&lt;p&gt;Our endgame is to replace the calls to decoder function (&lt;code&gt;_0x4ad0()&lt;/code&gt; a.k.a.
&lt;code&gt;_0x15ca68()&lt;/code&gt;) with the decoded string value that it would return. But since
we&amp;rsquo;re just learning, let us start small by making the code slightly more
readable. In the IIFE there&amp;rsquo;s this pesky &lt;code&gt;_0xdc848&lt;/code&gt; object that contains
key-value pairs, with values being used as arguments for decoding function.
When the code below calls the decoding function, it indirectly gets the
argument from this object. Let us change the code so that values are used
directly and the &lt;code&gt;_0xdc848&lt;/code&gt; object is gone.&lt;/p&gt;
&lt;p&gt;In the AST we got the following: there a &lt;code&gt;VariableDeclarator&lt;/code&gt; with an
&lt;code&gt;ObjectExpression&lt;/code&gt; that has multiple &lt;code&gt;ObjectProperty&lt;/code&gt; nodes in &lt;code&gt;properties&lt;/code&gt;
array. Each &lt;code&gt;value&lt;/code&gt; fields of these &lt;code&gt;ObjectProperty&lt;/code&gt; objects points to
a &lt;code&gt;NumericLiteral&lt;/code&gt; node. So that&amp;rsquo;s the &lt;code&gt;_0xdc848&lt;/code&gt; object. Each reference
to this object (e.g. &lt;code&gt;_0xdc848._0x3da25d&lt;/code&gt;) is a &lt;code&gt;MemberExpression&lt;/code&gt; with
object name at &lt;code&gt;object&lt;/code&gt; field and key at &lt;code&gt;property&lt;/code&gt; field (both represented
as &lt;code&gt;Identifier&lt;/code&gt;s).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-12_16.04.27.png&#34;&gt;Screenshot 6&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-12_16.05.13.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So what we want to do is to replace &lt;code&gt;MemberExpression&lt;/code&gt;s that reference the
constant map object with &lt;code&gt;NumericLiteral&lt;/code&gt; nodes containing the final value,
then get rid of the &lt;code&gt;_0xdc848&lt;/code&gt; thing as it won&amp;rsquo;t be needed anymore. We do this
with the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ObjectExpression&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;constant&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Object();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isMemberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kv&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We target &lt;code&gt;VariableDeclarator&lt;/code&gt; nodes with a visitor function, do some quick
checks that variable declarator we have found is declaring a constant object.
Then we gather key-value pairs into a new object while checking that each value
is a literal (e.g. some numeric constant). Next we iterate across references
by using Babel scope and binding (see previous post on this) to replace
an object reference (&lt;code&gt;MemberExpression&lt;/code&gt; node) with the final value (we get
it by saying &lt;code&gt;refPath.parentPath.node&lt;/code&gt; as each entry in &lt;code&gt;Binding.referencePaths&lt;/code&gt;
point to &lt;code&gt;Identifier&lt;/code&gt; node that does the referencing). Finally, we remove the
no longer needed object by removing the &lt;code&gt;VariableDeclarator&lt;/code&gt; node from AST.&lt;/p&gt;
&lt;p&gt;The IIFE at the bottom now looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22Not_A\x20Brand\x22;v=\x2299\x22,\x20\x22Google\x20Chrome\x22;v=\x22109\x22,\x20\x22Chromium\x22;v=\x22109\x22&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/109.0.0.0\x20Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x15ca68&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;)](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_15.38.49.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Well, that&amp;rsquo;s a bit better, but we still got some indirection in the code. There
are some variables/constants that do nothing but just serve as another name for
something else in the code. One culprit is &lt;code&gt;_0x15ca68&lt;/code&gt; in the IIFE, but there&amp;rsquo;s
some more in the functions further up. What we can do is get rid of these
secondary variables and replace all references to them with references to the
original value/object/function it points to.&lt;/p&gt;
&lt;p&gt;The AST transform to do this is as follow:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To recognise a &lt;code&gt;VariableDeclarator&lt;/code&gt; node that introduces a secondary identifier,
we merely have to check if there&amp;rsquo;s a &lt;code&gt;Identifier&lt;/code&gt; node at &lt;code&gt;init&lt;/code&gt; property.
If that is the case, we replace all references to this node with &lt;code&gt;Identifier&lt;/code&gt;
at &lt;code&gt;init&lt;/code&gt; property, so that all the referencing code uses the name of primary
variable/constant/function. Then we remove the redundant variable declaration
from the code.&lt;/p&gt;
&lt;p&gt;Now the code is further simplified and looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3d5a8f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;yxbPlM5PA2uUy29T&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;kI8Q&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw4Tr0iSzw4Tvvm7Ct0WlJKSzw47Ct0WlJG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20V&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iM1Hy09tiG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw1WDhK&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;y29YCW&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ANnVBG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Bg9N&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3d5a8f&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4a74f2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4d32cc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;); &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x40&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; String[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fromCharCode&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0xff&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4d32cc&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;indexOf&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;length&amp;#39;&lt;/span&gt;]; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charCodeAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x10&lt;/span&gt;))[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;slice&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; decodeURIComponent(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22Not_A\x20Brand\x22;v=\x2299\x22,\x20\x22Google\x20Chrome\x22;v=\x22109\x22,\x20\x22Chromium\x22;v=\x22109\x22&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/109.0.0.0\x20Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x23768c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1499a9&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;)](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_16.18.05.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the IIFE we got direct calls to decoder function, as well as slightly
simplified code in the decoder function itself. We&amp;rsquo;re getting close now.
Let us rename some of the identifiers to more readable form by applying
another simple transformation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;betterNames&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x4ad0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;decode&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x3d5a8f&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;encodedStrings&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x5d79&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;getEncodedStrings&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x4d32cc&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;base64Alphabet&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x23768c&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;response&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_0x1499a9&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;betterNames&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;betterNames&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newIdentifier&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newIdentifier&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;newIdentifier&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code now looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getEncodedStrings&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encodedStrings&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;yxbPlM5PA2uUy29T&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;kI8Q&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw4Tr0iSzw4Tvvm7Ct0WlJKSzw47Ct0WlJG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20V&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iM1Hy09tiG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw1WDhK&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;y29YCW&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ANnVBG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Bg9N&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;getEncodedStrings&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encodedStrings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getEncodedStrings&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getEncodedStrings&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4a74f2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;base64Alphabet&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x152116&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;); &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x40&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x4&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; String[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fromCharCode&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0xff&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x425bd6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1806d0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;base64Alphabet&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;indexOf&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1bc5e5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;length&amp;#39;&lt;/span&gt;]; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x24c07d&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;00&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x19f00a&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;charCodeAt&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4858d8&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;toString&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#ae81ff&#34;&gt;0x10&lt;/span&gt;))[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;slice&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; decodeURIComponent(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0xf75b57&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x1cb49b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sjCySo&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x2ee979&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x29a87a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rQzPwo&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x299466&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22Not_A\x20Brand\x22;v=\x2299\x22,\x20\x22Google\x20Chrome\x22;v=\x22109\x22,\x20\x22Chromium\x22;v=\x22109\x22&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/109.0.0.0\x20Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;)](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_16.35.02.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One minor issue is that it incorrectly renamed something to &lt;code&gt;response&lt;/code&gt; and
&lt;code&gt;json&lt;/code&gt; in the &lt;code&gt;decode()&lt;/code&gt; function, but we can afford not to worry about this,
as this function is going away soon.&lt;/p&gt;
&lt;p&gt;We are getting close to our grand finale, but there&amp;rsquo;s one small thing we would
like to fix. In the &lt;code&gt;decode()&lt;/code&gt; function there is a single call to
&lt;code&gt;getEncodedStrings()&lt;/code&gt; that returns a hardcoded array of concealed strings.
The &lt;code&gt;getEncodedStrings()&lt;/code&gt; function is not called anywhere else, so we
can do one more quick transform to replace a call to this function
(&lt;code&gt;CallExpression&lt;/code&gt; object) with the array itself (&lt;code&gt;ArrayExpression&lt;/code&gt; object). We
write another quick transform to do so and also remove the &lt;code&gt;getEncodedStrings()&lt;/code&gt;
function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;FunctionDeclaration&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;getEncodedStrings&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;encodedStringArrayExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;declarations&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CallExpression&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parentPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;encodedStringArrayExpr&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The function that returns an array is gone and array is now hardcoded into
&lt;code&gt;decode()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x150e0f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;yxbPlM5PA2uUy29T&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;kI8Q&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw4Tr0iSzw4Tvvm7Ct0WlJKSzw47Ct0WlJG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ahr0Chm6lY93D3CUBMLRzs5JB20V&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iM1Hy09tiG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zw1WDhK&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;y29YCW&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ANnVBG&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Bg9N&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_17.02.49.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What we have achieved is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String decoding logic is now contained into a single function - &lt;code&gt;decode()&lt;/code&gt;.
The code of this function could be regenerated with Babel and passed into
&lt;code&gt;Function.call()&lt;/code&gt; or Node&amp;rsquo;s &lt;a href=&#34;https://nodejs.org/api/vm.html&#34;&gt;&lt;code&gt;vm&lt;/code&gt; API&lt;/a&gt;
to get decoded values.&lt;/li&gt;
&lt;li&gt;We have explicit argument values to use with &lt;code&gt;decode()&lt;/code&gt; in the IIFE.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are now prepared to undo the string concealing. For the sake of
simplicity we will refrain from regenerating code for &lt;code&gt;decode()&lt;/code&gt; function
and will copy-paste it into AST Explorer&amp;rsquo;s transform pane instead. The
final transform is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x5d79db&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x4ad0a1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;decode&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decodedStr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arg&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;decodedStr&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;FunctionDeclaration&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;decode&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This yields a deobfuscated version of code that is very similar to what
we had in the initial snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.nike.com/cic/browse/v2?queryid=products&amp;amp;anonymousId=A7CA2A92E0F04E570767C7810552BBC5&amp;amp;country=au&amp;amp;endpoint=%2Fproduct_feed%2Frollup_threads%2Fv2%3Ffilter%3Dmarketplace(AU)%26filter%3Dlanguage(en-GB)%26filter%3DemployeePrice(true)%26filter%3DattributeIds(8529ff38-7de8-4f69-973c-9fdbfb102ed2%2C16633190-45e5-4830-a068-232ac7aea82c)%26anchor%3D24%26consumerChannelId%3Dd9a5bc42-4b9c-4976-858a-f159cf99c647%26count%3D24&amp;amp;language=en-GB&amp;amp;localizedRangeStr=%7BlowestPrice%7D%E2%80%94%7BhighestPrice%7D&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api.nike.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*/*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.nike.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.nike.com/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22Not_A\x20Brand\x22;v=\x2299\x22,\x20\x22Google\x20Chrome\x22;v=\x22109\x22,\x20\x22Chromium\x22;v=\x22109\x22&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;macOS\&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;empty&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-site&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/109.0.0.0\x20Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;json&amp;#34;&lt;/span&gt;]();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x307ab8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-11_17.35.49.png&#34;&gt;Screenshot 12&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We ended up not doing comprehensive reverse engineering to discover how
exactly the &lt;code&gt;decode()&lt;/code&gt; function works. Instead, we were able to side-step
that question by reusing a simplified version of string decoding code in our
AST transform, thus achieving the objective of undoing the string concealing.
Reversing the string decoding part could perhaps be a worthwhile exercise
in itself, but in this case it would be a needless distraction.&lt;/p&gt;
&lt;p&gt;Unifying all the AST transforms we did into a single script that Node.JS could
run is left as an exercise to the reader. Another exercise could be regenerating
the &lt;code&gt;decode()&lt;/code&gt; function from AST form, so that it would not need to be
copy-pasted into deobfuscator.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: constant folding and propagation</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-constant-folding-and-propagation/</link>
      <pubDate>Tue, 07 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-constant-folding-and-propagation/</guid>
      <description>&lt;p&gt;Introducing constant recomputation is a commonly used code obfuscation
technique. Let&amp;rsquo;s consider the following JavaScript snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The answer is:&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have one numeric constant (&lt;code&gt;fourtyTwo&lt;/code&gt;) and one string constant (&lt;code&gt;msg&lt;/code&gt;)
that are passed into &lt;code&gt;console.log()&lt;/code&gt;. Let us apply constant obfuscation by
using &lt;a href=&#34;https://obfuscator.io&#34;&gt;obfuscator.io&lt;/a&gt; with &amp;ldquo;Numbers To Expressions&amp;rdquo;
and &amp;ldquo;Split Strings&amp;rdquo; checkboxes being on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.33.39.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.34.55.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This yields the following obfuscated version of the above snippet that we put
into &lt;a href=&#34;https://astexplorer.net&#34;&gt;AST Explorer&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x25fb&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x3eb&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x28e&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;The\x20answer&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x20is:&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.43.50.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Instead of simple integer constant and string we got some expressions that
can be evaluated back into the original values, thus retaining the original
behaviour of the code.&lt;/p&gt;
&lt;p&gt;In the Babel AST we have the following. Each variable assignment is represented
by &lt;code&gt;VariableDeclarator&lt;/code&gt; object with stuff beyond the &lt;code&gt;=&lt;/code&gt; sign at &lt;code&gt;init&lt;/code&gt;
property. The &lt;code&gt;init&lt;/code&gt; property points to a multi-level tree made up of mostly
&lt;code&gt;BinaryExpression&lt;/code&gt; objects that represent a single arithmetic step (e.g.
adding two numbers or strings together). Number negation is represented
by &lt;code&gt;UnaryExpression&lt;/code&gt; nodes. Leaves of this subtree are &lt;code&gt;NumericLiteral&lt;/code&gt; and
&lt;code&gt;StringLiteral&lt;/code&gt; nodes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.44.54.png&#34;&gt;Screenshot 4&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.44.23.png&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.44.54.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Turning numbers into hexadecimal form is also done as a minor obfuscation
that we can easily reverse. The trick is to delete &lt;code&gt;.extra.raw&lt;/code&gt; in the
&lt;code&gt;NumericLiteral&lt;/code&gt; objects affected by this obfuscation. To accomplish this,
we write a quick AST transform in the AST Explorer tool:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;NumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0x&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;delete&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This cleans up the code a bit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9723&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1003&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;654&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;The\x20answer&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x20is:&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_15.57.11.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After this sidenote, we now want to use Babel to turn these expressions
back into original values. This can be done by doing a code transformation
known as constant folding. It is commonly performed by compilers during
code optimisation step. Since Babel is a bit like compiler, it can perform
it for us without the need of having to implement comprehensive JS expression
handling algorithms or calling &lt;code&gt;eval()&lt;/code&gt;. The key is to use &lt;code&gt;NodePath.evaluate()&lt;/code&gt;
method in the visitor function. It returns a JavaScript result object with
&lt;code&gt;confident&lt;/code&gt; flag to let us know if it was able to evaluate the expression
in a way that the result is conclusive (that is not always the case, as
expressions can be more complicated than what we have here and involve
variables that may change during runtime).&lt;/p&gt;
&lt;p&gt;Once again, we can prototype our deobfuscation trick in AST Explorer. A
simple example of constant folding would be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;BinaryExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;confident&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluated&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;valueToNode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;valueNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We target &lt;code&gt;BinaryExpression&lt;/code&gt; nodes with our visitor function and just call
the &lt;code&gt;evaluate()&lt;/code&gt; method on associated &lt;code&gt;NodePath&lt;/code&gt; object. If the result
passes some simple sanity checks (the aforementioned &lt;code&gt;confident&lt;/code&gt; flag and ability
to convert the resulting value into a &lt;code&gt;Literal&lt;/code&gt;) we replace the
&lt;code&gt;BinaryExpression&lt;/code&gt; node with newly generated &lt;code&gt;NumericLiteral&lt;/code&gt;/&lt;code&gt;StringLiteral&lt;/code&gt;
node containing the same value that JS interpreter would have to compute.&lt;/p&gt;
&lt;p&gt;The resulting deobfuscated code is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The answer is:&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;log&amp;#39;&lt;/span&gt;](&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_20.14.53.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So far, so good. But what if we wanted to take this a bit further? There&amp;rsquo;s
really no need to have two named constants that are only used in &lt;code&gt;console.log()&lt;/code&gt;
call, when we can just call it with the actual values. To replace references
to &lt;code&gt;msg&lt;/code&gt; and &lt;code&gt;fourtyTwo&lt;/code&gt; in the last statement we have to apply another
code optimisation technique - constant propagation. Let me show a quick AST
transform to demonstrate it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s put the following snippet into AST Explorer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The answer is:&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;fourtyTwo&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;floor&lt;/span&gt;(Date.&lt;span style=&#34;color:#a6e22e&#34;&gt;now&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since &lt;code&gt;t&lt;/code&gt; here is not a constant it should be left unmodified.&lt;/p&gt;
&lt;p&gt;Since code obfuscation solutions may introduce scope confusion by creating
multiple identifiers with same name in multiple lexical scopes, it&amp;rsquo;s insufficient
to substitute variable names into values based on name alone. We can use Babel
scopes and bindings to perform substitution in a smarter way. The transform
would be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;constant&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referencePaths&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;refPath&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We target the &lt;code&gt;VariableDeclarator&lt;/code&gt; nodes. In the visitor function we perform
some applicability checks. First we check if it declares a variable
with literal value by checking the &lt;code&gt;init&lt;/code&gt; field. Next, we get a scope binding
object and check the &lt;code&gt;constant&lt;/code&gt; Boolean property. If these checks pass, we
replace all the references to this variable with the literal node containing
the value. Lastly, we remove the constant declaration as we no longer need it
in the code. For more on scopes and bindings, see a
&lt;a href=&#34;https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-untangling-scope-confusion&#34;&gt;previous post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The modified snippet is now the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The answer is:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;floor&lt;/span&gt;(Date.&lt;span style=&#34;color:#a6e22e&#34;&gt;now&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-02-06_16.55.59.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One last thing&amp;hellip; In the above examples, a &lt;code&gt;const&lt;/code&gt; keyword was used when
declaring a constant, but it does not really matter for the purposes of this
kind of (de)obfuscation. All that matters is that a value is established once
and does not change later in the code.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: AST modification APIs</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-ast-modification-apis/</link>
      <pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-ast-modification-apis/</guid>
      <description>&lt;p&gt;In previous posts about using Babel for JavaScript deobfuscation, we have
used &lt;code&gt;NodePath.replaceWith()&lt;/code&gt; method to replace one node with another and
&lt;code&gt;NodePath.remove()&lt;/code&gt; to remove a single node. Since AST and it&amp;rsquo;s elements are
mutable, we can also modify the AST without traversing it. But there is more
to learn about AST modification than what we have seen before. We will go
through some more AST modification APIs that should prove to be useful
when developing JS deobfuscators.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NodePath.replaceWithMultiple()&lt;/code&gt; replaces a single node with a list of nodes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NodePath.replaceWithSourceString()&lt;/code&gt; is a convenience method that takes a source
string and parses it for you when replacing the node. However this is
inefficient and not recommended. There are better ways to convert a source
string (or template) into AST subtree.&lt;/p&gt;
&lt;p&gt;We can also insert a node before or after the current node.
&lt;code&gt;NodePath.insertBefore()&lt;/code&gt; and &lt;code&gt;NodePath.insertAfter()&lt;/code&gt; methods can be used for
that.&lt;/p&gt;
&lt;p&gt;Sometimes there are AST node property arrays that contain multiple child nodes.
You may want to to add a single new entry to beginning or end of that array.
&lt;code&gt;NodePath.unshiftContainer()&lt;/code&gt; prepends a new node and &lt;code&gt;NodePath.pushContainer()&lt;/code&gt;
appends it to the end of array. However it might be bit confusing on how to use
these methods. Consider the following snippet that we want to change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;say_abc&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;c&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;say_abc&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s suppose we want to add &lt;code&gt;debugger;&lt;/code&gt; before and after the &lt;code&gt;console.log()&lt;/code&gt;
calls in the function. When we parse this code into AST, we can see that we
have a following subtree:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FunctionDeclaration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;body&lt;/code&gt;: &lt;code&gt;BlockStatement&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;body&lt;/code&gt;: &lt;code&gt;[]&lt;/code&gt; (&lt;code&gt;ExpressionStatement&lt;/code&gt;s)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-29_19.14.55.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We want to modify the function body, so we will get a &lt;code&gt;NodePath&lt;/code&gt; for the
&lt;code&gt;BlockStatement&lt;/code&gt; at &lt;code&gt;body&lt;/code&gt; property of &lt;code&gt;FunctionDeclaration&lt;/code&gt;. The exact array
we want to modify is &lt;code&gt;body&lt;/code&gt; of &lt;code&gt;BlockStatement&lt;/code&gt;. Thus we call the above
methods on &lt;code&gt;BlockStatement&lt;/code&gt; object with two arguments: 1) node property name
(&lt;code&gt;body&lt;/code&gt;) and 2) the node object we want to insert. In AST Explorer we
develop the following transform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;FunctionDeclaration&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;unshiftContainer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;debuggerStatement&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pushContainer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;debuggerStatement&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As intended, this modifies the toy code snippet into the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;say_abc&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;debugger&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;c&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;debugger&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;say_abc&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-29_19.12.58.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-01-29_19.15.54.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If for some reason you want to skip traversal of children of current node
(e.g. to avoid infinite loop when nodes are being added) you can call
&lt;code&gt;NodePath.skip()&lt;/code&gt;. To completely stop the AST traversal, you can call
&lt;code&gt;NodePath.stop()&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: untangling scope confusion</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-untangling-scope-confusion/</link>
      <pubDate>Wed, 25 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-untangling-scope-confusion/</guid>
      <description>&lt;p&gt;In computer programming, a lexical scope of an identifier (name of function,
class, variable, constant, etc.) is area within the code where that identifier
can be used. Some identifiers have global scope meaning they can be used in the
entire program. Others have narrowed-down scope that is limited to one single
function or code block between curly braces. Identifier names can be reused
between scopes - conflict is resolved by preferring the innermost scope for a
given identifier. Lexical scopes are set in stone as the program is written.&lt;/p&gt;
&lt;p&gt;JavaScript also has some support for dynamic scoping that is resolved during
program runtime, but let us put that aside for time being.&lt;/p&gt;
&lt;p&gt;Introducing redundant lexical scopes with repeating identifiers is often done
for code obfuscation purposes, as it makes code hard to read and understand.
This motivates us to learn how to gain clarity when such scope obfuscation
technique is applied on client-side JavaScript code that we want to reverse
engineer.&lt;/p&gt;
&lt;p&gt;Since Babel parses the JS code into data model representing logical structure
of the code, it also has a capability to represent lexical scopes via &lt;code&gt;Scope&lt;/code&gt;
object. Furthermore, Babel is able to handle scope bindings - relations between
identifiers and scopes. Through a scope binding we can find the usage of
variable name or some other identifier to track down where in the code that
identifier is being referenced. Thus we have all the tools to programmatically
disambiguate between multiple identifiers across nested scopes and convert
the code into a more readable form.&lt;/p&gt;
&lt;p&gt;Let us use &lt;a href=&#34;https://astexplorer.net&#34;&gt;ASTExplorer&lt;/a&gt; with Chrome DevTools to
explore scopes and bindings. Put the following JS snippet in there:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-25_16.08.58.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can see that there is no data about scopes and bindings in the final AST
that Babel parser outputs. There&amp;rsquo;s a small trick to getting to that in
ASTExplorer. First, we need to turn on the tranform sections and make a small
AST visitor function (within provided boilerplate) that prints scopes/bindings
for the AST nodes we are interested in. Let us see the &lt;code&gt;Scope&lt;/code&gt; object for
the root AST node - &lt;code&gt;Program&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Program&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see what &lt;code&gt;console.log()&lt;/code&gt; prints, we must switch on Chrome DevTools panel
and select Console tab. Some properties or interest are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;block&lt;/code&gt; - AST node for code block (&lt;code&gt;BlockStatement&lt;/code&gt;) or something else (e.g.
&lt;code&gt;Program&lt;/code&gt;) that limits the scope.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bindings&lt;/code&gt; - mapping of identifier names to &lt;code&gt;Binding&lt;/code&gt; objects associated
with the current scope.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;globals&lt;/code&gt; - list of AST nodes that are associated with the global scope;
current scope inherits them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path&lt;/code&gt; - a &lt;code&gt;NodePath&lt;/code&gt; object that references back to the visited node and its
position within AST.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-25_16.18.57.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some interesting properties of &lt;code&gt;Binding&lt;/code&gt; object are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;identifier&lt;/code&gt; - an &lt;code&gt;Identifier&lt;/code&gt; node for the identifier in question (constant
name in this case)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path&lt;/code&gt; - a &lt;code&gt;NodePath&lt;/code&gt; object for AST node that introduces this identifier into
scope.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;referenced&lt;/code&gt; - flag to check if identifier has been referenced.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;references&lt;/code&gt; - number of references to identifier within the scope.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;referencePaths&lt;/code&gt; - an array of &lt;code&gt;NodePath&lt;/code&gt; objects that are associated with the
current identifier being referenced within the scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-25_16.18.57.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note that for &lt;code&gt;x&lt;/code&gt; we have 2 references and for &lt;code&gt;y&lt;/code&gt; we have only one reference.
That should be expected, as in the inner lexical scope there is new declaration
of &lt;code&gt;y&lt;/code&gt; that overrides the declaration of &lt;code&gt;y&lt;/code&gt; in the outer scope.&lt;/p&gt;
&lt;p&gt;These two constants that are both named &lt;code&gt;y&lt;/code&gt; require further attention. Let us
take a deeper look into their scopes and bindings. Update the visitor function
in the bottom left corner to the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PARENT&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are visiting &lt;code&gt;VariableDeclarator&lt;/code&gt; nodes, but we are only interested in the
ones that declare &lt;code&gt;y&lt;/code&gt;. For these, we log the current scope and parent scope.
In our example, parent scope is the scope for entire program and child scope
is within the curly braces.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-25_21.48.07.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To check for identifier name duplication, we can look into parent scope bindings
and see if there&amp;rsquo;s a binding for another identitifier with the same name that
is being overridden. To prevent identifiers from being overriden we can rename
the new identifiers in child scopes. Let me show an example of Babel transform
that would do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;VariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;idName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parentScope&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;parentScope&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;parentScope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;idName&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;generateUidIdentifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;idName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rename&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;idName&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;newName&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This transform modifies the snippet so that constant name duplication goes
away:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_y&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We used &lt;code&gt;Scope.rename()&lt;/code&gt; method to rename the constant within the scope
(covering both the declaration and statement that uses it) to a name that does
not duplicate with constant name in a parent scope. To save us from the hassle
of generating unique names, Babel provides us &lt;code&gt;Scope.generateUidIdentifier()&lt;/code&gt;
method that we used to get a new &lt;code&gt;Identifier&lt;/code&gt; node. Fixing name duplication
turned out to be just a few lines of code.&lt;/p&gt;
&lt;p&gt;Note that in some cases you may need to call &lt;code&gt;crawl()&lt;/code&gt; on current or parent
scope to make it update all the JS statements that reference the identifier
that has just been renamed.&lt;/p&gt;
&lt;p&gt;Let us get more serious now by applying DefendJS scope obfuscation feature:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cp 1.js 6.js  
$ defendjs --input 6.js --features scope --output . 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The obfuscated version of the code is now like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;]] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }).&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Applying the current Babel transform does rename some variables in the junk
functions, but code stays mostly the same:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;_c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;_b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_c2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;_c2&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_a2&lt;/span&gt;]] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_a2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_c2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }).&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_a3&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Can we remove these junk functions that do not get called anywhere?
Let us try reworking the transform.&lt;/p&gt;
&lt;p&gt;Remember that &lt;code&gt;Binding&lt;/code&gt; object also keeps track of references to the
identifier in question? We can remove these functions based on them not being
referenced anywhere:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;FunctionDeclaration&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fnName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fnName&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;referenced&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the code in the output is like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All but two junk functions got removed. A closer look into the remaining
functions reveals that they are referencing themselves. Furthermore, we ran
into a weird feature of JavaScript: &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var&#34;&gt;&lt;code&gt;var&lt;/code&gt; statements&lt;/a&gt;
that are able to transcend the child scope and set the variable in a more global
scope. For example, the following code snippet from Mozilla developer portal
counter-intuitively prints &lt;code&gt;2&lt;/code&gt; twice:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// Expected output: 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Expected output: 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the currently deobfuscated code &lt;code&gt;b()&lt;/code&gt; references itself and &lt;code&gt;c()&lt;/code&gt; references
&lt;code&gt;b()&lt;/code&gt; (and also itself) through &lt;code&gt;var&lt;/code&gt; statement.&lt;/p&gt;
&lt;p&gt;At the moment I can&amp;rsquo;t think of a solution to resolve this kind of circular
references, but we already made some progress. We can see that both remaining
functions are not called anywhere and don&amp;rsquo;t really do anything. Thus it is safe
to disregard them for time being.&lt;/p&gt;
&lt;p&gt;Note however that Babel does not track scopes across files. It can only work
with one file at a time.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: 3 ways to create nodes and subtrees</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-3-ways-to-create-nodes-and-subtrees/</link>
      <pubDate>Mon, 23 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-3-ways-to-create-nodes-and-subtrees/</guid>
      <description>&lt;p&gt;When doing Javascript deobfuscation work at AST level one will need to create
new parts of AST for replacing the existing parts of AST being processed. We
will go through 3 ways of doing that.&lt;/p&gt;
&lt;p&gt;For the sake of an example, let us try to build an AST for the following
JS snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;debugger&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;debugger&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Babel parses this into two &lt;code&gt;DebuggerStatement&lt;/code&gt; nodes and and one
&lt;code&gt;ExpressionStatement&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-23_14.41.51.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t need to worry about &lt;code&gt;File&lt;/code&gt; and &lt;code&gt;Program&lt;/code&gt; objects and should only focus
on the program body that contains the following child nodes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;DebuggerStatement&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExpressionStatement&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;expression&lt;/code&gt;: &lt;code&gt;CallExpression&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;callee&lt;/code&gt;: &lt;code&gt;MemberExpression&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;object&lt;/code&gt;: &lt;code&gt;Identifier&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;: &lt;code&gt;&amp;quot;console&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;computed&lt;/code&gt;: &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;property&lt;/code&gt;: &lt;code&gt;Identifier&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;: &lt;code&gt;&amp;quot;log&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arguments&lt;/code&gt;: &lt;code&gt;[]&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;StringLiteral&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;extra&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rawValue&lt;/code&gt;: &lt;code&gt;&amp;quot;!&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;: &lt;code&gt;&amp;quot;!&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DebuggerStatement&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once we build these three statements, we can replace some part of AST with
them. That will be shown as well.&lt;/p&gt;
&lt;h2 id=&#34;instantiate-node-objects-arrange-them-into-tree&#34;&gt;Instantiate node objects, arrange them into tree&lt;/h2&gt;
&lt;p&gt;In software development there is often a tradeoff to be made between simplicity
of the code and it&amp;rsquo;s flexibility. We will start with the least simple, but most
flexible approach - explicitly instantiating AST node objects and arranging
them in a way that recreates the AST subtree above.&lt;/p&gt;
&lt;p&gt;We need to refer to spec for Babel AST objects and their properties in Babel
codebase file &lt;a href=&#34;https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md&#34;&gt;packages/babel-parser/ast/spec.md&lt;/a&gt;.
Furthermore, we need to check node creation APIs in
&lt;a href=&#34;https://babeljs.io/docs/en/babel-types&#34;&gt;&lt;code&gt;@babel/types&lt;/code&gt; documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By using the information in these two documents, we can write the following
code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;debugStmt1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;debuggerStatement&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;consoleId&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;console&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;logId&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;identifier&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;memberExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;consoleId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;logId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpression&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;, [ &lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt; ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;exprStmt&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expressionStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callExpr&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;debugStmt2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;DebuggerStatement&amp;#39;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#a6e22e&#34;&gt;debugStmt1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;exprStmt&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;debugStmt2&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// Parsing empty string creates AST with File and Program nodes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;program&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;==================&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that we can build AST node objects either by using factory functions from
&lt;code&gt;@babel/types&lt;/code&gt; or by defining them as JS objects directly. Compare how
&lt;code&gt;debugStmt1&lt;/code&gt; and &lt;code&gt;debugStmt2&lt;/code&gt; were created.&lt;/p&gt;
&lt;p&gt;Running this Node.JS script prints the modified AST and the generated code:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node instantiate.js
Node {
  type: &amp;#39;File&amp;#39;,
  start: 0,
  end: 0,
  loc: SourceLocation {
    start: Position { line: 1, column: 0, index: 0 },
    end: Position { line: 1, column: 0, index: 0 },
    filename: undefined,
    identifierName: undefined
  },
  errors: [],
  program: Node {
    type: &amp;#39;Program&amp;#39;,
    start: 0,
    end: 0,
    loc: SourceLocation {
      start: [Position],
      end: [Position],
      filename: undefined,
      identifierName: undefined
    },
    sourceType: &amp;#39;script&amp;#39;,
    interpreter: null,
    body: [ [Object], [Object], [Object] ],
    directives: []
  },
  comments: []
}
==================
debugger;
console.log(&amp;#34;!&amp;#34;);
debugger;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thus we have demonstrated a very basic example of JS code generation with Babel.&lt;/p&gt;
&lt;h2 id=&#34;use-babel-template&#34;&gt;Use Babel template&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://babeljs.io/docs/en/babel-template&#34;&gt;&lt;code&gt;@babel/template&lt;/code&gt;&lt;/a&gt; module lets us
define a code template, parametrise it with AST nodes and create a subtree
from that.&lt;/p&gt;
&lt;p&gt;Let us generate the JS snippet using this approach:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/template&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;exampleTempl&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;debugger;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;console.log(%%msg%%);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;debugger;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;exampleTempl&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;msg&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;program&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;==================&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re getting the same output as from the previous script.&lt;/p&gt;
&lt;h2 id=&#34;parse-the-source-code&#34;&gt;Parse the source code&lt;/h2&gt;
&lt;p&gt;Sometimes you may want to add some code that does not depend on any context
is is fairly simple. Due to recursive structure of AST we can create a small
AST by parsing a JS snippet and plug it into where we want it to be in the
bigger tree.  This is as simple and basic as can get and I don&amp;rsquo;t think
it warrants any sample code. However we must not forget that in some cases
easy and simple way is the right way.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: transform prototyping and plugin development</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-transform-prototyping-and-plugin-development/</link>
      <pubDate>Wed, 18 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-transform-prototyping-and-plugin-development/</guid>
      <description>&lt;p&gt;In previous posts we have went through several JavaScript obfuscation techniques
and how they could be reversed by applying Abstract Syntax Tree transformations.
AST manipulation is a powerful skill that is of particular importance in
certain kinds of grayhat programming projects. Earlier, we have focused on how
exactly AST could be changed to undo specific kinds of obfuscations. This time,
however, we will take a bit broader look into Babel to see how it facilitates
development of AST manipulation code that goes beyond a single AST
transformation.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&#34;https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-the-first-steps&#34;&gt;one of the posts&lt;/a&gt;
we have shown some trivial examples of AST manipulation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Undoing hexadecimal string encoding&lt;/li&gt;
&lt;li&gt;Converting bracket notation to dot notation&lt;/li&gt;
&lt;li&gt;Removing excessive semi-colons from the code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, let me show a way to prototype AST transforms in
&lt;a href=&#34;https://astexplorer.net&#34;&gt;ASTExplorer&lt;/a&gt;. Let us put the following code in there:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\x41\x42\x43&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// ABC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;;;;;;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;123&amp;#34;&lt;/span&gt;);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure that JavaScript is chosen in the language drop-down menu and
&lt;code&gt;@babel/parser&lt;/code&gt; is selected in the parser drop-down. Press the &amp;ldquo;Transform&amp;rdquo;
switch and choose &lt;code&gt;babelv7&lt;/code&gt;. Now we have two new UI sections here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-17_19.56.13.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On the bottom left, there&amp;rsquo;s a transform code editing section. By default
ASTExplorer gives you the following example code that reverses the order
or characters in identifier names:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;reverse&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On the bottom right section we have the JS code that is the result of this
transformation.&lt;/p&gt;
&lt;p&gt;The current transform by itself is quite useless, but we can change it to our
needs. Note that there is no boilerplate code for reading the JS source code
and writing it back into files. Just the AST transform that is the core
of what we are doing here.&lt;/p&gt;
&lt;p&gt;Let us change the visitor function to undo hexadecimal string encoding:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;StringLiteral&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rawValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can see that &lt;code&gt;console[&amp;quot;log&amp;quot;](&amp;quot;\x41\x42\x43&amp;quot;);&lt;/code&gt; from the input indeed became
&lt;code&gt;console[&amp;quot;log&amp;quot;](&amp;quot;ABC&amp;quot;);&lt;/code&gt; in the output.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-17_20.20.35.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since other two changes we want to make work on different node classes, we
can extend the top &lt;code&gt;visitor&lt;/code&gt; object to include them as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ast-transform&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// not required
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;StringLiteral&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rawValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;computed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;EmptyStatement&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This cleaned up the code into the following form:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ABC&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// ABC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;123&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-17_20.24.51.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s nice, but our transform code is not particularly clean, as we are
performing three different kinds of changes in one go. It does not matter much
for simple example like this, but real-world deobfuscators will involve far
more complexity and should be written with one of the best programming practices
in mind: keeping stuff separate without letting it became a single intractable
mess of spaghetti code. Thus we want a better structure in our code.&lt;/p&gt;
&lt;p&gt;Babel is developed with plugin architecture in mind. Babel plugin is pluggable
piece of code that does a single change to the AST being processed. In our
example, there would be one plugin for undoing the hexadecimal string encoding,
another for converting bracket notation to do notation and third one for
removing &lt;code&gt;EmptyStatement&lt;/code&gt; nodes. There&amp;rsquo;s also a concept of preset - a
collection of plugins that make up some greater workflow, such as converting
the code between JavaScript dialects or undoing the entire set of obfuscation
techniques that antibot vendor is applying.&lt;/p&gt;
&lt;p&gt;Let us save the input code from AST Explorer into input.js and write a single
deobfuscator that benefits from Babel plugin architecture to a greater degree.&lt;/p&gt;
&lt;p&gt;Consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;transformFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input.js&amp;#39;&lt;/span&gt;) , {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;plugins&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo_hexadecimal_strings&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;StringLiteral&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rawValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bracket_to_dot&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;computed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rm_needless_semicolons&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;EmptyStatement&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;output.js&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our previous code in the previous posts, we had parsing, AST traversal,
code generation as separate steps, which is good for understanding what&amp;rsquo;s going
on in the code, but introduces a bit of repetitive boilerplate. Now we are using
&lt;code&gt;transformFileSync()&lt;/code&gt; function that does all three steps for us. It takes
a file path to input JS file and a list of plugins. Each plugin is a
named AST manipulation step that entails one or more visitor functions that are
associated with certain types of AST nodes.&lt;/p&gt;
&lt;p&gt;Asynchronous equivalent to this function would be &lt;code&gt;transformFile()&lt;/code&gt;. There&amp;rsquo;s
also &lt;code&gt;transform()&lt;/code&gt;, &lt;code&gt;transformSync()&lt;/code&gt; and &lt;code&gt;transformAsync()&lt;/code&gt; that take JS code
a string without reading it from the file.&lt;/p&gt;
&lt;p&gt;Plugin code can be quite long and complex, which makes us want to keep it in
one or more separate files. Babel allows that as well. Let us move the plugin
code out of the main script by creating new files for each plugin.&lt;/p&gt;
&lt;p&gt;First plugin file (plugin_unhex.js) would be as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undo_hexadecimal_strings&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;StringLiteral&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rawValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next file, plugin_unbracket.js would be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bracket_to_dot&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;computed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The third and last plugin file would contain the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rm_needless_semicolons&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;visitor&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;EmptyStatement&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By moving all three plugins to their own files, we can simplify the main script
to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/core&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;babel&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;transformFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;input.js&amp;#39;&lt;/span&gt;) , {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;plugins&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_unhex.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_unbracket.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;plugin_rm_empty.js&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;output.js&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that is far cleaner and less nested than it was before.&lt;/p&gt;
&lt;p&gt;Before we&amp;rsquo;re finished here, you may want to know one neat trick in ASTExplorer.
When you call &lt;code&gt;console.log()&lt;/code&gt; on a Babel object such as node, path, scope and
open Chrome DevTools you can inspect various properties to debug your
transformations.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-17_22.07.48.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To learn more about Babel plugin development, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md&#34;&gt;Babel plugin handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://babeljs.io/docs/en/babel-core&#34;&gt;&lt;code&gt;@babel/core&lt;/code&gt; documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Understanding Python generators for scraping and automation</title>
      <link>https://www.trickster.dev/post/understanding-python-generators-for-scraping-and-automation/</link>
      <pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-python-generators-for-scraping-and-automation/</guid>
      <description>&lt;p&gt;On the &lt;a href=&#34;https://scrapy.org/&#34;&gt;front page of Scrapy framework&lt;/a&gt; there&amp;rsquo;s the
following Python snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BlogSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;blogspider&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zyte.com/blog/&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; title &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.oxy-post-title&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;: title&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;::text&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; next_page &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a.next&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(next_page, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the usage of &lt;code&gt;yield&lt;/code&gt; keyword in this code. This is different from simply
returning some value from method or function. One way to look at this is that
&lt;code&gt;yield&lt;/code&gt; is a lazy equivalent of returning a value. All the code that precedes
the &lt;code&gt;yield&lt;/code&gt; keyword is only executed when the value is needed by the caller.
But there&amp;rsquo;s more to it. To really understand what&amp;rsquo;s going on here, we need
to understand Python generators. To understand Python generators, we need
to understand iterators and to understand iterators we need to understand
iterables.&lt;/p&gt;
&lt;p&gt;So, what&amp;rsquo;s an iterable? Iterable is a collection or object that you can
iterate upon in a loop, like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; l = [1, 2, 3]
&amp;gt;&amp;gt;&amp;gt; for x in l:
...     print(x)
... 
1
2
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Common iterables in Python are not just lists, but sequence-like things such
as sets, tuples, strings:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; s = set(l)
&amp;gt;&amp;gt;&amp;gt; for x in s:
...     print(x)
... 
1
2
3
&amp;gt;&amp;gt;&amp;gt; t = tuple(l)
&amp;gt;&amp;gt;&amp;gt; for x in t:
...     print(x)
... 
1
2
3
&amp;gt;&amp;gt;&amp;gt; for c in &amp;#34;abcd&amp;#34;:
...     print(c)
... 
a
b
c
d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When Python runs a for-loop across an iterable, an iterator is implicitly
created and used. Iterator is an object that is similar to database cursor.
It is used to keep track of state (e.g. collection index) across iteration
steps. Iterator is required to implement &lt;code&gt;__iter()__&lt;/code&gt; method to set the initial state
and &lt;code&gt;__next__()&lt;/code&gt; method to return the current element from iterable, then update
the state to point to the next one. Iterators can also be created explicitly with
an &lt;code&gt;iter()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; it = iter(l)
&amp;gt;&amp;gt;&amp;gt; type(it)
&amp;lt;class &amp;#39;list_iterator&amp;#39;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using them is simple - just replace an iterable reference with iterator
reference in your for-loop:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for x in it:
...     print(x)
... 
1
2
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once there&amp;rsquo;s nothing else to iterate, the &lt;code&gt;__next__()&lt;/code&gt; method will raise
a &lt;code&gt;StopIteration&lt;/code&gt; exception to break the loop.&lt;/p&gt;
&lt;p&gt;Sometimes you don&amp;rsquo;t want to use the iterator in for-loop, but just get a single
value from it. This can be done with &lt;code&gt;next()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; it = iter(&amp;#34;abcd&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; print(next(it))
a
&amp;gt;&amp;gt;&amp;gt; print(next(it))
b
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, what&amp;rsquo;s a generator? Generator is a special kind of iterator that does
not go through some iterable, but lazily creates each value when it is needed.
One way a generator can be created is using a generator expression:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; g = (i*i for i in range(10))
&amp;gt;&amp;gt;&amp;gt; type(g)
&amp;lt;class &amp;#39;generator&amp;#39;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; next(g)
0
&amp;gt;&amp;gt;&amp;gt; next(g)
1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another way to create a generator is the one typically used in Scrapy projects -
developing a generator function (or method) that uses the &lt;code&gt;yield&lt;/code&gt; keyword
to lazily return stuff to the caller:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; def gen_123():
...     print(&amp;#34;About to yield 1&amp;#34;)
...     yield 1
...     print(&amp;#34;About to yield 2&amp;#34;)
...     yield 2
...     print(&amp;#34;About to yield 3&amp;#34;)
...     yield 3
... 
&amp;gt;&amp;gt;&amp;gt; g = gen_123()
&amp;gt;&amp;gt;&amp;gt; type(g)
&amp;lt;class &amp;#39;generator&amp;#39;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; next(g)
About to yield 1
1
&amp;gt;&amp;gt;&amp;gt; next(g)
About to yield 2
2
&amp;gt;&amp;gt;&amp;gt; next(g)
About to yield 3
3
&amp;gt;&amp;gt;&amp;gt; next(g)
Traceback (most recent call last):
  File &amp;#34;&amp;lt;stdin&amp;gt;&amp;#34;, line 1, in &amp;lt;module&amp;gt;
StopIteration
&amp;gt;&amp;gt;&amp;gt; g = gen_123()
&amp;gt;&amp;gt;&amp;gt; for x in g:
...     print(x)
... 
About to yield 1
1
About to yield 2
2
About to yield 3
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Generator functions/methods can be quite useful in web scraper and bot
development as they enable us to avoid gathering scraped
results in a collection, which is undesirable from performance perspective.
By using generators, we can structure our code as pipeline, with each datum
going through the processing steps as soon as it is ready. Generator functions
can be stateful - all the variables in the function retain their values between
the moments values are generated.&lt;/p&gt;
&lt;p&gt;Consider the following example.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urljoin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;points&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;discussion_url&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_page&lt;/span&gt;(i):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://news.ycombinator.com/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;p&amp;#34;&lt;/span&gt;: i}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; athing_row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[@class=&amp;#34;athing&amp;#34;]&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; athing_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.//span[@class=&amp;#34;titleline&amp;#34;]/a/text()&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; athing_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.//span[@class=&amp;#34;titleline&amp;#34;]/a/@href&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        points &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; athing_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./following-sibling::tr//span[@class=&amp;#34;score&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; points&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        discussion_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; athing_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./following-sibling::tr//span[@class=&amp;#34;age&amp;#34;]/a/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        discussion_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, discussion_url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: title,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;points&amp;#34;&lt;/span&gt;: points,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;discussion_url&amp;#34;&lt;/span&gt;: discussion_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape&lt;/span&gt;(n_pages):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, n_pages &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield from&lt;/span&gt; scrape_page(i)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hn_posts.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we scrape the first three pages of Hacker News. For each posted link
we get an URL, title, number of points and link to discussion page. All of that
is being written to CSV file - one row at a time. Using generator functions we
can refrain from wasting resources putting each row into a list or set and
then iterating across that collection when writing it out into a file.
Furthermore, this is more desirable from error handling perspective, as we would
not lose all the data if the script crashed (error handling has been omitted for
the sake of brevity).&lt;/p&gt;
&lt;p&gt;Both &lt;code&gt;scrape_page()&lt;/code&gt; and &lt;code&gt;scrape()&lt;/code&gt; are generator functions. &lt;code&gt;scrape_page()&lt;/code&gt;
scrapes an &lt;code&gt;i&lt;/code&gt;-th page from Hacker News and generates item dictionaries one at
a time. If we just wanted a single page worth of data, we could just iterate
over it on the caller side and get each row when it was generated. However
we have &lt;code&gt;scrape()&lt;/code&gt; function in the middle that demonstrates iterator chaining
with a fairly new Python feature - &lt;code&gt;yield from&lt;/code&gt;. In this case, &lt;code&gt;yield from&lt;/code&gt;
is only used to avoid doing it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, n_pages &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape_page(i):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More generally, &lt;code&gt;yield from&lt;/code&gt; establishes a connection between caller and
sub-generator.&lt;/p&gt;
&lt;p&gt;In Scrapy framework generators are used in conjunction with asynchronuous
networking. Your Scrapy spider callbacks contain code that is lazily evaluated
exactly when needed and use &lt;code&gt;yield&lt;/code&gt; keyword to generate a request or data
item both when it is ready and when the underlying machinery of Scrapy is able
to process it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping Yahoo! Finance with Python</title>
      <link>https://www.trickster.dev/post/scraping-yahoo-finance-with-python/</link>
      <pubDate>Sat, 14 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-yahoo-finance-with-python/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://finance.yahoo.com/&#34;&gt;Yahoo! Finance&lt;/a&gt; is a prominent website featuring
financial news, press releases, financial reports and quantitive data on various
assets. We are going to go through some examples on how data could be scraped
from this portal. We are going to scrape two kinds of data: fundamental
infomation on how well various public companies are performing financially
and stock price time series.&lt;/p&gt;
&lt;p&gt;But first we need to have a list of stock tickers (symbols) for companies we
want to gather information on. We could write a simple text file with one ticker
per line, but it&amp;rsquo;s not difficult to apply a little technical trick here. If we
go to &lt;a href=&#34;https://finance.yahoo.com/most-active&#34;&gt;Most Actives&lt;/a&gt; page, we get a table
listing some well performing stocks. For the purpose of further development,
we want the first 25 of them. A little poking around reveals that there&amp;rsquo;s a
private API servicing the table and heat map here (load the page, press the
&amp;ldquo;Heatmap View&amp;rdquo; element, see what appears in DevTools Network tab with XHR
filter being enabled).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_14.53.56.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Data on the first 25 entries in the list can be retrieved with just a HTTPS
GET request:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;https://query1.finance.yahoo.com/v1/finance/screener/predefined/saved?formatted=true&amp;amp;lang=en-US&amp;amp;region=US&amp;amp;scrIds=most_actives&amp;amp;start=0&amp;amp;count=250&amp;amp;enableSectorIndustryLabelFix=true&amp;amp;sortField=intradaymarketcap&amp;amp;sortType=DESC&amp;amp;corsDomain=finance.yahoo.com&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&amp;rsquo;s just like that. No need to worry about headers or cookies or proxies.&lt;/p&gt;
&lt;p&gt;Now we can use &lt;a href=&#34;https://stedolan.github.io/jq/&#34;&gt;jq&lt;/a&gt; to parse the JSON we are
receiving from this API:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -s &amp;#34;https://query1.finance.yahoo.com/v1/finance/screener/predefined/saved?formatted=true&amp;amp;lang=en-US&amp;amp;region=US&amp;amp;scrIds=most_actives&amp;amp;start=0&amp;amp;count=25&amp;amp;enableSectorIndustryLabelFix=true&amp;amp;corsDomain=finance.yahoo.com&amp;#34; |\
 jq -r &amp;#34;.finance.result[0].quotes[].symbol&amp;#34; &amp;gt; tickers.txt
$ wc -l tickers.txt
      25 tickers.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just a single shell command got us the 25 tickers to play with.&lt;/p&gt;
&lt;p&gt;Now let us explore the page on Yahoo! Finance for each stock, so that we could
scrape the summary of fundamentals: trade volume, market cap, etc. One example
is &lt;a href=&#34;https://finance.yahoo.com/quote/GOOGL?p=GOOGL&#34;&gt;Alphabet Inc.&lt;/a&gt; page. Here
we have a table with various numbers we want to scrape in the summary tab and
a simple chart with price over time. We will scrape the former soon and get to
the latter later.&lt;/p&gt;
&lt;p&gt;How does the DOM structure look like in the summary table? Let&amp;rsquo;s select one of
the labels in the table, right-click on it and choose Inspect. We get the
Chrome DevTools panel with this element being highlighted in the Elements tab.
Now we are looking for some trait that uniquely identifies each part of the
page that we want to scrape. &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; element next to the one we selected
has &lt;code&gt;data-test&lt;/code&gt; attribute with value &lt;code&gt;PREV_CLOSE-value&lt;/code&gt;. This looks promising.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_15.14.33.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-01-14_15.14.51.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Could it be that other value fields also have &lt;code&gt;data-test&lt;/code&gt; attribute that we
can use for finding them in a DOM? Indeed they do: it&amp;rsquo;s &lt;code&gt;MARKET_CAP-value&lt;/code&gt;
for market cap, &lt;code&gt;BID-value&lt;/code&gt; for bid, &lt;code&gt;ASK-value&lt;/code&gt; for asking price and so on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_15.22.11.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s one last thing we should check. The DOM we see now in DevTools
Elements tab is the one that may or may not be a result of client-side
rendering. Since we are going to use plain requests for scraping, we want to
see if the the same &lt;code&gt;data-test&lt;/code&gt; attribute values appear in the HTML document
that browser downloads before the rendering. We can verify that by using
View Source feature.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_15.25.19.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Given what we have found, writing XPath queries for the fields is quite
trivial:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; from lxml import html
&amp;gt;&amp;gt;&amp;gt; resp = requests.get(&amp;#34;https://finance.yahoo.com/quote/GOOGL&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200]&amp;gt;
&amp;gt;&amp;gt;&amp;gt; tree = html.fromstring(resp.text)
&amp;gt;&amp;gt;&amp;gt; tree.xpath(&amp;#39;//td[@data-test=&amp;#34;EARNINGS_DATE-value&amp;#34;]/span/text()&amp;#39;)
[&amp;#39;Feb 02, 2023&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; tree.xpath(&amp;#39;//td[@data-test=&amp;#34;PREV_CLOSE-value&amp;#34;]/text()&amp;#39;)
[&amp;#39;91.13&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Getting a page URL given the stock symbol is easy: just append it to
&lt;code&gt;https://finance.yahoo.com/quote/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The entire Python script that reads tickers from a text file and scrapes the
summary table for each of them would be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ticker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;prev_close&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bid&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ask&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;days_range&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;52_wk_range&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;avg_volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;market_cap&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pe&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eps&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;earnings_date&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fw_dividend_and_yield&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ex_dividend_date&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1y_target_est&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_summary&lt;/span&gt;(ticker):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://finance.yahoo.com/quote/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ticker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    xpath_by_field &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;prev_close&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;PREV_CLOSE-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;OPEN-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;BID-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ask&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;ASK-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;days_range&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;DAYS_RANGE-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;52_wk_range&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;FIFTY_TWO_WK_RANGE-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;TD_VOLUME-value&amp;#34;]/fin-streamer/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;avg_volume&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;AVERAGE_VOLUME_3MONTH-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;market_cap&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;MARKET_CAP-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;BETA_5Y-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pe&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;PE_RATIO-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eps&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;EPS_RATIO-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;earnings_date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;EARNINGS_DATE-value&amp;#34;]/span/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fw_dividend_and_yield&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;DIVIDEND_AND_YIELD-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ex_dividend_date&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;EX_DIVIDEND_DATE-value&amp;#34;]//text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1y_target_est&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@data-test=&amp;#34;ONE_YEAR_TARGET_PRICE-value&amp;#34;]/text()&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ticker&amp;#34;&lt;/span&gt;: ticker, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; xpath_by_field&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;keys():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[f] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(xpath_by_field[f])[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tickers.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tickers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;summary.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; ticker &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tickers:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrape_summary(ticker)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When launching this, we get some stock summaries saved into summary.csv file.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_16.52.20.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now let us explore how that little stock price chart is getting data. Switch
on the DevTools and go to Network tab. Press XHR button so that it shows
only the requests that are launched by client-side JS code. Then press some
of the time range switches on top of the chart and see what happens.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2023-01-14_16.50.53.png&#34;&gt;Screenshot 7&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2023-01-14_17.01.31.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see that there&amp;rsquo;s an API call that fetches the the price data during time
window that the user chose. A list of valid ranges is included into
API response as well.&lt;/p&gt;
&lt;p&gt;We would like to scrape all the data that is needed to recreate a candle chart.
Each candlestick in the chart represents the following price number for a
day, hour, minute or some other interval:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open Price - price of asset at the beginning of interval.&lt;/li&gt;
&lt;li&gt;Close Price - price at the end of interval.&lt;/li&gt;
&lt;li&gt;Low Price - lowest price during the interval.&lt;/li&gt;
&lt;li&gt;High Price - highest price during the interval.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information, see &lt;a href=&#34;https://www.investopedia.com/trading/candlestick-charting-what-is-it/&#34;&gt;this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is indeed what the private Yahoo! Finance API gives us. Note that we get
many intervals across the big range that was selected on the chart. Both
candle interval and overall range can be chosen by setting the appropriate
parameters.&lt;/p&gt;
&lt;p&gt;Since this is fairly straightforward API scraping, our code is simple as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;close&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;high&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;low&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;symbol&amp;gt; &amp;lt;interval&amp;gt; &amp;lt;range&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    symbol &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    interval &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    range_ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; range_ &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1d&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5d&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1mo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3mo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;6mo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1y&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2y&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5y&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;10y&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ytd&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;max&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error: Invalid range&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;US&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lang&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-US&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;includePrePost&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;interval&amp;#34;&lt;/span&gt;: interval,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;useYfid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;range&amp;#34;&lt;/span&gt;: range_,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;corsDomain&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;finance.yahoo.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.tsrc&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;finance&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://query1.finance.yahoo.com/v8/finance/chart/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; symbol
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User-Agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chart&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    timestamps &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    quote_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;indicators&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;quote&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    open_prices &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; quote_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    close_prices &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; quote_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;close&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    high_prices &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; quote_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;high&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    low_prices &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; quote_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;low&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(symbol &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(len(timestamps)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;: symbol,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: timestamps[i],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;: open_prices[i],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;close&amp;#34;&lt;/span&gt;: close_prices[i],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;high&amp;#34;&lt;/span&gt;: high_prices[i],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;low&amp;#34;&lt;/span&gt;: low_prices[i],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We went through a couple of simple Yahoo! Finance scraping examples, but there&amp;rsquo;s
far more data that could be scraped from there. One could scrape financial
news articles to do market sentiment analysis on them, further quantitive
data from Statistic, Historical Data, Financials and other tabs of stock
page. For some markets, real time data is available via web socket.&lt;/p&gt;
&lt;p&gt;Furthermore, it is possible to do pure API scraping by tapping into Yahoo!
Finance mobile app API communications and reproducing them programmatically.
This would let us avoid doing any HTML parsing with XPath queries, like we
avoided in our time series example.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Vim is touch-typing on steroids</title>
      <link>https://www.trickster.dev/post/vim-is-touch-typing-on-steroids/</link>
      <pubDate>Thu, 12 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/vim-is-touch-typing-on-steroids/</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t suppose I need to do much explaining on the value of touch typing for developer
productivity. Today developers gain additional productivity by using Integrated Development
Environments such as PyCharm, Xcode, Visual Studio that provide features like auto-completion,
enhanced code navigation, integration with compilers, debuggers and static analysers. All of that
does not come for free. Running heavy GUI environment can be resource intensive. For example,
Android Studio is quite infamous for making laptop fans spin like crazy. Furthermore, big
GUI apps can potentially have quite a few bugs due to their size and complexity. Before
Android Studio became an established environment to develop Android apps it was not uncommon
for Android app developers to waste a lot of time fighting the bloated, unstable mess that is (was?)
Eclipse IDE. Lastly, large GUI apps can be pain to run in server environment. The general,
philosophical answer to all of these issues is to seek minimalism in your tooling to the
extent it is possible. It may not be very feasible to develop an iOS or Android app with no
GUI by merely using CLI tools, but if you&amp;rsquo;re the intended reader of this blog you can easily
do web scraper development by writing and running all your code in terminal environment
without any GUI tools other than web browser.&lt;/p&gt;
&lt;p&gt;The contents of this post are largely based on &lt;a href=&#34;https://learning.oreilly.com/videos/mastering-vim/9781491908334/&#34;&gt;Mastering Vim&lt;/a&gt;
course by &lt;a href=&#34;https://en.wikipedia.org/wiki/Damian_Conway&#34;&gt;Damian Conway&lt;/a&gt;, a prominent open source
figure. As I watched the course, I took notes that hopefully will be useful to other developers
as well. Do not be turned off by the oldness of source material here. Even though the course
was released back in 2014 the Vim editor is one of the timeless classics in the realm of Unix
tooling. Many things one could learn about Vim are still applicable today and will most likely
remain applicable in year 2034.&lt;/p&gt;
&lt;p&gt;Despite being primarily a Text User Interface (TUI) program, Vim is quite complex and has
literally thousands of commands. One does not have to know all of them to be a productive Vim
user. Furthermore, Vim has an extensive ecosystem of plugins, to the extent that Vim can be
extended almost into quite complex IDE. We will not be getting much into
that, as the current objective is to learn the common denominator of Vim usage and develop
a skill in using Vim in it&amp;rsquo;s pure form, so that if you connect via SSH into some remote Linux/Unix
server for the first time you would know exactly how to do text editing there.&lt;/p&gt;
&lt;p&gt;There are three primary modes in Vim. In normal (command) mode Vim accepts commands. In insert
mode it let&amp;rsquo;s you type and edit text. In visual mode it allows the user to visually highlight
text and do some bulk editing operations.&lt;/p&gt;
&lt;p&gt;So let us get started. As you launch Vim, you will be in normal mode by default, until you switch
to some other mode. The following things are applicable in the normal mode.&lt;/p&gt;
&lt;h2 id=&#34;normal-mode-commands&#34;&gt;Normal mode commands&lt;/h2&gt;
&lt;p&gt;To access Vim documentation system, type &lt;code&gt;:help&lt;/code&gt;. This will let you browse the docs. To search
the docs, you can use &lt;code&gt;:help &amp;lt;keyword&amp;gt;&lt;/code&gt;. If you&amp;rsquo;re unsure of the keyword, you can type some
of the keyword and use Tab key for a list of possible completions. Furthermore, one
can search the documentation by regular expression with &lt;code&gt;:helpgrep&lt;/code&gt; command, e.g.
&lt;code&gt;:helpgrep lookahead&lt;/code&gt; - no slashes needed. Use &lt;code&gt;:cnext&lt;/code&gt; and &lt;code&gt;:cprev&lt;/code&gt; commands to go
next and previous search result.  If you want to skip ahead to next file, use &lt;code&gt;:cnfile&lt;/code&gt; command.
To go to previous file, use &lt;code&gt;:cpfile&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;More generally, one can use &lt;code&gt;:vimgrep&lt;/code&gt; command to search for regex in files other than Vim docs,
such as your codebase. It takes a regex (with slashes this time) and one or more file names.&lt;/p&gt;
&lt;p&gt;When using Vim help system, you will find that some words or phrases are colored differently
and/or delineated by pipe characters. These are hyperlinks that can be traversed by positioning
your cursor within the bars and pressing Ctrl-]. To go back one level, one can use Ctrl-T.
To get out of help system, one can use &lt;code&gt;:q&lt;/code&gt; command or type &lt;code&gt;ZZ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One can save a lot of time and keystrokes by learning how to navigate text efficiently. One
can navigate text in normal mode one character at a time by using arrow keys or &lt;code&gt;h&lt;/code&gt; for left,
&lt;code&gt;l&lt;/code&gt; for right, &lt;code&gt;k&lt;/code&gt; for up and &lt;code&gt;l&lt;/code&gt; for down. Furthermore, there are ways to perform higher-order
motions across words, lines and greater &amp;ldquo;text objects&amp;rdquo;. In some systems text can also be navigated
vertically by mouse wheel or touch pad gestures.&lt;/p&gt;
&lt;p&gt;To find out where you are in the file one can use Ctrl-G. This will give you something like the
following line at the bottom of your Vim UI:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;vim-is-touch-typing-on-steroids.md&amp;#34; [Modified] line 64 of 66 --96%-- col 58
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have file name, file state (modified with unsaved changes), current line, number of total lines,
number of current column and what percentage the current line number is from the total. Furthermore,
you can put the following line in your .vimrc file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set ruler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will enable slightly simpler position indication with just line and column numbers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;w&lt;/code&gt; moves to start of next word, &lt;code&gt;b&lt;/code&gt; - to start of previous word, &lt;code&gt;e&lt;/code&gt; - to end of next word
and &lt;code&gt;ge&lt;/code&gt; to end of previous word. That accelerates your text navigation as you can use single
keystroke to move across word, not just a character. But what is a word? In Vim terminology,
word is any sequence of characters delimited by non-identifier chars (e.g. whitespace, comma).
However, there&amp;rsquo;s also capital-W Words in Vim that are delimited by whitespace. So we also
have &lt;code&gt;W&lt;/code&gt; command that moves to next capital-W Word and also &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;E&lt;/code&gt; and &lt;code&gt;gE&lt;/code&gt; commands that
perform equivalents to aforementioned word-level motions.&lt;/p&gt;
&lt;p&gt;Furthemore, there are line and paragraph motion commands. &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt; takes you to the start
of current line, &lt;code&gt;^&lt;/code&gt; to start of first word on current line, &lt;code&gt;$&lt;/code&gt; to end of the current line,
Enter to start of next line and &lt;code&gt;-&lt;/code&gt; to start of previous line. One can also use &lt;code&gt;{&lt;/code&gt; to move to
start of current paragraph (a continuous cluster of non-empty lines) and &lt;code&gt;}&lt;/code&gt; to end of current
paragraph. This will position the cursor to the empty line just before or just after a paragraph.
If these commands are used on empty lines Vim will move the cursor accordingly to previous or next
empty line.&lt;/p&gt;
&lt;p&gt;To move to the top of the buffer, one can use &lt;code&gt;gg&lt;/code&gt;. To move to the end - &lt;code&gt;G&lt;/code&gt;. To move to n-th
line, type the line number in normal mode and go &lt;code&gt;G&lt;/code&gt; or &lt;code&gt;gg&lt;/code&gt;. To move to a certain percentage
across the buffer, one can type number and percent sign. For example, &lt;code&gt;50%&lt;/code&gt; will move your
cursor halfway through the buffer. However if you just use single &lt;code&gt;%&lt;/code&gt; command it will bounce
you between some matching brackets (&lt;code&gt;{ ... }&lt;/code&gt;, &lt;code&gt;( ... )&lt;/code&gt;, &lt;code&gt;[ ... ]&lt;/code&gt;) in your code.&lt;/p&gt;
&lt;p&gt;One can also type a number in normal mode before a motion to do that motion a given number of
times. &lt;code&gt;lllll&lt;/code&gt; can be done faster as &lt;code&gt;5l&lt;/code&gt; and so on. This works on all motions commands and
many oter commands as well.&lt;/p&gt;
&lt;p&gt;To find the next instance of word under cursor, type &lt;code&gt;\*&lt;/code&gt;. For previous instance, type &lt;code&gt;#&lt;/code&gt;.
You can use &lt;code&gt;:set ignorecase&lt;/code&gt; command to make Vim search in case-insensitive manner. After
that is on, you can do &lt;code&gt;:set smartcase&lt;/code&gt; command which will let you search in case-sensitive
way on as-needed basis. For example &lt;code&gt;/selector&lt;/code&gt; would search in case-insensitive way, but
&lt;code&gt;/Selector&lt;/code&gt; would be a case-sensitive search.&lt;/p&gt;
&lt;p&gt;You can turn incremental search by launching command &lt;code&gt;:set incsearch&lt;/code&gt;. It searches text as you
type it and highlights it for you (but it only moves the cursor when you hit Enter; you can
hit Escape to back off from the search).&lt;/p&gt;
&lt;p&gt;To make Vim highlight all the search results within the current buffer one can turn on
search highlighting by doing &lt;code&gt;:set hlsearch&lt;/code&gt;. To get rid of the highlighting afterwards
one can do a &lt;code&gt;:nohlsearch&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;We can also run a command like &lt;code&gt;:s/&amp;lt;pattern&amp;gt;/&amp;lt;replacement&amp;gt;/&lt;/code&gt; to find a pattern (could be
regular expression) and replace it with another pattern within the current line. There
are some variations to this command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:10,33s/from/to/&lt;/code&gt; - apply search and substitution across a specific range of lines (e.g.
from 10th to 33rd line).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:1s/from/to/&lt;/code&gt; - do it on a specific line with given line number&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10,+33s/from/to/&lt;/code&gt; - line range can also be relative to the current line number&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10;+33s/from/to/&lt;/code&gt; - if you use semicolon between numbers, the second number will be
relative not to the current line number, but to the first line number in the range&lt;/li&gt;
&lt;li&gt;Metacharacters can also be used in ranges: &lt;code&gt;.&lt;/code&gt; means first line, &lt;code&gt;$&lt;/code&gt; means the last one
and so forth.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:%s/from/to/&lt;/code&gt; (or &lt;code&gt;:1,$s/from/to&lt;/code&gt;) does the substitution across the entire file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use patterns to specify ranges based on text content. For example, &lt;code&gt;/&amp;lt;pat&amp;gt;&lt;/code&gt;/
in the beginning of command tells Vim to do substitution on next line that matches the given
pattern. &lt;code&gt;?&amp;lt;pat&amp;gt;&lt;/code&gt; tells it to work on only the previous line that matches a pattern. This
can be combined to narrow down the substitution only to specific part of source code in the
file being edited (e.g. HTML body). This also works with offsets. For example:
&lt;code&gt;?foo?+1,$-10s/&amp;lt;pattern&amp;gt;/&amp;lt;replacement&amp;gt;/&lt;/code&gt; does the subsitutions from the line after previous
instance of &lt;code&gt;foo&lt;/code&gt; to 10 lines before end of file.&lt;/p&gt;
&lt;p&gt;The above commands will only do the substitution once, but typically you want it done multiple
times (e.g. across the entire file). To achieve this, append the &lt;code&gt;/g&lt;/code&gt; (global) modifier, e.g.
&lt;code&gt;s/from/to/g&lt;/code&gt;. If you&amp;rsquo;re unsure about replacing stuff all the time, you may want to use &lt;code&gt;/c&lt;/code&gt;
modifier, e.g. &lt;code&gt;%s/cat/feline/gc&lt;/code&gt;. This will let you use an interactive flow of approving or
rejecting every substitution.&lt;/p&gt;
&lt;p&gt;To repeat the last substitution one more time, you can press &lt;code&gt;&amp;amp;&lt;/code&gt; or &lt;code&gt;:s&lt;/code&gt; and Enter. To repeat
it globally, use &lt;code&gt;%s&amp;lt;Enter&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Beyond search-and-replace, many Vim commands also take ranges and support the above range syntax.
Thus it is valuable to learn them in order to be productive with Vim.&lt;/p&gt;
&lt;p&gt;Sometimes you want to match lines according to some regex or pattern across the entire file,
but want to do something else than replacing text. This can be done with &lt;code&gt;:global&lt;/code&gt; (or &lt;code&gt;:g&lt;/code&gt;)
command. It lets you match some lines according to a pattern and run another colon command
across the matching lines. Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:g /^\s/ :center&lt;/code&gt; - centering only the lines that are already indented (start with a space
character).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:g! /^\s/ :center&lt;/code&gt; - center only lines that are NOT indented.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:g /&amp;lt;ISBN&amp;gt;/ :normal gUU&lt;/code&gt; - run a normal mode command (convert all lines containing &lt;code&gt;&amp;lt;ISBN&amp;gt;&lt;/code&gt;
to upper-case).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:g /./ :.;/^$/join&lt;/code&gt; - convert all paragraphs into a single line to make the text less messy
if it was copy-pasted in MS Word or something. Note that we use a range &lt;code&gt;.;/^$/&lt;/code&gt; in a colon
command being invoked:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; - match non-empty line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;;&lt;/code&gt; - make the second part of the range relative to first one&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/^$/&lt;/code&gt; - regex to match empty line, closing the range to only have non-empty lines from
the start of paragraph.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:g /./ :.;/^\$/-1join&lt;/code&gt; - same as above, but leaves the empty lines for readability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is quite powerful and can support various advanced text editing use cases.&lt;/p&gt;
&lt;p&gt;Yanking is Vim term for copying some text and putting means pasting the text. &lt;code&gt;yy&lt;/code&gt; or &lt;code&gt;Y&lt;/code&gt;
yanks the current line. &lt;code&gt;yw&lt;/code&gt; and &lt;code&gt;y}&lt;/code&gt; yanks a word and paragraph respectively (from the
current position). More generally, &lt;code&gt;y&lt;/code&gt; can be followed by any valid motion. For example,
&lt;code&gt;y$&lt;/code&gt; copies text until end of line and &lt;code&gt;y/__END__&lt;/code&gt; copies it until first occurence of give
pattern (&lt;code&gt;__END__&lt;/code&gt; in this case). This works well with incremental search, as you would
get an indication of where the copied part would end.&lt;/p&gt;
&lt;p&gt;We can also use text object specification with this command. For example, to copy the entire
word under the cursor, we say &lt;code&gt;yaw&lt;/code&gt; (or &lt;code&gt;yaW&lt;/code&gt; for capital-W Word). Likewise, &lt;code&gt;yas&lt;/code&gt; copies
entire sentence and &lt;code&gt;yap&lt;/code&gt; does the same for entire paragraph that the cursor is in.&lt;/p&gt;
&lt;p&gt;For code editing, Vim lets you yank delimited portions of it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yab&lt;/code&gt; or &lt;code&gt;ya{&lt;/code&gt; for curly-brace delimited block&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ya[&lt;/code&gt; for &lt;code&gt;[ ... ]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ya(&lt;/code&gt; for &lt;code&gt;( ... )&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ya&amp;lt;&lt;/code&gt; for &lt;code&gt;&amp;lt; ... &amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ya&amp;quot;&lt;/code&gt; for double-quoted text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ya&#39;&lt;/code&gt; for single-quoted text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;p&lt;/code&gt; command is for pasting (putting) the text that was yanked.&lt;/p&gt;
&lt;p&gt;To undo the last buffer change, use &lt;code&gt;u&lt;/code&gt; command. To re-do it, use Ctrl-R. These can be done
multiple times. Think of it as timeline of changes. Since Vim version 7 this timeline
can be branched. &lt;code&gt;g-&lt;/code&gt; would switch to previous branch and &lt;code&gt;g+&lt;/code&gt; switches to next version
of history.&lt;/p&gt;
&lt;p&gt;Furthemore, one can &amp;ldquo;time-travel&amp;rdquo; in the text change history by using &lt;code&gt;:earlier&lt;/code&gt; and &lt;code&gt;:later&lt;/code&gt;
commands with time offset. For example, &lt;code&gt;:earlier 10m&lt;/code&gt; takes you to buffer state that
was 10 minutes ago and &lt;code&gt;:later 30s&lt;/code&gt; fast-forwards it to what it was 30 seconds later from
that time. So if you remember that your code was fine 10 minutes ago, you can go back to what
it was back then and either discard the changes you did or move a bit forward in the history
to keep some of your work.&lt;/p&gt;
&lt;p&gt;However, all of this change is history is discarded when you quit Vim. However, Vim 7.3 introduced
a feature of persistent undo history that can be enabled by putting the following lines
in your .vimrc file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if has(&amp;#39;persistent_undo&amp;#39;)
    set undofile
endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will make Vim create a history file in each directory being edited. To consolidate
all the edit histories in a single directory somewhere, set &lt;code&gt;undodir&lt;/code&gt; in your .vimrc.
By default, Vim will remember 1000 levels of changes. You can set &lt;code&gt;undolevel&lt;/code&gt; to customise
this amount.&lt;/p&gt;
&lt;p&gt;Vim also keeps track of you command history. If you want to rerun a colon command you
can type &lt;code&gt;:&lt;/code&gt; and use up and down keys to navigate the history until you find the command
that you want to repeat. You can also type the initial few characters of your command
to narrow it down. Furthermore, you can use autocompletion with Tab key when typing
your colon commands. This works with commands, file names, command arguments and so forth -
pretty much all Vim knows about can be autocompleted.&lt;/p&gt;
&lt;p&gt;To cut/delete the current line you can do &lt;code&gt;dd&lt;/code&gt; or &lt;code&gt;:d&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To write the contents of buffer to file, use &lt;code&gt;:w&lt;/code&gt; command (optionally with file name). To
open another file, use &lt;code&gt;:edit&lt;/code&gt; command. To quit Vim, use &lt;code&gt;:q&lt;/code&gt; (or &lt;code&gt;:q!&lt;/code&gt; if you want to
discard changes). Writing out and quitting can be done in a single command: &lt;code&gt;:wq&lt;/code&gt; or &lt;code&gt;:x&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;insert-mode-commands&#34;&gt;Insert mode commands&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Ctrl-Y&lt;/code&gt; duplicates whats in the same column on the preceding line (one character at a time).
&lt;code&gt;Ctrl-E&lt;/code&gt; does this from the next line. &lt;code&gt;Ctrl-A&lt;/code&gt; inserts the last inserted text again. &lt;code&gt;Ctrl-R=&lt;/code&gt;
evaluates any valid Vimscript expression (can be simple arithmetic) and insert the result.
For example, one could type &lt;code&gt;2+2&lt;/code&gt; in the prompt at the bottom, press Enter and &lt;code&gt;4&lt;/code&gt; would be
inserted.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Ctrl-T&lt;/code&gt; increases code indentation level by inserting a single Tab at the start of the line
without moving the cursor. &lt;code&gt;Ctrl-D&lt;/code&gt; is the opposite - it removes a single tab from start of
current line. &lt;code&gt;Ctrl-W&lt;/code&gt; deletes one word that precedes the cursor. &lt;code&gt;Ctrl-O&lt;/code&gt; takes you to the
normal mode for one command.&lt;/p&gt;
&lt;p&gt;Vim dialect of regular expressions is similar to these of sed(1) and Perl, but has some key
differences. For example, nearly all metasyntactic characters require escaping them with
a backslash.&lt;/p&gt;
&lt;p&gt;Typing everything in it&amp;rsquo;s entirety is quite a pain and one of major arguments for using an
IDE. However, can also do auto-completion in insert mode. One could type some of the word
and press Ctrl-X to enter completion. Next, you use another key combination to indicate what
kind of completion you want. For example, Ctrl-X Ctrl-F is for autocompleting file names
and file paths.  Doing this will present a pop-up with all possible completions that you
can choose from.&lt;/p&gt;
&lt;p&gt;Ctrl-X Ctrl-D completes a C preprocessor macro by default, but you can set &lt;code&gt;define&lt;/code&gt; to
any regular expression to use it for other things. But that&amp;rsquo;s a bit of pain.&lt;/p&gt;
&lt;p&gt;Ctrl-X Ctrl-N does identifier completion that works across all sequences of keyword
characters across the current edit session. One can edit &lt;code&gt;iskeyword&lt;/code&gt; option to change what
it considers a keyword character.&lt;/p&gt;
&lt;p&gt;Ctrl-X Ctrl-I is for C and C++ programming. It works like the regular identifier completion,
but also goes into source code files that were &lt;code&gt;#include&lt;/code&gt;d.&lt;/p&gt;
&lt;p&gt;Ctrl-X Ctrl-O is a smart, unified &amp;ldquo;omni-completion&amp;rdquo; that tries to use some heuristics
to present some options to you in a smart, automatic way that is informed by the context
of where you are in the code that you are editing. This typically requires a completion
specification (some are included in the standard installation - see &lt;code&gt;:help compl-omni-filetypes&lt;/code&gt;).
Furthermore, one may need to preprocess the source code with a tool called
&lt;a href=&#34;https://ctags.sourceforge.net&#34;&gt;Exuberant ctags&lt;/a&gt;. You may also want to enable file type
detection by doing &lt;code&gt;:filetype plugin on&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;visual-modes&#34;&gt;Visual modes&lt;/h2&gt;
&lt;p&gt;When you type &lt;code&gt;v&lt;/code&gt; Vim enters the visual mode. As you move your cursor around, it will hightlight
the text and select it. Once you have selected the exact text you want, you can run a normal
mode command and it will apply to that portion of text. That is the basic visual mode.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also visual line mode that entails selecting entire lines. You can enter it by typing
capital &lt;code&gt;V&lt;/code&gt; in the normal mode (or in basic visual mode if you want to switch).&lt;/p&gt;
&lt;p&gt;Lastly, there&amp;rsquo;s a visual block mode for selecting a rectangular block to deal with something like
ASCII art tables. It can be entered by typing Ctrl-V.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scrapy simplified: developing a single file web scraper</title>
      <link>https://www.trickster.dev/post/scrapy-simplified-developing-a-single-file-web-scraper/</link>
      <pubDate>Mon, 26 Dec 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scrapy-simplified-developing-a-single-file-web-scraper/</guid>
      <description>&lt;p&gt;The default way of using Scrapy entails running &lt;code&gt;scrapy startproject&lt;/code&gt; to generate
a bunch of starter code across multiple files. For small scale scrapers
that&amp;rsquo;s a bit of overkill. It&amp;rsquo;s far simpler to have a single Python script file that
you can run when you want to scrape some data. The &lt;code&gt;CrawlerProcess&lt;/code&gt; class in Scrapy
framework enables us to develop such a script.&lt;/p&gt;
&lt;p&gt;For the sake of example, let us suppose we are interested in extracting some basic
information about Fortune 500 companies from the Fortune 500 website. To do a little
exploration and planning, let us load the
&lt;a href=&#34;https://fortune.com/ranking/fortune500/2022/search/&#34;&gt;Fortune 500 search page&lt;/a&gt;
via Scrapy shell:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy view &amp;#34;https://fortune.com/ranking/fortune500/2022/search/&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This opens up a browser window with HTML as the Scrapy was able to fetch it.
Luckily we did not run into any countermeasures against scraping. If we view
the page source we find that all the info being rendered in the table is
available in JSON format inside &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; with &lt;code&gt;id&lt;/code&gt; equal to &lt;code&gt;__NEXT_DATA__&lt;/code&gt;.
This JSON string is the biggest thing in the HTML document that Scrapy fetched.
So that&amp;rsquo;s what we&amp;rsquo;re going to scrape to get links to company pages.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-12-24_12.20.54.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us look into a company profile page.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy view &amp;#34;https://fortune.com/company/verizon/fortune500/&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once again, we find the same kind of script tag from Next.js framework
that we can extract data from.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-12-24_12.30.44.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us go through the following example scraper.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.crawler &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; CrawlerProcess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.selector &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Selector
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;F500Spider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://fortune.com/ranking/fortune500/2022/search/&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;f500&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_search)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_search&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@id=&amp;#34;__NEXT_DATA__&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item_dicts &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;props&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pageProps&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;franchiseList&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; item_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; item_dicts:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            slug &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            rank &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Rank&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            rank &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(rank)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; slug &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; rank &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(slug, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_company_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_company_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@id=&amp;#34;__NEXT_DATA__&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fli &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;props&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pageProps&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;franchiseListItem&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; fli &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fli&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rank &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fli&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rank&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        company_info &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fli&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;companyInfo&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        website &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; company_info&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Website&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; website &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; website&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;a&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            website &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Selector(text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;website)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        country &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; company_info&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Country&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        industry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; company_info&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Industry&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ticker &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; company_info&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ticker&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rank&amp;#34;&lt;/span&gt;: rank,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;company_name&amp;#34;&lt;/span&gt;: company_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;: website,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;: country,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;: industry,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ticker&amp;#34;&lt;/span&gt;: ticker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# See: https://docs.scrapy.org/en/latest/topics/practices.html#run-from-script&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; CrawlerProcess(settings&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FEEDS&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;f500.csv&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;format&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;csv&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;crawl(F500Spider)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key part is the spider that we develop by creating a subclass of &lt;code&gt;scrapy.Spider&lt;/code&gt;,
the same way we would be doing in spiders/ directory of regular Scrapy project.
To launch the spider without the entire shebang that would come with Scrapy project we
need to instantiate a &lt;code&gt;scrapy.crawler.CrawlerProcess&lt;/code&gt; object with settings dictionary,
call the &lt;code&gt;crawl()&lt;/code&gt; method with our spider class and the call the &lt;code&gt;start()&lt;/code&gt; method.
This will make the spider run until completion. Script will finish running once the
scraping is done.&lt;/p&gt;
&lt;p&gt;If you run this you may notice that 498 rows are saved to f500.csv, not 500. That&amp;rsquo;s because
two of the company pages are implemented differently and have their data hard-coded in
JavaScript snippets. Since the objective here was to demonstrate how to use
the crawler process for developing a simplified scraper, I did not proceed with implementing
support for scraping these two pages, although that could be done with using
&lt;a href=&#34;https://github.com/scrapinghub/js2xml&#34;&gt;js2xml&lt;/a&gt; library.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting the data under version control with dolt</title>
      <link>https://www.trickster.dev/post/getting-the-data-under-version-control-with-dolt/</link>
      <pubDate>Thu, 15 Dec 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/getting-the-data-under-version-control-with-dolt/</guid>
      <description>&lt;p&gt;Many developers would agree that source code version control is a
great thing that they would not imagine the modern software
development without. What if it was applied for structured data
as well? Yes, technically it is possible to save SQLite file or SQL dump
into git repo, but that is rather clunky and outside the intended use
case of git. For proper version control, we would want row-level
diffing, ability to undo changes, group them into incremental chunks
(commits), have a way to review the proposed changes and many other nice
features that we have available in systems like git.&lt;/p&gt;
&lt;p&gt;Web scraper developers may find data version control to be of interest
for certain use cases. For example, one might be doing daily rescrapes
of product prices on eCommerce portal for the purpose of price intelligence.
Version-controlling the data provides an easy way to query the database
for changes between scraping jobs if we commit the data at the end of each
scraping job. This would enable automatically diffing the daily snapshots
and generating change report to be seen by the client paying for the
scraping operation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/dolthub/dolt&#34;&gt;Dolt&lt;/a&gt; project is an up-and-coming DBMS
that is meant to be a hybrid between MySQL and git.
It supports an SQL dialect that greatly overlaps with that of MySQL and
also provides a version control features that are based on the ones in git.&lt;/p&gt;
&lt;p&gt;Dolt is a single-binary Go program that can be downloaded from Github
&lt;a href=&#34;https://github.com/dolthub/dolt/tags&#34;&gt;tags page&lt;/a&gt; or compiled from source code.
It is also available from Homebrew on macOS. Furthermore there are two official
Docker images on Docker Hub:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hub.docker.com/r/dolthub/dolt&#34;&gt;&lt;code&gt;dolthub/dolt&lt;/code&gt;&lt;/a&gt; - for running dolt as CLI tool.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hub.docker.com/r/dolthub/dolt-sql-server&#34;&gt;&lt;code&gt;dolthub/dolt-sql-server&lt;/code&gt;&lt;/a&gt; - for
running it in SQL server mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dolt CLI is quite similar to git CLI:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dolt 
Valid commands for dolt are
                init - Create an empty Dolt data repository.
              status - Show the working tree status.
                 add - Add table changes to the list of staged table changes.
                diff - Diff a table.
               reset - Remove table changes from the list of staged table changes.
               clean - Remove untracked tables from working set.
              commit - Record changes to the repository.
                 sql - Run a SQL query against tables in repository.
          sql-server - Start a MySQL-compatible server.
          sql-client - Starts a built-in MySQL client.
                 log - Show commit logs.
              branch - Create, list, edit, delete branches.
            checkout - Checkout a branch or overwrite a table from HEAD.
               merge - Merge a branch.
           conflicts - Commands for viewing and resolving merge conflicts.
         cherry-pick - Apply the changes introduced by an existing commit.
              revert - Undo the changes introduced in a commit.
               clone - Clone from a remote data repository.
               fetch - Update the database from a remote data repository.
                pull - Fetch from a dolt remote data repository and merge.
                push - Push to a dolt remote.
              config - Dolt configuration.
              remote - Manage set of tracked repositories.
              backup - Manage a set of server backups.
               login - Login to a dolt remote host.
               creds - Commands for managing credentials.
                  ls - List tables in the working set.
              schema - Commands for showing and importing table schemas.
               table - Commands for copying, renaming, deleting, and exporting tables.
                 tag - Create, list, delete tags.
               blame - Show what revision and author last modified each row of a table.
         constraints - Commands for handling constraints.
             migrate - Executes a database migration to use the latest Dolt data format.
         read-tables - Fetch table(s) at a specific commit into a new dolt repo
                  gc - Cleans up unreferenced data from the repository.
       filter-branch - Edits the commit history using the provided query.
          merge-base - Find the common ancestor of two commits.
             version - Displays the current Dolt cli version.
                dump - Export all tables in the working set into a file.
                docs - Commands for working with Dolt documents.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have commands for adding changes to staging area, commiting them to change history,
making branches, communicating with remote servers, resolving merge conflicts and so on.
Note, however, that not every VCS feature supported by git is supported by dolt.
Furthemore, we see some commands related to DB functionality such as &lt;code&gt;sql&lt;/code&gt;,
&lt;code&gt;schema&lt;/code&gt; and &lt;code&gt;dump&lt;/code&gt;. Lastly, we have &lt;code&gt;docs&lt;/code&gt; command that provides a little of
functionality for version-controlling your database documentation, such as README
file and such.&lt;/p&gt;
&lt;p&gt;Dolt stores all the database contents in a single directory per database, which makes
it easy to backup and migrate between servers.&lt;/p&gt;
&lt;p&gt;SQL dialect of Dolt is largely the same as the one used in MySQL with two notable
differences. First, not all advanced features of MySQL are supported at this point
(see the list of &lt;a href=&#34;https://docs.dolthub.com/sql-reference/sql-support/supported-statements&#34;&gt;supported statements&lt;/a&gt;).
Second, Dolt introduces some more SQL statements that are related to version
control aspect (see &lt;a href=&#34;https://docs.dolthub.com/sql-reference/version-control&#34;&gt;Version control&lt;/a&gt;
section of the official documentation).&lt;/p&gt;
&lt;p&gt;To run Dolt in SQL server mode one could run &lt;code&gt;dolt sql-server&lt;/code&gt; command. This
launches a network server that talks the MySQL protocol and can be used
with a MySQL client programs or libraries (e.g. mysql-connector).
Furthermore, Dolt includes a simple SQL client that can be launched by
running &lt;code&gt;dolt sql-client&lt;/code&gt;. There are also some official Python client libraries for Dolt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/dolthub/doltpy&#34;&gt;DoltPy&lt;/a&gt; implements Dolt SQL client&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/dolthub/doltcli&#34;&gt;doltcli&lt;/a&gt; is a wrapper around dolt CLI tool
to run queries locally without running Dolt as server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For git users, there&amp;rsquo;s Github for community and collaboration.
&lt;a href=&#34;https://www.dolthub.com/&#34;&gt;DoltHub&lt;/a&gt; is a company and portal that not only
develops dolt, but also lets you publically host your databases. If you don&amp;rsquo;t
want to host it publically there are two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://doltlab.dolthub.com/&#34;&gt;DoltLab&lt;/a&gt; - a self-hostable, more private solution
similar to GitLab. DoltLab is to dolt what GitLab is to Git.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hosted.doltdb.com&#34;&gt;Hosted Dolt&lt;/a&gt; - an equivalent of AWS RDS for
Dolt server. You get a dolt server that is provisioned and maintained for you,
no DBA needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DoltHub Inc. is also running public data bounties that entail gathering open
data by scraping, wrangling and importing it into structured database. Participants
are paid based on how much of the data they submitted via pull requests was accepted.
This can be an opportunity to make some money if you&amp;rsquo;re a web scraper developer.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Understanding SPF, DKIM, DMARC for email security and deliverability</title>
      <link>https://www.trickster.dev/post/understanding-spf-dkim-dmarc-for-email-security-and-deliverability/</link>
      <pubDate>Wed, 14 Dec 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-spf-dkim-dmarc-for-email-security-and-deliverability/</guid>
      <description>&lt;p&gt;On it&amp;rsquo;s own SMTP protocol does not do much validation on the authenticity of sender.
One can spoof protocol headers at SMTP and MIME levels to send a message in another
users name. That can be problematic as it makes spam and social engineering attacks
easier. To address this problem, some email authentication technologies have been
developed. We will discuss the three major ones: SPF, DKIM and DMARC. SPF and DKIM
authenticate sender metadata and DMARC extends them to configure email handling
in case authentication fails. All of these rely on DNS records for the sender domain.&lt;/p&gt;
&lt;h2 id=&#34;spf-sender-policy-framework&#34;&gt;SPF: Sender Policy Framework&lt;/h2&gt;
&lt;p&gt;Sender Policy Framework, specified in &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc7208&#34;&gt;RFC 7208&lt;/a&gt;
is a way for domain owners to authorize or disallow email sending from email addresses
associated with that domain via DNS TXT records. This sets a policy regarding what entities
(if any) can use the domain for their sender email identities. If a receiving or intermediate email
server is compliant with SPF, it would check the DNS records against source email address
in the SMTP &lt;code&gt;HELO&lt;/code&gt;/&lt;code&gt;EHLO&lt;/code&gt;/&lt;code&gt;MAIL FROM&lt;/code&gt; messages and base the decision to allow or reject
the message based on the policy that domain owner has published. If a sending domain does
not publish any SPF records, the SPF-compliant email server is allowed to drop the email
message, which has implications for email deliverability. Setting up SPF records is one
of the recommended practices to prepare a domain for sending cold email.&lt;/p&gt;
&lt;p&gt;Suppose we have a domain example.org and we want to disallow any and all email from that
domain. We would publish the following DNS TXT record:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v=spf1 -all
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All SPF records start with version prefix &lt;code&gt;v=spf1&lt;/code&gt; and contains zero or more
directives (mechanism-qualifier pairs). It can also contains some optional modifiers.&lt;/p&gt;
&lt;p&gt;In this case &lt;code&gt;-&lt;/code&gt; is a qualifier that means &amp;ldquo;fail&amp;rdquo; (i.e. hard error on sending) and &lt;code&gt;all&lt;/code&gt;
is a mechanism that matches all sender IP addresses.&lt;/p&gt;
&lt;p&gt;On the other, if we wanted everyone to be allowed to use the domain for email sending,
we could publish this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v=spf1 +all
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now there&amp;rsquo;s a qualifier &lt;code&gt;+&lt;/code&gt; that means &amp;ldquo;pass&amp;rdquo;, thus email sending is allowed from all
IP addresses.&lt;/p&gt;
&lt;p&gt;There are two more possible qualifiers. &lt;code&gt;~&lt;/code&gt; means &amp;ldquo;softfail&amp;rdquo;, which corresponds to lesser
error condition on sending an email. This may cause email to be marked as possible spam
or bounced with a note to retry it later. &lt;code&gt;?&lt;/code&gt; neither allows not disallows email to be
sent.&lt;/p&gt;
&lt;p&gt;There are the following mechanisms that SPF supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;all&lt;/code&gt; - match any and all sender IP addresses&lt;/li&gt;
&lt;li&gt;&lt;code&gt;include&lt;/code&gt; - include SPF records from other domain (e.g. &lt;code&gt;include:example.net&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt; - match all IPs in domain name A records (e.g. just &lt;code&gt;a&lt;/code&gt; for the current domain or
&lt;code&gt;a:example.net&lt;/code&gt; for another domain)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mx&lt;/code&gt; - match all IPs in domain name MX records (can also point to another DNS name)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ptr&lt;/code&gt; - match IP addresses that reversely-resolves to given domain name or its subdomain
(not recommended)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip4&lt;/code&gt; - match a single IPv4 address or IPv4 range (e.g. &lt;code&gt;ip4:1.2.3.4&lt;/code&gt; or &lt;code&gt;ip4:192.168.0.1/16&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip6&lt;/code&gt; - like &lt;code&gt;ip4&lt;/code&gt;, but for IPv6 addresses&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exists&lt;/code&gt; - takes a domain spec and matches iff that matches at least one A record; meant
for advanced use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SPF implementations go through directives from left to right until it finds the one
that conclusively (dis)allows email transfer, runs into error conditions or runs out
of directives to check. In the case of conflicting directives, the one earlier in the
records has precedence.&lt;/p&gt;
&lt;p&gt;At the end of the SPF record there can be the following modifiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redirect&lt;/code&gt; - redirect SPF policy checks to another domain&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exp&lt;/code&gt; - used for configuring error message when email is rejected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we&amp;rsquo;re familiar with concepts and some technical details of SPF, let us see
how SPF is implemented by a real world email provider - hey.com. We use dig(1)
to see what kind of SPF records hey.com domain has published:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig -t TXT hey.com | grep spf1
hey.com.		60	IN	TXT	&amp;#34;v=spf1 include:_spf.hey.com ~all&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There&amp;rsquo;s two SPF directives here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;include:_spf.hey.com&lt;/code&gt; - include SPF policy of subdomain&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~all&lt;/code&gt; - soft-reject all email that is not covered by any other directive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;include&lt;/code&gt; mechanism triggers recursive check. Now we must check the SPF records
of a referenced subdomain:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig -t TXT _spf.hey.com | grep spf1
_spf.hey.com.		120	IN	TXT	&amp;#34;v=spf1 ip4:204.62.114.0/23 -all&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This specifies an IPv4 address range that is allowed to send email messages
from mailboxes under hey.com domain and strongly forbids all email traffic
to be sent from elsewhere, thus overriding the &lt;code&gt;~all&lt;/code&gt; directive in the previous
record.&lt;/p&gt;
&lt;p&gt;If you are growth hacker working on cold emails campaigns it is strongly
recommended to set up SPF for all your outreach domains. Your email provider
will most likely provide the exact instructions on how to do that.&lt;/p&gt;
&lt;p&gt;But what if you want to do bug bounties? In that case, SPF records or lack
thereof are not that relevant. Missing SPF records are not generally considered
to be a serious security problem and reporting that is unlikely to lead to any
payout.&lt;/p&gt;
&lt;h2 id=&#34;dkim-domain-keys-identified-mail&#34;&gt;DKIM: Domain Keys Identified Mail&lt;/h2&gt;
&lt;p&gt;DKIM, specified in &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc6376&#34;&gt;RFC 6376&lt;/a&gt; and some
other additional RFCs goes beyond simple domain-level policies and enables
email message signing by public key published in DNS record. This lets the
intermediate email server to check if email message was sent from the
keeper of respective private key (e.g. sender email provider) and that there
was no tampering by adversarial parties (e.g. rogue email servers) as the
message was enroute to the recipient. Depending on how exactly DKIM is deployed,
the cryptographic signature may cover all message or only some headers.&lt;/p&gt;
&lt;p&gt;DKIM also relies on DNS TXT records, but they are required to be published
at subdomain of following form: &lt;code&gt;[selector]._domainkey.[domain]&lt;/code&gt;. Here
&lt;code&gt;selector&lt;/code&gt; is provider-specific value and &lt;code&gt;domain&lt;/code&gt; is a sender domain.&lt;/p&gt;
&lt;p&gt;The DKIM signature is computed by sending email server that is managed
by your email provider. As the email message goes through further
DKIM-compliant servers it is being verified for the valid signature.
DKIM implementation by itself does not reject any messages, but introduces
extra email headers that can be used by spam filtering solutions.&lt;/p&gt;
&lt;p&gt;DKIM has implications for email deliverability. As far as anti-spam systems
are concerned, lack of DKIM record means less reliable sender identity.
Furthermore, DKIM misconfiguration can cause signature validation to fail,
which can lead to message being filtered as spam (especially if DMARC is
being used).&lt;/p&gt;
&lt;p&gt;RFC 6367 provides the following example of &lt;code&gt;DKIM-Signature&lt;/code&gt; header:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DKIM-Signature: v=1; a=rsa-sha256; d=example.net; s=brisbane;
   c=simple; q=dns/txt; i=@eng.example.net;
   t=1117574938; x=1118006938;
   h=from:to:subject:date;
   z=From:foo@eng.example.net|To:joe@example.com|
    Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700;
bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;
b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The following is an example of DKIM record that ZOHO provides:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXzlbk53i+yhhnbLM6Z/dzASCoGrRMbnMJzFsJK9Q17cHVDqmSUjQkNMfBhMD0JgwZcuUpHBE2aTVzwaLEQXRkR7BDOLBY+JpGjtJ45sjI9rmlN/4ntJLFEzuGaaHDp/XyR3eORKSt2QBHs9OM9U8zMoXCQc1+MW6vbtYJeWhacwIDAQAB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like with SPF, your email provider will probably give you exact DNS
record to use for enabling DKIM for your domain.&lt;/p&gt;
&lt;h2 id=&#34;dmarc-domain-based-message-authentication-reporting-and-conformance&#34;&gt;DMARC: Domain-based Message Authentication, Reporting and Conformance&lt;/h2&gt;
&lt;p&gt;DMARC is something that builds upon SPF and DKIM. It allows domain owners
to publish DNS records telling how messages that violate SPF or DKIM
authorization should be treated (rejected or quarantined). Furthermore,
domain owners can provide an email address for the aggregate or forensic
notifications to be sent to, so that they could be used for troubleshooting.
Furthermore, you may want to know if someone if trying to spoof your email
for malicious purposes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc7489&#34;&gt;RFC 7489&lt;/a&gt; is the specification to
read if you want to know more about DMARC.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SMTP, IMAP, POP3 and MIME: understanding email protocols</title>
      <link>https://www.trickster.dev/post/smtp-imap-pop3-and-mime-understanding-email-protocols/</link>
      <pubDate>Mon, 21 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/smtp-imap-pop3-and-mime-understanding-email-protocols/</guid>
      <description>&lt;h2 id=&#34;introduction-and-big-picture&#34;&gt;Introduction and big picture&lt;/h2&gt;
&lt;p&gt;Email is a fairly old school technology meant to replicate postal service digitally.
To understand how email works, we must know about network protocols that specify sets
of data formats and data exchange rules for exchanging messages over the network.
In the modern internet email sending part is conceptually (and sometimes technically)
separate from receiving part. Email sending aspects are formalised in RFCs that describe
SMTP protocol, whereas reception part can be done either via IMAP or POP3 protocol.
The conceptual difference between IMAP and POP3 is that IMAP is meant to provide a way
to browse mailboxes via network connection whereas POP3 is meant to download received
messages into user-controlled computer and let them read it offline without any persistent
connection to the server. Furthermore, MIME data format is used for encoding contents
and inner structure of the messages that are transferred.&lt;/p&gt;
&lt;p&gt;Broadly speaking, email system is federated and consists of the following parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mail Transfer Agent (MTA) is a program that implements SMTP protocol to transport messages
between hosts (e.g. Sendmail, qmail, Postfix).&lt;/li&gt;
&lt;li&gt;Mail User Agent (MUA) is the email client application (e.g. mutt, Mail.app, Outlook,
Thunderbird).&lt;/li&gt;
&lt;li&gt;There can also be a Mail Delivery Agent (MDA) - an intermediate piece of software that
bridges the gap between MTA and MUA on the receiving side (e.g. procmail).
This can be done for spam filtering purposes and to manage the email message
persistence on Unix/Linux systems.&lt;/li&gt;
&lt;li&gt;Lastly, there can also be a Mail Submission Agent (MSA) that is equivalent of MDA, but
for sending the email.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The general big-picture flow of email message is the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A sender creates the email message and sends it from MUA to MTA managed by their email
provider (possibly via MSA). SMTP protocol is being used here.&lt;/li&gt;
&lt;li&gt;Message traverses one or more MTAs. Again, this is done via SMTP protocol.&lt;/li&gt;
&lt;li&gt;Message reaches the final MTA belonging to the email provider of the recipient and is
stored there.&lt;/li&gt;
&lt;li&gt;Recipient uses MUA on their end to retrieve the message (possibly via MDA). Either IMAP
or POP3 protocol can be used here.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It should be noted that email provider specific REST API can be used between MUA and MTA
(possibly through a client library). For examples of such APIs, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/gmail/api&#34;&gt;Gmail API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.zoho.com/mail/help/api/&#34;&gt;ZOHO Mail API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.sendgrid.com/api-reference/how-to-use-the-sendgrid-v3-api/authentication&#34;&gt;Sendgrid API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This would replace SMTP, IMAP and POP3 protocols for communications between client and server.&lt;/p&gt;
&lt;h2 id=&#34;smtp-simple-mail-transfer-protocol&#34;&gt;SMTP: Simple Mail Transfer Protocol&lt;/h2&gt;
&lt;p&gt;SMTP was originally formalised back in 1981 in &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc788&#34;&gt;RFC 788&lt;/a&gt;.
Since then, the protocol specification has been extended across various RFCs to keep
it up to data with the changing needs of growing internet. At the time of writing,
&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc5321&#34;&gt;RFC 5321&lt;/a&gt; is the final version of core
SMTP spec. SMTP is a text-based protocol with two kinds of messages: commands and
responses.&lt;/p&gt;
&lt;p&gt;Let us use &lt;code&gt;smtplib&lt;/code&gt; module from the vanilla Python installation to explore the SMTP
debugging output. We want to see the protocol flow.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import smtplib
&amp;gt;&amp;gt;&amp;gt; smtp_conn = smtplib.SMTP(&amp;#34;smtp.rambler.ru&amp;#34;, port=587)
&amp;gt;&amp;gt;&amp;gt; smtp_conn.set_debuglevel(1)
&amp;gt;&amp;gt;&amp;gt; smtp_conn.starttls()
send: &amp;#39;ehlo [[REDACTED]]\r\n&amp;#39;
reply: b&amp;#39;250-mail.rambler.ru\r\n&amp;#39;
reply: b&amp;#39;250-SIZE 104857600\r\n&amp;#39;
reply: b&amp;#39;250-ENHANCEDSTATUSCODES\r\n&amp;#39;
reply: b&amp;#39;250-8BITMIME\r\n&amp;#39;
reply: b&amp;#39;250-AUTH PLAIN LOGIN\r\n&amp;#39;
reply: b&amp;#39;250 STARTTLS\r\n&amp;#39;
reply: retcode (250); Msg: b&amp;#39;mail.rambler.ru\nSIZE 104857600\nENHANCEDSTATUSCODES\n8BITMIME\nAUTH PLAIN LOGIN\nSTARTTLS&amp;#39;
send: &amp;#39;STARTTLS\r\n&amp;#39;
reply: b&amp;#39;220 2.0.0 Start TLS\r\n&amp;#39;
reply: retcode (220); Msg: b&amp;#39;2.0.0 Start TLS&amp;#39;
(220, b&amp;#39;2.0.0 Start TLS&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We established TCP connection to SMTP server and sent &lt;code&gt;EHLO&lt;/code&gt; command with our
IP address to initiate the conversation. The server responded with status code
250 (indicating success) and a list of SMTP service extensions that server is supporting.
To make the connection encrypted, we also issued a &lt;code&gt;STARTTLS&lt;/code&gt; command. To this command
server replied with status code 220, which means that it ready to serve the client.&lt;/p&gt;
&lt;p&gt;Now we need to login to the server with account credentials.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; username = &amp;#34;[REDACTED]&amp;#34;
&amp;gt;&amp;gt;&amp;gt; password = &amp;#34;[REDACTED]&amp;#34;
&amp;gt;&amp;gt;&amp;gt; smtp_conn.login(username, password)
send: &amp;#39;ehlo [202.3.218.138]\r\n&amp;#39;
reply: b&amp;#39;250-mail.rambler.ru\r\n&amp;#39;
reply: b&amp;#39;250-SIZE 104857600\r\n&amp;#39;
reply: b&amp;#39;250-ENHANCEDSTATUSCODES\r\n&amp;#39;
reply: b&amp;#39;250-8BITMIME\r\n&amp;#39;
reply: b&amp;#39;250 AUTH PLAIN LOGIN\r\n&amp;#39;
reply: retcode (250); Msg: b&amp;#39;mail.rambler.ru\nSIZE 104857600\nENHANCEDSTATUSCODES\n8BITMIME\nAUTH PLAIN LOGIN&amp;#39;
send: &amp;#39;AUTH PLAIN [REDACTED]\r\n&amp;#39;
reply: b&amp;#39;235 2.0.0 OK\r\n&amp;#39;
reply: retcode (235); Msg: b&amp;#39;2.0.0 OK&amp;#39;
(235, b&amp;#39;2.0.0 OK&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling &lt;code&gt;login()&lt;/code&gt; method repeated the &lt;code&gt;EHLO&lt;/code&gt; message over the now-encrypted channel
and sent &lt;code&gt;AUTH&lt;/code&gt; message that deals with &amp;ldquo;PLAIN Simple Authentication and Security Layer
(SASL) Mechanism&amp;rdquo; described in &lt;a href=&#34;https://www.ietf.org/rfc/rfc4616.txt&#34;&gt;RFC 4616&lt;/a&gt;. The
redacted part is Base64 string from the following sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Null byte (0x00).&lt;/li&gt;
&lt;li&gt;Email account username.&lt;/li&gt;
&lt;li&gt;Null byte.&lt;/li&gt;
&lt;li&gt;Email account password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We got response from the server saying that all is good with the login step. Now we
can send some test message.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; to_addr = &amp;#34;noreply@keyspace.lt&amp;#34;
&amp;gt;&amp;gt;&amp;gt; smtp_conn.sendmail(username, to_addr, &amp;#34;Test!&amp;#34;)
send: &amp;#39;mail FROM:&amp;lt;[REDACTED]&amp;gt; size=5\r\n&amp;#39;
reply: b&amp;#39;250 2.1.0 Ok\r\n&amp;#39;
reply: retcode (250); Msg: b&amp;#39;2.1.0 Ok&amp;#39;
send: &amp;#39;rcpt TO:&amp;lt;noreply@keyspace.lt&amp;gt;\r\n&amp;#39;
reply: b&amp;#39;250 2.1.5 Ok\r\n&amp;#39;
reply: retcode (250); Msg: b&amp;#39;2.1.5 Ok&amp;#39;
send: &amp;#39;data\r\n&amp;#39;
reply: b&amp;#39;354 End data with &amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;.&amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;\r\n&amp;#39;
reply: retcode (354); Msg: b&amp;#39;End data with &amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;.&amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;&amp;#39;
data: (354, b&amp;#39;End data with &amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;.&amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;&amp;#39;)
send: b&amp;#39;Test!\r\n.\r\n&amp;#39;
reply: b&amp;#39;250 2.0.0 Ok: queued as CB14F2642E52\r\n&amp;#39;
reply: retcode (250); Msg: b&amp;#39;2.0.0 Ok: queued as CB14F2642E52&amp;#39;
data: (250, b&amp;#39;2.0.0 Ok: queued as CB14F2642E52&amp;#39;)
{}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There a sequence of three steps here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We provide the email address of the sender which will correspond to email account
username in most cases via &lt;code&gt;MAIL FROM&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;We provide recipient address via &lt;code&gt;RCPT TO&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;We send &lt;code&gt;DATA&lt;/code&gt; command, contents of the message and message end sequence. In this
case the message is a very simple ASCII string, but in real world this is typically
a MIME payload that will be discussed later.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All step succeeded and a message was enqueued for further transfer. To finalise the
connection in a clean way we would send a &lt;code&gt;QUIT&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;We only covered the happy path here. &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc5321#appendix-D&#34;&gt;RFC 5321 Appendix D&lt;/a&gt;
lists more examples.&lt;/p&gt;
&lt;p&gt;Futhermore, modern SMTP implementation deal with additional considerations, such
as spam filtering and policy enforcement.&lt;/p&gt;
&lt;p&gt;Standard TCP port of plaintext SMTP is 25. SMTP-over-TLS typically uses port 587.&lt;/p&gt;
&lt;p&gt;One might ask: given an email address, how does MTA know the IP address of the next
MTA to pass the message to? This is typically found through DNS MX records that
point to hostname of the mail server servicing the given domain name.&lt;/p&gt;
&lt;h2 id=&#34;imap-internet-message-access-protocol&#34;&gt;IMAP: Internet Message Access Protocol&lt;/h2&gt;
&lt;p&gt;IMAPv4rev2 is the current version of IMAP protocol, specified in
&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc9051&#34;&gt;RFC 9501&lt;/a&gt;.
This is fairly new standard that is not always implemented to the letter. However,
IMAP protocol has been around for long time and is widely supported in the world
of network software. Just like SMTP, IMAP is text-based, line-oriented protocol.&lt;/p&gt;
&lt;p&gt;Let us receive an email with &lt;code&gt;imaplib&lt;/code&gt; Python module and go through IMAP protocol flow.
We will enable debug output in Python REPL and reproduce the steps that email client
would do when receiving messages.&lt;/p&gt;
&lt;p&gt;Like with SMTP protocol, we first need to establish the connection and login to an
account.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import imaplib
&amp;gt;&amp;gt;&amp;gt; imap_conn = imaplib.IMAP4_SSL(&amp;#34;imap.rambler.ru&amp;#34;, port=993)
&amp;gt;&amp;gt;&amp;gt; imap_conn.debug = 5
&amp;gt;&amp;gt;&amp;gt; imap_conn.login(username, password)
  33:33.96 &amp;gt; b&amp;#39;OGCE1 LOGIN [REDACTED] &amp;#34;[REDACTED]&amp;#34;&amp;#39;
  33:34.54 &amp;lt; b&amp;#39;OGCE1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA] Logged in&amp;#39;
  33:34.54 	matched b&amp;#39;(?P&amp;lt;tag&amp;gt;OGCE\\d+) (?P&amp;lt;type&amp;gt;[A-Z]+) (?P&amp;lt;data&amp;gt;.*)&amp;#39; =&amp;gt; (b&amp;#39;OGCE1&amp;#39;, b&amp;#39;OK&amp;#39;, b&amp;#39;[CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA] Logged in&amp;#39;)
  33:34.54 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;CAPABILITY&amp;#39;, b&amp;#39; IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA&amp;#39;, b&amp;#39;IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA&amp;#39;)
  33:34.54 untagged_responses[CAPABILITY] 0 += [&amp;#34;b&amp;#39;IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA&amp;#39;&amp;#34;]
(&amp;#39;OK&amp;#39;, [b&amp;#39;[CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA] Logged in&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unlike with SMTP connection, we establish TLS connection to server first and start
sending IMAP commands in the encrypted channel. IMAP protocol does not have any
equivalent of &lt;code&gt;STARTTLS&lt;/code&gt; message to upgrade plaintext connection to an encrypted one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LOGIN&lt;/code&gt; command was sent with username and password. Server responded with status &lt;code&gt;OK&lt;/code&gt; and
a list of supported capabilities.&lt;/p&gt;
&lt;p&gt;Now we need to select a mailbox on the server. If we provide no mailbox name the Python IMAP
client will switch to inbox by default.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; imap_conn.select()
  34:03.21 &amp;gt; b&amp;#39;OGCE2 SELECT INBOX&amp;#39;
  34:03.72 &amp;lt; b&amp;#39;* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;FLAGS&amp;#39;, b&amp;#39; (\\Answered \\Flagged \\Deleted \\Seen \\Draft)&amp;#39;, b&amp;#39;(\\Answered \\Flagged \\Deleted \\Seen \\Draft)&amp;#39;)
  34:03.72 untagged_responses[FLAGS] 0 += [&amp;#34;b&amp;#39;(\\Answered \\Flagged \\Deleted \\Seen \\Draft)&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)] Flags permitted.&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;OK&amp;#39;, b&amp;#39; [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)] Flags permitted.&amp;#39;, b&amp;#39;[PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)] Flags permitted.&amp;#39;)
  34:03.72 untagged_responses[OK] 0 += [&amp;#34;b&amp;#39;[PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)] Flags permitted.&amp;#39;&amp;#34;]
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;PERMANENTFLAGS&amp;#39;, b&amp;#39; (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)&amp;#39;, b&amp;#39;(\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)&amp;#39;)
  34:03.72 untagged_responses[PERMANENTFLAGS] 0 += [&amp;#34;b&amp;#39;(\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* 10 EXISTS&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;data&amp;gt;\\d+) (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data2&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;10&amp;#39;, b&amp;#39;EXISTS&amp;#39;, None, None)
  34:03.72 untagged_responses[EXISTS] 0 += [&amp;#34;b&amp;#39;10&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* 4 RECENT&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;data&amp;gt;\\d+) (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data2&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;4&amp;#39;, b&amp;#39;RECENT&amp;#39;, None, None)
  34:03.72 untagged_responses[RECENT] 0 += [&amp;#34;b&amp;#39;4&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* OK [UNSEEN 7] First unseen.&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;OK&amp;#39;, b&amp;#39; [UNSEEN 7] First unseen.&amp;#39;, b&amp;#39;[UNSEEN 7] First unseen.&amp;#39;)
  34:03.72 untagged_responses[OK] 1 += [&amp;#34;b&amp;#39;[UNSEEN 7] First unseen.&amp;#39;&amp;#34;]
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;UNSEEN&amp;#39;, b&amp;#39; 7&amp;#39;, b&amp;#39;7&amp;#39;)
  34:03.72 untagged_responses[UNSEEN] 0 += [&amp;#34;b&amp;#39;7&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* OK [UIDVALIDITY 1644072125] UIDs valid&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;OK&amp;#39;, b&amp;#39; [UIDVALIDITY 1644072125] UIDs valid&amp;#39;, b&amp;#39;[UIDVALIDITY 1644072125] UIDs valid&amp;#39;)
  34:03.72 untagged_responses[OK] 2 += [&amp;#34;b&amp;#39;[UIDVALIDITY 1644072125] UIDs valid&amp;#39;&amp;#34;]
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;UIDVALIDITY&amp;#39;, b&amp;#39; 1644072125&amp;#39;, b&amp;#39;1644072125&amp;#39;)
  34:03.72 untagged_responses[UIDVALIDITY] 0 += [&amp;#34;b&amp;#39;1644072125&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* OK [UIDNEXT 11] Predicted next UID&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;OK&amp;#39;, b&amp;#39; [UIDNEXT 11] Predicted next UID&amp;#39;, b&amp;#39;[UIDNEXT 11] Predicted next UID&amp;#39;)
  34:03.72 untagged_responses[OK] 3 += [&amp;#34;b&amp;#39;[UIDNEXT 11] Predicted next UID&amp;#39;&amp;#34;]
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;UIDNEXT&amp;#39;, b&amp;#39; 11&amp;#39;, b&amp;#39;11&amp;#39;)
  34:03.72 untagged_responses[UIDNEXT] 0 += [&amp;#34;b&amp;#39;11&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;* OK [HIGHESTMODSEQ 17] Highest&amp;#39;
  34:03.72 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;OK&amp;#39;, b&amp;#39; [HIGHESTMODSEQ 17] Highest&amp;#39;, b&amp;#39;[HIGHESTMODSEQ 17] Highest&amp;#39;)
  34:03.72 untagged_responses[OK] 4 += [&amp;#34;b&amp;#39;[HIGHESTMODSEQ 17] Highest&amp;#39;&amp;#34;]
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;HIGHESTMODSEQ&amp;#39;, b&amp;#39; 17&amp;#39;, b&amp;#39;17&amp;#39;)
  34:03.72 untagged_responses[HIGHESTMODSEQ] 0 += [&amp;#34;b&amp;#39;17&amp;#39;&amp;#34;]
  34:03.72 &amp;lt; b&amp;#39;OGCE2 OK [READ-WRITE] Select completed (0.009 + 0.000 + 0.008 secs).&amp;#39;
  34:03.72 	matched b&amp;#39;(?P&amp;lt;tag&amp;gt;OGCE\\d+) (?P&amp;lt;type&amp;gt;[A-Z]+) (?P&amp;lt;data&amp;gt;.*)&amp;#39; =&amp;gt; (b&amp;#39;OGCE2&amp;#39;, b&amp;#39;OK&amp;#39;, b&amp;#39;[READ-WRITE] Select completed (0.009 + 0.000 + 0.008 secs).&amp;#39;)
  34:03.72 	matched b&amp;#39;\\[(?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?\\]&amp;#39; =&amp;gt; (b&amp;#39;READ-WRITE&amp;#39;, None, None)
  34:03.72 untagged_responses[READ-WRITE] 0 += [&amp;#34;b&amp;#39;&amp;#39;&amp;#34;]
(&amp;#39;OK&amp;#39;, [b&amp;#39;10&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may notice that first command message was prefixed by &lt;code&gt;OGCE1&lt;/code&gt; and next one by
&lt;code&gt;OGCE2&lt;/code&gt;. This is IMAP message tagging in action. They are meant to associate some of
the reply messages with a command message, as is seen in one of the lines that
client got in reponse to &lt;code&gt;SELECT&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;We got several lines in response that mostly deal with message UIDs - 32-bit numbers that
uniquely identify messages within the email account.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)&lt;/code&gt; - a list of flags that can be
assigned to messages (answered, seen, deleted, etc.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;* 10 EXISTS&lt;/code&gt; - number of messages in the mailbox (10).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;* 4 RECENT&lt;/code&gt; - number of messages with &amp;ldquo;recent&amp;rdquo; flag set (4) that recently arrived to
mailbox.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;* OK [UNSEEN 7]&lt;/code&gt; - UID of the first unseen message.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;* OK [UIDNEXT 11]&lt;/code&gt; - next UID that would be assigned on newly arrived message (predicted
value).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we can search within selected mailbox for messages matching some predicate
(e.g. sender address being equal to known value). For the purpose of this example,
we will retrieve a list of UIDs of all messages in the inbox.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; imap_conn.search(None, &amp;#39;ALL&amp;#39;)
  34:42.42 &amp;gt; b&amp;#39;OGCE3 SEARCH ALL&amp;#39;
  34:42.73 &amp;lt; b&amp;#39;* SEARCH 1 2 3 4 5 6 7 8 9 10&amp;#39;
  34:42.73 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;SEARCH&amp;#39;, b&amp;#39; 1 2 3 4 5 6 7 8 9 10&amp;#39;, b&amp;#39;1 2 3 4 5 6 7 8 9 10&amp;#39;)
  34:42.73 untagged_responses[SEARCH] 0 += [&amp;#34;b&amp;#39;1 2 3 4 5 6 7 8 9 10&amp;#39;&amp;#34;]
  34:42.73 &amp;lt; b&amp;#39;OGCE3 OK Search completed (0.001 + 0.000 secs).&amp;#39;
  34:42.73 	matched b&amp;#39;(?P&amp;lt;tag&amp;gt;OGCE\\d+) (?P&amp;lt;type&amp;gt;[A-Z]+) (?P&amp;lt;data&amp;gt;.*)&amp;#39; =&amp;gt; (b&amp;#39;OGCE3&amp;#39;, b&amp;#39;OK&amp;#39;, b&amp;#39;Search completed (0.001 + 0.000 secs).&amp;#39;)
  34:42.73 untagged_responses[SEARCH] =&amp;gt; [b&amp;#39;1 2 3 4 5 6 7 8 9 10&amp;#39;]
(&amp;#39;OK&amp;#39;, [b&amp;#39;1 2 3 4 5 6 7 8 9 10&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We got a list of UIDs from 1 to 10. To get a single message, we can send a &lt;code&gt;FETCH&lt;/code&gt; command (response
removed for the sake of brevity):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; imap_conn.fetch(b&amp;#39;10&amp;#39;, &amp;#39;(RFC822)&amp;#39;)
  35:08.76 &amp;gt; b&amp;#39;OGCE5 FETCH 10 (RFC822)&amp;#39;
  35:09.15 &amp;lt; b&amp;#39;* 10 FETCH (FLAGS (\\Seen \\Recent) RFC822 {2940}&amp;#39;
  35:09.15 	matched b&amp;#39;\\* (?P&amp;lt;data&amp;gt;\\d+) (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data2&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;10&amp;#39;, b&amp;#39;FETCH&amp;#39;, b&amp;#39; (FLAGS (\\Seen \\Recent) RFC822 {2940}&amp;#39;, b&amp;#39;(FLAGS (\\Seen \\Recent) RFC822 {2940}&amp;#39;)
  35:09.15 	matched b&amp;#39;.*{(?P&amp;lt;size&amp;gt;\\d+)}$&amp;#39; =&amp;gt; (b&amp;#39;2940&amp;#39;,)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the response we get a MIME payload with all the headers.&lt;/p&gt;
&lt;p&gt;Finally, we close the connection and logout:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; imap_conn.close()
  36:17.68 &amp;gt; b&amp;#39;OGCE7 CLOSE&amp;#39;
  36:17.96 &amp;lt; b&amp;#39;OGCE7 OK Close completed (0.001 + 0.000 secs).&amp;#39;
  36:17.96 	matched b&amp;#39;(?P&amp;lt;tag&amp;gt;OGCE\\d+) (?P&amp;lt;type&amp;gt;[A-Z]+) (?P&amp;lt;data&amp;gt;.*)&amp;#39; =&amp;gt; (b&amp;#39;OGCE7&amp;#39;, b&amp;#39;OK&amp;#39;, b&amp;#39;Close completed (0.001 + 0.000 secs).&amp;#39;)
(&amp;#39;OK&amp;#39;, [b&amp;#39;Close completed (0.001 + 0.000 secs).&amp;#39;])
&amp;gt;&amp;gt;&amp;gt; imap_conn.logout()
  36:22.24 &amp;gt; b&amp;#39;OGCE8 LOGOUT&amp;#39;
  36:22.57 &amp;lt; b&amp;#39;* BYE Logging out&amp;#39;
  36:22.57 	matched b&amp;#39;\\* (?P&amp;lt;type&amp;gt;[A-Z-]+)( (?P&amp;lt;data&amp;gt;.*))?&amp;#39; =&amp;gt; (b&amp;#39;BYE&amp;#39;, b&amp;#39; Logging out&amp;#39;, b&amp;#39;Logging out&amp;#39;)
  36:22.57 untagged_responses[BYE] 0 += [&amp;#34;b&amp;#39;Logging out&amp;#39;&amp;#34;]
  36:22.57 BYE response: b&amp;#39;Logging out&amp;#39;
(&amp;#39;BYE&amp;#39;, [b&amp;#39;Logging out&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;pop3-post-office-protocol-version-3&#34;&gt;POP3: Post Office Protocol version 3&lt;/h2&gt;
&lt;p&gt;POP3 is a network protocol that enables end users to download email messages for offline
reading and archiving. Python supports it via &lt;code&gt;poplib&lt;/code&gt; module. Like SMTP, POP3 supports both
using TLS from start of connection and upgrading the plaintext connection before login step
to POP3-over-TLS.&lt;/p&gt;
&lt;p&gt;Overall POP3 is much simpler protocol than IMAP.  Listing messages and retrieving them one at
a time is fairly simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import poplib
&amp;gt;&amp;gt;&amp;gt; pop_conn = poplib.POP3(&amp;#34;pop3.rambler.ru&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; pop_conn.user(username)
b&amp;#39;+OK&amp;#39;
&amp;gt;&amp;gt;&amp;gt; pop_conn.pass_(password)
b&amp;#39;+OK Logged in.&amp;#39;
&amp;gt;&amp;gt;&amp;gt; pop_conn.list()
(b&amp;#39;+OK 10 messages:&amp;#39;, [b&amp;#39;1 18561&amp;#39;, b&amp;#39;2 18978&amp;#39;, b&amp;#39;3 58337&amp;#39;, b&amp;#39;4 47221&amp;#39;, b&amp;#39;5 48119&amp;#39;, b&amp;#39;6 47562&amp;#39;, b&amp;#39;7 66608&amp;#39;, b&amp;#39;8 71147&amp;#39;, b&amp;#39;9 32400&amp;#39;, b&amp;#39;10 2940&amp;#39;], 90)
&amp;gt;&amp;gt;&amp;gt; pop_conn.retr(1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To gracefully close the connection, we send a &lt;code&gt;QUIT&lt;/code&gt; command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; pop_conn.quit()
b&amp;#39;+OK Logging out.&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For a specification of POP3, see &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1939.html&#34;&gt;RFC 1939&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;mime-multipurpose-internet-mail-extension&#34;&gt;MIME: Multipurpose Internet Mail Extension&lt;/h2&gt;
&lt;p&gt;Mail client apps often have the feature to view the message in it&amp;rsquo;s raw form.
One would notice that email messages consist of headers and payload, not unlike
HTTP requests and and responses.&lt;/p&gt;
&lt;p&gt;A very simple MIME message would be something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Content-Type: text/plain
MIME-Version: 1.0
To: &amp;lt;developer@evilsoft.com&amp;gt;
From: &amp;lt;developer@fb.com&amp;gt;
Subject: Sample MIME Message

This is a simple MIME Message!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Headers being used here are pretty much self-explanatory. This kind of message is a
single part one - there is a single type of payload of given content type. For plain
text messages message contents are transferred in their original form. If the payload
was of some binary format (e.g. PDF document or image file) it would be represented
in Base64 form. Some of the popular MIME content types are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text/plain&lt;/code&gt; - just a plain text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text/html&lt;/code&gt; - HTML document&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/png&lt;/code&gt; - PNG image&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application/pdf&lt;/code&gt; - PDF document&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But nowadays many, if not most, email messages are more complex than this. MIME messages
can also be multipart - consisting of several segments.&lt;/p&gt;
&lt;p&gt;RFC 1341 provides the following example of two-part MIME message:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;From: Nathaniel Borenstein &amp;lt;nsb@bellcore.com&amp;gt; 
To:  Ned Freed &amp;lt;ned@innosoft.com&amp;gt; 
Subject: Sample message 
MIME-Version: 1.0 
Content-type: multipart/mixed; boundary=&amp;#34;simple 
boundary&amp;#34; 

This is the preamble.  It is to be ignored, though it 
is a handy place for mail composers to include an 
explanatory note to non-MIME compliant readers. 
--simple boundary 

This is implicitly typed plain ASCII text. 
It does NOT end with a linebreak. 
--simple boundary 
Content-type: text/plain; charset=us-ascii 

This is explicitly typed plain ASCII text. 
It DOES end with a linebreak. 

--simple boundary-- 
This is the epilogue.  It is also to be ignored.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Multipart messages can also be mixed and alternative. Mixed messages (content type
&lt;code&gt;multipart/mixed&lt;/code&gt;) are multipart messages with parts of different content types that
are independent and meant to be displayed serially (one after another). Alternative
messages (content type &lt;code&gt;multipart/alternative&lt;/code&gt; contain at least two parts that are
equivalent representation of the same information (e.g. the same
message could be in plain text and HTML). MIME messages are powerful enough to
form a tree structure with message as a root node and parts as descendant nodes.
That way, parts of message could have child parts with files or previous messages
as attachments.&lt;/p&gt;
&lt;p&gt;Various modules in Python &lt;code&gt;email&lt;/code&gt; package deal with parsing and generating the
MIME messages.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Wget tips and tricks</title>
      <link>https://www.trickster.dev/post/wget-tips-and-tricks/</link>
      <pubDate>Fri, 11 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/wget-tips-and-tricks/</guid>
      <description>&lt;p&gt;So, what is wget? &lt;a href=&#34;https://www.gnu.org/software/wget/&#34;&gt;Wget&lt;/a&gt; is a prominent command
line tool to download stuff from the web. It has a fairly extensive feature set that
includes recursive downloading, proxy support, site mirroring, cookie handling and so
on. Let us  go through some use cases of wget.&lt;/p&gt;
&lt;p&gt;To download a single file/page using wget, just pass the corresponding URL into &lt;code&gt;argv[1]&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget http://www.textfiles.com/100/crossbow
--2022-11-11 18:24:33--  http://www.textfiles.com/100/crossbow
Resolving www.textfiles.com (www.textfiles.com)... 208.86.224.90
Connecting to www.textfiles.com (www.textfiles.com)|208.86.224.90|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 29200 (29K)
Saving to: ‘crossbow’

crossbow                                           100%[================================================================================================================&amp;gt;]  28,52K   114KB/s    in 0,3s    

2022-11-11 18:24:33 (114 KB/s) - ‘crossbow’ saved [29200/29200]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This shows us the HTTP status code, server IP address, file size and even visualises
the progress on ASCII-art progress bar. This is default level of output verbosity.
But what if we wanted to see more details? Perhaps something is off and we want to
troubleshoot it? We can enable debug output by using &lt;code&gt;--debug&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --debug http://www.textfiles.com/100/crossbow
DEBUG output created by Wget 1.21.3 on darwin21.3.0.

Reading HSTS entries from /Users/rl/.wget-hsts
URI encoding = ‘UTF-8’
Converted file name &amp;#39;crossbow&amp;#39; (UTF-8) -&amp;gt; &amp;#39;crossbow&amp;#39; (UTF-8)
--2022-11-11 18:26:48--  http://www.textfiles.com/100/crossbow
Resolving www.textfiles.com (www.textfiles.com)... 208.86.224.90
Caching www.textfiles.com =&amp;gt; 208.86.224.90
Connecting to www.textfiles.com (www.textfiles.com)|208.86.224.90|:80... connected.
Created socket 5.
Releasing 0x0000600000a3aae0 (new refcount 1).

---request begin---
GET /100/crossbow HTTP/1.1
Host: www.textfiles.com
User-Agent: Wget/1.21.3
Accept: */*
Accept-Encoding: identity
Connection: Keep-Alive

---request end---
HTTP request sent, awaiting response... 
---response begin---
HTTP/1.1 200 OK
Date: Fri, 11 Nov 2022 10:26:49 GMT
Server: Apache/2.4.54 (FreeBSD) OpenSSL/1.1.1o-freebsd
Last-Modified: Sun, 01 Aug 1999 17:20:50 GMT
ETag: &amp;#34;7210-35109efcee080&amp;#34;
Accept-Ranges: bytes
Content-Length: 29200
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

---response end---
200 OK
Registered socket 5 for persistent reuse.
Length: 29200 (29K)
Saving to: ‘crossbow.1’

crossbow.1                                         100%[================================================================================================================&amp;gt;]  28,52K   112KB/s    in 0,3s    

2022-11-11 18:26:49 (112 KB/s) - ‘crossbow.1’ saved [29200/29200]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We got the HTTP message headers and even some socket lifecycle logs.&lt;/p&gt;
&lt;p&gt;If we wanted to make output less verbose than it usually is, we could use
&lt;code&gt;--no-verbose&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --no-verbose http://www.textfiles.com/100/crossbow
2022-11-11 18:30:32 URL:http://www.textfiles.com/100/crossbow [29200/29200] -&amp;gt; &amp;#34;crossbow.2&amp;#34; [1]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make the wget print no output, run it with &lt;code&gt;--quiet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to download something from legacy FTP server you can do it with wget. Running
wget with directory URL fetches the file list for that directory and converts it to HTML
(don&amp;rsquo;t forget slash character at the end):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget ftp://ftp.cs.brown.edu/pub/
--2022-11-11 18:37:22--  ftp://ftp.cs.brown.edu/pub/
           =&amp;gt; ‘.listing’
Resolving ftp.cs.brown.edu (ftp.cs.brown.edu)... 128.148.32.111
Connecting to ftp.cs.brown.edu (ftp.cs.brown.edu)|128.148.32.111|:21... connected.
Logging in as anonymous ... Logged in!
==&amp;gt; SYST ... done.    ==&amp;gt; PWD ... done.
==&amp;gt; TYPE I ... done.  ==&amp;gt; CWD (1) /pub ... done.
==&amp;gt; PASV ... done.    ==&amp;gt; LIST ... done.

.listing                                               [ &amp;lt;=&amp;gt;                                                                                                             ]   6,16K  --.-KB/s    in 0s      

2022-11-11 18:37:24 (55,2 MB/s) - ‘.listing’ saved [6312]

Removed ‘.listing’.
Wrote HTML-ized index to ‘index.html’ [9267].
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-11-11_18.38.42.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once we have chosen a file to download, we can do it the same way as with HTTP protocol:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget ftp://ftp.cs.brown.edu/pub/README 
--2022-11-11 18:39:53--  ftp://ftp.cs.brown.edu/pub/README
           =&amp;gt; ‘README’
Resolving ftp.cs.brown.edu (ftp.cs.brown.edu)... 128.148.32.111
Connecting to ftp.cs.brown.edu (ftp.cs.brown.edu)|128.148.32.111|:21... connected.
Logging in as anonymous ... Logged in!
==&amp;gt; SYST ... done.    ==&amp;gt; PWD ... done.
==&amp;gt; TYPE I ... done.  ==&amp;gt; CWD (1) /pub ... done.
==&amp;gt; SIZE README ... 7998
==&amp;gt; PASV ... done.    ==&amp;gt; RETR README ... done.
Length: 7998 (7,8K) (unauthoritative)

README                                             100%[================================================================================================================&amp;gt;]   7,81K  --.-KB/s    in 0,006s  

2022-11-11 18:39:56 (1,25 MB/s) - ‘README’ saved [7998]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, what if we wanted wget to follow links and traverse pages recursively to download
entire website? This is called website mirroring and wget has multiple CLI options
to support this use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--adjust-extension&lt;/code&gt; fixes the extension of downloaded HTML pages.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--recursive&lt;/code&gt;, &lt;code&gt;-r&lt;/code&gt; enables recursive crawling (default depth is 5).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--level&lt;/code&gt;, &lt;code&gt;-l&lt;/code&gt; specifies crawl depth.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--convert-links&lt;/code&gt;, &lt;code&gt;-k&lt;/code&gt; converts links in HTML pages so that they are suitable for local viewing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--domains&lt;/code&gt;, &lt;code&gt;-D&lt;/code&gt; specifies a comma-separated list of domains to set boundaries when crawling.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--mirror&lt;/code&gt; enables mirroring mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, we could run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --mirror --domains quotes.toscrape.com,fonts.googleapis.com,fonts.gstatic.com --convert-links  https://quotes.toscrape.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would download a static website for offline browsing. Note, however, that this is pretty
much limited to static websites that are based on simple HTML. This technique will not be
able to replicate the API calls and other stuff that JS-heavy sites are doing in the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-11-11_19.19.36.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, what do we do if there&amp;rsquo;s a login wall or something similar standing in our way? We may need
to submit a login form or press a button to get a cookie that would allow us to download a file.
Fortunately, we can use wget with &lt;code&gt;--post-data&lt;/code&gt; to submit form data and &lt;code&gt;--save-cookies&lt;/code&gt; to save
cookies into file. We also need to use &lt;code&gt;--keep-session-cookies&lt;/code&gt; so that cookies without
expiration date would be saved as well.&lt;/p&gt;
&lt;p&gt;The command to reproduce VMWare Customer Connect login action would be the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --keep-session-cookies --save-cookies cookies.txt --post-data &amp;#34;username=[REDACTED]&amp;amp;password=[REDACTED]&amp;#34; &amp;#34;https://auth.vmware.com/oam/server/auth_cred_submit?Auth-AppID=WMVMWR&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we have some cookies saved into text file. We can use them with &lt;code&gt;--load-cookies&lt;/code&gt; to
download some software we have from VMWare:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --load-cookies cookies.txt https://download3.vmware.com/software/FUS-1224/VMware-Fusion-12.2.4-20071091_x86.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The last use case we want to look into is user wget as a crawler that saves a list of pages it
goes through as it traverses the site. To this purpose we can use &lt;code&gt;--spider&lt;/code&gt; and &lt;code&gt;--delete-after&lt;/code&gt;
with some of the CLI options we utilised when we did the mirroring:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget --no-verbose --recursive --domains quotes.toscrape.com,fonts.googleapis.com,fonts.gstatic.com --spider --delete-after https://quotes.toscrape.com 2&amp;gt;&amp;amp;1 | tee wget_urls.txt 
2022-11-11 20:25:07 URL:https://quotes.toscrape.com/ [11053/11053] -&amp;gt; &amp;#34;quotes.toscrape.com/index.html.tmp.tmp&amp;#34; [1]
https://quotes.toscrape.com/robots.txt:
2022-11-11 20:25:07 ERROR 404: NOT FOUND.
2022-11-11 20:25:10 URL:https://quotes.toscrape.com/static/bootstrap.min.css [125934/125934] -&amp;gt; &amp;#34;quotes.toscrape.com/static/bootstrap.min.css.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:11 URL:https://quotes.toscrape.com/static/main.css [1370/1370] -&amp;gt; &amp;#34;quotes.toscrape.com/static/main.css.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:11 URL:https://quotes.toscrape.com/login [1869/1869] -&amp;gt; &amp;#34;quotes.toscrape.com/login.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:12 URL:http://quotes.toscrape.com/author/Albert-Einstein/ [5324/5324] -&amp;gt; &amp;#34;quotes.toscrape.com/author/Albert-Einstein.tmp.tmp&amp;#34; [1]
http://quotes.toscrape.com/robots.txt:
2022-11-11 20:25:13 ERROR 404: NOT FOUND.
2022-11-11 20:25:14 URL:https://quotes.toscrape.com/tag/change/page/1/ [3851/3851] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/change/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:15 URL:https://quotes.toscrape.com/tag/deep-thoughts/page/1/ [3865/3865] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/deep-thoughts/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:16 URL:https://quotes.toscrape.com/tag/thinking/page/1/ [4687/4687] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/thinking/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:16 URL:https://quotes.toscrape.com/tag/world/page/1/ [3849/3849] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/world/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:18 URL:http://quotes.toscrape.com/author/J-K-Rowling/ [5343/5343] -&amp;gt; &amp;#34;quotes.toscrape.com/author/J-K-Rowling.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:19 URL:https://quotes.toscrape.com/tag/abilities/page/1/ [3638/3638] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/abilities/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:19 URL:https://quotes.toscrape.com/tag/choices/page/1/ [3634/3634] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/choices/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:21 URL:https://quotes.toscrape.com/tag/inspirational/page/1/ [12815/12815] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/inspirational/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:22 URL:https://quotes.toscrape.com/tag/life/page/1/ [12648/12648] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/life/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:22 URL:https://quotes.toscrape.com/tag/live/page/1/ [3942/3942] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/live/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:23 URL:https://quotes.toscrape.com/tag/miracle/page/1/ [3948/3948] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/miracle/page/1/index.html.tmp.tmp&amp;#34; [1]
2022-11-11 20:25:24 URL:https://quotes.toscrape.com/tag/miracles/page/1/ [3950/3950] -&amp;gt; &amp;#34;quotes.toscrape.com/tag/miracles/page/1/index.html.tmp.tmp&amp;#34; [1]
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output needs a little cleanup, but essentially we are getting a list of pages on the site.
This can save your day if for some reason you cannot install hakrawler or similar tool when doing bounty hunting, but really
need some site crawled.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Axiom: just-in-time dynamic infra for offensive security operations</title>
      <link>https://www.trickster.dev/post/axiom-just-in-time-dynamic-infra-for-offensive-security-operations/</link>
      <pubDate>Mon, 07 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/axiom-just-in-time-dynamic-infra-for-offensive-security-operations/</guid>
      <description>&lt;p&gt;When doing recon part of penetration testing or bug bounty hunting engagements one
may want to run various tools (such as port scanners, crawlers, vulnerability scanners,
headless browsers and so on) in a VPS environment that will no longer be needed when
the task at hand is complete. For large-scale scanning it is highly desirable to spread
the workload across many servers. To address these need, &lt;a href=&#34;https://github.com/pry0cc/axiom&#34;&gt;axiom&lt;/a&gt;
was developed. Axiom is a dynamic infrastructure framework designed for quick setup and
teardown of reproducible infrastructure.&lt;/p&gt;
&lt;p&gt;0xtavian, one of the founding developers of this project, was doing security work and
has found that there&amp;rsquo;s a great variety of security tools, but no established way to
use them in unified way to easily set up an VPS for hacking. Furthermore, security
centric Linux distros such as Kali provided a variety of tools out of the box, but
lacked tooling for truly scalable bounty hunting and pentesting. These two problems
motivated 0xtavian to create Axiom with pry0cc, another developer in security space.&lt;/p&gt;
&lt;p&gt;Let us install Axiom into VPS instance running one of the operating systems
that support the easy install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debian Linux&lt;/li&gt;
&lt;li&gt;Ubuntu Linux&lt;/li&gt;
&lt;li&gt;Kali Linux&lt;/li&gt;
&lt;li&gt;Windows (with Ubuntu on WSL)&lt;/li&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am recommending doing this in a VPS environment because Axiom installs some
packages on your local system through APT or Homebrew that you may not need otherwise.
Furthermore, you may want to run Axiom scans in tmux session on the server for longer
scans that may take overnight or multiple days.&lt;/p&gt;
&lt;p&gt;The easy install command is the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bash &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;curl -s https://raw.githubusercontent.com/pry0cc/axiom/master/interact/axiom-configure&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will download Axiom source code, install dependencies, establish API connection to
cloud provider (Digital Ocean, Linode, Azure, IBM Cloud or AWS) - you will be asked to provide
API credentials for your cloud providers, as well as some other details - and use Packer to create
VPS image with all the tooling to be used for scanning. For the purpose of trying it out choose
the default provisioner.&lt;/p&gt;
&lt;p&gt;Once installation is complete, let us start small by setting up a single VPS with &lt;code&gt;axiom-init&lt;/code&gt;
command (this should take a few minutes):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-init first
Initializing &amp;#39;first&amp;#39; at &amp;#39;lon1&amp;#39; with image &amp;#39;axiom-default-1666592377&amp;#39;
INITIALIZING IN 5 SECONDS, CTRL+C to quit... 
Initialized instance &amp;#39;first&amp;#39; at &amp;#39;[REDACTED]&amp;#39;!
To connect, run &amp;#39;axiom-ssh first&amp;#39; or &amp;#39;axiom-connect&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let us run a single command through &lt;code&gt;axiom-exec&lt;/code&gt; on the worker server:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-exec &amp;#39;dig google.com&amp;#39;
=====================================================
Interlace v1.9.6	by Michael Skelton (@codingo_)
                  	&amp;amp; Sajeeb Lohani (@sml555_)
=====================================================
  0%|                                                                                                                                                                                 | 0/1 [00:00&amp;lt;?, ?it/s][11:19:50] [THREAD] [ssh -F /root/.axiom/tmp/exec/axiom-exec+1667819982/sshconfig -o StrictHostKeyChecking=no -o PasswordAuthentication=no first &amp;#39;tmux new -d -s axiom-exec+1667819982 &amp;amp;&amp;amp; tmux send-keys -t axiom-exec+1667819982 &amp;#34;bash -i /tmp/exec/axiom-exec+1667819982/command  &amp;gt; &amp;gt;(tee -a /tmp/exec/axiom-exec+1667819982/stdout.log) 2&amp;gt; &amp;gt;(tee -a /tmp/exec/axiom-exec+1667819982/stderr.log &amp;gt;&amp;amp;2) ; touch /tmp/exec/axiom-exec+1667819982/first&amp;#34; ENTER &amp;#39; &amp;#34;&amp;amp;&amp;amp; tmux send-keys -t axiom-exec+1667819982 exit ENTER&amp;#34;] Added to Queue 
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00&amp;lt;00:00, 16.72it/s]

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.16.1-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; google.com
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 7177
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		31	IN	A	142.250.187.206

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Mon Nov 07 11:19:50 UTC 2022
;; MSG SIZE  rcvd: 55
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Axiom ships with bunch of recipes (modules) that integrate various scanning tools into the system.
Let us try running Nmap on the worker server. First, we need to create an input file
with target hostname(s):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;scanme.nmap.org&amp;#34; &amp;gt;&amp;gt; target.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then we can run &lt;code&gt;axiom-scan&lt;/code&gt; to run a scan with Nmap module and our text file as input:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-scan target.txt -m nmap -o nmap_out.txt -oG nmap_out.txt
sorting and greping unique for: &amp;#39;nmap_out.txt&amp;#39;
              _
  ____ __  __(_)___  ____ ___        ______________ _____
 / __ `/ |/_/ / __ \/ __ `__ \______/ ___/ ___/ __ `/ __ \
/ /_/ /&amp;gt;  &amp;lt;/ / /_/ / / / / / /_____(__  ) /__/ /_/ / / / /
\__,_/_/|_/_/\____/_/ /_/ /_/     /____/\___/\__,_/_/ /_/

                                    @pry0cc
                                 &amp;amp; @0xtavian

creating scan working directory at : /home/op/scan/nmap+166782070320617/
module: [ nmap ] | module args: [  ] | input: [ 1 lines ] |
instances:  1  [ first ] |
command: [ sudo nmap -iL input -oG output ] | ext: [ txt ] | threads: [ null ]
spliting and distributing input file...
[ OK ]
Starting Nmap 7.92 ( https://nmap.org ) at 2022-11-07 11:31 UTC
Stats: 0:00:00 elapsed; 0 hosts completed (0 up), 1 undergoing Ping Scan
Ping Scan Timing: About 100.00% done; ETC: 11:31 (0:00:00 remaining)
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.15s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 996 closed tcp ports (reset)
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
9929/tcp  open  nping-echo
31337/tcp open  Elite

Nmap done: 1 IP address (1 host up) scanned in 2.53 seconds
first scan finished
Mode set to txt.. Sorting unique.
Appending axiom-scan runtime statistics to : /root/.axiom/stats.log
module: [ nmap ] | module args: [  ] | instances: [ 1 ] | targets: [ 1 targets ] | results: [ 4 results ] |
runtime: [ 00h:00m:41s ] | date: [ Mon Nov  7 11:31:43 UTC 2022 ] | id: [ nmap+166782070320617 ] |
output: [ /root/nmap_out.txt ] | log: [ /root/.axiom/logs/nmap+166782070320617 ] | remote: [ /home/op/scan/nmap+166782070320617 ]  |
command: [ sudo nmap -iL input -oG output ] | ext: [ txt ] | threads: [ null ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We used &lt;code&gt;-m&lt;/code&gt; to select an Axiom module and &lt;code&gt;-o&lt;/code&gt; for output file. After that, we can
pass the CLI arguments to the tool that Axiom would run - we used &lt;code&gt;-oG&lt;/code&gt; for selecting
a greppable output.&lt;/p&gt;
&lt;p&gt;To list currently running instances, we can run &lt;code&gt;axiom-ls&lt;/code&gt;. To make a backup of an instance,
we can use &lt;code&gt;axiom-backup&lt;/code&gt; with the name of an instance (to be restored using &lt;code&gt;axiom-restore&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;But for now, we want to simply remove an instance we have created. We are doing this with
&lt;code&gt;axiom-rm&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-rm first
Deleting &amp;#39;first&amp;#39;...
Warning: Are you sure you want to delete this Droplet? (y/N) ? y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we want to go bigger. To fully benefit from Axiom we want to create a fleet -
a set of worker servers that will perform the workloads in a load-balanced
way. Let us create a fleet of 8 servers.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-fleet -i 8
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We will try running some scans against &lt;a href=&#34;https://public-firing-range.appspot.com/&#34;&gt;Firing Range&lt;/a&gt; - a deliberately insecure
website developed for testing web security scanners. Let us put the corresponding
URL to target.txt file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;https://public-firing-range.appspot.com/&amp;#34; &amp;gt; target.txt 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To crawl the site and save all the page URLs into a textfile, let us run
&lt;a href=&#34;https://github.com/hakluke/hakrawler&#34;&gt;Hakrawler&lt;/a&gt; through Axiom:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-scan target.txt -m hakrawler -o crawl.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That gave us a list of 287 URLs:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wc -l crawl.txt 
287 crawl.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This new list can be used as input file for the next scan that will entail running
&lt;a href=&#34;https://github.com/projectdiscovery/nuclei&#34;&gt;Nuclei&lt;/a&gt; vulnerability scanner:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-scan crawl.txt -m nuclei -o nuclei.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Axiom now splits the input file into even parts across all worker server so that workload
is distributed evenly, runs Nuclei on them via SSH, waits for it to finish, downloads the
results, deduplicates them and saves into a textfile. Large scans can take a while even
when done across several servers, thus I recommend doing them from a tmux session.&lt;/p&gt;
&lt;p&gt;There will be quite a bit of verbose output in the terminal that mostly matters for
seeing progress and troubleshooting, thus is not reproduced here. What matters is stuff
in the output file. Not everything here is something that warrants further attention,
but some of the entries will be marked as medium or high in severity and in real
engagement would need to be investigated further.&lt;/p&gt;
&lt;p&gt;To remove the fleet, we can use &lt;code&gt;axiom-rm&lt;/code&gt; with server name prefix:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ axiom-rm &amp;#34;kalam*&amp;#34; -f
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To daisy-chain several scans into larger workload one can trivially write a Bash
script consisting of Axiom commands in the order you would run them on the shell.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping legacy ASP.Net sites with Scrapy: a real world example</title>
      <link>https://www.trickster.dev/post/scraping-legacy-asp-net-site-with-scrapy-a-real-example/</link>
      <pubDate>Wed, 12 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-legacy-asp-net-site-with-scrapy-a-real-example/</guid>
      <description>&lt;p&gt;Legacy ASP.Net sites are some of the most difficult ones to deal with for web scraper
developers due to their archaic state management mechanism that entails hidden form
submissions via POST requests. This stuff is not easy to reproduce programmatically.
In this post we will discuss a real world example of scraping a site based on some old
version of ASP.Net technology.&lt;/p&gt;
&lt;p&gt;The site in question is &lt;a href=&#34;https://publicaccess.claytoncountyga.gov/search/advancedsearch.aspx?mode=sales&#34;&gt;publicaccess.claytoncounty.gov&lt;/a&gt; -
a real estate record website for Clayton County, Georgia.
Our objective is to scrape all the real estate sales records from this site (fun fact:
there&amp;rsquo;s plenty of juicy data various branches of US government publish on
official sites that can be scraped and monetised). We will be using Scrapy framework.
It is assumed that the reader is already familiar with this framework, thus some of
the basic technical details will be skipped. Data we want to scrape will correspond
to the following subclass of &lt;code&gt;scrapy.Item&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SalesItem&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# define the fields for your item here like:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# name = scrapy.Field()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_zip5 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_street_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_city &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_county &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sale_datetime &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sale_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seller_1_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buyer_1_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_num_units &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_year_built &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    source_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    book &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    transfer_deed_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_township &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_lat &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    property_lon &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sale_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    deed_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_num_stories &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_num_beds &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_num_baths &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_area_sqft &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_assessed_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_assessed_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_area_acres &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_area_sqft &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_assessed_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seller_2_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buyer_2_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_assessed_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seller_1_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seller_2_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buyer_1_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buyer_2_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total_assessed_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total_appraised_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_appraised_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    building_appraised_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    land_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We don&amp;rsquo;t need to fill all the fields here, just the ones that are available from
the source website.&lt;/p&gt;
&lt;p&gt;When we endeavor to scrape a non-trivial site we first need to do some exploration
to learn about the site so that we could strategise and plan development efforts.&lt;/p&gt;
&lt;p&gt;If we load the site without any cookies being present in the browser we will be
redirected to disclaimer page that the county has made to cover its ass legally.
This will also give us the very first cookie with ASP.Net session ID.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_16.57.02.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inspecting the DOM tree reveals that pressing &amp;ldquo;Agree&amp;rdquo; button on this page would
cause a hidden form to be submitted. This form contains &lt;code&gt;__VIEWSTATE&lt;/code&gt;,
&lt;code&gt;__VIEWSTATEGENERATOR&lt;/code&gt; and &lt;code&gt;__EVENTVALIDATION&lt;/code&gt; fields with pre-filled valued
that deal with client session state management. We will have to submit these
values as they are without any changes when we are going to reproduce the button
press in the Scrapy spider. The same applies to our further interactions with the
site.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_16.58.27.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pressing the &amp;ldquo;Agree&amp;rdquo; button submits the hidden form via POST requests and redirects
us to a page with some search functionality. At this point we want to get a complete
list of all the real estate property records that are available, but merely pressing
the search button on this form tells us to fill in at least one search criteria.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.04.07.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the page header there&amp;rsquo;s links to other search pages. &amp;ldquo;Real Property Search&amp;rdquo; is the
default one and &amp;ldquo;Advanced Search&amp;rdquo; provides us with more options/criteria to search records
by. We will be using this one. Others are not that relevant to us. &amp;ldquo;Map Search&amp;rdquo; didn&amp;rsquo;t
even work for me and &amp;ldquo;Sales Search&amp;rdquo; is similar to &amp;ldquo;Advanced Search&amp;rdquo;, but with less
stuff to search by. So we go to &amp;ldquo;Advanced Search&amp;rdquo; and try to get all records from
1900 January 1 to 2022 December 31 (it does not allow us to search earlier in time
than 1900). Note, however, that the default form is not completely useless to us - it
can be used with ID of specific parcel to check the scraped data manually since
we will not be able to fill unique URL into &lt;code&gt;source_url&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.16.37.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This did give us some search results, but not all of them as the site caps the number
of results to 10 000. This is a common problem when doing web scraping and it can
be solved by doing search what I call search space sharding. Instead of searching for
entire recorded history, we will search in smaller increments. A little experimentation
reveals that searching for one month at a time allows each search result list to fit
within the limit of 10 000 results.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.23.04.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us go a bit deeper now. What happens when Search button is pressed? Well, we have another
form submission with bunch of crazy fields in the payload. Some of values for this form
are available in the original HTML page. Some are not and are filled by client side
JS code instead (based on search criteria the user entered). Some can be safely ignored.
A key challenge in scraping ASP.Net sites is reproducing this kind of requests accurately
enough. We will be dealing with this later.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.26.25.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What if we click one of the rows in the results table? There is another POST request to
the same endpoint, but somewhat different form data being submitted. For example,
&lt;code&gt;hdLink&lt;/code&gt; contains a relative link to a page we get redirected to. We don&amp;rsquo;t get a unique
URL to parcel page as accessing this exact page is predicated on client state earlier
in the session and the form data in the request that happens when result row is clicked.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.41.25.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So where does it get the value for &lt;code&gt;hdLink&lt;/code&gt; from? If we go to search results page and
inspect the row, we will find that each row (&lt;code&gt;tr&lt;/code&gt; element) has &lt;code&gt;onclick&lt;/code&gt; property
with JS snippet like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;javascript&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;selectSearchRow&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;../Datalets/Datalet.aspx?sIndex=3&amp;amp;idx=1&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_17.43.10.png&#34;&gt;Screenshot 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can scrape this from HTML document and use it in our code. There will be more finer
parts of the POST request that we need to get right, but let us leave them for later.&lt;/p&gt;
&lt;p&gt;When we go see the result we find that data about parcel is broken down across several
pages. The first page we get is &amp;ldquo;Tax Commisioner Summary&amp;rdquo; page that contains some
information on property address, most common owner, property class and so on. There
are links on the side to further pages (no hidden forms this time, but we&amp;rsquo;re not
getting unique URLs either).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_18.09.27.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We will be scraping these pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Tax Commissioner Summary&amp;rdquo; for &lt;code&gt;property_id&lt;/code&gt;, &lt;code&gt;property_street_address&lt;/code&gt; and
&lt;code&gt;property_type&lt;/code&gt; fields.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Residential&amp;rdquo; - &lt;code&gt;building_year_built&lt;/code&gt;, &lt;code&gt;building_num_beds&lt;/code&gt;, &lt;code&gt;building_num_baths&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Value History&amp;rdquo; - assessed/appraised monetary values (land, buildings and total).
We will need to make sure that the year of appraisal/assessment matches that of a
sale for each row we output.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Land&amp;rdquo; - &lt;code&gt;land_area_acres&lt;/code&gt;, &lt;code&gt;land_area_sqft&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Sales&amp;rdquo; - everything about parcel sale: &lt;code&gt;sale_datetime&lt;/code&gt;, &lt;code&gt;sale_price&lt;/code&gt;, &lt;code&gt;seller_1_name&lt;/code&gt;,
&lt;code&gt;buyer_1_name&lt;/code&gt;, &lt;code&gt;seller_2_name&lt;/code&gt;, &lt;code&gt;buyer_2_name&lt;/code&gt;, &lt;code&gt;book&lt;/code&gt;, &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;transfer_deed_type&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s one sale per page, but sale pages form a linked list through the little navigation
links that we can use.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_18.23.35.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The same applies to parcels - there&amp;rsquo;s a way to navigate between adjacent search results
(based on form submission this time - we will need to work out details here).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_18.24.56.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This lets us have a linear flow between pages without the inconvenience of backtracking.&lt;/p&gt;
&lt;p&gt;Based on what we learned so far the scraping strategy will be as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shard search space by searching for one month worth of results per search query.
&lt;ul&gt;
&lt;li&gt;For each search, go to the first result (reproduce form submission).
&lt;ul&gt;
&lt;li&gt;Scrape data from &amp;ldquo;Tax Commisioner Summary&amp;rdquo;, &amp;ldquo;Residential&amp;rdquo;, &amp;ldquo;Value History&amp;rdquo;, &amp;ldquo;Land&amp;rdquo;
pages. Use &lt;code&gt;meta&lt;/code&gt; dictionary to pass information between requests so that stuff
is being scraped incrementally.&lt;/li&gt;
&lt;li&gt;Scrape sales data by traversing linked list of sales records. Create items here by
filling stuff from sales record and stuff from earlier. Make sure that appraised/
assessed values match the sales year. Refrain from including them if it&amp;rsquo;s not
possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go to next parcel so that list of parcels is traversed to the end.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Due to the nature of legacy ASP.Net technology we cannot benefit much from built-in
concurrency of Scrapy at session level - we risk messing up the session if we don&amp;rsquo;t
load pages one at a time. However we can have multiple sessions at the same time - we
will be writing some extra code to make the scraping faster and more reliable.&lt;/p&gt;
&lt;p&gt;Let us go through the Scrapy spider code now, one step at a time.&lt;/p&gt;
&lt;p&gt;We start scraping by loading the disclaimer page (this will give us the ASP.Net session cookie)
and reproducing the action of user pressing the &amp;ldquo;Agree&amp;rdquo; button:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.http &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; FormRequest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClaytonSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clayton&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;publicaccess.claytoncountyga.gov&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/Search/Disclaimer.aspx?FromUrl=../search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1939&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    county &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CLAYTON&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_disclaimer_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_disclaimer_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_response(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formxpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;Form1&amp;#34;]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clickdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;btAgree&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case we are able to use &lt;code&gt;from_response&lt;/code&gt; utility method of &lt;code&gt;FormRequest&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;We have skipped this part for now, but &lt;code&gt;shards&lt;/code&gt; list will be filled with year-month tuples
to be used in search step.&lt;/p&gt;
&lt;p&gt;Callback to request that submits the agreement to legal terms is &lt;code&gt;parse_form_page()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_form_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        year, month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Year: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, month: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        days_in_month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; calendar&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;monthrange(year, month)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; date(year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;year, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;month, day&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; date(year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;year, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;month, day&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;days_in_month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;make_search_form_request(response, start_date, end_date)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have a form we can fill, thus we remove one search shard from the list and use it to
fill the search form. Since we will be doing this a lot, we have another method for this
purpose:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;make_search_form_request&lt;/span&gt;(self, response, start_date, end_date):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stringify_date(start_date)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stringify_date(end_date)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;salesdate|&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(start_date_str, end_date_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal1&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal1$dateInput&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;txtCrit&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal2&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal2$dateInput&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;txtCrit2&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PageNum&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PageSize&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdCriteriaTypes&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;N|N|C|C|C|N|C|C|N|D|N|N|C|C|C|N|N&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_search_result_list,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see that it calls a helper method to extract form fields already present in the
page, but also fills bunch of fields explicitly. This is significant. How do I know that
these form fields have to be filled in this exact way? Well, through experimentation,
persistance and plain old paying the fucking attention. It took me quite a bit of effort to
work these exact details out, but there was nothing magical or secret in doing that. There
was multiple takes of painstakingly comparing what &lt;code&gt;logging.debug()&lt;/code&gt; logs out with what I
see in the Chrome dev tools until I got it working correctly.&lt;/p&gt;
&lt;p&gt;Next, we get to parse the HTML page with result list:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_search_result_list&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        first_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[@class=&amp;#34;SearchResults&amp;#34;][1]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(first_row) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page, dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(first_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        onclick &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; first_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;onclick&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rel_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; onclick&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;javascript:selectSearchRow(&amp;#39;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;)&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdLink&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rel_url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdAction&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Link&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdSelectAllChecked&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]/@action&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, action)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            form_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_main_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We try to extract the first row of the results table. If it&amp;rsquo;s not there we take it that there&amp;rsquo;s
no records for the given month, thus we move on to the next shard in the list. If it is there,
we extract &lt;code&gt;onclick&lt;/code&gt; property, do some basic string manipulation to extract the relative link
from that, fill the hidden form and submit it. This will give us a parcel summary page on the
next callback.&lt;/p&gt;
&lt;p&gt;The next callback is &lt;code&gt;parse_property_main_page()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_main_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@id=&amp;#34;hdXPin&amp;#34;]/@value&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_street_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@class=&amp;#34;DataletHeaderBottom&amp;#34;][last()]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_street_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            property_street_address&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )  &lt;span style=&#34;color:#75715e&#34;&gt;# https://stackoverflow.com/a/1546251&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SalesItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;state
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_id&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_street_address&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_street_address
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_county&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;county
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[contains(text(),&amp;#34;Property Class&amp;#34;)]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        residential_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Residential&amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            residential_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_residential_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we do some XPath queries to extract some fields into newly created item object. We also
extract link to &amp;ldquo;Residential&amp;rdquo; page and go there. Since we&amp;rsquo;re not done filling the item, we
pass it along the request by using the meta dictionary.&lt;/p&gt;
&lt;p&gt;At next callback we scrape the &amp;ldquo;Residential&amp;rdquo; page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_residential_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@id=&amp;#34;hdXPin&amp;#34;]/@value&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; parid &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_id&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_year_built&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Year Built&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_num_beds&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Bedrooms&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_num_baths&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Full Baths&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        value_history_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Value History&amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            value_history_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_value_history_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We get the item object from &lt;code&gt;response.meta&lt;/code&gt;, fill in some more fields and pass it along the
next request that will fetch us &amp;ldquo;Value History&amp;rdquo; page that we parse at next callback:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_value_history_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_values_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//table[@id=&amp;#34;Appraised Values&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_values_table &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; appr_values_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./tr&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./td/text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_header &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    appr_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(row) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(zip(appr_header, row))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                appr_rows[year] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(appr_rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_values_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//table[@id=&amp;#34;Assessed Values&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_values_table &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; as_values_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./tr&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./td/text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_header &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    as_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(row) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(zip(as_header, row))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                as_rows[year] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(as_rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;: appr_rows, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;: as_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        land_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Land&amp;#34;]]/@href&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            land_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta_dict,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_land_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have two tables. One for assessed values and one for appraised values (these are different
ways to evaluate real estate prices). In each row there will be year and numbers for land, building
and overall value. For some parcels these tables will be missing, which means we have to
gracefully skip this page without crashing the scraping job. So what we do is look for tables
via XPath queries, scrape their rows and store the results in dictionaries indexed by years.
Since this data will be needed later we pass it along the request through meta dictionary.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-11_18.16.50.png&#34;&gt;Screenshot 12&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At next callback, &amp;ldquo;Land&amp;rdquo; page is scraped:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_land_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Acres&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; round(float(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_sqft&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Square Feet&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Land Type&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;: appr_rows, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;: as_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sales_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Sales&amp;#34;]]/@href&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sales_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta_dict,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_sales_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We extract some more fields into the item object and go to the &amp;ldquo;Sales&amp;rdquo; page, like we did
in previous steps. Note that &lt;code&gt;meta&lt;/code&gt; dictionary has to be recreated like it is being done here.
One cannot take a reference to &lt;code&gt;meta&lt;/code&gt; from response object, change something it a bit and pass
it into new request object, as Scrapy framework also sets some key-value pairs in this
dictionary for internal use. Not recreating the meta dictionary will mess up the Scrapy internals.&lt;/p&gt;
&lt;p&gt;So finally we get to scrape the sales data at in &lt;code&gt;parse_property_sales_page()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_sales_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Sale Date&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_datetime(sale_date_str)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;T&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sale_datetime&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sale_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sale_price&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Sale Price&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;seller_1_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantor&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;buyer_1_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantee&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;seller_2_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantor 2&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;buyer_2_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantee 2&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;book&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Deed Book&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Deed Page&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;transfer_deed_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Instrument Type&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;source_url&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_rows&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Land&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Building&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_rows&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Buidling&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_date&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Land&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, nothing particularly fancy here. Just doing XPath queries and string manipulation to fill
some more fields in the item object. We also access the assessed/appraised value dictionaries
we created earlier to fill the relevant numbers for the same year as the sale (if possible).&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s more. We also have to traverse to next page, to get data for next sale.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_sale_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./i[@class=&amp;#34;icon-angle-right &amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_sale_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                next_sale_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_sales_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We extract the link to next sale and follow it. But why did we do &lt;code&gt;return&lt;/code&gt; after yielding
the new request? Well, that&amp;rsquo;s because at this point we merely want to go the page of next sale
record. Further in the method we have code that does form submission to go the summary page
of next parcel, so this entire shebang can be done again for next property:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        to_from_input_text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@name=&amp;#34;DTLNavigator$txtFromTo&amp;#34;]/@value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        idx, total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; to_from_input_text&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; of &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(idx)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(total)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; idx &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; total:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;del&lt;/span&gt; form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;del&lt;/span&gt; form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageLast&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext.x&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# XXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext.y&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(idx &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdMode&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEK_PROFILE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]/@action&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, action)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode=sales&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode=dek_profile&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            form_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_main_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We take care to modify URL and &lt;code&gt;hdMode&lt;/code&gt; field to make it go to the summary page, not the &amp;ldquo;Sales&amp;rdquo;
page. We also address the possibility of the current parcel being the last in the list, which
would warrant going to the next scheduled search space shard.&lt;/p&gt;
&lt;p&gt;You can notice that requests tend to be instantiated with &lt;code&gt;dont_filter&lt;/code&gt; flag. This is because
site being scraped does not have unique URLs for many pages, which would trigger Scrapy
deduplication mechanism needlessly, thus cancelling the scraping job before it was properly
finished.&lt;/p&gt;
&lt;p&gt;One more thing I would like to explain is how the scraping was parallelised to allow running
multiple client sessions in parallel and retry the search shard scraping if needed.
The spider has the following constructor method:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, stats_filepath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stats_filepath &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stats_filepath
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; year &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; month &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(year, month)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; year &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_year, datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;today()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; month &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                shard &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(shard)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If year and month values are provided through Scrapy CLI it will plan to scrape a single
month worth of data. Otherwise, it will plan to scrape the entire data, one month at a time.
How does this help us? Well, there is a helper script that will launch bunch of Scrapy processes
in parallel and retry them if needed (based on some error conditions that may happen due to
site instability):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; multiprocessing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; subprocess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spider_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run_scrapy_subprocess&lt;/span&gt;(year, month):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.csv&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(spider_name, year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isfile(csv_filename):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;unlink(csv_filename)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log_filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.log&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isfile(log_filename):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;unlink(log_filename)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stats-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.json&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cmd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;runspider&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-o&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_filename,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--logfile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log_filename,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;housingprices/spiders/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.py&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(spider_name),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;year=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;month=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(month),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stats_filepath=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(stats_filename),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(cmd)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; subprocess&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run(cmd)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;returncode &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(stats_filename, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(json_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; stats&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_count/ERROR&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stats&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;downloader/response_status_count/500&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; stats&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;downloader/response_status_count/500&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stats&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;downloader/response_status_count/501&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; stats&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;downloader/response_status_count/501&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;perform_task&lt;/span&gt;(shard):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    year, month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; shard
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Year: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, month: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        success &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; run_scrapy_subprocess(year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; success:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; succeeded&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; failed - retrying&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; spider_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;spider_name&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    spider_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    from_year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1901&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    to_year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2022&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; year &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(from_year, to_year &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; month &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            shard &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            shards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(shard)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pool &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; multiprocessing&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Pool(&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pool&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;map(perform_task, shards)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I was running this on VPS inside tmux session to perform the scraping. One thing you may
notice is that it sets &lt;code&gt;stats_filepath&lt;/code&gt; to file name unique to scraping job and read JSON from
this file to check if scraping has succceeded. This requires some setup in Scrapy project.
One thing we need is following piece of code to save the scraping stats when spider is done
scraping:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Based on:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# https://stackoverflow.com/questions/61402939/scrapy-how-to-save-crawling-statistics-to-json-file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.statscollectors &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; StatsCollector
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.utils.serialize &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ScrapyJSONEncoder
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStatsCollector&lt;/span&gt;(StatsCollector):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_persist_stats&lt;/span&gt;(self, stats, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        encoder &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ScrapyJSONEncoder()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stats.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; spider&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stats_filepath &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            filename &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; spider&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stats_filepath
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(filename, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; encoder&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;encode(stats)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;write(data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This has to be enabled in the settings as the linked Stack Overflow answer describes.&lt;/p&gt;
&lt;p&gt;To sum it up, a complete Scrapy spider code is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.http &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; FormRequest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; dateutil.parser &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; parse &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; parse_datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; calendar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime, date
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urljoin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; housingprices.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; SalesItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ClaytonSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clayton&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;publicaccess.claytoncountyga.gov&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/Search/Disclaimer.aspx?FromUrl=../search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1939&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    county &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CLAYTON&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stats_filepath &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, stats_filepath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stats_filepath &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stats_filepath
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; year &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; month &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(year, month)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; year &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_year, datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;today()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; month &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                shard &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (year, month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(shard)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_disclaimer_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_disclaimer_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_response(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formxpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;Form1&amp;#34;]&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            clickdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;btAgree&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stringify_date&lt;/span&gt;(self, d):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{:02d}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{:02d}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{:04d}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(d&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;month, d&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;day, d&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extract_form&lt;/span&gt;(self, response, form_xpath):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hidden_input &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(form_xpath)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//input&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hidden_input&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; name &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hidden_input&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; value &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            form_data[name] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; form_data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;make_search_form_request&lt;/span&gt;(self, response, start_date, end_date):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stringify_date(start_date)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stringify_date(end_date)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;salesdate|&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(start_date_str, end_date_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal1&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal1$dateInput&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;txtCrit&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal2&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ctl01$cal2$dateInput&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;txtCrit2&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; end_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PageNum&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PageSize&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdCriteriaTypes&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;N|N|C|C|C|N|C|C|N|D|N|N|C|C|C|N|N&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_search_result_list,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_form_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        year, month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Year: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, month: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(year, month))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        days_in_month &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; calendar&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;monthrange(year, month)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; date(year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;year, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;month, day&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end_date &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; date(year&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;year, month&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;month, day&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;days_in_month)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;make_search_form_request(response, start_date, end_date)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_search_result_list&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        first_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[@class=&amp;#34;SearchResults&amp;#34;][1]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(first_row) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page, dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(first_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        onclick &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; first_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;onclick&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rel_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; onclick&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;javascript:selectSearchRow(&amp;#39;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;)&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdLink&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rel_url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdAction&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Link&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdSelectAllChecked&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sCriteria&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]/@action&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, action)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            form_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_main_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_main_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@id=&amp;#34;hdXPin&amp;#34;]/@value&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_street_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//td[@class=&amp;#34;DataletHeaderBottom&amp;#34;][last()]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_street_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            property_street_address&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )  &lt;span style=&#34;color:#75715e&#34;&gt;# https://stackoverflow.com/a/1546251&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SalesItem()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;state
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_id&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_street_address&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_street_address
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_county&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;county
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[contains(text(),&amp;#34;Property Class&amp;#34;)]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        residential_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Residential&amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            residential_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_residential_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_residential_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@id=&amp;#34;hdXPin&amp;#34;]/@value&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; parid &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;property_id&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_year_built&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Year Built&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_num_beds&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Bedrooms&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_num_baths&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Full Baths&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        value_history_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Value History&amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            value_history_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_value_history_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_value_history_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_values_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//table[@id=&amp;#34;Appraised Values&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_values_table &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; appr_values_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./tr&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./td/text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_header &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    appr_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(row) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(zip(appr_header, row))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                appr_rows[year] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(appr_rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_values_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//table[@id=&amp;#34;Assessed Values&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_values_table &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; as_values_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./tr&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./td/text()&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_header &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    as_header &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(row) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(zip(as_header, row))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                as_rows[year] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(as_rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;: appr_rows, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;: as_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        land_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Land&amp;#34;]]/@href&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            land_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta_dict,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_land_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_land_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Acres&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; round(float(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_acres&amp;#34;&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_area_sqft&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Square Feet&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Land Type&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        meta_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;: appr_rows, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;: as_rows}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sales_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./span[text()=&amp;#34;Sales&amp;#34;]]/@href&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sales_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meta_dict,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_sales_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_sales_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;appr_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;as_rows&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Sale Date&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sale_date_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_datetime(sale_date_str)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;T&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sale_datetime&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sale_date_str
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sale_price&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Sale Price&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;seller_1_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantor&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;buyer_1_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantee&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;seller_2_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantor 2&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;buyer_2_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Grantee 2&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\xa0&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;book&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Deed Book&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Deed Page&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;transfer_deed_type&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;Instrument Type&amp;#34;]]/td[@class=&amp;#34;DataletData&amp;#34;]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;source_url&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://publicaccess.claytoncountyga.gov/search/advancedsearch.aspx?mode=advanced&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        appr_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_rows&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; appr_row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Land&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; appr_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Building&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_appraised_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        as_row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_rows&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; as_row &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Buidling&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_date&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sale_date&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;year
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Land&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; as_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;building_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;land_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_assessed_value&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_sale_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//a[./i[@class=&amp;#34;icon-angle-right &amp;#34;]]/@href&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_sale_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                next_sale_link,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_sales_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        to_from_input_text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//input[@name=&amp;#34;DTLNavigator$txtFromTo&amp;#34;]/@value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        idx, total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; to_from_input_text&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; of &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(idx)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(total)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; idx &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; total:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_form_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                meta&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extract_form(response, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;del&lt;/span&gt; form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;del&lt;/span&gt; form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageLast&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext.x&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# XXX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DTLNavigator$imageNext.y&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(idx &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hdMode&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEK_PROFILE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//form[@name=&amp;#34;frmMain&amp;#34;]/@action&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, action)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode=sales&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode=dek_profile&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; FormRequest(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            form_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            formdata&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_main_page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dont_filter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One last thing of interest are the following lines in HTML source code of the site
being scraped:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;http-equiv&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Description&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;iasWorld&amp;#39;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Copyright&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Tyler Technologies Inc, Akanda Solutions, LLC&amp;#39;&lt;/span&gt;&amp;gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This points us to a product named iasWorld by a company called Tyler Technologies, Inc. That
means there&amp;rsquo;s more sites like this and we can reuse the above code to scrape them (some
modifications will be needed).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Making heuristics smarter with GPT-3</title>
      <link>https://www.trickster.dev/post/making-heuristics-smarter-with-gpt3/</link>
      <pubDate>Sun, 09 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/making-heuristics-smarter-with-gpt3/</guid>
      <description>&lt;p&gt;Sometimes data being scraped is a little fuzzy - easy to understand for human mind,
but not strict and formal enough to be easily tractable by algorithms. This can
get in a way of analysing the data or basing further automations off it. This is
where heuristics come in. A heuristic is an inexact, but quick way to solve a
problem. We will discuss how GPT-3 API can be used to implement heuristic
techniques to deal with somewhat fuzzy data that might otherwise require a
human to take a look into it.&lt;/p&gt;
&lt;p&gt;Earlier this year I participated in data bounty that involved sourcing museum
collection data and wrangling all the records into a single database schema.
Some websites provided a great deal of museum collection data, but they also
provided data on private art collections, libraries, universities, schools
and other entitities that were neither art galleries nor museums, meaning that
some data was out of scope. This presented a bit of a problem since some data
needed to be scraped and submitted, but some had to be ignored based on institution
name alone.&lt;/p&gt;
&lt;p&gt;So what do we do? Well, given that GPT-3 was trained on massive amounts of text,
it should be able to tell based on institution name if it is of interest for
scraping. We just need to ask it properly. For example, we could give it a
prompt:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Answer YES if &amp;#34;National Gallery of Art&amp;#34; is a gallery or museum. Answer NO otherwise.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To which it answers:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The part of the prompt that we keep changing is the substring between duoble quotes.
We keep everything else the same. We also set the temperature gauge to 0 as we need
no randomisation/&amp;ldquo;creativity&amp;rdquo; on AI part to answer this simple question.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-09_10.42.58.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now that we got the prompt right we can use OpenAI client library to develop a Python
script that will filter a scraped list of institutions to shortlist them for further
scraping.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; openai
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openai&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;api_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_answer&lt;/span&gt;(institution_name):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Archive&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;NO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Library&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Libraries&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;NO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Private Collection&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;NO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Museum&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Gallery&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; institution_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; openai&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Completion&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text-davinci-002&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prompt&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Answer YES if &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; is a gallery or museum. Answer NO otherwise.&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            institution_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temperature&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        max_tokens&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        top_p&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        frequency_penalty&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        presence_penalty&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    answer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;upper()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; answer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;orgs.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_reader &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictReader(in_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;filtered_orgs.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; csv_reader:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        answer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_answer(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(name, answer)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; answer &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make things a little faster, we reject or approve some of the entries based
on conclusive-enough substrings in the institution name. We only consult GPT-3
for the entries that remained.&lt;/p&gt;
&lt;p&gt;Let us take a look into another example. Hurricane Electric provides a small
&lt;a href=&#34;https://bgp.he.net/&#34;&gt;web app&lt;/a&gt; to look up autonomous systems and IP ranges gives a company name.
This might be of interest for bug bounty purposes. However, the result data
is somewhat hairy. For example, searching for &amp;ldquo;Tesla Motors Inc.&amp;rdquo; also gives
results related to &amp;ldquo;AD Aerodrom &amp;lsquo;Nikola Tesla&amp;rsquo; Beograd&amp;rdquo;, &amp;ldquo;Tesla Comunicaciones SRL&amp;rdquo;
and so on. Clearly the result set needs some filtering before it can be used
for further work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-08_14.16.53.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Like in our previous example, GPT-3 can be used to implement the heuristic.
A preliminary prompt would something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Answer YES if &amp;#34;Tesla Motors, Inc.&amp;#34; and &amp;#34;Tesla Comunicaciones SRL&amp;#34; are the same company. Answer NO otherwise.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like in previous example, the code would perform string interpolation to generate
a prompt for each entry in the result list.&lt;/p&gt;
&lt;p&gt;Since we&amp;rsquo;re doing heuristics here we should not expect the answer to be 100%
correct for each and every entry. Neither GPT-3 nor human intelligence can guarrantee
that. However if we have a lot of fuzzy data to go through GPT-3 can do it far faster
than people.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: defeating string array mapping</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-defeating-string-array-mapping/</link>
      <pubDate>Sat, 08 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-defeating-string-array-mapping/</guid>
      <description>&lt;p&gt;One Javascript obfuscation technique that can be found in the wild is string array mapping.
It entails gathering all the string constants from the code into an array and modifying the
code so that string literals are accesssed by referencing the array at various indices instead
of having string literals being used directly. Like with previously discussed obfuscation techniques
we will be exploring it in isolation, but in real websites it will most likely be
used in combination with another techniques.&lt;/p&gt;
&lt;p&gt;Let us consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Venus&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Earth&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Mars&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obfuscating this with &lt;a href=&#34;https://javascriptobfuscator.com/Javascript-Obfuscator.aspx&#34;&gt;Javascript Obfuscator&lt;/a&gt;
&amp;ldquo;Move Strings&amp;rdquo; option turns it into the following obfuscated version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Venus&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Earth&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Mars&amp;#34;&lt;/span&gt;];&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-07_14.11.47.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We have a single line consisting of multiple JS statements. Let us break this down for easier readability:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Venus&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Earth&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Mars&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can see that &lt;code&gt;_0x3baf&lt;/code&gt; is an array of strings with all the string literals that were used in the
code (including &lt;code&gt;log&lt;/code&gt; as &lt;code&gt;console.log&lt;/code&gt; is equivalent to &lt;code&gt;console[&amp;quot;log&amp;quot;]&lt;/code&gt;) and that is being referenced
in further statements. Let us analyse this code in ASTExplorer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-10-08_10.10.10.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Whenever the aforementioned string array is being used to get a string constant, we have a &lt;code&gt;MemberExpression&lt;/code&gt;
node in the tree with &lt;code&gt;object&lt;/code&gt; instance variable being &lt;code&gt;Identifier&lt;/code&gt; for the array name and &lt;code&gt;property&lt;/code&gt; being
&lt;code&gt;NumericLiteral&lt;/code&gt; with index value. Of course, not every reference to an array will be related to string
obfuscation, so we will have to check in our deobfuscation script if the array element at given index is indeed
a string literal.&lt;/p&gt;
&lt;p&gt;Like in previous posts, our code will have three parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reading the JS code and parsing it into Babel AST data model.&lt;/li&gt;
&lt;li&gt;Traversing the AST and modifying it using a visitor function.&lt;/li&gt;
&lt;li&gt;Regenerating the code from AST.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In step 2 we will be using &lt;code&gt;getBinding()&lt;/code&gt; method to get a binding of an array name. This will let us dig up
an &lt;code&gt;ArrayExpression&lt;/code&gt; node that has all the array elements so that we can perform an AST node substitution.&lt;/p&gt;
&lt;p&gt;The code to deobfuscate this simple case of string array mapping is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;obfuscated.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;MemberExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNumericLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isVariableDeclarator&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;array&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;array&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;array&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;member&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clean.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We tie our visitor function to &lt;code&gt;MemberExpression&lt;/code&gt; node type as that corresponds to parts of AST
we want to modify. We make sure that &lt;code&gt;property&lt;/code&gt; is indeed a numeric literal and take an array
index from there. Next, we get the binding based on array name. On success, binding will point
to a relevant &lt;code&gt;VariableDeclarator&lt;/code&gt; node (through &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt;) that points to the
&lt;code&gt;ArrayExpression&lt;/code&gt; node through &lt;code&gt;init&lt;/code&gt; property. We have a reference to an array and we also
have an index. We get the the array member. If it is indeed a string literal we perform the node
substitution.&lt;/p&gt;
&lt;p&gt;The deobfuscated code is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x3baf&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Venus&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Earth&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Mars&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Venus&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Earth&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello Mars&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By working with Abstract Syntax Tree we were able to treat the code as data structure that
can be manipulated to simplify it for undoing the obfuscation.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Active DNS recon techniques</title>
      <link>https://www.trickster.dev/post/active-dns-recon-techniques/</link>
      <pubDate>Sat, 24 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/active-dns-recon-techniques/</guid>
      <description>&lt;p&gt;We discussed some &lt;a href=&#34;passive-dns-recon-techniques/&#34;&gt;passive DNS recon techniques&lt;/a&gt;, but that&amp;rsquo;s
only half of the story. There is also the active DNS reconnaissance that involves generating
actual DNS requests to gather data. Active DNS recon does not rely on information footprints
and secondary sources. Thus it enables us to get more up-to-date information on target systems.&lt;/p&gt;
&lt;p&gt;So how do we send the DNS queries? We want to control what exact request we send and bypass
DNS caching on the local system, so using &lt;code&gt;getaddrinfo()&lt;/code&gt; is out of question.
Simplest way is to use nslookup(1) - a standard POSIX tool for doing DNS queries:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ nslookup hackerone.com 8.8.8.8  
Server:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
Name:	hackerone.com
Address: 104.16.99.52
Name:	hackerone.com
Address: 104.16.100.52

$ nslookup -query=&amp;#34;MX&amp;#34; hackerone.com 8.8.8.8
Server:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
hackerone.com	mail exchanger = 30 aspmx2.googlemail.com.
hackerone.com	mail exchanger = 20 alt1.aspmx.l.google.com.
hackerone.com	mail exchanger = 10 aspmx.l.google.com.
hackerone.com	mail exchanger = 30 aspmx3.googlemail.com.
hackerone.com	mail exchanger = 20 alt2.aspmx.l.google.com.

Authoritative answers can be found from:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can differentiate between successful and failed queries by checking the return code. It
will be 0 on success on 1 on failure. That can be useful when checking a list of gueesses for
subdomains in a shell script.&lt;/p&gt;
&lt;p&gt;Another, more flexible tool to query DNS servers is dig(1). Depending on specifics of your exact
system you may need to install it separately. It provides the output in a different, more
machine readable form:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig @8.8.8.8 hackerone.com

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @8.8.8.8 hackerone.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 16157
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;hackerone.com.			IN	A

;; ANSWER SECTION:
hackerone.com.		300	IN	A	104.16.100.52
hackerone.com.		300	IN	A	104.16.99.52

;; Query time: 55 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Sep 23 17:34:16 +07 2022
;; MSG SIZE  rcvd: 74

$ dig @8.8.8.8 -t MX hackerone.com

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @8.8.8.8 -t MX hackerone.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 7903
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;hackerone.com.			IN	MX

;; ANSWER SECTION:
hackerone.com.		300	IN	MX	30 aspmx2.googlemail.com.
hackerone.com.		300	IN	MX	20 alt1.aspmx.l.google.com.
hackerone.com.		300	IN	MX	10 aspmx.l.google.com.
hackerone.com.		300	IN	MX	30 aspmx3.googlemail.com.
hackerone.com.		300	IN	MX	20 alt2.aspmx.l.google.com.

;; Query time: 48 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Sep 23 17:34:40 +07 2022
;; MSG SIZE  rcvd: 172
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you code in Python you can use &lt;a href=&#34;https://www.dnspython.org/&#34;&gt;dnspython&lt;/a&gt; module for
performing DNS queries:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.10.6 (main, Aug 30 2022, 04:58:14) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import dns.resolver
&amp;gt;&amp;gt;&amp;gt; resolver = dns.resolver.Resolver()
&amp;gt;&amp;gt;&amp;gt; resolver.nameservers = [&amp;#39;8.8.8.8&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; ans = resolver.resolve(&amp;#39;hackerone.com&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; ans
&amp;lt;dns.resolver.Answer object at 0x1058cae30&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ans.response
&amp;lt;DNS message, ID 55540&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ans.response.answer
[&amp;lt;DNS hackerone.com. IN A RRset: [&amp;lt;104.16.100.52&amp;gt;, &amp;lt;104.16.99.52&amp;gt;]&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; ans = resolver.resolve(&amp;#39;hackerone.com&amp;#39;, &amp;#39;A&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; ans.response.answer
[&amp;lt;DNS hackerone.com. IN A RRset: [&amp;lt;104.16.100.52&amp;gt;, &amp;lt;104.16.99.52&amp;gt;]&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; ans = resolver.resolve(&amp;#39;hackerone.com&amp;#39;, &amp;#39;MX&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; ans.response.answer
[&amp;lt;DNS hackerone.com. IN MX RRset: [&amp;lt;30 aspmx2.googlemail.com.&amp;gt;, &amp;lt;20 alt1.aspmx.l.google.com.&amp;gt;, &amp;lt;10 aspmx.l.google.com.&amp;gt;, &amp;lt;30 aspmx3.googlemail.com.&amp;gt;, &amp;lt;20 alt2.aspmx.l.google.com.&amp;gt;]&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that the data model of this library mirrors that of DNS protocol - there&amp;rsquo;s a object
tree representing various layers/parts of a response.&lt;/p&gt;
&lt;p&gt;We may want to perform a reverse DNS query that would give us a list of DNS names for a given
IP address. For example, there might a single web server belonging to web hosting company that
is hosting many websites, thus having multiple DNS addresses pointing to it. First we need to
convert IPv4 address to domain-namish form by appending &lt;code&gt;.in-addr.arpa&lt;/code&gt;. Next, we launch a PTR
query:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig @8.8.8.8 -t PTR 8.8.8.8.in-addr.arpa

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @8.8.8.8 -t PTR 8.8.8.8.in-addr.arpa
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 17913
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;8.8.8.8.in-addr.arpa.		IN	PTR

;; ANSWER SECTION:
8.8.8.8.in-addr.arpa.	18929	IN	PTR	dns.google.

;; Query time: 71 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Sep 23 17:59:05 +07 2022
;; MSG SIZE  rcvd: 73
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So given that we can launch DNS queries and they can yield some data on the target, how do
we perform this at larger scale? One way is DNS bruteforcing that entails using a wordlist to
generate possible subdomains (possibly by applying word permutations) and launching many DNS
queries to check if they exist. Some tools to do this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mschwager/fierce&#34;&gt;Fierce&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/OWASP/Amass&#34;&gt;Amass&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/OJ/gobuster&#34;&gt;gobuster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that doing this will generate a lot of DNS requests that may crash your router, thus
it is recommended to launch these tools from VPS. Furthermore, it may not be enough to use
single DNS resolver like we did in earlier examples. When using multiple DNS resolvers, there&amp;rsquo;s
a potential issue of data reliability. &lt;a href=&#34;https://github.com/vortexau/dnsvalidator&#34;&gt;DNSValidator&lt;/a&gt;
is a tool that takes a long list of DNS servers, runs test queries on them and over time narrows
down the list to a subset of reliable servers.&lt;/p&gt;
&lt;p&gt;Furthemore, if we know of one subdomain, e.g. dev.example.org we may suppose that numbered
variants, such as dev1.example.org also exist. DNS alteration scanning is automated process of
checking for these kind of subdomains. &lt;a href=&#34;https://github.com/infosec-au/altdns&#34;&gt;Altdns&lt;/a&gt; is open
source tool that implements this technique.&lt;/p&gt;
&lt;p&gt;So far we discussed launching DNS requests to learn about the target, but did not touch anything
that interacts with the target. Yet another way to gather target subdomains is to crawl across
the site and scrape them from the links or Javascript snippets. This would be fairly trivial
to implement with Scrapy, but there are some ready-made tools to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/hakluke/hakrawler&#34;&gt;hakrawler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jaeles-project/gospider&#34;&gt;Gospider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DNS zone transfer is mechanism to transfer DNS records between servers. It can be either
authoritative that relies on AXFR sub-protocol or incremental that relies on IXFR.
For recon purposes the feasibility of doing this relies on misconfigured access control
on DNS server, which is not that common in modern world and should not be relied upon.
There is a deliberately insecure DNS server that can be used to demonstrate this
kind of vulnerability:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig axfr @nsztm1.digi.ninja zonetransfer.me

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; axfr @nsztm1.digi.ninja zonetransfer.me
; (1 server found)
;; global options: +cmd
zonetransfer.me.	7200	IN	SOA	nsztm1.digi.ninja. robin.digi.ninja. 2019100801 172800 900 1209600 3600
zonetransfer.me.	300	IN	HINFO	&amp;#34;Casio fx-700G&amp;#34; &amp;#34;Windows XP&amp;#34;
zonetransfer.me.	301	IN	TXT	&amp;#34;google-site-verification=tyP28J7JAUHA9fw2sHXMgcCC0I6XBmmoVi04VlMewxA&amp;#34;
zonetransfer.me.	7200	IN	MX	0 ASPMX.L.GOOGLE.COM.
zonetransfer.me.	7200	IN	MX	10 ALT1.ASPMX.L.GOOGLE.COM.
zonetransfer.me.	7200	IN	MX	10 ALT2.ASPMX.L.GOOGLE.COM.
zonetransfer.me.	7200	IN	MX	20 ASPMX2.GOOGLEMAIL.COM.
zonetransfer.me.	7200	IN	MX	20 ASPMX3.GOOGLEMAIL.COM.
zonetransfer.me.	7200	IN	MX	20 ASPMX4.GOOGLEMAIL.COM.
zonetransfer.me.	7200	IN	MX	20 ASPMX5.GOOGLEMAIL.COM.
zonetransfer.me.	7200	IN	A	5.196.105.14
zonetransfer.me.	7200	IN	NS	nsztm1.digi.ninja.
zonetransfer.me.	7200	IN	NS	nsztm2.digi.ninja.
_acme-challenge.zonetransfer.me. 301 IN	TXT	&amp;#34;6Oa05hbUJ9xSsvYy7pApQvwCUSSGgxvrbdizjePEsZI&amp;#34;
_sip._tcp.zonetransfer.me. 14000 IN	SRV	0 0 5060 www.zonetransfer.me.
14.105.196.5.IN-ADDR.ARPA.zonetransfer.me. 7200	IN PTR www.zonetransfer.me.
asfdbauthdns.zonetransfer.me. 7900 IN	AFSDB	1 asfdbbox.zonetransfer.me.
asfdbbox.zonetransfer.me. 7200	IN	A	127.0.0.1
asfdbvolume.zonetransfer.me. 7800 IN	AFSDB	1 asfdbbox.zonetransfer.me.
canberra-office.zonetransfer.me. 7200 IN A	202.14.81.230
cmdexec.zonetransfer.me. 300	IN	TXT	&amp;#34;; ls&amp;#34;
contact.zonetransfer.me. 2592000 IN	TXT	&amp;#34;Remember to call or email Pippa on +44 123 4567890 or pippa@zonetransfer.me when making DNS changes&amp;#34;
dc-office.zonetransfer.me. 7200	IN	A	143.228.181.132
deadbeef.zonetransfer.me. 7201	IN	AAAA	dead:beaf::
dr.zonetransfer.me.	300	IN	LOC	53 20 56.558 N 1 38 33.526 W 0.00m 1m 10000m 10m
DZC.zonetransfer.me.	7200	IN	TXT	&amp;#34;AbCdEfG&amp;#34;
email.zonetransfer.me.	2222	IN	NAPTR	1 1 &amp;#34;P&amp;#34; &amp;#34;E2U+email&amp;#34; &amp;#34;&amp;#34; email.zonetransfer.me.zonetransfer.me.
email.zonetransfer.me.	7200	IN	A	74.125.206.26
Hello.zonetransfer.me.	7200	IN	TXT	&amp;#34;Hi to Josh and all his class&amp;#34;
home.zonetransfer.me.	7200	IN	A	127.0.0.1
Info.zonetransfer.me.	7200	IN	TXT	&amp;#34;ZoneTransfer.me service provided by Robin Wood - robin@digi.ninja. See http://digi.ninja/projects/zonetransferme.php for more information.&amp;#34;
internal.zonetransfer.me. 300	IN	NS	intns1.zonetransfer.me.
internal.zonetransfer.me. 300	IN	NS	intns2.zonetransfer.me.
intns1.zonetransfer.me.	300	IN	A	81.4.108.41
intns2.zonetransfer.me.	300	IN	A	167.88.42.94
office.zonetransfer.me.	7200	IN	A	4.23.39.254
ipv6actnow.org.zonetransfer.me.	7200 IN	AAAA	2001:67c:2e8:11::c100:1332
owa.zonetransfer.me.	7200	IN	A	207.46.197.32
robinwood.zonetransfer.me. 302	IN	TXT	&amp;#34;Robin Wood&amp;#34;
rp.zonetransfer.me.	321	IN	RP	robin.zonetransfer.me. robinwood.zonetransfer.me.
sip.zonetransfer.me.	3333	IN	NAPTR	2 3 &amp;#34;P&amp;#34; &amp;#34;E2U+sip&amp;#34; &amp;#34;!^.*$!sip:customer-service@zonetransfer.me!&amp;#34; .
sqli.zonetransfer.me.	300	IN	TXT	&amp;#34;&amp;#39; or 1=1 --&amp;#34;
sshock.zonetransfer.me.	7200	IN	TXT	&amp;#34;() { :]}; echo ShellShocked&amp;#34;
staging.zonetransfer.me. 7200	IN	CNAME	www.sydneyoperahouse.com.
alltcpportsopen.firewall.test.zonetransfer.me. 301 IN A	127.0.0.1
testing.zonetransfer.me. 301	IN	CNAME	www.zonetransfer.me.
vpn.zonetransfer.me.	4000	IN	A	174.36.59.154
www.zonetransfer.me.	7200	IN	A	5.196.105.14
xss.zonetransfer.me.	300	IN	TXT	&amp;#34;&amp;#39;&amp;gt;&amp;lt;script&amp;gt;alert(&amp;#39;Boo&amp;#39;)&amp;lt;/script&amp;gt;&amp;#34;
zonetransfer.me.	7200	IN	SOA	nsztm1.digi.ninja. robin.digi.ninja. 2019100801 172800 900 1209600 3600
;; Query time: 232 msec
;; SERVER: 81.4.108.41#53(81.4.108.41)
;; WHEN: Fri Sep 23 18:53:56 +07 2022
;; XFR size: 50 records (messages 1, bytes 1994)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that all records have been dumped without us needing to query them one-by-one.&lt;/p&gt;
&lt;p&gt;If DNSSEC is deployed, NSEC records would exist and conveniently provide subdomains for us.
Each NSEC points to next subdomain in DNS zone (a portion of DNS namespace managed by
single admin/organisation) in a way that forms a linked list. Traversal of this list gives
us a list of subdomains. This is known as DNS zone walking.&lt;/p&gt;
&lt;p&gt;Some tools that can automate this technique:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/darkoperator/dnsrecon&#34;&gt;DNSrecon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;dnssecwalk from &lt;a href=&#34;https://github.com/vanhauser-thc/thc-ipv6&#34;&gt;thc-ipv6&lt;/a&gt; toolkit.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Passive DNS recon techniques</title>
      <link>https://www.trickster.dev/post/passive-dns-recon-techniques/</link>
      <pubDate>Sat, 17 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/passive-dns-recon-techniques/</guid>
      <description>&lt;p&gt;DNS is a network protocol and a distributed system that maps human-readable
domain names to IP addresses. It can be thought of as a big phone book for
internet hosts. Passive DNS recon is activity of using various data sources
to map out a footprint of site or organisation without ever directly
accessing the target or launching DNS requests. That&amp;rsquo;s basically doing
OSINT on DNS names and records that are linked to the target. Passive
DNS recon is not limited to the current state of DNS records, but also
covers historical data and temporal analysis.&lt;/p&gt;
&lt;p&gt;Passive DNS recon can be done for various purposes, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Investigating security incidents.&lt;/li&gt;
&lt;li&gt;Detecting shadow IT, misconfigurations and legacy/forgotten servers
that might be insecure.&lt;/li&gt;
&lt;li&gt;Mapping out attack surface for the organisation.&lt;/li&gt;
&lt;li&gt;Detecting dangling subdomains linked to third party services that may
provide opportunities for subdomain takeovers, perhaps leading to a bug bounty
payment.&lt;/li&gt;
&lt;li&gt;Detecting third party SaaS and cloud services being used by the target.&lt;/li&gt;
&lt;li&gt;Gaining insights on business activities of competitors - for example,
a SaaS company might have customer-specific subdomains.&lt;/li&gt;
&lt;li&gt;Finding origin server to scrape the site in a way that bypasses
countermeasures implemented by content delivery network.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us go through some of the ways one can perform passive DNS recon,
assuming we have one or more seed domains to start from.&lt;/p&gt;
&lt;p&gt;One way to do it is search engine dorking. We use &lt;code&gt;site:&lt;/code&gt; operator
with our seed domain to find some results from the site. Some of the
results will probably be from subdomains. You exclude these subdomains
from search results by using &lt;code&gt;-site:&lt;/code&gt; thingy to get some more subdomains:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;site:tiktok.com
site:tiktok.com -site:effecthouse.tiktok.com -site:business.tiktok.com -site:www.tiktok.com -site:newsroom.tiktok.com
site:tiktok.com -site:effecthouse.tiktok.com -site:business.tiktok.com -site:www.tiktok.com -site:newsroom.tiktok.com -site:vm.tiktok.com -site:seller-vn.tiktok.com -site:careers.tiktok.com -site:ads.tiktok.com
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-09-16_15.14.04.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-09-16_15.15.24.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is performed until there are no more new subdomains. There&amp;rsquo;s
&lt;a href=&#34;https://github.com/lanmaster53/recon-ng-marketplace/blob/master/modules/recon/domains-hosts/google_site_web.py&#34;&gt;recon-ng module&lt;/a&gt;
based on this trick to automatically scrape subdomains from Google SERPs.
One can also use Bright Data Search Engine Crawler or similar product
to outsource search engine scraping via API. Furthermore, Bing and
other search engines can also be used for finding subdomains.&lt;/p&gt;
&lt;p&gt;Github is another source for finding subdomains. Merely searching
for seed domain in Github search (which can also be done via API)
is likely to get you some subdomains that can be extracted with little
string processing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-09-16_15.30.32.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Yet another source is Internet Archive/Wayback machine. It does not
give you subdomains directly, but allows searching for URLs with
wildcard queries:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;http://web.archive.org/cdx/search/cdx?url=*.hackerone.com/*&amp;amp;output=json&amp;amp;collapse=urlkey&amp;#34; &amp;gt; urls.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Subdomains can be trivially extracted from these URLs.&lt;/p&gt;
&lt;p&gt;X.509 certificates can be mined to extract subdomains. To search through a lot
of certificates, one can use certificate transparency tool such as
&lt;a href=&#34;https://crt.sh/&#34;&gt;crt.sh&lt;/a&gt; (it also exposes PostgreSQL database that you can
connect to with psql(1): &lt;code&gt;psql -h crt.sh -p 5432 -U guest certwatch&lt;/code&gt;) or query Facebook&amp;rsquo;s
&lt;a href=&#34;https://developers.facebook.com/docs/certificate-transparency-api/&#34;&gt;Certificate Transparency API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-09-16_16.22.47.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some other sources for finding subdomains include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.virustotal.com/reference/subdomains&#34;&gt;VirusTotal API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://index.commoncrawl.org/&#34;&gt;CommonCrawl Index&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since DNS data is relevant to security, many companies in the world
gather it for you and provide it for your analysis via some web UI
or API. Some of these companies are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Censys&lt;/li&gt;
&lt;li&gt;Shodan&lt;/li&gt;
&lt;li&gt;SecurityTrails&lt;/li&gt;
&lt;li&gt;IntelligenceX&lt;/li&gt;
&lt;li&gt;RiskIQ&lt;/li&gt;
&lt;li&gt;ZoomEye&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To gather as much subdomains as possible, one needs to have automated
way to query many data sources. Two most prominent tools to do that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/OWASP/Amass&#34;&gt;Amass&lt;/a&gt; from OWASP.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/projectdiscovery/subfinder&#34;&gt;Subfinder&lt;/a&gt; from Project Discovery.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://chaos.projectdiscovery.io/#/&#34;&gt;Chaos&lt;/a&gt; is a project that provides
subdomain datasets for companies running public bounty program (gathered
through active enumeration that is outside the scope of this post).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://dnsdumpster.com/&#34;&gt;DNSdumpster&lt;/a&gt; is a free domain research tool one
can use to find subdomains and DNS records. It also provides a nice graphical
visualisation on links between records.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-09-16_16.36.58.png&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-09-16_16.36.31.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>HTTPX: modern Python module for doing HTTP(S) requests</title>
      <link>https://www.trickster.dev/post/httpx-modern-python-module-for-doing-https-requests/</link>
      <pubDate>Fri, 16 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/httpx-modern-python-module-for-doing-https-requests/</guid>
      <description>&lt;p&gt;Python &lt;a href=&#34;https://requests.readthedocs.io/en/latest/&#34;&gt;requests&lt;/a&gt; module is a
well established open source library for doing HTTP requests that is widely
used in web scraping and other fields. However it has some limitations.
At the time of writing, requests module only supports HTTP/1.1 yet
significant fraction of sites are supporting more modern, faster HTTP/2
protocol and there is no support for asynchronous communication
in requests module.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.python-httpx.org/&#34;&gt;HTTPX&lt;/a&gt; is a newer, more modern Python
module that addresses some of the limitations of requests module (not to be
confused with &lt;a href=&#34;https://github.com/projectdiscovery/httpx&#34;&gt;another httpx&lt;/a&gt;
that is CLI tool for probing HTTP servers). It can be installed as &lt;code&gt;httpx&lt;/code&gt;
through PIP. Furthermore, one can install the following optional dependencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;h2&lt;/code&gt; for HTTP/2 support (through &lt;code&gt;httpx[http2]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;socksio&lt;/code&gt; for SOCKS proxy support (through &lt;code&gt;httpx[socks]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;click&lt;/code&gt; for optional CLI tool (through &lt;code&gt;httpx[cli]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rich&lt;/code&gt; for making the optional CLI tool prettier&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brotli&lt;/code&gt; or &lt;code&gt;brotlicffi&lt;/code&gt; for supporting Cloudflare&amp;rsquo;s Brotli compression (through &lt;code&gt;httpx[brotli]&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTPX is largely, but not entirely a drop-in replacement for the aforementioned
requests module. There are some differences, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It does not follow redirects automatically by default (we need to set pass &lt;code&gt;follow_redirects=True&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.url&lt;/code&gt; on response object is URL object, not a string&lt;/li&gt;
&lt;li&gt;Keys of proxy dictionary are &lt;code&gt;http://&lt;/code&gt; and &lt;code&gt;https://&lt;/code&gt;, not &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;https&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;All request have timeout set them by default to avoid things getting stuck.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us launch Python REPL and make a simple HTTPS request.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.10.6 (main, Aug 30 2022, 04:58:14) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import httpx
&amp;gt;&amp;gt;&amp;gt; resp = httpx.get(&amp;#34;https://api.github.com/users/facebook-github-bot/events/public&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200 OK]&amp;gt;
&amp;gt;&amp;gt;&amp;gt; resp.status_code
200
&amp;gt;&amp;gt;&amp;gt; resp.text[:100]
&amp;#39;[{&amp;#34;id&amp;#34;:&amp;#34;24020632984&amp;#34;,&amp;#34;type&amp;#34;:&amp;#34;IssueCommentEvent&amp;#34;,&amp;#34;actor&amp;#34;:{&amp;#34;id&amp;#34;:6422482,&amp;#34;login&amp;#34;:&amp;#34;facebook-github-bot&amp;#34;,&amp;#39;
&amp;gt;&amp;gt;&amp;gt; type(resp.json())
&amp;lt;class &amp;#39;list&amp;#39;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; resp.json()[0]
{&amp;#39;id&amp;#39;: &amp;#39;24020632984&amp;#39;, &amp;#39;type&amp;#39;: &amp;#39;IssueCommentEvent&amp;#39;, &amp;#39;actor&amp;#39;: {&amp;#39;id&amp;#39;: 6422482, &amp;#39;login&amp;#39;: &amp;#39;facebook-github-bot&amp;#39;, &amp;#39;display_login&amp;#39;: &amp;#39;facebook-github-bot&amp;#39;, &amp;#39;gravatar_id&amp;#39;: &amp;#39;&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot&amp;#39;, &amp;#39;avatar_url&amp;#39;: &amp;#39;https://avatars.githubusercontent.com/u/6422482?&amp;#39;}, &amp;#39;repo&amp;#39;: {&amp;#39;id&amp;#39;: 392055467, &amp;#39;name&amp;#39;: &amp;#39;facebookresearch/fbpcs&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs&amp;#39;}, &amp;#39;payload&amp;#39;: {&amp;#39;action&amp;#39;: &amp;#39;created&amp;#39;, &amp;#39;issue&amp;#39;: {&amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432&amp;#39;, &amp;#39;repository_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs&amp;#39;, &amp;#39;labels_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432/labels{/name}&amp;#39;, &amp;#39;comments_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432/comments&amp;#39;, &amp;#39;events_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432/events&amp;#39;, &amp;#39;html_url&amp;#39;: &amp;#39;https://github.com/facebookresearch/fbpcs/pull/432&amp;#39;, &amp;#39;id&amp;#39;: 1070015172, &amp;#39;node_id&amp;#39;: &amp;#39;PR_kwDOF15Kq84vVQRm&amp;#39;, &amp;#39;number&amp;#39;: 432, &amp;#39;title&amp;#39;: &amp;#39;update graphapi version&amp;#39;, &amp;#39;user&amp;#39;: {&amp;#39;login&amp;#39;: &amp;#39;benliugithub&amp;#39;, &amp;#39;id&amp;#39;: 90293689, &amp;#39;node_id&amp;#39;: &amp;#39;MDQ6VXNlcjkwMjkzNjg5&amp;#39;, &amp;#39;avatar_url&amp;#39;: &amp;#39;https://avatars.githubusercontent.com/u/90293689?v=4&amp;#39;, &amp;#39;gravatar_id&amp;#39;: &amp;#39;&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub&amp;#39;, &amp;#39;html_url&amp;#39;: &amp;#39;https://github.com/benliugithub&amp;#39;, &amp;#39;followers_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/followers&amp;#39;, &amp;#39;following_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/following{/other_user}&amp;#39;, &amp;#39;gists_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/gists{/gist_id}&amp;#39;, &amp;#39;starred_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/starred{/owner}{/repo}&amp;#39;, &amp;#39;subscriptions_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/subscriptions&amp;#39;, &amp;#39;organizations_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/orgs&amp;#39;, &amp;#39;repos_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/repos&amp;#39;, &amp;#39;events_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/events{/privacy}&amp;#39;, &amp;#39;received_events_url&amp;#39;: &amp;#39;https://api.github.com/users/benliugithub/received_events&amp;#39;, &amp;#39;type&amp;#39;: &amp;#39;User&amp;#39;, &amp;#39;site_admin&amp;#39;: False}, &amp;#39;labels&amp;#39;: [{&amp;#39;id&amp;#39;: 3223376167, &amp;#39;node_id&amp;#39;: &amp;#39;MDU6TGFiZWwzMjIzMzc2MTY3&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/labels/CLA%20Signed&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;CLA Signed&amp;#39;, &amp;#39;color&amp;#39;: &amp;#39;009900&amp;#39;, &amp;#39;default&amp;#39;: False, &amp;#39;description&amp;#39;: &amp;#39;This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.&amp;#39;}, {&amp;#39;id&amp;#39;: 3251191745, &amp;#39;node_id&amp;#39;: &amp;#39;MDU6TGFiZWwzMjUxMTkxNzQ1&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/labels/fb-exported&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;fb-exported&amp;#39;, &amp;#39;color&amp;#39;: &amp;#39;ededed&amp;#39;, &amp;#39;default&amp;#39;: False, &amp;#39;description&amp;#39;: None}], &amp;#39;state&amp;#39;: &amp;#39;open&amp;#39;, &amp;#39;locked&amp;#39;: False, &amp;#39;assignee&amp;#39;: None, &amp;#39;assignees&amp;#39;: [], &amp;#39;milestone&amp;#39;: None, &amp;#39;comments&amp;#39;: 1, &amp;#39;created_at&amp;#39;: &amp;#39;2021-12-02T21:54:32Z&amp;#39;, &amp;#39;updated_at&amp;#39;: &amp;#39;2022-09-15T07:20:13Z&amp;#39;, &amp;#39;closed_at&amp;#39;: None, &amp;#39;author_association&amp;#39;: &amp;#39;NONE&amp;#39;, &amp;#39;active_lock_reason&amp;#39;: None, &amp;#39;draft&amp;#39;: False, &amp;#39;pull_request&amp;#39;: {&amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/pulls/432&amp;#39;, &amp;#39;html_url&amp;#39;: &amp;#39;https://github.com/facebookresearch/fbpcs/pull/432&amp;#39;, &amp;#39;diff_url&amp;#39;: &amp;#39;https://github.com/facebookresearch/fbpcs/pull/432.diff&amp;#39;, &amp;#39;patch_url&amp;#39;: &amp;#39;https://github.com/facebookresearch/fbpcs/pull/432.patch&amp;#39;, &amp;#39;merged_at&amp;#39;: None}, &amp;#39;body&amp;#39;: &amp;#39;Summary: As title\n\nReviewed By: leegross\n\nDifferential Revision: D32811061\n\n&amp;#39;, &amp;#39;reactions&amp;#39;: {&amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432/reactions&amp;#39;, &amp;#39;total_count&amp;#39;: 0, &amp;#39;+1&amp;#39;: 0, &amp;#39;-1&amp;#39;: 0, &amp;#39;laugh&amp;#39;: 0, &amp;#39;hooray&amp;#39;: 0, &amp;#39;confused&amp;#39;: 0, &amp;#39;heart&amp;#39;: 0, &amp;#39;rocket&amp;#39;: 0, &amp;#39;eyes&amp;#39;: 0}, &amp;#39;timeline_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432/timeline&amp;#39;, &amp;#39;performed_via_github_app&amp;#39;: None, &amp;#39;state_reason&amp;#39;: None}, &amp;#39;comment&amp;#39;: {&amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/comments/1247684175&amp;#39;, &amp;#39;html_url&amp;#39;: &amp;#39;https://github.com/facebookresearch/fbpcs/pull/432#issuecomment-1247684175&amp;#39;, &amp;#39;issue_url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/432&amp;#39;, &amp;#39;id&amp;#39;: 1247684175, &amp;#39;node_id&amp;#39;: &amp;#39;IC_kwDOF15Kq85KXiZP&amp;#39;, &amp;#39;user&amp;#39;: {&amp;#39;login&amp;#39;: &amp;#39;facebook-github-bot&amp;#39;, &amp;#39;id&amp;#39;: 6422482, &amp;#39;node_id&amp;#39;: &amp;#39;MDQ6VXNlcjY0MjI0ODI=&amp;#39;, &amp;#39;avatar_url&amp;#39;: &amp;#39;https://avatars.githubusercontent.com/u/6422482?v=4&amp;#39;, &amp;#39;gravatar_id&amp;#39;: &amp;#39;&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot&amp;#39;, &amp;#39;html_url&amp;#39;: &amp;#39;https://github.com/facebook-github-bot&amp;#39;, &amp;#39;followers_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/followers&amp;#39;, &amp;#39;following_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/following{/other_user}&amp;#39;, &amp;#39;gists_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/gists{/gist_id}&amp;#39;, &amp;#39;starred_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/starred{/owner}{/repo}&amp;#39;, &amp;#39;subscriptions_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/subscriptions&amp;#39;, &amp;#39;organizations_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/orgs&amp;#39;, &amp;#39;repos_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/repos&amp;#39;, &amp;#39;events_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/events{/privacy}&amp;#39;, &amp;#39;received_events_url&amp;#39;: &amp;#39;https://api.github.com/users/facebook-github-bot/received_events&amp;#39;, &amp;#39;type&amp;#39;: &amp;#39;User&amp;#39;, &amp;#39;site_admin&amp;#39;: False}, &amp;#39;created_at&amp;#39;: &amp;#39;2022-09-15T07:20:13Z&amp;#39;, &amp;#39;updated_at&amp;#39;: &amp;#39;2022-09-15T07:20:13Z&amp;#39;, &amp;#39;author_association&amp;#39;: &amp;#39;CONTRIBUTOR&amp;#39;, &amp;#39;body&amp;#39;: &amp;#39;Hi @benliugithub! \n\nThank you for your pull request. \n\nWe **require** contributors to sign our **Contributor License Agreement**, and yours needs attention.\n\nYou currently have a record in our system, but the CLA is no longer valid, and will need to be **resubmitted**.\n\n# Process\n\nIn order for us to review and merge your suggested changes, please sign at &amp;lt;https://code.facebook.com/cla&amp;gt;. **If you are contributing on behalf of someone else (eg your employer)**, the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.\n\nOnce the CLA is signed, our tooling will perform checks and validations. Afterwards, the **pull request will be tagged** with `CLA signed`. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.\n\nIf you have received this in error or have any questions, please contact us at [cla@fb.com](mailto:cla@fb.com?subject=CLA%20for%20facebookresearch%2Ffbpcs%20%23432). Thanks!&amp;#39;, &amp;#39;reactions&amp;#39;: {&amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/repos/facebookresearch/fbpcs/issues/comments/1247684175/reactions&amp;#39;, &amp;#39;total_count&amp;#39;: 0, &amp;#39;+1&amp;#39;: 0, &amp;#39;-1&amp;#39;: 0, &amp;#39;laugh&amp;#39;: 0, &amp;#39;hooray&amp;#39;: 0, &amp;#39;confused&amp;#39;: 0, &amp;#39;heart&amp;#39;: 0, &amp;#39;rocket&amp;#39;: 0, &amp;#39;eyes&amp;#39;: 0}, &amp;#39;performed_via_github_app&amp;#39;: None}}, &amp;#39;public&amp;#39;: True, &amp;#39;created_at&amp;#39;: &amp;#39;2022-09-15T07:20:14Z&amp;#39;, &amp;#39;org&amp;#39;: {&amp;#39;id&amp;#39;: 16943930, &amp;#39;login&amp;#39;: &amp;#39;facebookresearch&amp;#39;, &amp;#39;gravatar_id&amp;#39;: &amp;#39;&amp;#39;, &amp;#39;url&amp;#39;: &amp;#39;https://api.github.com/orgs/facebookresearch&amp;#39;, &amp;#39;avatar_url&amp;#39;: &amp;#39;https://avatars.githubusercontent.com/u/16943930?&amp;#39;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This was HTTP/1.1 request. But what if we wanted to launch a HTTP/2 request?
Assuming &lt;code&gt;httpx[http2]&lt;/code&gt; is installed, we would need instantiate a client object
with &lt;code&gt;http2&lt;/code&gt; keyword argument set to true:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; client = httpx.Client(http2=True)
&amp;gt;&amp;gt;&amp;gt; client
&amp;lt;httpx.Client object at 0x105f3a2c0&amp;gt;
&amp;gt;&amp;gt;&amp;gt; resp = client.get(&amp;#34;https://api.github.com/users/facebook-github-bot/events/public&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200 OK]&amp;gt;
&amp;gt;&amp;gt;&amp;gt; resp.text[:100]
&amp;#39;[{&amp;#34;id&amp;#34;:&amp;#34;24020972057&amp;#34;,&amp;#34;type&amp;#34;:&amp;#34;PullRequestEvent&amp;#34;,&amp;#34;actor&amp;#34;:{&amp;#34;id&amp;#34;:6422482,&amp;#34;login&amp;#34;:&amp;#34;facebook-github-bot&amp;#34;,&amp;#34;&amp;#39;
&amp;gt;&amp;gt;&amp;gt; resp.http_version
&amp;#39;HTTP/2&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The client object in HTTPX is largely equivalent to &lt;code&gt;requests.Session&lt;/code&gt;: it can manage cookies
between requests, reuse headers, proxy URLs and other settings. Furthermore, HTTPX client implements HTTP
connection pooling for improved performance. This entails refraining from closing TCP connections
to remote servers after getting response and keeping them available to save time when new request
is available. The client object can be used as context manager, but if we wanted to explicitly
close all connection in the pool we could do so by calling &lt;code&gt;close()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;If we want to benefit from async I/O we can use &lt;code&gt;httpx.AsyncClient&lt;/code&gt; instead of &lt;code&gt;httpx.Client&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 -m asyncio
asyncio REPL 3.10.6 (main, Aug 30 2022, 04:58:14) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Use &amp;#34;await&amp;#34; directly instead of &amp;#34;asyncio.run()&amp;#34;.
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import asyncio
&amp;gt;&amp;gt;&amp;gt; import httpx
&amp;gt;&amp;gt;&amp;gt; async with httpx.AsyncClient() as client:
...     resp = await client.get(&amp;#34;https://instagram.com&amp;#34;)
... 
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [301 Moved Permanently]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you have installed the optional &lt;code&gt;httpx[cli]&lt;/code&gt; submodule, you can use the CLI tool that is
developed with the library:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ httpx --download fb_bot.json https://api.github.com/users/facebook-github-bot/events/public
HTTP/1.1 200 OK
Server: GitHub.com
Date: Thu, 15 Sep 2022 07:53:21 GMT
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=60, s-maxage=60
Vary: Accept, Accept-Encoding, Accept, X-Requested-With
ETag: W/&amp;#34;0d377aecaac2a62f7662fc84bfb6ca33a1e6372d14da1fa4f89a3a39f02e8191&amp;#34;
Last-Modified: Thu, 15 Sep 2022 07:44:01 GMT
X-Poll-Interval: 60
X-GitHub-Media-Type: github.v3; format=json
Link: &amp;lt;https://api.github.com/user/6422482/events/public?page=2&amp;gt;; rel=&amp;#34;next&amp;#34;, &amp;lt;https://api.github.com/user/6422482/events/public?page=8&amp;gt;; rel=&amp;#34;last&amp;#34;
Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, 
X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
Access-Control-Allow-Origin: *
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Frame-Options: deny
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
Content-Security-Policy: default-src &amp;#39;none&amp;#39;
Content-Encoding: gzip
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 52
X-RateLimit-Reset: 1663229406
X-RateLimit-Resource: core
X-RateLimit-Used: 8
Accept-Ranges: bytes
Transfer-Encoding: chunked
X-GitHub-Request-Id: EA77:1A4B:39239D:3B3337:6322D9F0


Downloading fb_bot.json   0% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16,286/0 bytes ?
$ file fb_bot.json 
fb_bot.json: JSON data
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HTTPX heeds the following environment variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HTTPX_LOG_LEVEL&lt;/code&gt; - enables extra logging for debugging purposes (can be &lt;code&gt;debug&lt;/code&gt; or &lt;code&gt;trace&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSLKEYLOGFILE&lt;/code&gt; - path for saving TLS handshake secrets, so that TLS connections can
be &lt;a href=&#34;../decrypting-your-own-https-traffic-with-wireshark&#34;&gt;decrypted in Wireshark&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HTTP_PROXY&lt;/code&gt; and &lt;code&gt;HTTPS_PROXY&lt;/code&gt; - proxy URLs to be used for HTTP and HTTPS requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ALL_PROXY&lt;/code&gt; - proxy URL to be used for all requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NO_PROXY&lt;/code&gt; - comma-separated list of hostnames and URLs that should be accessed without
proxying.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NETRC&lt;/code&gt; - a path to a &lt;a href=&#34;https://everything.curl.dev/usingcurl/netrc&#34;&gt;.netrc&lt;/a&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSL_CERT_FILE&lt;/code&gt; - path to custom CA certificate (.crt file). This may be useful when
working with proxies that intercept TLS connections.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSL_CERT_DIR&lt;/code&gt; - X.509 certificate directory path. This directory is expected to follow
the OpenSSL layout.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Recon-ng: modular framework for OSINT automation</title>
      <link>https://www.trickster.dev/post/recon-ng-modular-framework-for-osint-automation/</link>
      <pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/recon-ng-modular-framework-for-osint-automation/</guid>
      <description>&lt;p&gt;OSINT is collection and analysis of information from public sources. Nowadays it
can largely be automated through web scraping and API integrations.
&lt;a href=&#34;https://github.com/lanmaster53/recon-ng&#34;&gt;Recon-ng&lt;/a&gt; is open source OSINT framework
that features a modular architecture. Each module can be thought as a pluggable
piece of code that can be loaded on as-needed basis. Most modules are API integrations
and scrapers of data sources. Some deal with report generation and other auxillary tasks.
Since recon-ng was developed by and for infosec people, the user interface resembles
that of Metasploit Framework, thus making the learning curve easier to people who
are working on the offensive side of cybersecurity.&lt;/p&gt;
&lt;p&gt;Some security-centric Linux distributions ship recon-ng on their package managers.
Furthermore, macOS users can install it through Homebrew. If none of these options
are applicable one can simply run it from source code in a fairly vanilla Python
dev environment.&lt;/p&gt;
&lt;p&gt;The recon-ng script gives us a command line for running OSINT tasks:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ recon-ng

    _/_/_/    _/_/_/_/    _/_/_/    _/_/_/    _/      _/            _/      _/    _/_/_/
   _/    _/  _/        _/        _/      _/  _/_/    _/            _/_/    _/  _/       
  _/_/_/    _/_/_/    _/        _/      _/  _/  _/  _/  _/_/_/_/  _/  _/  _/  _/  _/_/_/
 _/    _/  _/        _/        _/      _/  _/    _/_/            _/    _/_/  _/      _/ 
_/    _/  _/_/_/_/    _/_/_/    _/_/_/    _/      _/            _/      _/    _/_/_/    


                                          /\
                                         / \\ /\
    Sponsored by...               /\  /\/  \\V  \/\
                                 / \\/ // \\\\\ \\ \/\
                                // // BLACK HILLS \/ \\
                               www.blackhillsinfosec.com

                  ____   ____   ____   ____ _____ _  ____   ____  ____
                 |____] | ___/ |____| |       |   | |____  |____ |
                 |      |   \_ |    | |____   |   |  ____| |____ |____
                                   www.practisec.com

                      [recon-ng v5.1.2, Tim Tomes (@lanmaster53)]                       

[85] Recon modules
[13] Disabled modules
[8]  Reporting modules
[4]  Import modules
[2]  Exploitation modules
[2]  Discovery modules

[recon-ng][default] &amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point it is likely to print bunch of errors about missing API keys
and some Python modules you are advised to install. It is okay to disregard
these when starting. We still have stuff to try out at this point.&lt;/p&gt;
&lt;p&gt;Let us get familiar with the concept of workspaces. In recon-ng, a workspace
is basically a project that holds together various pieces of data you collect.
When you launch recon-ng you will be assigned a default workspace. Let us switch
create new workspace and switch to it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][default] &amp;gt; workspaces create test
[recon-ng][test] &amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;workspaces list&lt;/code&gt; command lists all the available workspaces:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test] &amp;gt; workspaces list

  +----------------------------------+
  | Workspaces |       Modified      |
  +----------------------------------+
  | default    | 2022-09-05 11:30:15 |
  | test       | 2022-09-05 11:55:27 |
  +----------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we wanted to go back to default workspaces, we can say &lt;code&gt;workspaces load default&lt;/code&gt;.
To remove a workspace we no longer need we can say &lt;code&gt;workspaces remove&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Like mentioned before, a recon-ng module is pluggable piece of code that can loaded
when needed. All the modules are stored in a separate git repo called
&lt;a href=&#34;https://github.com/lanmaster53/recon-ng-marketplace&#34;&gt;recon-ng-marketplace&lt;/a&gt;. Run
&lt;code&gt;marketplace install all&lt;/code&gt; to download them into a local installation.
When that is done, running &lt;code&gt;marketplace search&lt;/code&gt; will give you a table with
available modules. Running &lt;code&gt;modules list&lt;/code&gt; will give you list of modules
by category. To search modules by keyword, we can run these commands
with the keyword:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test] &amp;gt; modules search domain
[*] Searching installed modules for &amp;#39;domain&amp;#39;...

  Recon
  -----
    recon/companies-domains/censys_subdomains
    recon/companies-domains/pen
    recon/companies-domains/viewdns_reverse_whois
    recon/companies-domains/whoxy_dns
    recon/contacts-domains/migrate_contacts
    recon/domains-companies/pen
    recon/domains-companies/whoxy_whois
    recon/domains-contacts/hunter_io
    recon/domains-contacts/pen
    recon/domains-contacts/pgp_search
    recon/domains-contacts/whois_pocs
    recon/domains-contacts/wikileaker
    recon/domains-credentials/pwnedlist/api_usage
    recon/domains-credentials/pwnedlist/domain_ispwned
    recon/domains-credentials/pwnedlist/leak_lookup
    recon/domains-credentials/pwnedlist/leaks_dump
    recon/domains-domains/brute_suffix
    recon/domains-hosts/binaryedge
    recon/domains-hosts/bing_domain_api
    recon/domains-hosts/bing_domain_web
    recon/domains-hosts/brute_hosts
    recon/domains-hosts/builtwith
    recon/domains-hosts/certificate_transparency
    recon/domains-hosts/google_site_web
    recon/domains-hosts/hackertarget
    recon/domains-hosts/mx_spf_ip
    recon/domains-hosts/netcraft
    recon/domains-hosts/shodan_hostname
    recon/domains-hosts/spyse_subdomains
    recon/domains-hosts/ssl_san
    recon/domains-hosts/threatcrowd
    recon/domains-hosts/threatminer
    recon/domains-vulnerabilities/ghdb
    recon/domains-vulnerabilities/xssed
    recon/hosts-domains/migrate_hosts

[recon-ng][test] &amp;gt; marketplace search domain
[*] Searching module index for &amp;#39;domain&amp;#39;...

  +-----------------------------------------------------------------------------------------------+
  |                        Path                        | Version |   Status  |  Updated   | D | K |
  +-----------------------------------------------------------------------------------------------+
  | discovery/info_disclosure/cache_snoop              | 1.1     | installed | 2020-10-13 |   |   |
  | recon/companies-domains/censys_subdomains          | 2.0     | installed | 2021-05-10 | * | * |
  | recon/companies-domains/pen                        | 1.1     | installed | 2019-10-15 |   |   |
  | recon/companies-domains/viewdns_reverse_whois      | 1.1     | installed | 2021-08-24 |   |   |
  | recon/companies-domains/whoxy_dns                  | 1.1     | installed | 2020-06-17 |   | * |
  | recon/companies-hosts/censys_tls_subjects          | 2.0     | disabled  | 2021-05-11 | * | * |
  | recon/contacts-domains/migrate_contacts            | 1.1     | installed | 2020-05-17 |   |   |
  | recon/domains-companies/censys_companies           | 2.0     | disabled  | 2021-05-10 | * | * |
  | recon/domains-companies/pen                        | 1.1     | installed | 2019-10-15 |   |   |
  | recon/domains-companies/whoxy_whois                | 1.1     | installed | 2020-06-24 |   | * |
  | recon/domains-contacts/hunter_io                   | 1.3     | installed | 2020-04-14 |   | * |
  | recon/domains-contacts/metacrawler                 | 1.1     | disabled  | 2019-06-24 | * |   |
  | recon/domains-contacts/pen                         | 1.1     | installed | 2019-10-15 |   |   |
  | recon/domains-contacts/pgp_search                  | 1.4     | installed | 2019-10-16 |   |   |
  | recon/domains-contacts/whois_pocs                  | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-contacts/wikileaker                  | 1.0     | installed | 2020-04-08 |   |   |
  | recon/domains-credentials/pwnedlist/account_creds  | 1.0     | disabled  | 2019-06-24 | * | * |
  | recon/domains-credentials/pwnedlist/api_usage      | 1.0     | installed | 2019-06-24 |   | * |
  | recon/domains-credentials/pwnedlist/domain_creds   | 1.0     | disabled  | 2019-06-24 | * | * |
  | recon/domains-credentials/pwnedlist/domain_ispwned | 1.0     | installed | 2019-06-24 |   | * |
  | recon/domains-credentials/pwnedlist/leak_lookup    | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-credentials/pwnedlist/leaks_dump     | 1.0     | installed | 2019-06-24 |   | * |
  | recon/domains-domains/brute_suffix                 | 1.1     | installed | 2020-05-17 |   |   |
  | recon/domains-hosts/binaryedge                     | 1.2     | installed | 2020-06-18 |   | * |
  | recon/domains-hosts/bing_domain_api                | 1.0     | installed | 2019-06-24 |   | * |
  | recon/domains-hosts/bing_domain_web                | 1.1     | installed | 2019-07-04 |   |   |
  | recon/domains-hosts/brute_hosts                    | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-hosts/builtwith                      | 1.1     | installed | 2021-08-24 |   | * |
  | recon/domains-hosts/censys_domain                  | 2.0     | disabled  | 2021-05-10 | * | * |
  | recon/domains-hosts/certificate_transparency       | 1.2     | installed | 2019-09-16 |   |   |
  | recon/domains-hosts/google_site_web                | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-hosts/hackertarget                   | 1.1     | installed | 2020-05-17 |   |   |
  | recon/domains-hosts/mx_spf_ip                      | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-hosts/netcraft                       | 1.1     | installed | 2020-02-05 |   |   |
  | recon/domains-hosts/shodan_hostname                | 1.1     | installed | 2020-07-01 | * | * |
  | recon/domains-hosts/spyse_subdomains               | 1.1     | installed | 2021-08-24 |   | * |
  | recon/domains-hosts/ssl_san                        | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-hosts/threatcrowd                    | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-hosts/threatminer                    | 1.0     | installed | 2019-06-24 |   |   |
  | recon/domains-vulnerabilities/ghdb                 | 1.1     | installed | 2019-06-26 |   |   |
  | recon/domains-vulnerabilities/xssed                | 1.1     | installed | 2020-10-18 |   |   |
  | recon/hosts-domains/migrate_hosts                  | 1.1     | installed | 2020-05-17 |   |   |
  | recon/hosts-hosts/censys_query                     | 2.0     | disabled  | 2021-05-10 | * | * |
  | recon/hosts-hosts/virustotal                       | 1.0     | installed | 2019-06-24 |   | * |
  | recon/netblocks-hosts/virustotal                   | 1.0     | installed | 2019-06-24 |   | * |
  +-----------------------------------------------------------------------------------------------+

  D = Has dependencies. See info for details.
  K = Requires keys. See info for details.

[recon-ng][test] &amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see that some modules require API keys and/or additional dependencies to be used.
Running &lt;code&gt;marketplace info&lt;/code&gt; on a module path describes the module:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test] &amp;gt; marketplace info recon/domains-hosts/google_site_web  

  +---------------------------------------------------------------------------------------------------------------------------------+
  | path          | recon/domains-hosts/google_site_web                                                                             |
  | name          | Google Hostname Enumerator                                                                                      |
  | author        | Tim Tomes (@lanmaster53)                                                                                        |
  | version       | 1.0                                                                                                             |
  | last_updated  | 2019-06-24                                                                                                      |
  | description   | Harvests hosts from Google.com by using the &amp;#39;site&amp;#39; search operator. Updates the &amp;#39;hosts&amp;#39; table with the results. |
  | required_keys | []                                                                                                              |
  | dependencies  | []                                                                                                              |
  | files         | []                                                                                                              |
  | status        | installed                                                                                                       |
  +---------------------------------------------------------------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us try using this module for subdomain enumerator given a seed domain. Running
&lt;code&gt;modules load&lt;/code&gt; command with the module path activates the modules and gives us a
subshell for running commands within context of the module.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test] &amp;gt; modules load recon/domains-hosts/google_site_web 
[recon-ng][test][google_site_web] &amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For example, running &lt;code&gt;info&lt;/code&gt; gives us a simple description on the current module
and it&amp;rsquo;s parameters.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;recon-ng][test][google_site_web] &amp;gt; info

      Name: Google Hostname Enumerator
    Author: Tim Tomes (@lanmaster53)
   Version: 1.0

Description:
  Harvests hosts from Google.com by using the &amp;#39;site&amp;#39; search operator. Updates the &amp;#39;hosts&amp;#39; table with
  the results.

Options:
  Name    Current Value  Required  Description
  ------  -------------  --------  -----------
  SOURCE  default        yes       source of input (see &amp;#39;info&amp;#39; for details)

Source Options:
  default        SELECT DISTINCT domain FROM domains WHERE domain IS NOT NULL
  &amp;lt;string&amp;gt;       string representing a single input
  &amp;lt;path&amp;gt;         path to a file containing a list of inputs
  query &amp;lt;sql&amp;gt;    database query returning one column of inputs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us set the &lt;code&gt;SOURCE&lt;/code&gt; option to a domain of a company that has an open
&lt;a href=&#34;https://hackerone.com/tencent?type=team&#34;&gt;bug bounty program&lt;/a&gt; and run the
module.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test][google_site_web] &amp;gt; options set SOURCE tencent.com
SOURCE =&amp;gt; tencent.com
[recon-ng][test][google_site_web] &amp;gt; run

-----------
TENCENT.COM
-----------
[*] Searching Google for: site:tencent.com
[*] Country: None
[*] Host: buy.cloud.tencent.com
[*] Ip_Address: None
[*] Latitude: None
[*] Longitude: None
[*] Notes: None
[*] Region: None
[*] --------------------------------------------------
[*] Country: None
[*] Host: intl.cloud.tencent.com
[*] Ip_Address: None
[*] Latitude: None
[*] Longitude: None
[*] Notes: None
[*] Region: None

...
[*] Searching Google for: site:tencent.com -site:buy.cloud.tencent.com -site:intl.cloud.tencent.com -site:meeting.tencent.com -site:open.tencent.com -site:tdesign.tencent.com -site:s.tencent.com -site:dnspod.cloud.tencent.com -site:www.tencent.com -site:ioa.tencent.com -site:market.cloud.tencent.com -site:ad.tencent.com -site:xiaowei.tencent.com -site:spd.tencent.com -site:opensource.tencent.com -site:isux.tencent.com -site:hiflow.tencent.com -site:tmc.tencent.com -site:ur.tencent.com -site:partner.cloud.tencent.com -site:ieg.tencent.com -site:app.cloud.tencent.com -site:8000.tencent.com -site:drug.ai.tencent.com -site:code.tencent.com -site:cloud.tencent.com -site:mxd.tencent.com -site:tea-lab.tencent.com -site:retail.tencent.com -site:careers.tencent.com -site:gss.tencent.com -site:todo.tencent.com -site:gameinstitute.tencent.com -site:broker.tencent.com -site:tapd.tencent.com -site:light.mofyi.tencent.com -site:tdw.tencent.com -site:aiarena.tencent.com -site:developers.tencent.com -site:om.tencent.com -site:keenlab.tencent.com -site:gwb.tencent.com -site:service.security.tencent.com -site:cdc.tencent.com -site:mtp.tencent.com -site:calendar.tencent.com -site:design.tencent.com -site:ess.tencent.com -site:gcloud.tencent.com -site:dsic.tencent.com -site:tengshi.tencent.com -site:matrix.tencent.com -site:research.tencent.com -site:jarvislab.tencent.com -site:x5.tencent.com -site:rtx.tencent.com -site:aidata.tencent.com -site:fapiao.tencent.com -site:beacon.tencent.com -site:edu.tencent.com -site:tcaplusdb.tencent.com -site:cii.tencent.com -site:healthcare.tencent.com -site:wemake.tencent.com -site:ai.tencent.com -site:security.tencent.com -site:arc.tencent.com -site:idc.tencent.com -site:quantum.tencent.com -site:tosp.tencent.com -site:en.security.tencent.com -site:app.v.tencent.com -site:blade.tencent.com -site:ipr.tencent.com -site:multimedia.tencent.com -site:jubao.tencent.com -site:xlab.tencent.com
[*] Country: None
[*] Host: tengyun.tencent.com
[*] Ip_Address: None
[*] Latitude: None
[*] Longitude: None
[*] Notes: None
[*] Region: None
[*] --------------------------------------------------
[*] Country: None
[*] Host: supplier.tencent.com
[*] Ip_Address: None
[*] Latitude: None
[*] Longitude: None
[*] Notes: None
[*] Region: None
[*] --------------------------------------------------
[*] Country: None
[*] Host: yzt.tencent.com
[*] Ip_Address: None
[*] Latitude: None
[*] Longitude: None
[*] Notes: None
[*] Region: None

...

[*] Searching Google for: site:tencent.com -site:buy.cloud.tencent.com -site:intl.cloud.tencent.com -site:meeting.tencent.com -site:open.tencent.com -site:tdesign.tencent.com -site:s.tencent.com -site:dnspod.cloud.tencent.com -site:www.tencent.com -site:ioa.tencent.com -site:market.cloud.tencent.com -site:ad.tencent.com -site:xiaowei.tencent.com -site:spd.tencent.com -site:opensource.tencent.com -site:isux.tencent.com -site:hiflow.tencent.com -site:tmc.tencent.com -site:ur.tencent.com -site:partner.cloud.tencent.com -site:ieg.tencent.com -site:app.cloud.tencent.com -site:8000.tencent.com -site:drug.ai.tencent.com -site:code.tencent.com -site:cloud.tencent.com -site:mxd.tencent.com -site:tea-lab.tencent.com -site:retail.tencent.com -site:careers.tencent.com -site:gss.tencent.com -site:todo.tencent.com -site:gameinstitute.tencent.com -site:broker.tencent.com -site:tapd.tencent.com -site:light.mofyi.tencent.com -site:tdw.tencent.com -site:aiarena.tencent.com -site:developers.tencent.com -site:om.tencent.com -site:keenlab.tencent.com -site:gwb.tencent.com -site:service.security.tencent.com -site:cdc.tencent.com -site:mtp.tencent.com -site:calendar.tencent.com -site:design.tencent.com -site:ess.tencent.com -site:gcloud.tencent.com -site:dsic.tencent.com -site:tengshi.tencent.com -site:matrix.tencent.com -site:research.tencent.com -site:jarvislab.tencent.com -site:x5.tencent.com -site:rtx.tencent.com -site:aidata.tencent.com -site:fapiao.tencent.com -site:beacon.tencent.com -site:edu.tencent.com -site:tcaplusdb.tencent.com -site:cii.tencent.com -site:healthcare.tencent.com -site:wemake.tencent.com -site:ai.tencent.com -site:security.tencent.com -site:arc.tencent.com -site:idc.tencent.com -site:quantum.tencent.com -site:tosp.tencent.com -site:en.security.tencent.com -site:app.v.tencent.com -site:blade.tencent.com -site:ipr.tencent.com -site:multimedia.tencent.com -site:jubao.tencent.com -site:xlab.tencent.com -site:tengyun.tencent.com -site:supplier.tencent.com -site:yzt.tencent.com -site:rfq.tencent.com -site:moa.tencent.com -site:growing.tencent.com -site:we.tencent.com -site:youshu.tencent.com -site:cloudsec.tencent.com -site:yunding.tencent.com -site:dmp.tencent.com -site:privacy.tencent.com -site:rg.tencent.com -site:agency.tencent.com -site:tpa.tencent.com -site:tssp.tencent.com -site:hebao.tencent.com -site:yixing.tencent.com -site:sign2.tencent.com -site:tfdp.tencent.com
[!] Google CAPTCHA triggered. No bypass available.

-------
SUMMARY
-------
[*] 96 total (96 new) hosts found.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that the modules incrementally discovers new subdomains by using &lt;code&gt;site:&lt;/code&gt;
operator to get Google search results matching the seed domain, but also
using minus sign to remove the already known subdomains from search result
pages. It was able to scrape 96 subdomains before running into captcha that
Google gives the user to solve when it detects too many search requests too
quickly from single IP address. To see all the data it got so far we can
run &lt;code&gt;show hosts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point we have a bunch of subdomains, but no IP addresses related to
them. We can enrich the data in the hosts table by loading and running another
module that will perform DNS requests for the subdomains:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test][google_site_web] &amp;gt; modules load recon/hosts-hosts/resolve
[recon-ng][test][resolve] &amp;gt; info

      Name: Hostname Resolver
    Author: Tim Tomes (@lanmaster53)
   Version: 1.0

Description:
  Resolves the IP address for a host. Updates the &amp;#39;hosts&amp;#39; table with the results.

Options:
  Name    Current Value  Required  Description
  ------  -------------  --------  -----------
  SOURCE  default        yes       source of input (see &amp;#39;info&amp;#39; for details)

Source Options:
  default        SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL AND ip_address IS NULL
  &amp;lt;string&amp;gt;       string representing a single input
  &amp;lt;path&amp;gt;         path to a file containing a list of inputs
  query &amp;lt;sql&amp;gt;    database query returning one column of inputs

Comments:
  * Note: Nameserver must be in IP form.

[recon-ng][test][resolve] &amp;gt; run
[*] buy.cloud.tencent.com =&amp;gt; 158.79.1.174
[*] buy.cloud.tencent.com =&amp;gt; 158.79.1.160
[*] intl.cloud.tencent.com =&amp;gt; 158.79.1.160
[*] intl.cloud.tencent.com =&amp;gt; 158.79.1.174
[*] meeting.tencent.com =&amp;gt; 203.205.137.236
[*] open.tencent.com =&amp;gt; 129.226.107.102
[*] open.tencent.com =&amp;gt; 129.226.106.5
[*] tdesign.tencent.com =&amp;gt; 158.79.1.56
[*] tdesign.tencent.com =&amp;gt; 158.79.1.178
[*] tdesign.tencent.com =&amp;gt; 158.79.1.187
[*] tdesign.tencent.com =&amp;gt; 158.79.1.185
[*] s.tencent.com =&amp;gt; 43.135.106.184
[*] s.tencent.com =&amp;gt; 43.135.106.117
[*] dnspod.cloud.tencent.com =&amp;gt; 42.194.253.127
[*] www.tencent.com =&amp;gt; 43.152.54.219
[*] www.tencent.com =&amp;gt; 43.152.56.217
[*] ioa.tencent.com =&amp;gt; 119.28.121.91
[*] market.cloud.tencent.com =&amp;gt; 183.2.144.115
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running &lt;code&gt;show hosts&lt;/code&gt; again will show us that &lt;code&gt;ip_address&lt;/code&gt; column is now filled.
Internally each workspace will have SQLite database for persisting the data and
the recon-ng command line gives us a way to run SQL queries on it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test][resolve] &amp;gt; db
Interfaces with the workspace&amp;#39;s database

Usage: db &amp;lt;delete|insert|notes|query|schema&amp;gt; [...]

[recon-ng][test][resolve] &amp;gt; db query SELECT * FROM hosts LIMIT 10;

  +----------------------------------------------------------------------------------------------------------------+
  |           host           |    ip_address   | region | country | latitude | longitude | notes |      module     |
  +----------------------------------------------------------------------------------------------------------------+
  | buy.cloud.tencent.com    | 158.79.1.174    |        |         |          |           |       | google_site_web |
  | intl.cloud.tencent.com   | 158.79.1.160    |        |         |          |           |       | google_site_web |
  | meeting.tencent.com      | 203.205.137.236 |        |         |          |           |       | google_site_web |
  | open.tencent.com         | 129.226.107.102 |        |         |          |           |       | google_site_web |
  | tdesign.tencent.com      | 158.79.1.56     |        |         |          |           |       | google_site_web |
  | s.tencent.com            | 43.135.106.184  |        |         |          |           |       | google_site_web |
  | dnspod.cloud.tencent.com | 42.194.253.127  |        |         |          |           |       | google_site_web |
  | www.tencent.com          | 43.152.54.219   |        |         |          |           |       | google_site_web |
  | ioa.tencent.com          | 119.28.121.91   |        |         |          |           |       | google_site_web |
  | market.cloud.tencent.com | 183.2.144.115   |        |         |          |           |       | google_site_web |
  +----------------------------------------------------------------------------------------------------------------+

[*] 10 rows returned
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For further analysis of data outside recon-ng command line we can take a snapshot
of the database:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test][resolve] &amp;gt; back
[recon-ng][test] &amp;gt; snapshots take
[*] Snapshot created: snapshot_20220906192805.db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The newly created file at ~/.recon-ng/workspaces/test/ contains the exported
SQLite database that can be further analysed within Jupyter notebook or some
other environment. Note that we used the &lt;code&gt;back&lt;/code&gt; command to exit the module
sub-shell since some commands are not available there.&lt;/p&gt;
&lt;p&gt;Now let us generate an HTML report. There&amp;rsquo;s also a module for that.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[recon-ng][test] &amp;gt; modules load reporting/html
[recon-ng][test][html] &amp;gt; info

      Name: HTML Report Generator
    Author: Tim Tomes (@lanmaster53)
   Version: 1.0

Description:
  Creates an HTML report.

Options:
  Name      Current Value                                     Required  Description
  --------  -------------                                     --------  -----------
  CREATOR                                                     yes       use creator name in the report footer
  CUSTOMER                                                    yes       use customer name in the report header
  FILENAME  /Users/rl/.recon-ng/workspaces/test/results.html  yes       path and filename for report output
  SANITIZE  True                                              yes       mask sensitive data in the report

[recon-ng][test][html] &amp;gt; options set CREATOR rl1987
CREATOR =&amp;gt; rl1987
[recon-ng][test][html] &amp;gt; options set CUSTOMER Shady Industries, Inc
CUSTOMER =&amp;gt; Shady Industries, Inc
[recon-ng][test][html] &amp;gt; run
[*] Report generated at &amp;#39;/Users/rl/.recon-ng/workspaces/test/results.html&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-09-06_19.33.29.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But what if we wanted to have a script to run everything we just did now in a reproducible
manner? Running recon-ng as a subprocess within some programming language and feeding the commands
through standard input could work, but is rather clunky. The good news is that we can create
a text file with one command per line and use &lt;code&gt;-r&lt;/code&gt; CLI option to run all the commands in batch
mode (don&amp;rsquo;t forget the &lt;code&gt;exit&lt;/code&gt; command at the end to make it finish without human intervention):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ recon-ng -r script
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>cURL beyond the basics</title>
      <link>https://www.trickster.dev/post/curl-beyond-the-basics/</link>
      <pubDate>Sun, 04 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/curl-beyond-the-basics/</guid>
      <description>&lt;p&gt;Curl is a prominent CLI tool and library for performing client-side
requests in a number of application layer protocols. Commonly it is used
for HTTP requests. However, there is more to curl than that. We will
go through some less known features of curl that can be used in web
scraping and automation systems.&lt;/p&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;p&gt;Sometimes things fail and require us to take a deeper look into technical
details of what is happening. Running curl with &lt;code&gt;--verbose&lt;/code&gt; (or just &lt;code&gt;-v&lt;/code&gt;)
gives us a certain level of technical details regarding connection establishment
and protocol messages being transmitted:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl http://httpbin.org/status/401 -v 
*   Trying 34.227.213.82:80...
* Connected to httpbin.org (34.227.213.82) port 80 (#0)
&amp;gt; GET /status/401 HTTP/1.1
&amp;gt; Host: httpbin.org
&amp;gt; User-Agent: curl/7.79.1
&amp;gt; Accept: */*
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 401 UNAUTHORIZED
&amp;lt; Date: Sat, 03 Sep 2022 07:28:47 GMT
&amp;lt; Server: gunicorn/19.9.0
&amp;lt; WWW-Authenticate: Basic realm=&amp;#34;Fake Realm&amp;#34;
&amp;lt; Access-Control-Allow-Origin: *
&amp;lt; Access-Control-Allow-Credentials: true
&amp;lt; Cache-Control: proxy-revalidate
&amp;lt; Content-Length: 0
&amp;lt; Connection: Keep-Alive
&amp;lt; 
* Connection #0 to host httpbin.org left intact
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we may want to look deeper. There are CLI options that gives us even
more detailed output for troubleshooting. &lt;code&gt;--trace&lt;/code&gt; saves full dump of data
being sent and received into a text file (or standard output if we use &lt;code&gt;-&lt;/code&gt;
instead of file path):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl http://httpbin.org/status/401 --trace -
== Info:   Trying 34.227.213.82:80...
== Info: Connected to httpbin.org (34.227.213.82) port 80 (#0)
=&amp;gt; Send header, 85 bytes (0x55)
0000: 47 45 54 20 2f 73 74 61 74 75 73 2f 34 30 31 20 GET /status/401 
0010: 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 HTTP/1.1..Host: 
0020: 68 74 74 70 62 69 6e 2e 6f 72 67 0d 0a 55 73 65 httpbin.org..Use
0030: 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f 37 2e r-Agent: curl/7.
0040: 37 39 2e 31 0d 0a 41 63 63 65 70 74 3a 20 2a 2f 79.1..Accept: */
0050: 2a 0d 0a 0d 0a                                  *....
== Info: Mark bundle as not supporting multiuse
&amp;lt;= Recv header, 27 bytes (0x1b)
0000: 48 54 54 50 2f 31 2e 31 20 34 30 31 20 55 4e 41 HTTP/1.1 401 UNA
0010: 55 54 48 4f 52 49 5a 45 44 0d 0a                UTHORIZED..
&amp;lt;= Recv header, 37 bytes (0x25)
0000: 44 61 74 65 3a 20 53 61 74 2c 20 30 33 20 53 65 Date: Sat, 03 Se
0010: 70 20 32 30 32 32 20 30 37 3a 33 36 3a 32 35 20 p 2022 07:36:25 
0020: 47 4d 54 0d 0a                                  GMT..
&amp;lt;= Recv header, 25 bytes (0x19)
0000: 53 65 72 76 65 72 3a 20 67 75 6e 69 63 6f 72 6e Server: gunicorn
0010: 2f 31 39 2e 39 2e 30 0d 0a                      /19.9.0..
&amp;lt;= Recv header, 44 bytes (0x2c)
0000: 57 57 57 2d 41 75 74 68 65 6e 74 69 63 61 74 65 WWW-Authenticate
0010: 3a 20 42 61 73 69 63 20 72 65 61 6c 6d 3d 22 46 : Basic realm=&amp;#34;F
0020: 61 6b 65 20 52 65 61 6c 6d 22 0d 0a             ake Realm&amp;#34;..
&amp;lt;= Recv header, 32 bytes (0x20)
0000: 41 63 63 65 73 73 2d 43 6f 6e 74 72 6f 6c 2d 41 Access-Control-A
0010: 6c 6c 6f 77 2d 4f 72 69 67 69 6e 3a 20 2a 0d 0a llow-Origin: *..
&amp;lt;= Recv header, 40 bytes (0x28)
0000: 41 63 63 65 73 73 2d 43 6f 6e 74 72 6f 6c 2d 41 Access-Control-A
0010: 6c 6c 6f 77 2d 43 72 65 64 65 6e 74 69 61 6c 73 llow-Credentials
0020: 3a 20 74 72 75 65 0d 0a                         : true..
&amp;lt;= Recv header, 33 bytes (0x21)
0000: 43 61 63 68 65 2d 43 6f 6e 74 72 6f 6c 3a 20 70 Cache-Control: p
0010: 72 6f 78 79 2d 72 65 76 61 6c 69 64 61 74 65 0d roxy-revalidate.
0020: 0a                                              .
&amp;lt;= Recv header, 19 bytes (0x13)
0000: 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 Content-Length: 
0010: 30 0d 0a                                        0..
&amp;lt;= Recv header, 24 bytes (0x18)
0000: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 4b 65 65 70 Connection: Keep
0010: 2d 41 6c 69 76 65 0d 0a                         -Alive..
&amp;lt;= Recv header, 2 bytes (0x2)
0000: 0d 0a                                           ..
== Info: Connection #0 to host httpbin.org left intact
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we wanted to skip hexdumps of protocol messages we can use &lt;code&gt;--trace-ascii&lt;/code&gt;
instead of &lt;code&gt;--trace&lt;/code&gt;. To augment the debug output with timing information
we can use &lt;code&gt;--trace-time&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl http://httpbin.org/status/401 --trace-ascii - --trace-time
14:38:54.540384 == Info:   Trying 54.147.68.244:80...
14:38:54.573724 == Info: Connected to httpbin.org (54.147.68.244) port 80 (#0)
14:38:54.573928 =&amp;gt; Send header, 85 bytes (0x55)
0000: GET /status/401 HTTP/1.1
001a: Host: httpbin.org
002d: User-Agent: curl/7.79.1
0046: Accept: */*
0053: 
14:38:55.131822 == Info: Mark bundle as not supporting multiuse
14:38:55.131875 &amp;lt;= Recv header, 27 bytes (0x1b)
0000: HTTP/1.1 401 UNAUTHORIZED
14:38:55.131907 &amp;lt;= Recv header, 37 bytes (0x25)
0000: Date: Sat, 03 Sep 2022 07:38:55 GMT
14:38:55.131932 &amp;lt;= Recv header, 25 bytes (0x19)
0000: Server: gunicorn/19.9.0
14:38:55.131958 &amp;lt;= Recv header, 44 bytes (0x2c)
0000: WWW-Authenticate: Basic realm=&amp;#34;Fake Realm&amp;#34;
14:38:55.131983 &amp;lt;= Recv header, 32 bytes (0x20)
0000: Access-Control-Allow-Origin: *
14:38:55.132004 &amp;lt;= Recv header, 40 bytes (0x28)
0000: Access-Control-Allow-Credentials: true
14:38:55.132028 &amp;lt;= Recv header, 33 bytes (0x21)
0000: Cache-Control: proxy-revalidate
14:38:55.132050 &amp;lt;= Recv header, 19 bytes (0x13)
0000: Content-Length: 0
14:38:55.132070 &amp;lt;= Recv header, 24 bytes (0x18)
0000: Connection: Keep-Alive
14:38:55.132091 &amp;lt;= Recv header, 2 bytes (0x2)
0000: 
14:38:55.132122 == Info: Connection #0 to host httpbin.org left intact
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be used with &lt;code&gt;--trace&lt;/code&gt;, &lt;code&gt;--trace-ascii&lt;/code&gt; and &lt;code&gt;--verbose&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dictionary-lookups-with-dict-protocol&#34;&gt;Dictionary lookups with DICT protocol&lt;/h2&gt;
&lt;p&gt;DICT (&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc2229&#34;&gt;RFC 2229&lt;/a&gt;) is a application
level protocol for performing dictionary lookups. It can be used
via curl like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl dict://dict.org/d:linux       
220 dict.dict.org dictd 1.12.1/rf on Linux 4.19.0-10-amd64 &amp;lt;auth.mime&amp;gt; &amp;lt;138373555.5356.1662191318@dict.dict.org&amp;gt;
250 ok
150 1 definitions retrieved
151 &amp;#34;linux&amp;#34; wn &amp;#34;WordNet (r) 3.0 (2006)&amp;#34;
Linux
    n 1: an open-source version of the UNIX operating system
.
250 ok [d/m/c = 1/0/30; 0.000r 0.000u 0.000s]
221 bye [d/m/c = 0/0/0; 0.000r 0.000u 0.000s]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This may seem like it&amp;rsquo;s nothing to write home about. After all, we can
search Google with &lt;code&gt;define:&lt;/code&gt; and the word we want to lookup. Scraping
Google SERPs either directly or through some SaaS API is not hard. However
this provides an example of a fairly obscure network protocol that
curl has support for.&lt;/p&gt;
&lt;h2 id=&#34;transferring-files-via-sftp&#34;&gt;Transferring files via (S)FTP&lt;/h2&gt;
&lt;p&gt;Let us something bit more useful now. Curl can be used to transfer files
via FTP, SFTP and FTPS protocols. For example, we can use curl to list
files on FTP server and download them:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --list-only ftp://ftp.sunet.se             
HEADER.html
Public
about
cdimage
conspiracy
debian
debian-cd
favicon.ico
images
mirror
pub
releases
robots.txt
tails
ubuntu
$ curl --list-only ftp://ftp.sunet.se/debian/
README
README.CD-manufacture
README.html
README.mirrors.html
README.mirrors.txt
dists
doc
extrafiles
indices
ls-lR.gz
pool
project
tools
zzz-dists
$ curl ftp://ftp.sunet.se/debian/README -o README
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we wanted to upload a local file to remote path at FTP server we could use
&lt;code&gt;-T&lt;/code&gt; or &lt;code&gt;--upload-file&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However plain text anonymous servers are going out of fashion within last couple
decades. When we have SSH access to a remote server
we can also use SFTP - a more modern file transfer protocol that is built upon
the foundation of SSH. This is not merely FTP over SSH, but a separate protocol
designed from ground up by IETF. It is also more generalised than the legacy
SCP protocol.&lt;/p&gt;
&lt;p&gt;To use SFTP with curl, we would use the same URL format, except we would have
&lt;code&gt;sftp://&lt;/code&gt; at the beginning. If username/password auth is required we can
pass the credentials in colon-separated form via &lt;code&gt;--user&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;If we are trying to connect to FTPS (FTP over TLS) server we would use &lt;code&gt;ftps://&lt;/code&gt;
for the URLs.&lt;/p&gt;
&lt;h2 id=&#34;sending-and-receiving-email&#34;&gt;Sending and receiving email&lt;/h2&gt;
&lt;p&gt;Curl also supports SMTP protocol for email sending. Let us try sending an email
via Sendgrid. First we need to prepare a text file with some email headers and
content. This is what we save into email.txt:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;From: No Reply &amp;lt;noreply@keyspace.lt&amp;gt;
To: Test User &amp;lt;test2048@mailinator.com&amp;gt;
Subject: Test email

A specter is haunting the modern world, the specter of crypto anarchy.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A command to send this email is the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --insecure --ssl smtp://smtp.sendgrid.com:587 --user &amp;#34;apikey:[REDACTED]&amp;#34; --mail-rcpt &amp;#34;test2048@mailinator.com&amp;#34; --mail-from &amp;#34;noreply@keyspace.lt&amp;#34; --upload-file email.txt  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The redacted part is Sendgrid API key that we use as SMTP password.&lt;/p&gt;
&lt;p&gt;Curl can be used to send emails, but what about receiving them? This is also
possible via POP3 and IMAP protocols.&lt;/p&gt;
&lt;p&gt;Let us first list message IDs and sizes via POP3 protocol:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl pop3://pop3.rambler.ru --user &amp;#34;ermakova_g01bm@rambler.ru:[REDACTED]&amp;#34;
1 4388
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have a single message with ID 1 and size 4388. It can be downloaded by
using the message ID as URL path:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl pop3://pop3.rambler.ru/1 --user &amp;#34;ermakova_g01bm@rambler.ru:[REDACTED]&amp;#34; 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Things are a bit different with IMAP, as IMAP protocol supports different
mailboxes that we can list:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --ssl imap://imap.rambler.ru/ --user &amp;#34;ermakova_g01bm@rambler.ru:[REDACTED]&amp;#34;    
* LIST (\HasNoChildren \UnMarked \Sent) &amp;#34;/&amp;#34; SentBox
* LIST (\HasNoChildren \Junk) &amp;#34;/&amp;#34; Spam
* LIST (\HasNoChildren \Marked \Trash) &amp;#34;/&amp;#34; Trash
* LIST (\HasNoChildren \Drafts) &amp;#34;/&amp;#34; DraftBox
* LIST (\HasNoChildren) &amp;#34;/&amp;#34; INBOX
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To check the status of mailbox, let us send &lt;code&gt;examine&lt;/code&gt; IMAP command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --ssl &amp;#34;imap://mail.rambler.ru/&amp;#34; -X &amp;#34;examine INBOX&amp;#34; --user &amp;#34;ermakova_g01bm@rambler.ru:[REDACTED]&amp;#34; 
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 2 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 1644072147] UIDs valid
* OK [UIDNEXT 20] Predicted next UID
* OK [HIGHESTMODSEQ 37] Highest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get a single message at specific index, query it like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --ssl &amp;#34;imap://mail.rambler.ru/INBOX;MAILINDEX=1&amp;#34; --user &amp;#34;ermakova_g01bm@rambler.ru:[REDACTED]&amp;#34;  
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;libcurl-and-its-bindings&#34;&gt;libcurl and it&amp;rsquo;s bindings&lt;/h2&gt;
&lt;p&gt;Curl is not only a CLI tool, but also a C library. If we have a curl snippet we can
add &lt;code&gt;--libcurl&lt;/code&gt; with file path to generate some boilerplate C code:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl http://httpbin.org/status/401 --libcurl example.c
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It will generate something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/********* Sample code generated by the curl command line tool **********
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * All curl_easy_setopt() options are documented at:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * https://curl.se/libcurl/c/curl_easy_setopt.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; ************************************************************************/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;curl/curl.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; argc, &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;argv[])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  CURLcode ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  CURL &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;hnd;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hnd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_init&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_BUFFERSIZE, &lt;span style=&#34;color:#ae81ff&#34;&gt;102400L&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_URL, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://httpbin.org/status/401&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_NOPROGRESS, &lt;span style=&#34;color:#ae81ff&#34;&gt;1L&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_USERAGENT, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;curl/7.85.0&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_MAXREDIRS, &lt;span style=&#34;color:#ae81ff&#34;&gt;50L&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_HTTP_VERSION, (&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt;)CURL_HTTP_VERSION_2TLS);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_FTP_SKIP_PASV_IP, &lt;span style=&#34;color:#ae81ff&#34;&gt;1L&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_setopt&lt;/span&gt;(hnd, CURLOPT_TCP_KEEPALIVE, &lt;span style=&#34;color:#ae81ff&#34;&gt;1L&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* Here is a list of options the curl code used that cannot get generated
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     as source easily. You may choose to either not use them or implement
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     them yourself.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_WRITEDATA set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_INTERLEAVEDATA set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_WRITEFUNCTION set to a functionpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_READDATA set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_READFUNCTION set to a functionpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_SEEKDATA set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_SEEKFUNCTION set to a functionpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_ERRORBUFFER set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_STDERR set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_HEADERFUNCTION set to a functionpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  CURLOPT_HEADERDATA set to a objectpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_perform&lt;/span&gt;(hnd);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;curl_easy_cleanup&lt;/span&gt;(hnd);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hnd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; NULL;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;)ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**** End of sample code ****/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are not working in fairly deep systems programming you may not be willing
to code in C. However, there&amp;rsquo;s libcurl bindings available in a lot of languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C++ - &lt;a href=&#34;http://www.curlpp.org/&#34;&gt;curlpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Go - &lt;a href=&#34;https://github.com/andelf/go-curl&#34;&gt;go-curl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Haskell - &lt;a href=&#34;https://hackage.haskell.org/package/curl&#34;&gt;curl Haskell package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Java - &lt;a href=&#34;https://github.com/pjlegato/curl-java&#34;&gt;curl-java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;JavaScript - &lt;a href=&#34;https://github.com/JCMais/node-libcurl&#34;&gt;node-libcurl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PHP - &lt;a href=&#34;https://www.php.net/curl&#34;&gt;PHP curl library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python - &lt;a href=&#34;http://pycurl.io/&#34;&gt;PyCURL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rust - &lt;a href=&#34;https://github.com/alexcrichton/curl-rust&#34;&gt;curl-rust&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you merely need to do some basic HTTP requests using libcurl is likely to be
an overkill. For example, Python requests module is far easier to use than
PyCURL. However, if you&amp;rsquo;re doing some nonstandard stuff libcurl might be a
good abstraction layer to base your code upon. For example, once I had to
integrate proxy servers that require TLS handshake to be performed
&lt;em&gt;before&lt;/em&gt; HTTP CONNECT message is sent by the client. Using such proxy servers
is possible with PyCURL.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Running Stable Diffusion on Vultr</title>
      <link>https://www.trickster.dev/post/running-stable-diffusion-on-vultr/</link>
      <pubDate>Wed, 31 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/running-stable-diffusion-on-vultr/</guid>
      <description>&lt;p&gt;Stable Diffusion is a recently released open source text-to-image AI system that
challenges DALL-E by OpenAI. Nowadays OpenAI products are open in name only:
aside from client libraries and some other inconsequential things, all the
new products by OpenAI (GPT-3, DALL-E 2) are not only proprietary, but also
offered in SaaS form only. In constrast with locked-down proprietarism of DALL-E,
Stable Diffusion is fully open source and can be self-hosted on a server the
end user controls. This is what we are going to do today.&lt;/p&gt;
&lt;p&gt;For setting up our own installation of Stable Diffusion, we are going to use
&lt;a href=&#34;https://vultr.com/&#34;&gt;Vultr&lt;/a&gt; - a cloud provider that is comparable to Digital
Ocean, but has an option for GPU servers.&lt;/p&gt;
&lt;p&gt;The following assumes that you have Vultr account with some credits and/or a
configured payment method. In the &amp;ldquo;Product&amp;rdquo; tab press the plus button and
choose &amp;ldquo;Deploy new server&amp;rdquo;. Out of server types, choose &amp;ldquo;Cloud GPU&amp;rdquo;. At the
time of writing, cloud GPU instances are only available in New Jersey region.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_16.58.06.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We choose Debian 11 image because we want to base our setup on vanilla Linux
distribution that does rely on ML/data science software being pre-installed
(as is the case with Anaconda image and some other options).&lt;/p&gt;
&lt;p&gt;When it comes to server size, we choose at least the third option that has
10 GB of VRAM.&lt;/p&gt;
&lt;p&gt;We launch the server and watch the console until cloud-init finishes setting
everything up.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_17.58.28.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to SSH into the server. We will need to use credentials that
are available on the dashboard page for the server we just launched, Overview
tab.&lt;/p&gt;
&lt;p&gt;Let us install some developer tools from APT, as well as Python interpreter.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# apt-get update
# apt-get install -y git vim tmux
# apt-get install -y python3 python3-pip
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to install Pytorch, as Stable Diffusion is heavily reliant on that.
On Pytorch homepage we use the little widget to get the exact command we need
to run (CUDA version 11.6 was found by running &lt;code&gt;nvidia-smi&lt;/code&gt; on the server):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_18.04.02.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we need to login into Hugging Face, which is a portal similar to Github,
but meant specifically for ML community. Make sure you have an account there,
go to your account settings and create an access token.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_17.10.54.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then install Hugging Face CLI client through PIP and run it to login:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# pip3 install huggingface_hub
# huggingface-cli login
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will ask you to paste in the access token from account settings.&lt;/p&gt;
&lt;p&gt;Furthermore, it&amp;rsquo;s useful to set up git credential helper so that we are
not being repeatedly asked to enter git credentials:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# git config --global credential.helper store
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let us go to Hugging Face page for &lt;a href=&#34;https://huggingface.co/CompVis/stable-diffusion-v1-4&#34;&gt;Stable Diffusion v. 1.4&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It recommends us to install &lt;a href=&#34;https://github.com/huggingface/diffusers&#34;&gt;&lt;code&gt;diffusers&lt;/code&gt;&lt;/a&gt; library via PIP:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# pip3 install --upgrade diffusers transformers scipy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once we have that installed, we can run the sample code that is provided.
One thing I found is that Vultr instance with 10GB of VRAM is still not enough
to run image generation at 32 bit floating point precision - one has to lower it
to 16 bit. An image of astronaut riding a horse on Mars is successfully generated.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/astronaut_rides_horse.png&#34;&gt;astronaut_rides_horse.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I took the liberty to tweak the sample code from Stable Diffusion project to make it
more interactive:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; torch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; torch &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; autocast
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; diffusers &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; StableDiffusionPipeline
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    prompt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Prompt: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    filepath &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Image file path: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CompVis/stable-diffusion-v1-4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    device &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cuda&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#pipe = StableDiffusionPipeline.from_pretrained(model_id, use_auth_token=True)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pipe &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; StableDiffusionPipeline&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_pretrained(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model_id, torch_dtype&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;torch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;float16, revision&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fp16&amp;#34;&lt;/span&gt;, use_auth_token&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pipe &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pipe&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to(device)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; autocast(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cuda&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        image &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pipe(prompt, guidance_scale&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7.5&lt;/span&gt;)[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sample&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save(filepath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you find that is pain to download output images via SFTP you can run a quick
HTTP server within tmux session in your output directory like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ufw disable
# python3 -m http.server
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the firewall is being disabled to allow ingress for HTTP requests
from outside. Furthemore, I would advise creating new directory for this so that no
sensitive information is exposed.&lt;/p&gt;
&lt;p&gt;It takes approx. 18 seconds to generate an image on $250/month Vultr instance.&lt;/p&gt;
&lt;p&gt;Funnily enough, it censored itself when asked what the origin of mankind looks like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# python3 sd_gen.py 
Prompt: origin of mankind
Image file path: origin-of-mankind.png
ftfy or spacy is not installed using BERT BasicTokenizer instead of ftfy.
51it [00:18,  2.82it/s]
Potential NSFW content was detected in one or more images. A black image will be returned instead. Try again with a different prompt and/or seed.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I guess there was something NSFW involved in that.&lt;/p&gt;
&lt;p&gt;Creating Terraform configuration with provisioning script to make the above
setup automated for quick installation and teardown is left as an exercise to
the reader.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Experimenting with DALL-E</title>
      <link>https://www.trickster.dev/post/experimenting-with-dall-e/</link>
      <pubDate>Tue, 30 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/experimenting-with-dall-e/</guid>
      <description>&lt;p&gt;DALL-E 2 is a cutting edge AI system for creating and manipulating images based
on natural language instructions. Although it is not 100% available to the general
public yet, it is possible to apply for access by filling a form at OpenAI site.
This will place you in a waitlist. I was able to get access after some 20
days. It&amp;rsquo;s unclear on what basis they choose or prioritise people for access,
but my guess is that having spent a few dollars on GPT-3 had helped.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s been a lot of hype about DALL-E recently. Some people are posting images
that seem almost too good to be true. On the other hand, people are also posting
almost carricature level stuff. This post will be a showcase of me playing with
DALL-E web interface to see for myself what it can and cannot do.&lt;/p&gt;
&lt;p&gt;Let us start with abstract, subjective concepts. Can DALL-E do the work of an
artist and make the non-visual visual?&lt;/p&gt;
&lt;p&gt;According to DALL-E, &amp;ldquo;Peace of mind&amp;rdquo; looks like a lady doing a yoga pose and some nature
landscapes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.29.41.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s passable, I guess, but it looks like stock images. The pictures generated are
realistic and look like photographs, but they are also fairly bland.&lt;/p&gt;
&lt;p&gt;What does a frontier look like? DALL-E generates some derelict natural landscapes when
asked to visualise it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.34.25.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What about &amp;ldquo;Dawn of man&amp;rdquo;? When given this prompt, DALL-E generates a picture of man,
illuminated by a light of dawn. Not very creative, and not what I had in mind.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.36.13.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Perhaps we can rephrase it? What about &amp;ldquo;Origin of mankind&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.38.21.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not it got abstract and science-fictiony by generating something that is quasi-spiritual
and maybe references the &lt;a href=&#34;https://en.wikipedia.org/wiki/Ancient_astronauts&#34;&gt;ancient astronaut&lt;/a&gt;
idea&amp;hellip; Weird stuff here.&lt;/p&gt;
&lt;p&gt;Asking to show us what &amp;ldquo;paradigm shift&amp;rdquo; looks like gives us some weird human figures, accompanied
by distorted text.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.42.30.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When asked to show us &amp;ldquo;computational complexity&amp;rdquo; it generates some distorted version of corporate
stock images with people looking at screens.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.43.46.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Faces are kinda messed up and text in the images is completely destroyed. It seems these are the
things DALL-E currently has trouble generating properly.&lt;/p&gt;
&lt;p&gt;Enough with the abstract stuff for now. Let us generate images of some birds and animals.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.48.54.png&#34;&gt;Screenshot 7&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.49.02.png&#34;&gt;Screenshot 8&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.49.39.png&#34;&gt;Screenshot 9&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.50.35.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pretty much all good here. We&amp;rsquo;re getting what we asked for, even if we want it hand-drawn.
The only exception was a single picture of girl wearing peacock feathers when we asked
for a picture of peacock.&lt;/p&gt;
&lt;p&gt;Let us to visualise some sci-fi quotes. &amp;ldquo;The sky above the port was the color of television,
tuned to a dead channel&amp;rdquo; gives us a cloudy sky over water, with something like harbour being
visible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_13.57.56.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not too bad, I guess. After all, cloudy sky is what William Gibson had in mind when he wrote
that back in the 80s.&lt;/p&gt;
&lt;p&gt;What about something more elaborate? &amp;ldquo;Cyberspace. A consensual hallucination experienced daily
by billions of legitimate operators, in every nation, by children being taught mathematical concepts&amp;hellip;
A graphic representation of data abstracted from banks of every computer in the human system.
Unthinkable complexity. Lines of light ranged in the nonspace of the mind, clusters and
constellations of data. Like city lights, receding&amp;hellip;&amp;rdquo; gives us the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.00.20.png&#34;&gt;Screenshot 12&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Well, it generated something digital-looking, but this is not something I would be happy with if
was paying for graphical designer to make a wall art based on that quote.&lt;/p&gt;
&lt;p&gt;We tried two quotes from William Gibson, now let&amp;rsquo;s try something from Neal Stephenson. What about
&amp;ldquo;We are all susceptible to the pull of viral ideas. Like mass hysteria. Or a tune that gets
into your head that you keep humming all day until you spread it to someone else. Jokes.
Urban legends. Crackpot religions. Marxism. No matter how smart we get, there is always this
deep irrational part that makes us potential hosts for self-replicating information.&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.13.31.png&#34;&gt;Screenshot 13&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Well, that&amp;rsquo;s even worse. We got one image with distorted red text, one abstract image with some
lights in the distance, one image of stone artwork and one comic strip with messed up text.
No good.&lt;/p&gt;
&lt;p&gt;Perhaps we need something bit more visual. Let us try &amp;ldquo;Hiro watches the large, radioactive,
spear-throwing killer drug lord ride his motorcycle into Chinatown. Which is the same as
riding it into China, as far as chasing him down is concerned&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.22.56.png&#34;&gt;Screenshot 14&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ugh. Anyway&amp;hellip; I think we&amp;rsquo;re done here.&lt;/p&gt;
&lt;p&gt;Let us try generating images that are related to various cultures, traditional activities and
dress codes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.26.45.png&#34;&gt;Screenshot 15&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.28.13.png&#34;&gt;Screenshot 16&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.29.29.png&#34;&gt;Screenshot 17&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can also spot some distorted faces here, but generally we&amp;rsquo;re getting what we asked for.&lt;/p&gt;
&lt;p&gt;DALL-E can also be used to modify existing images. Let us try that. We can upload a random
picture from Flickr, crop it to square, mark the part we want to modify and write a prompt
on how we want to modify it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.36.55.png&#34;&gt;Screenshot 18&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.37.19.png&#34;&gt;Screenshot 19&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.37.35.png&#34;&gt;Screenshot 20&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.38.23.png&#34;&gt;Screenshot 21&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Not too bad, huh?&lt;/p&gt;
&lt;p&gt;Lastly, we can use the uploaded image as &amp;ldquo;inspiration&amp;rdquo; to get DALL-E generate more images
like the one that was uploaded.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-30_14.43.29.png&#34;&gt;Screenshot 22&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Overall this is impressive stuff. However, it is unlikely to completely replace entire
trade of graphic design due to its limited ability to visualise complex ideas and no
ability to ask follow-up question to clarify what we meant, or how we want something
to be depicted.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: removing unreachable code</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-removing-unreachable-code/</link>
      <pubDate>Sun, 28 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-removing-unreachable-code/</guid>
      <description>&lt;p&gt;Unreachable parts can be injected into JavaScript source as a form of obfuscation.
We will go through a couple of simple examples to show how the unreachable code paths
can be removed at Abstract Syntax Tree level for reverse engineering purposes.&lt;/p&gt;
&lt;p&gt;For the first example, let us consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 == 2&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 == 1&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 == 2&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unreachable as well&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running it will produce three lines of output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node dead_branches.js 
1 == 1
yes
Hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We want to simplify it to the point of having only three &lt;code&gt;console.log()&lt;/code&gt; calls in the
cleaned up version of the script.&lt;/p&gt;
&lt;p&gt;Let us copy-paste this code into &lt;a href=&#34;https://astexplorer.net/&#34;&gt;AST Explorer&lt;/a&gt; to see what
it looks like when parsed into Babel AST data model. We notice that if statements
are parsed into &lt;code&gt;IfStatement&lt;/code&gt; node and that line with question mark (conditional/ternary
operator) is parsed into &lt;code&gt;ConditionalExpression&lt;/code&gt; node. Both of these nodes have &lt;code&gt;test&lt;/code&gt;
property for representing condition itself, &lt;code&gt;consequent&lt;/code&gt; property for part of the code
that would run if condition was true and &lt;code&gt;alternate&lt;/code&gt; property for the code that would run
otherwise. The &lt;code&gt;alternate&lt;/code&gt; property might be null if there was no &lt;code&gt;else&lt;/code&gt; block in the if
statement or it might be pointing to another &lt;code&gt;IfStatement&lt;/code&gt; node if we have multiple
conditions that the code tries to match against. Both branches of If statement or conditional
expression may point to &lt;code&gt;BlockStatement&lt;/code&gt; node if there&amp;rsquo;s a block of statements to be executed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-27_21.14.44.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In our sample code &lt;code&gt;1 == 2&lt;/code&gt; will always be false and &lt;code&gt;1 == 1&lt;/code&gt; will always be true. To make
things easier for us, Babel provides a way to evaluate if stuff in &lt;code&gt;test&lt;/code&gt; is &amp;ldquo;truthy&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;All of this informs us on how to implement our cleanup code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dead_branches.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ConditionalExpression|IfStatement&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isTruthy&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;evaluateTruthy&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;isTruthy&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBlockStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;consequent&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWithMultiple&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;consequent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;consequent&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alternate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isBlockStatement&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alternate&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWithMultiple&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alternate&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replaceWith&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alternate&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no_dead_branches.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We use &lt;code&gt;ConditionalExpression|IfStatement&lt;/code&gt; string value as a key to match our visitor
function to both kinds of nodes we want to modify when traversing the AST. We use
&lt;code&gt;evaluateTruthy()&lt;/code&gt; method to check if condition in &lt;code&gt;test&lt;/code&gt; evaluates to Boolean true.
If it does, then we replace the If statement or conditional expression with the code
under the positive branch. This removes the If statement (or conditional expression)
and all the negative branches (if there are any). If it does not evaluate to Boolean
true value, we need to check if &lt;code&gt;alternate&lt;/code&gt; points to &lt;code&gt;else&lt;/code&gt; part - we modify the AST
to leave this part only. If &lt;code&gt;alternate&lt;/code&gt; is null then we remove the entire shebang (this
applies to the very first If statement in the sample code). If there&amp;rsquo;s block
statement on the relevant branch, we need to replace the parent node with all the nodes
in the &lt;code&gt;body&lt;/code&gt; of that &lt;code&gt;BlockStatement&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;Running this deobfuscation code produces the following cleaned-up JS code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 == 1&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another example will be about unreachable functions that are never going to be executed.&lt;/p&gt;
&lt;p&gt;Consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dead1&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dead2&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;!!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dead3&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dead1&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dead2&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want all three functions removed. In AST Explorer we can see that Babel parses
each function into &lt;code&gt;FunctionDeclaration&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;We will use Babel &lt;code&gt;getBinding()&lt;/code&gt; method to check if they are being referenced
anywhere (in a way that heeds lexical scopes). But here&amp;rsquo;s a little problem:
&lt;code&gt;dead1()&lt;/code&gt; and &lt;code&gt;dead2()&lt;/code&gt; are being called by &lt;code&gt;dead3()&lt;/code&gt; so they do have references
to them. Out of three functions, only &lt;code&gt;dead3()&lt;/code&gt; is not being references by any other
code. We are going to address this (perhaps in hacky manner) by doing more than one pass
of AST traversal.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-27_21.42.06.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The code that cleans up these dead functions is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dead_functions.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;removed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;FunctionDeclaration&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getBinding&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;referenced&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;removed&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;removed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no_dead_functions.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After running this script, we have the following clean version of the code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Building Python script CLIs with argparse and Click</title>
      <link>https://www.trickster.dev/post/building-python-script-clis-with-argparse-and-click/</link>
      <pubDate>Sat, 27 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/building-python-script-clis-with-argparse-and-click/</guid>
      <description>&lt;p&gt;To make Python script parametrisable with some input data we need to develop
a Command Line Interface. This will enable the end user to run the script with
various inputs and provide a way to choose optional behaviors that the script
supports. For example, running the script with &lt;code&gt;--silent&lt;/code&gt; suppresses the output
that it would otherwise print to the terminal, whereas &lt;code&gt;--verbose&lt;/code&gt; makes the
output extra detailed with fine-grained technical details included. Scripts
with well developed CLIs can be invoked in a reproducible way and thus it is
possible to integrate them into larger automation flows.&lt;/p&gt;
&lt;p&gt;The simplest way to make Python script accept input from command line is to
read the &lt;code&gt;sys.argv&lt;/code&gt; list that is populated as the script is launched. Like in
C programs, &lt;code&gt;sys.argv[0]&lt;/code&gt; is name of the program being launched,
&lt;code&gt;sys.argv[1]&lt;/code&gt; is the first argument, &lt;code&gt;sys.argv[2]&lt;/code&gt; is the second one and so on.
Length of &lt;code&gt;sys.argv&lt;/code&gt; list is number of argument plus one - if there&amp;rsquo;s no CLI
arguments being passed into the script it will be 1.&lt;/p&gt;
&lt;p&gt;The following trivial script exemplifies how this can be used to implement
a very basic CLI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;arg1&amp;gt; &amp;lt;arg2&amp;gt; ...&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; arg &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(arg)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we can see, the CLI arguments are accessed from within the script and printed to
standard output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 argv_demo.py 
Usage:
argv_demo.py &amp;lt;arg1&amp;gt; &amp;lt;arg2&amp;gt; ...
$ python3 argv_demo.py 1 2 3
1
2
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However this way of implementing the command line interface is fairly low-level
and under-abstracted for more advanced use cases. For example, if we wanted to
implement subcommands or multiple options that may or may not be associated with values
we would need to do quite a bit of work to parse that list of arguments into
an internal form that we would be using further in the code.&lt;/p&gt;
&lt;p&gt;There are some Python modules to make it easier. The argparse module ships with
vanilla Python installation. &lt;a href=&#34;https://click.palletsprojects.com/en/8.1.x/&#34;&gt;Click&lt;/a&gt;
is an open source project that provides further abstraction layer to enable
setting up the CLI with very little code.&lt;/p&gt;
&lt;p&gt;For demonstration purposes, let us assume that we want to develop a simple
script that takes one or more HTTP(S) URLs, tries fetching them with GET
requests and prints the HTTP response status codes to the user. We want to be
able to pass either single URL or a list of URLs in a file. Futhermore, we want
to optionally set timeout duration and HTTP debug information.&lt;/p&gt;
&lt;h2 id=&#34;using-argparse&#34;&gt;Using argparse&lt;/h2&gt;
&lt;p&gt;When using &lt;a href=&#34;https://docs.python.org/3/library/argparse.html#module-argparse&#34;&gt;&lt;code&gt;argparse&lt;/code&gt;&lt;/a&gt;
module, we need to instantiate &lt;code&gt;ArgumentParser&lt;/code&gt; object,
set it up with all the CLI arguments and use it to parse &lt;code&gt;sys.argv&lt;/code&gt; (without
the first element). As a starting point, we can use Python REPL for prototyping.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.10.6 (main, Aug 11 2022, 13:36:31) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import argparse
&amp;gt;&amp;gt;&amp;gt; parser = argparse.ArgumentParser(description=&amp;#34;Simple HTTP URL Checker&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; parser
ArgumentParser(prog=&amp;#39;&amp;#39;, usage=None, description=&amp;#39;Simple HTTP URL Checker&amp;#39;, formatter_class=&amp;lt;class &amp;#39;argparse.HelpFormatter&amp;#39;&amp;gt;, conflict_handler=&amp;#39;error&amp;#39;, add_help=True)
&amp;gt;&amp;gt;&amp;gt; parser.add_argument(&amp;#34;--timeout&amp;#34;, type=float, default=1.0, required=False, help=&amp;#34;Timeout duration for HTTP GET request in seconds (default: 1.0)&amp;#34;)
_StoreAction(option_strings=[&amp;#39;--timeout&amp;#39;], dest=&amp;#39;timeout&amp;#39;, nargs=None, const=None, default=1.0, type=&amp;lt;class &amp;#39;float&amp;#39;&amp;gt;, choices=None, required=False, help=&amp;#39;Timeout duration for HTTP GET request in seconds (default: 1.0)&amp;#39;, metavar=None)
&amp;gt;&amp;gt;&amp;gt; parser.add_argument(&amp;#34;--verbose&amp;#34;, default=False, required=False, action=&amp;#39;store_true&amp;#39;, help=&amp;#34;Enable HTTP debug output (off by default)&amp;#34;)
_StoreTrueAction(option_strings=[&amp;#39;--verbose&amp;#39;], dest=&amp;#39;verbose&amp;#39;, nargs=0, const=True, default=False, type=None, choices=None, required=False, help=&amp;#39;Enable HTTP debug output (off by default)&amp;#39;, metavar=None)
&amp;gt;&amp;gt;&amp;gt; parser.add_argument(&amp;#34;--url-list&amp;#34;, required=True, nargs=&amp;#39;*&amp;#39;, help=&amp;#34;HTTP URLs or files containing them (one URL per line)&amp;#34;)
_StoreAction(option_strings=[&amp;#39;--url-list&amp;#39;], dest=&amp;#39;url_list&amp;#39;, nargs=&amp;#39;*&amp;#39;, const=None, default=None, type=None, choices=None, required=True, help=&amp;#39;HTTP URLs or files containing them (one URL per line)&amp;#39;, metavar=None)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have configured the optional &lt;code&gt;--verbose&lt;/code&gt; and &lt;code&gt;--timeout&lt;/code&gt; options, as well the required
&lt;code&gt;--url-list&lt;/code&gt; argument that will take a list of URLs or files. Note that when setting up
the latter we call &lt;code&gt;add_argument()&lt;/code&gt; with &lt;code&gt;nargs&lt;/code&gt; set to &lt;code&gt;*&lt;/code&gt; - this will tell the parser
that we expect a list of values that are to be parsed into Python list.&lt;/p&gt;
&lt;p&gt;Now we can do a little testing on our argument parser.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; parser.print_help()
usage: [-h] [--timeout TIMEOUT] [--verbose] --url-list [URL_LIST ...]

Simple HTTP URL Checker

options:
  -h, --help            show this help message and exit
  --timeout TIMEOUT     Timeout duration for HTTP GET request in seconds (default: 1.0)
  --verbose             Enable HTTP debug output (off by default)
  --url-list [URL_LIST ...]
                        HTTP URLs or files containing them (one URL per line)
&amp;gt;&amp;gt;&amp;gt; parser.parse_args(&amp;#39;--timeout 2.0 --verbose --url-list http://localhost:1313 http://localhost:1212&amp;#39;.split(&amp;#39; &amp;#39;))
Namespace(timeout=2.0, verbose=True, url_list=[&amp;#39;http://localhost:1313&amp;#39;, &amp;#39;http://localhost:1212&amp;#39;])
&amp;gt;&amp;gt;&amp;gt; parser.parse_args(&amp;#39;--url-list http://localhost:1313&amp;#39;.split(&amp;#39; &amp;#39;))
Namespace(timeout=1.0, verbose=False, url_list=[&amp;#39;http://localhost:1313&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It seems like it works properly. The complete script with &lt;code&gt;argparse&lt;/code&gt;-based CLI is as following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; argparse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; http.client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HEADERS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Chromium&amp;#34;;v=&amp;#34;104&amp;#34;, &amp;#34; Not A;Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;104&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;document&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;navigate&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cross-site&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upgrade-insecure-requests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DEFAULT_TIMEOUT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;check_url&lt;/span&gt;(url, timeout):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;HEADERS, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;timeout)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; str(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;check_urls&lt;/span&gt;(urls, timeout, verbose):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; verbose:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Based on: https://stackoverflow.com/a/16630836&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;basicConfig()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getLogger()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setLevel(logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DEBUG)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        requests_log &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getLogger(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;requests.packages.urllib3&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        requests_log&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setLevel(logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DEBUG)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        requests_log&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;propagate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; u &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; u&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; u&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\t&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(u, check_url(u, timeout)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isfile(u):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                urls_from_file &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(u, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lines &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; line &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; lines:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; line&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; line&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        urls_from_file&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(line)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                check_urls(urls_from_file, timeout, verbose)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;File not found: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(u), file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stderr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; argparse&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ArgumentParser(description&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Simple HTTP URL Checker&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--timeout&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;float,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;DEFAULT_TIMEOUT,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Timeout duration for HTTP GET request in seconds (default: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            DEFAULT_TIMEOUT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--verbose&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        action&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;store_true&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enable HTTP debug output (off by default)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_argument(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--url-list&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        nargs&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP URLs or files containing them (one URL per line)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    args &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_args(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    check_urls(args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url_list, args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;timeout, args&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;verbose)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can do some test runs to check that the CLI works correctly:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 url_scan_argparse.py -h
usage: url_scan_argparse.py [-h] [--timeout TIMEOUT] [--verbose] --url-list [URL_LIST ...]

Simple HTTP URL Checker

options:
  -h, --help            show this help message and exit
  --timeout TIMEOUT     Timeout duration for HTTP GET request in seconds (default: 1.0)
  --verbose             Enable HTTP debug output (off by default)
  --url-list [URL_LIST ...]
                        HTTP URLs or files containing them (one URL per line)
$ python3 url_scan_argparse.py --url-list https://trickster.dev https://trickster.dev/.git
https://trickster.dev	200
https://trickster.dev/.git	404
$ python3 url_scan_argparse.py --timeout 2.0
usage: url_scan_argparse.py [-h] [--timeout TIMEOUT] [--verbose] --url-list [URL_LIST ...]
url_scan_argparse.py: error: the following arguments are required: --url-list
$ python3 url_scan_argparse.py --verbose --url-list https://trickster.dev https://trickster.dev/.git
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): trickster.dev:443
DEBUG:urllib3.connectionpool:https://trickster.dev:443 &amp;#34;GET / HTTP/1.1&amp;#34; 301 41
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): www.trickster.dev:443
DEBUG:urllib3.connectionpool:https://www.trickster.dev:443 &amp;#34;GET / HTTP/1.1&amp;#34; 200 None
https://trickster.dev	200
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): trickster.dev:443
DEBUG:urllib3.connectionpool:https://trickster.dev:443 &amp;#34;GET /.git HTTP/1.1&amp;#34; 301 45
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): www.trickster.dev:443
DEBUG:urllib3.connectionpool:https://www.trickster.dev:443 &amp;#34;GET /.git HTTP/1.1&amp;#34; 404 None
https://trickster.dev/.git	404
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since this code is based on requests module it heeds &lt;code&gt;HTTP_PROXY&lt;/code&gt; and &lt;code&gt;HTTPS_PROXY&lt;/code&gt; environment
variables, which means it can be used to run some tests to check proxy pool suitability for a
particular target site.&lt;/p&gt;
&lt;h2 id=&#34;using-click&#34;&gt;Using Click&lt;/h2&gt;
&lt;p&gt;Modifying the code to use Click is not that difficult. First, we import it by putting the following
import statement at the top:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; click
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then at them bottom we make &lt;code&gt;check_urls()&lt;/code&gt; function to be an entry point of the script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    check_urls()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we annotate this function with Click-specific annotations that will connect it to
the command line interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@click.command&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@click.option&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--timeout&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;DEFAULT_TIMEOUT,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Timeout duration for HTTP GET request in seconds&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@click.option&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--verbose&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    is_flag&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enable HTTP debug output (off by default)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@click.option&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--url-list&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    required&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    multiple&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP URLs or files containing them (one URL per line)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;check_urls&lt;/span&gt;(url_list, timeout, verbose):
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Click module will make sure that value of &lt;code&gt;--timeout&lt;/code&gt; from CLI will be assigned
to &lt;code&gt;timeout&lt;/code&gt; function parameter. The same applies to other two arguments.&lt;/p&gt;
&lt;p&gt;However there is one limitation. Although Click can take multiple values
for &lt;code&gt;--url-list&lt;/code&gt; and parse them into Python array, it does not support
the variadic form we used in the previous script. Each entry in the list will
need to have &lt;code&gt;--url-list&lt;/code&gt; before it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 url_scan_click.py --url-list https://trickster.dev --url-list https://trickster.dev/.git
https://trickster.dev	200
https://trickster.dev/.git	404
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>ImageMagick: programmable image processing toolkit</title>
      <link>https://www.trickster.dev/post/imagemagick-programmable-image-processing-toolkit/</link>
      <pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/imagemagick-programmable-image-processing-toolkit/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://imagemagick.org&#34;&gt;ImageMagick&lt;/a&gt; is a set of CLI tools and a C library for performing a variety of digital image handling tasks,
such as converting between formats, resizing, mirroring, rotating, adjusting colors, rendering
text, drawing shapes and so on. In many cases ImageMagick is not being used directly, but exists as
part of the hidden substrate of C code that the modern shiny stuff is built upon. For example, it may
be used to generate thumbnails of images that users uploaded to a web application. ImageMagick supports
more than 200 image formats and runs on all the major operating systems. If you&amp;rsquo;re running Linux, macOS
or other well established Unix system it will most likely be available through your package manager.&lt;/p&gt;
&lt;p&gt;ImageMagick can be used through the following CLI tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;animate&lt;/code&gt; - animate a sequence of images (requires X Windows).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conjure&lt;/code&gt; - run an image processing script written in XML-based Magick Scripting Language (MSL).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt; - show a single image (requires X Windows).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;magick&lt;/code&gt; - general purpose CLI tool for pretty much everything. This one is recommended for modern usage.
Others are leftovers from legacy versions of ImageMagick.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;convert&lt;/code&gt; - convert between image formats, optionally with some tranformations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;composite&lt;/code&gt; - overlap or overlay two or more image on each other.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;montage&lt;/code&gt; - combine several images into a single output image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compare&lt;/code&gt; - show difference between images.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;identify&lt;/code&gt; - describes one or more image files and prints their metadata.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;import&lt;/code&gt; - save a screenshot of entire X Windows screen or some portion of it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stream&lt;/code&gt; - extract raw values of pixel components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us start simple by extracting some metadata from image files. For the sake of example,
we will use a geotagged image file that contains GPS coordinates within EXIF fields.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget https://www.geoimgr.com/images/samples/irland-dingle.jpg
--2022-08-13 13:10:14--  https://www.geoimgr.com/images/samples/irland-dingle.jpg
Resolving www.geoimgr.com (www.geoimgr.com)... 108.157.214.28, 108.157.214.104, 108.157.214.75, ...
Connecting to www.geoimgr.com (www.geoimgr.com)|108.157.214.28|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 694078 (678K) [image/jpeg]
Saving to: ‘irland-dingle.jpg’

irland-dingle.jpg                                  100%[================================================================================================================&amp;gt;] 677,81K   979KB/s    in 0,7s    

2022-08-13 13:10:16 (979 KB/s) - ‘irland-dingle.jpg’ saved [694078/694078]

$ file irland-dingle.jpg 
irland-dingle.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=14, manufacturer=Panasonic, model=DMC-FX60, orientation=upper-left, xresolution=202, yresolution=210, resolutionunit=2, software=Ver.1.0  , datetime=2012:09:16 16:58:02, GPS-DataTIFF image data, little-endian, direntries=14, manufacturer=Panasonic, model=DMC-FX60, orientation=upper-left, xresolution=202, yresolution=210, resolutionunit=2, software=Ver.1.0  , datetime=2012:09:16 16:58:02, GPS-Data], baseline, precision 8, 2048x1536, components 3
$ magick identify -verbose irland-dingle.jpg 
Image:
  Filename: irland-dingle.jpg
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Mime type: image/jpeg
  Class: DirectClass
  Geometry: 2048x1536+0+0
  Resolution: 92.16x92.16
  Print size: 22.2222x16.6667
  Units: PixelsPerInch
  Colorspace: sRGB
  Type: TrueColor
  Base type: Undefined
  Endianness: Undefined
  Depth: 8-bit
  Channel depth:
    Red: 8-bit
    Green: 8-bit
    Blue: 8-bit
  Channel statistics:
    Pixels: 3145728
    Red:
      min: 0  (0)
      max: 255 (1)
      mean: 147.255 (0.57747)
      median: 139 (0.545098)
      standard deviation: 71.5477 (0.280579)
      kurtosis: -1.25311
      skewness: 0.0244421
      entropy: 0.960027
    Green:
      min: 0  (0)
      max: 255 (1)
      mean: 146.551 (0.574708)
      median: 137 (0.537255)
      standard deviation: 73.5709 (0.288513)
      kurtosis: -1.26019
      skewness: 0.0407684
      entropy: 0.957017
    Blue:
      min: 0  (0)
      max: 255 (1)
      mean: 148.024 (0.580487)
      median: 140 (0.54902)
      standard deviation: 73.7421 (0.289185)
      kurtosis: -1.28316
      skewness: 0.0582366
      entropy: 0.937305
  Image statistics:
    Overall:
      min: 0  (0)
      max: 255 (1)
      mean: 147.277 (0.577555)
      median: 138.667 (0.543791)
      standard deviation: 72.9536 (0.286092)
      kurtosis: -1.26427
      skewness: 0.0417203
      entropy: 0.951449
  Rendering intent: Perceptual
  Gamma: 0.454545
  Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
  Matte color: grey74
  Background color: white
  Border color: srgb(223,223,223)
  Transparent color: none
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 2048x1536+0+0
  Dispose: Undefined
  Iterations: 0
  Compression: JPEG
  Quality: 90
  Orientation: TopLeft
  Profiles:
    Profile-exif: 16766 bytes
  Properties:
    date:create: 2022-08-13T10:10:16+00:00
    date:modify: 2019-07-13T17:45:39+00:00
    date:timestamp: 2022-08-13T10:13:31+00:00
    exif:ColorSpace: 1
    exif:ComponentsConfiguration: ....
    exif:CompressedBitsPerPixel: 4/1
    exif:Contrast: 0
    exif:CustomRendered: 0
    exif:DateTime: 2012:09:16 16:58:02
    exif:DateTimeDigitized: 2012:09:16 16:58:02
    exif:DateTimeOriginal: 2012:09:16 16:58:02
    exif:DigitalZoomRatio: 0/10
    exif:ExifOffset: 648
    exif:ExifVersion: 0221
    exif:ExposureBiasValue: 0/100
    exif:ExposureMode: 0
    exif:ExposureProgram: 2
    exif:ExposureTime: 10/2000
    exif:FileSource: .
    exif:Flash: 24
    exif:FlashPixVersion: 0100
    exif:FNumber: 40/10
    exif:FocalLength: 77/10
    exif:FocalLengthIn35mmFilm: 43
    exif:GainControl: 0
    exif:GPSInfo: 10396
    exif:GPSLatitude: 52/1, 8/1, 20155/942
    exif:GPSLatitudeRef: N
    exif:GPSLongitude: 10/1, 16/1, 17981/630
    exif:GPSLongitudeRef: W
    exif:GPSVersionID: ....
    exif:InteroperabilityOffset: 10366
    exif:LightSource: 0
    exif:Make: Panasonic
    exif:MakerNote: Panasonic...=......................................................... ...................................%... ...........!.... ..h...&amp;#34;...........#...........$...........%.......p&amp;#39;..&amp;amp;.......0291&amp;#39;...........(...........).......?...*...........+...........,...........-......................./...........0...........1...........2...........3........&amp;#39;..4...........5...........6.......??..7...........8...........:...........;...........&amp;lt;.......??..=...........&amp;gt;...........?...........M........&amp;#39;..N...*...?&amp;#39;..O...........W...........Y...........]...........^...........a.......?&amp;#39;..b...........c...................0132........%...................................?.......................................................................b(..............DV..EP..??DB?.??AF?.??..??..??.0??..ʯ2.??..??..??..??????..??..??..??X.???.ȯ@.د????..??..Ư..ίB.ү..Я(.??v.??&amp;gt;.??&amp;gt;.ԯ0.?? .?(.?..?..௯.??.??.??.?..??..گ..֯..?........?..?...?...?...?..??ST.. ?..&amp;#34;?..$?..&amp;amp;?..(?..*?..,?...?..0?..2?..4?..6?..8?..:?..&amp;lt;?..`?..b?..d?..h?..f?..j?..x?..|?..~?..&amp;gt;?..p?..r?..t?..v?..R?..L?..N?..P?..??AEn..?,..?X..?,..?...?...?..*?..,?..$??.&amp;gt;?..2? ..??.(?...?.. ?f.&amp;#34;??.0?#.&amp;amp;?..??..????4?...???.???.?..??..?????..?...???¦??.?...?..??...?...???.?...???.?..T?..V?..D??.J??.F??.H?..N?..^?..R?..X?..@?..B??.`?..Z?..\?...??.d?...?..b?...?..L?..??..??..??..6?..j?..l?..n?6.Ħ..Ʀ..Ȧ].ʦ9.̦..Φa.Ц..֦..ئ..Ҧ..Ԧ..??#.??..??#.??..0?x..?x..?P..?W..?#..?...?...?@.2?..??WB2..??..?...?...?...?..h?..`?o.d??.f?..@??.B?..D?..F? .??j.???.??..??..??u.??&amp;#34;.??s.??..???.??..??..???.??j.???.??..??..j?..l?!..??..?..b??.??d.Ȩ..¨?.ʨ?.Ĩ??̨..ƨ..Ψ..Ш..$?..&amp;amp;?.. ?..&amp;#34;?..(??.*?..,?...?..0?..2?..H??.J?..L??.N?..P??.R?..T??.V?...?\..?j..?...?...?...?...?...?...?...?...?...?...?...?..??YC?.N?..P?..R?..T?..D?..F???H?..J?..L?..8?..:?..&amp;lt;?..&amp;gt;?...?..0??.2?fD4?&amp;#34;.6?...............?`.`?..b?..d?Xrf?Xrh?]]j?..l?..n?...?...?...?...?...?...?...?...?...?...?..???.??..???.??..??..X?..Z?..\?..^? ..?...?..????ª??Ī??ƪ??Ȫ????CM..?. p.?..??..ک..ܩ..??DS:..?...?...??..??..?...?...??U.?uU.?&amp;#34;&amp;#34;.?! .?&amp;#34;&amp;#34;.?! .?..??ISn..?..??..??..???.??...?...?e..?...?e..?...?...??..?&amp;lt;..?n..?N..?...??..??..??..??..??..??..??..??..??..??.??FD?.`?..b?...?...?...?...?...?...?...?...?...?...?...?...?...?...?...?...?..@?..B?..D?..F?..H?..J?..L?..N?..P?..R?..T?..V?..X?..Z?..\?..^?..ħ..̧..Χ..Ч..ʧd.§..??ATB.&amp;lt;?..&amp;#34;?..$?..&amp;amp;?..(?..*?..,?...?..0?..2?..4?..6?..8?..:?..&amp;gt;?..??IAn.???.???.??..??..??????????????????..??..??..??..????????????????.?r..?...?...?...?...?...?!..?...?...?..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................MOIS..?...,...?...8.,.p.L.x.?.....?... .?... .?...?.?.`.?.?.....@.(.T.,.?.&amp;lt;.?.(...h.?...p...h.D.....?.$...d.8.?.x. ...?.T.?.?.........?.....L...X.?.?. ...?.?.?...H.x.8...?.@.t.H...?.@.?.4.?.(...?...........?.....?.@.`...?...D.D...?...$.?.d.&amp;lt;.x.?.?.?.?.?...?.?.....?.....?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.J.J.....}.?.......?.?. . .?.?.....?.?.....?.?.t.t.H.H.0.8.?.`.......D.H.?.?.l.?...?.d.$.?.?.`.x...t...?.?...?.?...?.$.?.?.$.?.....?.....8.,...|.?.?.?.?.....?.8...D.?...?...?.l...H.....?.@.....?.4.d.0.D.?.?.d.T...d...`...?...x.X. .&amp;lt;.0.?.4.?...$.?.?.?...?.&amp;lt;.?...?.P...?.?.|.$...?.x.@.$.?.?...?.?.2.2.9.2.9.9.......?.?.?.?.......?.?.?.?.............?.?.?.?.m.m.m.m.m.....?.k.k.?.?.?...Z.H.H.i.i.P.P.I.I.%.%.........;.AEBM?.....2.?.?.....?.2.V.?...5.?.?.s.[.?.9.j.f.?...y.?.X.a...-.f.?.?.?.?.?.t.?.?.).5.=...?.?.?.].H.?.?.`.?.....?...a.g.....?.-.....1.S.?.?...?.?.?.?.B.e.?.?.?.?.N.C...!.@.?.?.s.G.O...X.?.?.?.f.I.?.1.?.k.?...,.?.?. .?.E.?.?.....?.?.......k.?.V...?.?.?.`.?.?.u.?.?.P...?.?.?.?...i...?.l.+.k...PRST................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................FCCV....?...?.?.?.?.?.......?.?.r.?.X.......................X.....*...&amp;#34;.?...../.?.....?.9.K...X.?.?...|.?.?.....?.#...u.?.?...?.?.......?.?.....?.|.?.*.A.`...{.?.......?.5.m.?.T.S.....%...?...?.......?.?.....?.y.?.U.]...?.?.?.&amp;amp;.-.?.?.?.?.I.?.......r.g.h.......?.#.?.?.....?.|.,.....?.?.?. .T.R.?...:.?.?.....2...?.?.............?.......?.v...?.?.#.H.4.p.....D.A...o...J...?.H.`.i.?... .....(...?.....?.....q.R...?.?.?.?.?.?...?.8.H...%.....?...x.E.7.D.%.D...0...;.....?.7.?.-...]..._...h.?.F.?...?.?.F.?.a.?.(.?.?.?.{.?...w.V.?...?.?.?.?...m.*.h.?.l.$.i.?...?.....?.s.?.j.?.m.?.....X.g.t.&amp;#39;...e...?.?.?.?.U...I...\.?.?.S.?.A...?.+.?.?.....L.?.?.?.?...?...?.m.R.?.u.u.?.?.?.....?.?.e.?.5...\.@.l.c.......7.?.?.?.{.?.l.?.?...X.?.p...?.?.?.P.?...?.i.?.9...G./.l.b...,.....=...&amp;gt;.r.F.?.?...Z.r.?.?.?.?.#.&amp;amp;...g.P...?.i.?.\...?.:...?.?...?...?.q.......;.?...?.?.?.?.?...P.?.?._.4.Z.?.|.?.?.;.?.?.?.e.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................WBCZ................................................................................................................................................BMHL........+.......................................................................................................................................F060909280144.b,9999:99:99 00:00:00...........................................................................................9999:99:99 00:00:00.............................9999:99:99 00:00:00.............................9999:99:99 00:00:00.9999:99:99 00:00:00.
    exif:MaxApertureValue: 30/10
    exif:MeteringMode: 5
    exif:Model: DMC-FX60
    exif:PhotographicSensitivity: 80
    exif:PixelXDimension: 2048
    exif:PixelYDimension: 1536
    exif:PrintImageMatching: PrintIM.0250..................d.............................?.................?..........................&amp;#39;.......&amp;#39;.......&amp;#39;..?....&amp;#39;.......&amp;#39;..^....&amp;#39;.......&amp;#39;..?....&amp;#39;..?....&amp;#39;......................................
    exif:Saturation: 0
    exif:SceneCaptureType: 0
    exif:SceneType: .
    exif:SensingMethod: 2
    exif:Sharpness: 0
    exif:Software: Ver.1.0  
    exif:thumbnail:Compression: 6
    exif:thumbnail:InteroperabilityIndex: R98
    exif:thumbnail:InteroperabilityVersion: 0100
    exif:thumbnail:JPEGInterchangeFormat: 10628
    exif:thumbnail:JPEGInterchangeFormatLength: 6131
    exif:thumbnail:Orientation: 1
    exif:thumbnail:ResolutionUnit: 2
    exif:thumbnail:XResolution: 180/1
    exif:thumbnail:YCbCrPositioning: 2
    exif:thumbnail:YResolution: 180/1
    exif:WhiteBalance: 0
    exif:YCbCrPositioning: 2
    jpeg:colorspace: 2
    jpeg:sampling-factor: 2x2,1x1,1x1
    signature: 43f09d8ff5c5df045697fccde02cd88200c8c657eb4c2458a48c72a4cd8a9576
    unknown: ................................................................
  Artifacts:
    verbose: true
  Tainted: False
  Filesize: 694078B
  Number pixels: 3.14573M
  Pixel cache type: Memory
  Pixels per second: 74.1853MP
  User time: 0.030u
  Elapsed time: 0:01.042
  Version: ImageMagick 7.1.0-45 Q16-HDRI aarch64 20319 https://imagemagick.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that ImageMagick was able to print far more information on the image file than a standard Unix
file(1) utility. Not only it printed some image representation characteristics, but it also gave us
the entire EXIF metadata including coordinates, camera settings and information on technical properties
of the camera. All of this leaks quite a bit of information on how, where and when the picture was
taken and can be valuable to those working on digital forensics. Furthemore, it computed various
statistical properties of pixel component value distribution within the image (e.g. mean, median,
standard deviation, entropy).&lt;/p&gt;
&lt;p&gt;Let us try doing some transformations on the image. Converting the image file to another format is rather
simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ magick convert irland-dingle.jpg irland-dingle.png 
$ magick identify irland-dingle.png 
irland-dingle.png PNG 2048x1536 2048x1536+0+0 8-bit sRGB 3.22077MiB 0.000u 0:00.000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What if we wanted to make it grayscale? The &lt;code&gt;convert&lt;/code&gt; (sub-)command can also be used for this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ convert -colorspace Gray irland-dingle.png irland-dingle-gray.png
$ magick identify irland-dingle-gray.png                           
irland-dingle-gray.png PNG 2048x1536 2048x1536+0+0 8-bit Gray 256c 1.36152MiB 0.000u 0:00.001
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s one way to do this. For another ways, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/13317753/convert-rgb-to-grayscale-in-imagemagick-command-line&#34;&gt;Convert RGB to Grayscale in ImageMagick command-line&lt;/a&gt; on StackOverflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resizing the image is also fairly simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ magick convert irland-dingle.png -resize 64x64 irland-dingle-thumb.png
$ magick identify irland-dingle-thumb.png 
irland-dingle-thumb.png PNG 64x48 64x48+0+0 8-bit sRGB 28196B 0.000u 0:00.000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that by default this will keep the original aspect ratio of the image, thus the result image
may not be of size you passed into &lt;code&gt;-resize&lt;/code&gt; argument or may have some empty parts. To force the
resizing into an exact size we provide, we need to use exclamation marks next to width and height values:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ magick convert irland-dingle.png -resize 64\!x64\! irland-dingle-thumb-forced.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-13_13.39.08.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For further information on resizing, see the &lt;a href=&#34;https://legacy.imagemagick.org/Usage/resize/#resize&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However there is more to image processing than simple stuff like this. Digital image processing is a fairly
large field that deals with computational handling of image data for applications such as image compression,
computational photography, medical imaging, augmented reality, computer vision and others.&lt;/p&gt;
&lt;p&gt;One of the foundational techniques of digital image processing is convolutions that entails applying a
linear-algebra based filter on images. Convolution kernel is a two dimensional table of numbers (matrix)
that defines how the output image is created from input image. For example, low pass filter that implements
a simple image blurring can be implemented by a square matrix with each member being a constant:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ magick convert irland-dingle.png -convolve &amp;#34;3x3: 0.1111, 0.1111, 0.1111  0.1111, 0.1111, 0.1111  0.1111, 0.1111, 0.1111&amp;#34; irland-dingle-blur.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In fact, ImageMagick has some pre-made kernels for Gaussian blurring, among other applications:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ magick convert irland-dingle.png -convolve Gaussian:0x8 irland-dingle-blur-gaussian.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-13_14.05.19.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since ImageMagick is a bunch of CLI tools it can already be used as part of automation scripts
by calling magick(1) program as subprocess or by writing a shell script that launches it with
the appropriate arguments. However, ImageMagick also exposes two kinds of C APIs.
&lt;a href=&#34;https://imagemagick.org/script/magick-wand.php&#34;&gt;MagickWand&lt;/a&gt; is high-level, thread safe API
and &lt;a href=&#34;https://imagemagick.org/script/magick-core.php&#34;&gt;MagickCore&lt;/a&gt; is a low-level one. Most
developers should only consider using MagickWand. Don&amp;rsquo;t worry if you don&amp;rsquo;t want to deal with
C programming language. Wrappers in multiple popular programming languages are available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://imagemagick.org/script/magick++.php&#34;&gt;Magick++&lt;/a&gt; for C++.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/gographics/imagick&#34;&gt;Go Imagick&lt;/a&gt; for Go.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.jmagick.org/&#34;&gt;JMagick&lt;/a&gt; for Java.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.wand-py.org/en/0.6.9/&#34;&gt;Wand&lt;/a&gt; for Python.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/nlfiedler/magick-rust&#34;&gt;MagickRust&lt;/a&gt; for Rust.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a real-world code that uses ImageMagick, see &lt;a href=&#34;http://www.fmwconcepts.com/imagemagick/textcleaner/index.php&#34;&gt;textcleaner&lt;/a&gt;
script for cleaning up images of text before applying OCR on them. Potentially something like
this can be implemented to deal with simple image captchas that can be decoded with something
like Tesseract when proper pre-processing has been applied.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Writing custom Scrapy middleware for proxy pool integration</title>
      <link>https://www.trickster.dev/post/writing-custom-scrapy-middleware-for-proxy-pool-integration/</link>
      <pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/writing-custom-scrapy-middleware-for-proxy-pool-integration/</guid>
      <description>&lt;p&gt;One needs to use proxy pool to scrape some of the sites that implement countermeasures
against scraping. It might be because of IP-based rate limiting, geographic restrictions,
AS-level traffic filtering or even something more advanced such as TLS fingerprinting.
When Scrapy framework is used to implement a scraper, there are some ways to do so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting &lt;code&gt;HTTPS_PROXY&lt;/code&gt; environment variable when running a spider will make the traffic
go through the proxy. Note that this will also apply to requests being made not only from
spider, but from pipelines and other parts of the project, which may not be desirable.&lt;/li&gt;
&lt;li&gt;Setting proxy URL at &lt;code&gt;proxy&lt;/code&gt; key of the &lt;code&gt;meta&lt;/code&gt; dictionary for each request. This allows
us to be flexible on which requests are being sent through a proxy, but makes code less
clean by moving the proxy integration into spider class.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these options depend on the standard HTTP proxy middleware that is available
in a vanilla Scrapy project.&lt;/p&gt;
&lt;p&gt;However we may want to have more flexibility and introduce additional logic regarding
how exactly the requests are routed through a proxy pool. This can be done through
implementing an additional downloaded middleware that will augment
&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/downloader-middleware.html?highlight=HttpProxyMiddleware#module-scrapy.downloadermiddlewares.httpproxy&#34;&gt;&lt;code&gt;HttpProxyMiddleware&lt;/code&gt;&lt;/a&gt;
by assigning a proxy URL to some or all of the requests. To do so, we edit middlewares.py
file in a Scrapy project and create a downloader middleware class there.
To process each HTTP request (represented by &lt;code&gt;scrapy.Request&lt;/code&gt; object) we must
implement our own &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/downloader-middleware.html?highlight=HttpProxyMiddleware#scrapy.downloadermiddlewares.DownloaderMiddleware.process_request&#34;&gt;&lt;code&gt;process_request()&lt;/code&gt;&lt;/a&gt; method. If we need to assign a proxy URL to request, we put it into &lt;code&gt;meta&lt;/code&gt; dictionary
at &lt;code&gt;proxy&lt;/code&gt; key. If not, we leave it as-is. Either way we don&amp;rsquo;t need to return anything
from this method.&lt;/p&gt;
&lt;p&gt;For example, we may want to integrate Bright Data proxy pool in a way that forces exit IP
randomisation. The following code implements a simple downloader middleware that
takes Bright Data zone credentials from settings, appends session ID to the user name,
generates proxy URL and puts it into &lt;code&gt;meta&lt;/code&gt; dictionary:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; museums.settings &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ENABLED,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ZONE_USERNAME,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BRIGHT_DATA_ZONE_PASSWORD,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; w3lib.http &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; basic_auth_header
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; random
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BrightDataDownloaderMiddleware&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@classmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from_crawler&lt;/span&gt;(cls, crawler):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; cls()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_request&lt;/span&gt;(self, request, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; BRIGHT_DATA_ENABLED:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;meta[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://zproxy.lum-superproxy.io:22225&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; BRIGHT_DATA_ZONE_USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-session-&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; str(random&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;random())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Proxy-Authorization&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; basic_auth_header(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            username, BRIGHT_DATA_ZONE_PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It also sets a &lt;code&gt;Proxy-Authorization&lt;/code&gt; in a way that it is used for HTTP Basic Auth.&lt;/p&gt;
&lt;p&gt;To use this middleware in the Scrapy project, we must not only set the configuration variables,
but also edit &lt;code&gt;DOWNLOADER_MIDDLEWARES&lt;/code&gt; to activate this middleware with a priority value:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Enable or disable downloader middlewares&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DOWNLOADER_MIDDLEWARES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;museums.middlewares.BrightDataDownloaderMiddleware&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this does not work you may want to check that priority of your custom middleware is higher
(numerically lower) than that of &lt;code&gt;HttpProxyMiddleware&lt;/code&gt;, as we need to set proxy URL and
credentials before it gets to the regular middleware.&lt;/p&gt;
&lt;p&gt;For further examples on Scrapy middlewares that integrate with proxy pools you may
want to read the code of the following projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scrapy-plugins/scrapy-zyte-smartproxy&#34;&gt;scrapy-zyte-smartproxy&lt;/a&gt; - official
middleware for the Zyte Smart Proxy service. Implements some reliability and error handling
features such as exponential backoff and gracefully handling an outage of proxy pool, as well
as managing vendor-specific HTTP headers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/TeamHG-Memex/scrapy-rotating-proxies&#34;&gt;scrapy-rotating-proxies&lt;/a&gt; - two
middlewares that autorotate proxies for you with some ban detection logic. Lets you customise
the ban detection policy.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Decompiling Android apps</title>
      <link>https://www.trickster.dev/post/decompiling-android-apps/</link>
      <pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/decompiling-android-apps/</guid>
      <description>&lt;p&gt;To get a deeper insight into how an Android app works we may want to convert the binary
form inside APK file to some sort of textual representation that we can read and edit.
This may be desirable for working out how to defeat some automation countermeasures
(e.g. HMAC being applied on API requests), finding vulnerabilities in the apps themselves
or in backend systems that service the apps, analysing malicious code and so on.
Reverse engineering uncovers the technical reality that is otherwise hidden and not
meant to be seen.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s an APK, in and of itself? Well, it turns out that APK is merely a Zip archive that we can unpack:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ file &amp;#34;FirstNet Cybersecurity Aware_v1.2.5.0_apkpure.com.apk&amp;#34;
$ unzip FirstNet\ Cybersecurity\ Aware_v1.2.5.0_apkpure.com.apk 
$ ls -lh
total 30512
-rw-r--r--@  1 rl  staff    11K Dec 31  1979 AndroidManifest.xml
-rw-r--r--@  1 rl  staff    10M Aug  2 12:47 FirstNet Cybersecurity Aware_v1.2.5.0_apkpure.com.apk
drwxr-xr-x@ 70 rl  staff   2.2K Aug  2 14:21 META-INF
drwxr-xr-x@  3 rl  staff    96B Aug  2 14:21 assets
-rw-r--r--@  1 rl  staff   3.5M Dec 31  1979 classes.dex
-rw-r--r--@  1 rl  staff    68B Jan  1  1970 firebase-common.properties
-rw-r--r--@  1 rl  staff    78B Jan  1  1970 firebase-iid-interop.properties
-rw-r--r--@  1 rl  staff    62B Jan  1  1970 firebase-iid.properties
-rw-r--r--@  1 rl  staff    98B Jan  1  1970 firebase-measurement-connector.properties
-rw-r--r--@  1 rl  staff    74B Jan  1  1970 firebase-messaging.properties
drwxr-xr-x@ 86 rl  staff   2.7K Aug  2 14:21 kotlin
drwxr-xr-x@  6 rl  staff   192B Aug  2 14:21 lib
drwxr-xr-x@  3 rl  staff    96B Aug  2 14:21 okhttp3
-rw-r--r--@  1 rl  staff    74B Jan  1  1970 play-services-base.properties
-rw-r--r--@  1 rl  staff    82B Jan  1  1970 play-services-basement.properties
-rw-r--r--@  1 rl  staff    94B Jan  1  1970 play-services-cast-framework.properties
-rw-r--r--@  1 rl  staff    74B Jan  1  1970 play-services-cast.properties
-rw-r--r--@  1 rl  staff    76B Jan  1  1970 play-services-flags.properties
-rw-r--r--@  1 rl  staff    76B Jan  1  1970 play-services-stats.properties
-rw-r--r--@  1 rl  staff    76B Jan  1  1970 play-services-tasks.properties
drwxr-xr-x@ 57 rl  staff   1.8K Aug  2 14:21 res
-rw-r--r--@  1 rl  staff   974K Dec 31  1979 resources.arsc
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After unpacking, we have some package metadata, app assets (images, XML documents, config files)
and a Dalvik Executable file classes.dex. You see, Android does not run a regular JVM even
though Android apps can still be written in Java. It runs Dalvik - a replacement for JVM that
is specifically designed for running it within mobile devices. Dalvik bytecode is different
from regular JVM bytecode. Our objective is to turn Dalvik bytecode into something we can read.
Let us go through some tools that can help us with this.&lt;/p&gt;
&lt;h2 id=&#34;apktool&#34;&gt;&lt;a href=&#34;https://ibotpeaches.github.io/Apktool/&#34;&gt;Apktool&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If Dalvik bytecode is essentially a binary machine code for the Dalvik VM, then there has
to be Dalvik assembly language that has one-to-one correspondence between language statement
and VM instruction. This language is called Smali and Apktool is a program to disassemble
Dalvik bytecode into Smali. Only a single command is needed to unpack APK and convert
classes.dex file into Smali source code:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ apktool d &amp;#34;FirstNet Cybersecurity Aware_v1.2.5.0_apkpure.com.apk&amp;#34;  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the new directory that Apktool creates there&amp;rsquo;s smali directory that has subdirectories
according to Android package structure. The smali code looks something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-smali&#34; data-lang=&#34;smali&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# instance fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; b:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;O&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# direct methods
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.method&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;init&amp;gt;&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;O&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.locals&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-direct {p0}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;L&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;init&amp;gt;&lt;/span&gt;()&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iput-object p1, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt;;-&amp;gt;a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iput-object p2, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt;;-&amp;gt;b:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/squareup/picasso/&lt;span style=&#34;color:#a6e22e&#34;&gt;O&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return-void
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.end&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make sense of this, we need to reference
&lt;a href=&#34;https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html&#34;&gt;Dalvik bytecode&lt;/a&gt;
page on the Android documentation portal. In this case, &lt;code&gt;invoke-direct&lt;/code&gt; is an
instruction that calls a non-static Java or Kotlin method and &lt;code&gt;iput-object&lt;/code&gt;
performs instance variable assignment. As we can see this is still relatively
high level code that is far easier to make sense of than x86 or ARM assembly.&lt;/p&gt;
&lt;p&gt;We can edit the Smali code for our own purposes and use Apktool to rebuild the
modified version of the app. For example, a member of XDA Developers community
&lt;a href=&#34;https://forum.xda-developers.com/t/camera-mod-armani-power-button-focusing-video-recording.777707/&#34;&gt;patched&lt;/a&gt;
an app to get the hardware button events handled to his liking.&lt;/p&gt;
&lt;h2 id=&#34;jadx&#34;&gt;&lt;a href=&#34;https://github.com/skylot/jadx&#34;&gt;JADX&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;But what if we want something closer to Java/Kotlin than assembly language?
JADX is another reverse engineering tool for reconstructing Java from Dalvik
bytecode to the extent that is possible. In most cases original symbol names
will be lost, but we will get a somewhat readable Java code from this.&lt;/p&gt;
&lt;p&gt;JADX project ships CLI variant of the tool and a GUI variant. Command for
running the CLI variant is the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jadx FirstNet\ Cybersecurity\ Aware_v1.2.5.0_apkpure.com.apk 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To run GUI variant and get a graphical environment to browse the decompiled
source code run it like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ jadx-gui FirstNet\ Cybersecurity\ Aware_v1.2.5.0_apkpure.com.apk 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-02_14.54.14.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;dex2jar&#34;&gt;&lt;a href=&#34;https://github.com/pxb1988/dex2jar&#34;&gt;dex2jar&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dex2jar is a set of tools to deal with Dalvik bytecode. It is often used
to convert DEX file to JAR file that can be handled by many tools in Java
ecosystem. It does not perform decompilation by itself and is largely redundant
in the context of Apktool and JADX.&lt;/p&gt;
&lt;h2 id=&#34;android-studio&#34;&gt;Android Studio&lt;/h2&gt;
&lt;p&gt;If you have Android Studio installed you can open APK file and get it disassembled
into Smali code without installing anything else.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-02_15.04.06.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-02_15.04.44.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However Android Studio is fairly heavy piece of software and I would not install
it just for this purpose.&lt;/p&gt;
&lt;p&gt;Some Android apps will be based on Android NDK and thus largely written in C or C++.
The above tools will not help in that case. One would need to use binary reverse
engineering tools such as IDA Pro, Ghidra or radare2.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: extracting hardcoded data</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-extracting-hardcoded-data/</link>
      <pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-extracting-hardcoded-data/</guid>
      <description>&lt;p&gt;When scraping the web one sometimes comes across data being hardcoded in JS snippets.
One could be using regular expressions or naive string operations to extract it.
However, it is also possible to parse JavaScript code into Abstract Syntax Tree to
extract the hardcoded data in a more structured way than by using simple string
processing. Let us go through couple of examples.&lt;/p&gt;
&lt;p&gt;Suppose you were scraping some site and came across to JS code similar to
following &lt;a href=&#34;https://developers.facebook.com/docs/javascript/examples&#34;&gt;example from Meta developer portal&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;FB&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ui&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;share&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://developers.facebook.com/docs/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;){});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to extract the URL that is being shared. Putting this snippet into
&lt;a href=&#34;https://astexplorer.net/&#34;&gt;AST Explorer&lt;/a&gt; converts it to AST that we can look into.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.12.29.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can see that each key-value pair in JS object is represented by &lt;code&gt;ObjectProperty&lt;/code&gt; node
with &lt;code&gt;method&lt;/code&gt; boolean property being false. Key is represented by &lt;code&gt;Identifier&lt;/code&gt; node and
value (in this case) is represented by a &lt;code&gt;StringLiteral&lt;/code&gt;). This informs us how to write
Node.JS script based on Babel to dig out the URL being shared:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fb_example.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ObjectProperty&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If there was more key-value pairs that were interested in it would be fairly trivial to
update the code accordingly.&lt;/p&gt;
&lt;p&gt;Another form of hardcoded data you may run across is the one that is almost-JSON:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;change&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;deep-thoughts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;thinking&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Albert Einstein&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;goodreads_link&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/author/show/9810.Albert_Einstein&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Albert-Einstein&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\u201cThe world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.\u201d&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;abilities&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;J.K. Rowling&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;goodreads_link&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/author/show/1077326.J_K_Rowling&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;J-K-Rowling&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\u201cIt is our choices, Harry, that show what we truly are, far more than our abilities.\u201d&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See: &lt;a href=&#34;http://quotes.toscrape.com/js/&#34;&gt;http://quotes.toscrape.com/js/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.28.50.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.29.11.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once again, let us investigate the AST through AST Explorer web app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.31.17.png&#34;&gt;Screenshot 4&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.31.28.png&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-08-02_18.33.52.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Each member of &lt;code&gt;data&lt;/code&gt; array is represented by &lt;code&gt;ObjectExpression&lt;/code&gt;. Under each &lt;code&gt;ObjectExpression&lt;/code&gt; node,
there&amp;rsquo;s &lt;code&gt;ObjectProperty&lt;/code&gt; for each key-value pair. This is similar to previous example now, except that
key is represented by &lt;code&gt;StringLiteral&lt;/code&gt; node and value is not necessarily represented as &lt;code&gt;StringLiteral&lt;/code&gt;
due to nestedness of the structure. For example, list of tags is represented by &lt;code&gt;ArrayExpression&lt;/code&gt;
that has &lt;code&gt;StringLiteral&lt;/code&gt;s as child nodes.&lt;/p&gt;
&lt;p&gt;To keep things short and up to the point, we will save the above JS snippet to a file and process it from there.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;quotes.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rows&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ArrayExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayNode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arrayNode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;elements&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;idx&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;row&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;row&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;author_properties&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;properties&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;author_properties&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;author_property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;author_properties&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;author_property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#a6e22e&#34;&gt;row&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author_name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;author_property&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;rows&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;row&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rows&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case we match our visitor function to &lt;code&gt;ArrayExpression&lt;/code&gt; as it&amp;rsquo;s the only node of this type
in the code being processed. Then we go iterate across &lt;code&gt;ArrayExpression&lt;/code&gt; node children that represent
elements in the array - each of them is &lt;code&gt;ObjectExpression&lt;/code&gt;. Each of these is treated as subtree that
correspond to a row of data we want to extract - author name and quote text for each quote. These
pieces of data are being extracted by going deeper down the subtree.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>More tools of the trade for scraping and automation</title>
      <link>https://www.trickster.dev/post/more-tools-of-the-trade-for-scraping-and-automation/</link>
      <pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/more-tools-of-the-trade-for-scraping-and-automation/</guid>
      <description>&lt;p&gt;Since the &lt;a href=&#34;https://www.trickster.dev/post/tools-of-the-trade-for-scraping-and-automation&#34;&gt;previous post&lt;/a&gt;
I realised that there&amp;rsquo;s more interesting and valuable tools that were not covered
and that this warrants a new post. So let&amp;rsquo;s discuss several of them.&lt;/p&gt;
&lt;h1 id=&#34;ast-explorer&#34;&gt;&lt;a href=&#34;https://astexplorer.net/&#34;&gt;AST Explorer&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Sometimes you run into obfuscated code that you need to make sense of and write
some code to undo the obfuscation. AST explorer is a web app that let&amp;rsquo;s you put
in code snippets and browse the AST for multiple programming languages and
parsing libraries. For some of the languages it is even possible to apply
AST-level transformations to prototype things like deobfuscation techniques.&lt;/p&gt;
&lt;h1 id=&#34;appsmith&#34;&gt;&lt;a href=&#34;https://www.appsmith.com/&#34;&gt;AppSmith&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;AppSmith is a low-code, self hostable platform for creating internal dashboards
for people who would rather not do frontend programming. If you have written
some code that deals with data and automation AppSmith will provide a way to
drag-and-drop some frontend web UI from existing widgets, as well as customise
it with JavaScript. There&amp;rsquo;s many integrations for databases and external APIs
for building simple internal CRUD apps.&lt;/p&gt;
&lt;h1 id=&#34;burp-suite&#34;&gt;&lt;a href=&#34;https://portswigger.net/burp&#34;&gt;Burp Suite&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Burp is a widely known interception proxy that is highly popular in the
bug bounty community. Think of it as mitmproxy on steroids - not only it can
intercept HTTP messages for inspection, but also allow you to do things like
catching a request and modifying it in flight, running directory enumerations,
bruteforce attacks and much more. Altough Burp is a GUI app largely written in
Java it is possible to extend it with your own plugins that can be developed
in Python.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s multiple editions of Burp, but the free Community Edition is a good
starting point.&lt;/p&gt;
&lt;h1 id=&#34;gau&#34;&gt;&lt;a href=&#34;https://github.com/lc/gau&#34;&gt;GAU&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;GAU (Get All Urls) is a command line tool for fetching a list of known URLs
for a given domain from several sources (Wayback Machine, Common Crawl and some
others). This can be useful when exploring the site before developing scraper code
or when doing recon for bounty hunting purposes.&lt;/p&gt;
&lt;h1 id=&#34;mautic&#34;&gt;&lt;a href=&#34;https://www.mautic.org/&#34;&gt;Mautic&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Mautic is an open source email marketing solution that you can self host to
run your own email campaigns. It has a REST API that enable integration
with other stuff for list building and transactional emails. Mautic supports
many things that email marketing / cold outreach SaaS solutions do, such as
A/B testing, drip campaigns, personalisation, sending emails based on triggers
and so on. Furthermore, it has ready-made integrations with multiple email
service providers, such as AWS SES and SendGrid.&lt;/p&gt;
&lt;p&gt;If you are doing gray hat growth hacking, such as cold-emailing a scraped list
of companies or individuals there&amp;rsquo;s an argument to be made in favour of self-hosting
your stuff to pre-empt the potential problem of email SaaS vendor you are using
cutting you off.&lt;/p&gt;
&lt;h1 id=&#34;insomnia&#34;&gt;&lt;a href=&#34;https://insomnia.rest/&#34;&gt;Insomnia&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Insomnia is a GUI app that lets you interact with REST APIs. It can take a curl
snippet, break it down it request components, let you edit the request to
experiment with the API and regenerate either Bash snippet that relies on curl
or code snippet in one of the several programming languages. Furthermore, Insomnia
has features for designing and testing your own APIs.&lt;/p&gt;
&lt;h1 id=&#34;publicwww&#34;&gt;&lt;a href=&#34;https://publicwww.com/&#34;&gt;PublicWWW&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;PublicWWW is a code-level search engine that lets you search the web based
on things like JS file names, HTML fragments, variable names, HTTP headers
and so on. Unfortunately the free version does not give you all the
data, but the paid version is only accessible through Tor Onion Service and the
only way to pay them is through Bitcoin. This is rather weird and I suspect
the developers of this thing are evading sanctions. However the tool itself
has some value if you are looking to search the web in a deeper way than it
is possible with regular search engines. For your convenience, results
can be exported as CSV and you can even use regular expression when searching
for something. For example, the following query would give you list of Wordpress
plugins being deployed on various websites:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;/wp-content/plugins/&amp;#34; snipexp:|/plugins/([\w\d\-\.\_]+)/|
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;visidata&#34;&gt;&lt;a href=&#34;https://www.visidata.org/&#34;&gt;Visidata&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;If you are running your scrapers on servers without any GUI environment it might
be a hassle to download CSV files or spreadsheets to check the data that
was scraped. This is where Visidata comes in. Visidata is a terminal-based
tool to view and analyse tabular data that even supports basic data visualisations
such as frequency histograms and scatterplots. Since this tool is written in
Python it can be installed through PIP.&lt;/p&gt;
&lt;h1 id=&#34;wireshark&#34;&gt;&lt;a href=&#34;https://www.wireshark.org/&#34;&gt;Wireshark&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Wireshark is a prominent packet sniffer that lets you see what exactly happens
in the network. It can be used for things like troubleshooting proxy issues
and other network-related stuff, as well as for getting a deeper insight into
how networked software is truly operating.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>JavaScript AST manipulation with Babel: the first steps</title>
      <link>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-the-first-steps/</link>
      <pubDate>Wed, 20 Jul 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-ast-manipulation-with-babel-the-first-steps/</guid>
      <description>&lt;p&gt;Previously on Trickster Dev:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.trickster.dev/post/understanding-abstract-syntax-trees/&#34;&gt;Understanding Abstract Syntax Trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.trickster.dev/post/javascript-obfuscation-techniques-by-example/&#34;&gt;JavaScript Obfuscation Techniques by Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When it comes to reverse engineering obfuscated JavaScript code there are two major approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamic analysis - using debugger to step through the code and observing it&amp;rsquo;s behaviour over time.&lt;/li&gt;
&lt;li&gt;Static analysis - performing source code analysis without running it, but parsing and analysing the code itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is bad idea to rely exclusively on regular expressions and naive string manipulation to do web scraping.
The same applies to Javascript static analysis. Thus we will parse the JS code into Abstract Syntax Tree. This
will enable us to perform manipulations within a data model capable of representing the logical structure of the code.
We are going to write code to demonstrate how some of the basic obfuscation techniques could be reverted.&lt;/p&gt;
&lt;p&gt;Our deobfuscation code will be based on &lt;a href=&#34;https://babeljs.io/&#34;&gt;Babel&lt;/a&gt; - a programmable toolkit for manipulating
JavaScript code. Typically it is being used in build systems of web-based software projects, but it has also
been found to be quite useful by developers working on gray hat scraper/automation stuff
(not to be confused Babel from Python world that deals with internationalization/localisation stuff, and with
OpenBabel the chemical data processing software).&lt;/p&gt;
&lt;p&gt;Deobfuscation code we will be writing will work in three stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parsing JS code into AST.&lt;/li&gt;
&lt;li&gt;Modifying AST to undo obfuscations.&lt;/li&gt;
&lt;li&gt;Regenerating JS code from AST.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 2 is where the bulk of work will be done.&lt;/p&gt;
&lt;p&gt;Initially I wanted to do this in Python, but it turned out that there&amp;rsquo;s no established Python module to regenerate
the JS code from AST. Altough some JS static analysis can be done in Python, JavaScript tooling and libraries are best
equipped to deal with JS code. However I don&amp;rsquo;t typically code in JS so bear with me if code quality seems lacking.&lt;/p&gt;
&lt;p&gt;Babel can be installed through NPM:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ npm install --save-dev @babel/core @babel/cli
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://hackerone.com/hacktivity&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hackerone.com&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;max-age=0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cookie&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;_dd_s=&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://hackerone.com/&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;.Not/A)Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;103&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;103&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;document&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;navigate&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-origin&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-user&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;upgrade-insecure-requests&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We use the &lt;a href=&#34;https://www.digitalocean.com/community/tools/minify&#34;&gt;DigitalOcean Code Minify&lt;/a&gt; tool to remove
all the whitespace from this snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://hackerone.com/hacktivity&amp;#34;&lt;/span&gt;,{&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;authority&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hackerone.com&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;accept&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;max-age=0&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_dd_s=&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;referer&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://hackerone.com/&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;.Not/A)Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;103&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;103&amp;#34;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;document&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;navigate&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;same-origin&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-user&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?1&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upgrade-insecure-requests&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;}});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-20_13.30.30.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since minification is not really an obfuscation we can parse and regenerate the code without modifying
the AST:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v18.6.0.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; const parser = require(&amp;#34;@babel/parser&amp;#34;);
undefined
&amp;gt; const fs = require(&amp;#34;fs&amp;#34;);
undefined
&amp;gt; const mjs = fs.readFileSync(&amp;#34;minified.js&amp;#34;, &amp;#34;utf-8&amp;#34;);
undefined
&amp;gt; mjs
`fetch(&amp;#34;https://hackerone.com/hacktivity&amp;#34;,{headers:{authority:&amp;#34;hackerone.com&amp;#34;,accept:&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#34;,&amp;#34;accept-language&amp;#34;:&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;,&amp;#34;cache-control&amp;#34;:&amp;#34;max-age=0&amp;#34;,cookie:&amp;#34;_dd_s=&amp;#34;,referer:&amp;#34;https://hackerone.com/&amp;#34;,&amp;#34;sec-ch-ua&amp;#34;:&amp;#39;&amp;#34;.Not/A)Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;103&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;103&amp;#34;&amp;#39;,&amp;#34;sec-ch-ua-mobile&amp;#34;:&amp;#34;?0&amp;#34;,&amp;#34;sec-ch-ua-platform&amp;#34;:&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;,&amp;#34;sec-fetch-dest&amp;#34;:&amp;#34;document&amp;#34;,&amp;#34;sec-fetch-mode&amp;#34;:&amp;#34;navigate&amp;#34;,&amp;#34;sec-fetch-site&amp;#34;:&amp;#34;same-origin&amp;#34;,&amp;#34;sec-fetch-user&amp;#34;:&amp;#34;?1&amp;#34;,&amp;#34;upgrade-insecure-requests&amp;#34;:&amp;#34;1&amp;#34;,&amp;#34;user-agent&amp;#34;:&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36&amp;#34;}});`
&amp;gt; let ast = parser.parse(mjs);
undefined
&amp;gt; const generate = require(&amp;#34;@babel/generator&amp;#34;).default;
undefined
&amp;gt; generate(ast).code
&amp;#39;fetch(&amp;#34;https://hackerone.com/hacktivity&amp;#34;, {\n&amp;#39; +
  &amp;#39;  headers: {\n&amp;#39; +
  &amp;#39;    authority: &amp;#34;hackerone.com&amp;#34;,\n&amp;#39; +
  &amp;#39;    accept: &amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;accept-language&amp;#34;: &amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;cache-control&amp;#34;: &amp;#34;max-age=0&amp;#34;,\n&amp;#39; +
  &amp;#39;    cookie: &amp;#34;_dd_s=&amp;#34;,\n&amp;#39; +
  &amp;#39;    referer: &amp;#34;https://hackerone.com/&amp;#34;,\n&amp;#39; +
  `    &amp;#34;sec-ch-ua&amp;#34;: &amp;#39;&amp;#34;.Not/A)Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;103&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;103&amp;#34;&amp;#39;,\n` +
  &amp;#39;    &amp;#34;sec-ch-ua-mobile&amp;#34;: &amp;#34;?0&amp;#34;,\n&amp;#39; +
  `    &amp;#34;sec-ch-ua-platform&amp;#34;: &amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;,\n` +
  &amp;#39;    &amp;#34;sec-fetch-dest&amp;#34;: &amp;#34;document&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;sec-fetch-mode&amp;#34;: &amp;#34;navigate&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;sec-fetch-site&amp;#34;: &amp;#34;same-origin&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;sec-fetch-user&amp;#34;: &amp;#34;?1&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;upgrade-insecure-requests&amp;#34;: &amp;#34;1&amp;#34;,\n&amp;#39; +
  &amp;#39;    &amp;#34;user-agent&amp;#34;: &amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36&amp;#34;\n&amp;#39; +
  &amp;#39;  }\n&amp;#39; +
  &amp;#39;});&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since Babel automatically formats the generated code by default it was trivial to undo the minification.&lt;/p&gt;
&lt;p&gt;Applying hexadecimal string encoding to our test snippet yields the following obfuscated version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x68\x74\x74\x70\x73\x3A\x2F\x2F\x68\x61\x63\x6B\x65\x72\x6F\x6E\x65\x2E\x63\x6F\x6D\x2F\x68\x61\x63\x6B\x74\x69\x76\x69\x74\x79&amp;#39;&lt;/span&gt;,{&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x61\x75\x74\x68\x6F\x72\x69\x74\x79&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x68\x61\x63\x6B\x65\x72\x6F\x6E\x65\x2E\x63\x6F\x6D&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x61\x63\x63\x65\x70\x74&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x74\x65\x78\x74\x2F\x68\x74\x6D\x6C\x2C\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x78\x68\x74\x6D\x6C\x2B\x78\x6D\x6C\x2C\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x78\x6D\x6C\x3B\x71\x3D\x30\x2E\x39\x2C\x69\x6D\x61\x67\x65\x2F\x61\x76\x69\x66\x2C\x69\x6D\x61\x67\x65\x2F\x77\x65\x62\x70\x2C\x69\x6D\x61\x67\x65\x2F\x61\x70\x6E\x67\x2C\x2A\x2F\x2A\x3B\x71\x3D\x30\x2E\x38\x2C\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x73\x69\x67\x6E\x65\x64\x2D\x65\x78\x63\x68\x61\x6E\x67\x65\x3B\x76\x3D\x62\x33\x3B\x71\x3D\x30\x2E\x39&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x61\x63\x63\x65\x70\x74\x2D\x6C\x61\x6E\x67\x75\x61\x67\x65&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x65\x6E\x2D\x47\x42\x2C\x65\x6E\x2D\x55\x53\x3B\x71\x3D\x30\x2E\x39\x2C\x65\x6E\x3B\x71\x3D\x30\x2E\x38&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x63\x61\x63\x68\x65\x2D\x63\x6F\x6E\x74\x72\x6F\x6C&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x6D\x61\x78\x2D\x61\x67\x65\x3D\x30&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x63\x6F\x6F\x6B\x69\x65&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x5F\x64\x64\x5F\x73\x3D&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x72\x65\x66\x65\x72\x65\x72&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x68\x74\x74\x70\x73\x3A\x2F\x2F\x68\x61\x63\x6B\x65\x72\x6F\x6E\x65\x2E\x63\x6F\x6D\x2F&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x63\x68\x2D\x75\x61&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22\x2E\x4E\x6F\x74\x2F\x41\x29\x42\x72\x61\x6E\x64\x22\x3B\x76\x3D\x22\x39\x39\x22\x2C\x20\x22\x47\x6F\x6F\x67\x6C\x65\x20\x43\x68\x72\x6F\x6D\x65\x22\x3B\x76\x3D\x22\x31\x30\x33\x22\x2C\x20\x22\x43\x68\x72\x6F\x6D\x69\x75\x6D\x22\x3B\x76\x3D\x22\x31\x30\x33\x22&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x63\x68\x2D\x75\x61\x2D\x6D\x6F\x62\x69\x6C\x65&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x3F\x30&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x63\x68\x2D\x75\x61\x2D\x70\x6C\x61\x74\x66\x6F\x72\x6D&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x22\x6D\x61\x63\x4F\x53\x22&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x66\x65\x74\x63\x68\x2D\x64\x65\x73\x74&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x64\x6F\x63\x75\x6D\x65\x6E\x74&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x66\x65\x74\x63\x68\x2D\x6D\x6F\x64\x65&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x6E\x61\x76\x69\x67\x61\x74\x65&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x66\x65\x74\x63\x68\x2D\x73\x69\x74\x65&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x61\x6D\x65\x2D\x6F\x72\x69\x67\x69\x6E&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x73\x65\x63\x2D\x66\x65\x74\x63\x68\x2D\x75\x73\x65\x72&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x3F\x31&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x75\x70\x67\x72\x61\x64\x65\x2D\x69\x6E\x73\x65\x63\x75\x72\x65\x2D\x72\x65\x71\x75\x65\x73\x74\x73&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x31&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x75\x73\x65\x72\x2D\x61\x67\x65\x6E\x74&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\x4D\x6F\x7A\x69\x6C\x6C\x61\x2F\x35\x2E\x30\x20\x28\x4D\x61\x63\x69\x6E\x74\x6F\x73\x68\x3B\x20\x49\x6E\x74\x65\x6C\x20\x4D\x61\x63\x20\x4F\x53\x20\x58\x20\x31\x30\x5F\x31\x35\x5F\x37\x29\x20\x41\x70\x70\x6C\x65\x57\x65\x62\x4B\x69\x74\x2F\x35\x33\x37\x2E\x33\x36\x20\x28\x4B\x48\x54\x4D\x4C\x2C\x20\x6C\x69\x6B\x65\x20\x47\x65\x63\x6B\x6F\x29\x20\x43\x68\x72\x6F\x6D\x65\x2F\x31\x30\x33\x2E\x30\x2E\x30\x2E\x30\x20\x53\x61\x66\x61\x72\x69\x2F\x35\x33\x37\x2E\x33\x36&amp;#39;&lt;/span&gt;}})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-20_15.52.41.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Before we proceed with writing code to deobfuscate this, let us do a quick experiment on
&lt;a href=&#34;https://astexplorer.net/&#34;&gt;AST Explorer&lt;/a&gt;. Make sure that JavaScript language is chosen in
language dropdown and choose &lt;code&gt;@babel/parser&lt;/code&gt; in parser dropdown. Then let us check the
AST-level difference between the following JS statements:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\x41\x41\x41&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-20_16.01.29.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see that in hex-encoded version of the &lt;code&gt;StringLiteral&lt;/code&gt; the &lt;code&gt;extra.raw&lt;/code&gt; field is a
not the same as &lt;code&gt;extra.raw&lt;/code&gt;, whereas they are the same in a normal version
of the string (except &lt;code&gt;extra.raw&lt;/code&gt; being enclosed in double quotes). This gives us
an idea on how to undo the hex encoding: when processing the AST, let us set &lt;code&gt;raw&lt;/code&gt;
based on the clean form of a string in &lt;code&gt;rawValue&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;To manipulate the Abstract Syntax Tree, we are going to apply the Visitor design
pattern that entails some decoupling of data manipulation code from the specifics of
the underlying data structure. Babel has us covered here: we don&amp;rsquo;t need to
worry about AST traversal algorithm and merely have to call &lt;code&gt;traverse()&lt;/code&gt; with a
callback function(s) for the type(s) of AST nodes that we want to modify.&lt;/p&gt;
&lt;p&gt;Code that undoes string hex-encoding is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hjs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hexcoded.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;hjs&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;StringLiteral&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;raw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;extra&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rawValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clean1.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fairly simple, right?&lt;/p&gt;
&lt;p&gt;Now what if we wanted to convert bracket notation into dot notation? That is, we could have
statements like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we wanted them to be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the sake of the example, let us try cleaning up the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;o&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;o&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that we specifically want to get rid of bracket notation for function calls, but not
for object member assignment.&lt;/p&gt;
&lt;p&gt;Let us put both statements into AST Explorer and see how do they differ at AST level.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-20_17.28.55.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see that in both cases &lt;code&gt;CallExpression&lt;/code&gt; has &lt;code&gt;MemberExpression&lt;/code&gt; at &lt;code&gt;callee&lt;/code&gt; field, but
there&amp;rsquo;s a difference at &lt;code&gt;MemberExpression&lt;/code&gt; level: in the case of dot notation there&amp;rsquo;s
&lt;code&gt;Identifier&lt;/code&gt; object at &lt;code&gt;property&lt;/code&gt;, but in the case of bracket notation it&amp;rsquo;s a
&lt;code&gt;StringLiteral&lt;/code&gt;. Furthermore, &lt;code&gt;computed&lt;/code&gt; is false when dot notation is used and true
when bracket notation is used.&lt;/p&gt;
&lt;p&gt;So we write some code to find all parts of AST that match the latter pattern and fix
it to be like the former.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/types&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brackets.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;CallExpression&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isStringLiteral&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;types&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Identifier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;callee&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;computed&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clean2.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this script converts our sample code into the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AAA&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;o&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;o&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we did matching on &lt;code&gt;CallExpression&lt;/code&gt; it did not touch the last statement that involves
assigning member a of JS object.&lt;/p&gt;
&lt;p&gt;Lastly, let us consider the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;;;;;;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;123&amp;#34;&lt;/span&gt;);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This has way too many semi-colons that we want to remove. Turns out each extra semicolon
is represented by &lt;code&gt;EmptyStatement&lt;/code&gt; in a Babel AST data model.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-20_17.14.15.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thus we simply remove the
&lt;code&gt;EmptyStatement&lt;/code&gt; nodes when traversing the AST:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/parser&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/generator&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@babel/traverse&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;too_many_semicolons.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;js&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;EmptyStatement&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ast&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clean3.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;clean&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this gives us a cleaned up version of the code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;123&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Javascript obfuscation techniques by example</title>
      <link>https://www.trickster.dev/post/javascript-obfuscation-techniques-by-example/</link>
      <pubDate>Sun, 10 Jul 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/javascript-obfuscation-techniques-by-example/</guid>
      <description>&lt;p&gt;Sometimes when working on scraping some website you look into JavaScript code and it looks
like a complete mess that is impossible to read - no matter how much you squint, you cannot
make sense of it. That&amp;rsquo;s because it has been obfuscated. Code obfuscation is a transformation
that is meant to make code difficult to read and reverse engineer. Many web sites utilize
JavaScript obfuscation to make things difficult to scraper/automation developers.&lt;/p&gt;
&lt;p&gt;Consider the following very trivial Javascript statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, world! &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will discuss various ways it can be obfuscated. To make things clear, we will go through
one obfuscation technique at a time and provide an example of how each technique changes this
code in a form that is meant to be unreadable. However, it should be noted that typically
multiple obfuscation methods are being utilized in scraping-resistant websites.&lt;/p&gt;
&lt;p&gt;We will experiment with some online tools that obfuscate the JS code.&lt;/p&gt;
&lt;h2 id=&#34;hexadecimal-string-encoding&#34;&gt;Hexadecimal string encoding&lt;/h2&gt;
&lt;p&gt;If we go to the uncreatively named &lt;a href=&#34;https://javascriptobfuscator.com/Javascript-Obfuscator.aspx&#34;&gt;Javascript Obfuscator&lt;/a&gt;
and switch off all checkboxes but &amp;ldquo;Encode Strings&amp;rdquo; the obfuscated code will look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\x6C\x6F\x67&amp;#34;&lt;/span&gt;](&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21\x20&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-09_16.19.07.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Each ASCII character of the hardcoded string was converted into hexadecimal form, as can be
verified with calculator in programmers mode. This is commonly done, but is pretty weak
way to hide stuff, as JavaScript REPL can be trivially used to reverse the transformation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ node
Welcome to Node.js v18.5.0.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; &amp;#34;\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21\x20&amp;#34;
&amp;#39;Hello, world! &amp;#39;
&amp;gt; &amp;#34;\x6C\x6F\x67&amp;#34;
&amp;#39;log&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also notice that bracket notation is being used in the obfuscated form of the code -
&lt;code&gt;console.log&lt;/code&gt; became &lt;code&gt;console[&amp;quot;log&amp;quot;]&lt;/code&gt;. That is also fairly common thing that JS obfuscation
solutions tend to do.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also another variation of this technique that involves replacing characters with their
unicode encoding.&lt;/p&gt;
&lt;h2 id=&#34;string-array-mapping&#34;&gt;String Array Mapping&lt;/h2&gt;
&lt;p&gt;Now let us uncheck the &amp;ldquo;Encode Strings&amp;rdquo; checkbox and make the &amp;ldquo;Move Strings&amp;rdquo; checkbox on.
Clicking the Obfuscate button gives us the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_0x8b75&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, world! &amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;];&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x8b75&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]](&lt;span style=&#34;color:#a6e22e&#34;&gt;_0x8b75&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-09_16.31.59.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can see that the code now has an array of strings that are being used indirectly
by referencing the array at various indices.&lt;/p&gt;
&lt;h2 id=&#34;dead-code-injection&#34;&gt;Dead code injection&lt;/h2&gt;
&lt;p&gt;Dead or unreachable code can be injected to make the Javascript source code larger and more
difficult to read without changing the functionality. Let us try it with
&lt;a href=&#34;https://github.com/alexhorn/defendjs&#34;&gt;defendjs&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ defendjs --input hello.js --features dead_code --output .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This turns our trivial JS code into bit of a mess:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Array(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15229&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;21176&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;]] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        }).&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello, world! &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15229&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;$$defendjs$tobethrown&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;21176&lt;/span&gt;, {});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can see our original statement, but it is nested pretty deep now. At the top level, there
is something called IIFE - Immediately Invoked Function Expression - a JS function that is
defined and called in a single statement. This function has two statements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Declaration of function &lt;code&gt;a()&lt;/code&gt; that takes two parameters.&lt;/li&gt;
&lt;li&gt;Invocation of the function &lt;code&gt;a()&lt;/code&gt; with first parameter value being 21176.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If we read the code of function &lt;code&gt;a()&lt;/code&gt; we can see that the second parameter is ignored and
that the only code path it can possibly take leads to &lt;code&gt;console.log()&lt;/code&gt; statement with our
message. Around that there&amp;rsquo;s quite a bit of code that can never be executed - some functions
nested deep in a &lt;code&gt;switch&lt;/code&gt; statement and also &lt;code&gt;try/catch&lt;/code&gt; without any exceptions being possible.
Furthemore, it declares couple of variables in the beginning that will never be used because
the code will never take the paths where they are being accessed after being declared.&lt;/p&gt;
&lt;h2 id=&#34;scope-obfuscation&#34;&gt;Scope obfuscation&lt;/h2&gt;
&lt;p&gt;Let us rerun DefendJS with &lt;code&gt;scope&lt;/code&gt; feature:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cp hello.js scope.js
$ defendjs --input scope.js --features scope --output .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives us the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;]] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }).&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello, world! &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This technique may look like a simple version of previous one, but there&amp;rsquo;s a key difference:
it introduces multiple lexical scopes with duplicate identifiers. For example, &lt;code&gt;a&lt;/code&gt; might be
an argument to the first function in the innermost scope, or it can be a variable in the second
function, or even a variable in the same scope as our log statement. In our trivial example
it is easy to see through this as none of the functions in the innermost scope are called anywhere,
but it&amp;rsquo;s not going to be so simple in real world cases.&lt;/p&gt;
&lt;h2 id=&#34;control-flow-obfuscation&#34;&gt;Control flow obfuscation&lt;/h2&gt;
&lt;p&gt;Our dead code example also exemplifies a way that control flow can be made confusing -
there a seemingly infinite loop that has try-catch structure inside it. Under &lt;code&gt;try&lt;/code&gt;
there&amp;rsquo;s a switch statement with two cases: one for returning out of the fuction, another
for calling the log function and setting the variable with a value that triggers the first
case with &lt;code&gt;return&lt;/code&gt; statement. This introduces a confusing, roundabout codepath that is
functionally identical to our original single-line code.&lt;/p&gt;
&lt;h2 id=&#34;recomputation-of-literals&#34;&gt;Recomputation of literals&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s try another feature of DefendsJS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cp hello.js literals.js
$ defendjs --input literals.js --features literals --output .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The resulting code is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;72&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;101&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;108&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;108&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;111&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;119&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;111&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;114&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;108&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concat&lt;/span&gt;(Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Array.&lt;span style=&#34;color:#a6e22e&#34;&gt;prototype&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;]] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;h&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }).&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; String.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromCharCode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case the hardcoded string is being recomputed in a confusing way so that it&amp;rsquo;s far
from obvious from reading the code what it will be.&lt;/p&gt;
&lt;h2 id=&#34;mangling&#34;&gt;Mangling&lt;/h2&gt;
&lt;p&gt;Mangling is a transformation that shortens variable and property names for optimization and obfuscation purposes.&lt;/p&gt;
&lt;p&gt;Consider the following JS code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;oneTwoThree&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hello&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, world! &amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;hello&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;oneTwoThree&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s use mangling feature of DefendJS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cp hello2.js mangling.js 
$ defendjs --input mangling.js --features mangle --output .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The resulting code is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello, world! &amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;oneTwoThree&lt;/code&gt; became &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;hello&lt;/code&gt; was renamed to &lt;code&gt;b&lt;/code&gt;. This does not make a trivial example
that much harder to read, but could be one more thing that can be done to make big pieces of
JS difficult to understand, with the added benefit of making HTML pages containing inline JS lighter.
Developers tend to name their variables in a way that signify their meaning, which gets lost
when code is mangled.&lt;/p&gt;
&lt;h2 id=&#34;code-minification&#34;&gt;Code minification&lt;/h2&gt;
&lt;p&gt;This isn&amp;rsquo;t much of obfuscation technique by itself, but minification - removing indentation, newlines
and whitespace from the code is often done as part of obfuscation process, in combination with
other techniques.&lt;/p&gt;
&lt;h2 id=&#34;jsfuck&#34;&gt;JSFuck&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.jsfuck.com/&#34;&gt;JSFuck&lt;/a&gt; is a way to convert JS code into esoteric style that resembles
the infamous Brainfuck programming language. &lt;code&gt;false&lt;/code&gt; becomes &lt;code&gt;![]&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt; becomes &lt;code&gt;!![]&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;Converting our original JS code into JSFuck&amp;rsquo;ed version gives us the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]][([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]))[(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]][([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;((&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]](&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]])()([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]][([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]))[(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]][([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;((&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]](&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]](([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]][([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])()[([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]((&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]])[([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][[]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]])&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;([][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[][(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[]]]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!!&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[])[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]]()[&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;[&lt;span style=&#34;color:#f92672&#34;&gt;!+&lt;/span&gt;[]&lt;span style=&#34;color:#f92672&#34;&gt;+!+&lt;/span&gt;[]]])())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-09_17.55.29.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, that&amp;rsquo;s practically unreadable, but there&amp;rsquo;s way to deobfuscate this kind of mess by performing
AST manipulation.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to scrape Zillow with Python and Scrapy</title>
      <link>https://www.trickster.dev/post/how-to-scrape-zillow-with-python-and-scrapy/</link>
      <pubDate>Sat, 02 Jul 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-to-scrape-zillow-with-python-and-scrapy/</guid>
      <description>&lt;p&gt;The following text assumes some knowledge and experience with Scrapy and is meant to
provide a realistic example of scraping moderately difficult site that you may be doing
as freelancer working in web scraping. This post is not meant for people completely
unfamiliar with Scrapy. The reader is encouraged to read some content earlier in the
blog that introduces Scrapy from ground up. This time, we are building up from the knowledge
about basics of Scrapy and thus skipping some details.&lt;/p&gt;
&lt;p&gt;For North America, Zillow is a number one portal for real estate listings for sale and rent.
Due to its significance in real estate industry it is of interest for web scraping. We
are going to use Python programming language and Scrapy framework to scrape information
on various properties on Zillow. Let us assume that FSBO (for sale by owner) listings
are of particular interest to us. They are sold directly by the owner and represent
a business opportunity for realtor to help them make a sale.&lt;/p&gt;
&lt;p&gt;Before we write a first of code, we need to explore the site to plan a general strategy
of how we are going to scrape it. We want to take a look how site handles cookies so
we force-delete them from the browser to see how they are acquired in the request flow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.40.25.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On the top header we see options to rent, buy, sell, etc. If we hover the word &amp;ldquo;Buy&amp;rdquo; with
mouse cursor we can choose properties that are sold by the owner. We click this link with
DevTools being open.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.44.04.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now if we enter some location into search bar we only get the FSBO listings, but there&amp;rsquo;s
nothing in the URL that seems to tell the site to only return such results. This suggests
that cookies are being used for session management and to keep track of state between
requests. Let us look bit deeper into this.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.47.14.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By scrolling up in the Network tab of DevTools panel we find the request that was sent
when we clicked the &amp;ldquo;For Sale By Owner&amp;rdquo; link earlier. In response, Zillow gives us some
cookies, one of which is named &lt;code&gt;search&lt;/code&gt; and has letters &lt;code&gt;fsbo&lt;/code&gt; in the value.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.51.41.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However there&amp;rsquo;s a bit of a problem. Search results section has two tabs. The first one which
is on by default is for results that already have real estate agent helping with the
sale. We are actually interested in results on the second tab. Let us see what happens
when we switch the tab. We find that API request is made when we switch the tab to update
the data being shown.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.57.25.png&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.59.45.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are two key parameters to this request. One is for search query state that contains
geographical boundaries and other search criteria. Another specifies which result category
we want to get. Both are serialised as JSON strings. Response payload contains data about
properties being found in JSON format. We are talking to a private API here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_16.58.33.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we click on one of the search results we see that few more API requests are made. One
of them is of particular interest to us. There&amp;rsquo;s a request sent to &lt;code&gt;/graphql&lt;/code&gt; endpoint
having &lt;code&gt;ForSaleShopperPlatformFullRenderQuery&lt;/code&gt; value for URL parameter &lt;code&gt;operationName&lt;/code&gt;.
In the response there&amp;rsquo;s bunch of data that is rendered into property details page,
including contact details for a person trying to sell their place. This request is
parametrised not only by URL paramers, but there&amp;rsquo;s also JSON payload with Zillow
property ID and operation name. We will be reproducing this request programmatically, as
well as the previous one that gives us search results.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_17.06.26.png&#34;&gt;Screenshot 7&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-07-02_17.08.36.png&#34;&gt;Screenshot 8&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-07-02_17.11.10.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To start sending search API requests we need to generate or extract the
&lt;code&gt;searchQueryState&lt;/code&gt; parameter. Turns out, it is available in the HTML of search results
page, assuming it was requested with the proper cookies.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_17.17.12.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So the general strategy for scraping Zillow is the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We reproduce the HTTP request for choosing FSBO listings so that proper cookies
would be received by our code.&lt;/li&gt;
&lt;li&gt;Load the first (HTML) page of search results to extract the search query state.&lt;/li&gt;
&lt;li&gt;We iterate across all pages of search results by updating the part of search
query state that deals with pagination.&lt;/li&gt;
&lt;li&gt;We extract the relevant parts from property detail API response and save them
to output file. We also download property images.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In summary, we will be using a hybrid approach that starts with scraping a small
piece of information from HTML page to bootstrap private API scraping.&lt;/p&gt;
&lt;p&gt;One last thing to consider is blocking evasion. As of 2022, Zillow uses PerimeterX for
scraping countermeasures. This can be bypassed by using ISP proxies from Bright Data.
We cannot use advanced proxies (such as Web Unlocker from Bright Data) here because
they overtake cookie management and would not allow us to pass the cookie we received
early in the flow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-07-02_17.30.24.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We create a Scrapy project with &lt;code&gt;scrapy genspider&lt;/code&gt; command and use Scrapy CLI to create new spider class.
Now let us edit the settings.py file and tell it to disobey robots.txt rules:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ROBOTSTXT_OBEY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also need to override default HTTP headers to make the requests look like they have been
sent by a browser:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DEFAULT_REQUEST_HEADERS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;www.zillow.com&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Google Chrome&amp;#34;;v=&amp;#34;93&amp;#34;, &amp;#34; Not;A Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;93&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;upgrade-insecure-requests&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;none&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;navigate&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-user&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;document&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9,en-US;q=0.8,lt;q=0.7&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our spider class we may want to set things up for accepting a list of location search
URLs via command line interface. Since Zillow URLs tend to use commas we will use pipe symbol
for separation. We make a constructor method that takes an optional list of pipe-separated
URLs and sets them to &lt;code&gt;start_urls&lt;/code&gt; property after splitting them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, start_urls&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; start_urls &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_urls&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This enables us to either give a list of URLs via &lt;code&gt;scrapy runspider&lt;/code&gt; command or fill them into
web UI in Scrapy Cloud. Scrapy framework ensures that the spider is initialized with the
parameter we passed in when starting a scraping job.&lt;/p&gt;
&lt;p&gt;Once spider is inialized and launched by Scrapy framework, &lt;code&gt;start_requests()&lt;/code&gt; method will be called.
We don&amp;rsquo;t proceed with loading pages listed in &lt;code&gt;start_urls&lt;/code&gt; property just yet, because we need
to reproduce a step that gets the cookies. If we used requests module we could create a session
object for storing the cookies. That is not needed with Scrapy. We can simply generate a request
into URL that gives us the cookies we need - the default middleware in Scrapy will remember it for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fsbo_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/homes/fsbo/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(fsbo_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_main_requests)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the next step we now proceed with loading HTML pages at URLs we got from the user:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_main_requests&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; start_url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(start_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_html)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At next callback there&amp;rsquo;s &lt;code&gt;parse_property_list_html()&lt;/code&gt; method that extracts the search query state and generates
the very first API request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_list_html&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[contains(text(), &amp;#34;searchQueryState&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; js &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            parsed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; js2xml&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse(js)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_state_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parsed&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//string[contains(text(), &amp;#34;searchQueryState&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qs(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; params[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchQueryState&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@data-zrr-shared-data-key=&amp;#34;mobileSearchPageStore&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; json_str &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to find search query state&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;lt;!--&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--&amp;gt;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryState&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(search_query_state)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;generate_api_url_from_search_query_state(search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(api_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_json)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s two ways to extract the search query state. One is to use XPath query that looks for JavaScript
snippet containing substring &lt;code&gt;searchQueryState&lt;/code&gt;, parsing it into tree structure with js2xml module, then digging up
the part we need from there (js2xml is a convenient way to convert JS code into XML trees to run XPath queries against
them). Another approach is finding out a commented out JSON in the HTML, removing the comments, parsing it into Python
dictionary and extracting the part we need from that. Since the API expects search query state in JSON format we JSONify
it back before generating search API request URL. You may notice that we don&amp;rsquo;t do that in previous codepath. That&amp;rsquo;s because
the previous case depends on JS code having API URLs hardcoded in them that we can parse and extract &lt;code&gt;searchQueryState&lt;/code&gt;
for reusing it. With this appraoch we cover some diversity in how the page might be structured to make the scraper more reliable.
We call a helper method to create a complete URL for the request and yield the request.&lt;/p&gt;
&lt;p&gt;At then next callback method we process the search API response by extracting the part that contains search results and
iterating across them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_list_json&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        search_results &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat2&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchResults&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;listResults&amp;#39;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; result &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; search_results:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Only thing we need from here is ZPID - Zillow property ID. We use this to reproduce the request that client side JS
code makes to get data for property details page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            zpid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4c19ac196fd0f1e7e30ca7e7f9af85d5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            operation_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ForSaleDoubleScrollFullRenderQuery&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            url_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt;: zpid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderParameter&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryId&amp;#39;&lt;/span&gt;: query_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;operationName&amp;#39;&lt;/span&gt;: operation_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;clientVersion&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;home-details/6.0.11.5970.master.8d248cb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;operationName&amp;#39;&lt;/span&gt; : operation_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryId&amp;#39;&lt;/span&gt; : query_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;variables&amp;#39;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderParameter&amp;#39;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;isDoubleScroll&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;platform&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;desktop&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt; : zpid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt; : zpid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/graphql/?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(url_params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; JsonRequest(url&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;api_url, data&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_payload, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_page_json)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we need to pass JSON payload in the POST request we use &lt;code&gt;JsonRequest&lt;/code&gt; class here.&lt;/p&gt;
&lt;p&gt;We went through all the results in the current API page, but there might be more pages we may want to scrape.
In the API response there&amp;rsquo;s a field called &lt;code&gt;nextUrl&lt;/code&gt; if further search results are available. It is not present
otherwise. So let us check for the presence of this field:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat2&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchList&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nextUrl&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_url &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we found it, we need to extract old search query state from the URL of the request corresponding to the current
invocation of callback method we are writing and update the &lt;code&gt;currentPage&lt;/code&gt; counter in &lt;code&gt;pagination&lt;/code&gt; section:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            o &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            old_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qs(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            old_search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; old_params[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchQueryState&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(old_search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; search_query_state&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                search_query_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currentPage&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                search_query_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currentPage&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(search_query_state)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then we create one more search API request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;generate_api_url_from_search_query_state(search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request(api_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_json)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When property detail API call succeeds, the &lt;code&gt;parse_property_page_json()&lt;/code&gt; method will be called.&lt;/p&gt;
&lt;p&gt;But now let us go to items.py file and define a item class for real estate properties we are scraping:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Property&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    city &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    zipcode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    zestimate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_bedrooms &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_bathrooms &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    area &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    latitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    longitude &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# See: https://docs.scrapy.org/en/latest/topics/media-pipeline.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    images &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two fields that are bit special here: &lt;code&gt;image_urls&lt;/code&gt; and &lt;code&gt;images&lt;/code&gt;. These will be used by image downloading
pipeline bit later. In our spider code, we need to set &lt;code&gt;image_urls&lt;/code&gt; to let image pipeline download the pictures.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;parse_property_page_json()&lt;/code&gt; we instantiate &lt;code&gt;Property&lt;/code&gt; object and use basic Python dictionary operations to
set the relevant fields:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_page_json&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; data_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;property&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; property_dict &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Property()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hdpUrl&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;streetAddress&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;city&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;city&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;state&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;state&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zipcode&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zipcode&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zestimate&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zestimate&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;n_bedrooms&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bedrooms&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;n_bathrooms&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bathrooms&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;area&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;livingArea&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;latitude&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;latitude&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;longitude&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;longitude&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        contact_data_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderData&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        agent_module &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; contact_data_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;agent_module&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; agent_module &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phone_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; agent_module&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phone&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phone_dict &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phone&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;) &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;areacode&amp;#39;&lt;/span&gt;), phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;prefix&amp;#39;&lt;/span&gt;), phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;number&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        huge_photos &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hugePhotos&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; huge_photos &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;image_urls&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; img: img&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;), huge_photos))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let us edit settings.py some more to set up the images pipeline. We uncomment the pipelines section and put the
images pipeline at the top priority:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ITEM_PIPELINES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;scrapy.pipelines.images.ImagesPipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;#&amp;#39;zillow.pipelines.ZillowPipeline&amp;#39;: 300,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also set &lt;code&gt;IMAGES_STORE&lt;/code&gt; to directory path that images will be saved in after they are downloaded and normalised into
a consistent format (make sure you have PIL installed).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IMAGES_STORE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete spider code for scraping Zillow properties is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.http &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; JsonRequest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; urllib.parse &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urlparse, parse_qs, urlencode, urljoin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; js2xml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; zillow.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Property
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HomeSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;home&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zillow.com&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/homes/NY_rb/&amp;#39;&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    request_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, start_urls&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; start_urls &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start_urls&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;|&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fsbo_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/homes/fsbo/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(fsbo_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_main_requests)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_main_requests&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; start_url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(start_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_html)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate_api_url_from_search_query_state&lt;/span&gt;(self, search_query_state_json_str):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        wants &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat1&amp;#39;&lt;/span&gt; : [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;total&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat2&amp;#39;&lt;/span&gt; : [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;listResults&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        wants_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(wants)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchQueryState&amp;#39;&lt;/span&gt; : search_query_state_json_str,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;wants&amp;#39;&lt;/span&gt; : wants_json_str,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;requestId&amp;#39;&lt;/span&gt; : self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/search/GetSearchPageState.htm?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(new_params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request_id &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; api_url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_list_html&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[contains(text(), &amp;#34;searchQueryState&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; js &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            parsed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; js2xml&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse(js)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_state_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parsed&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//string[contains(text(), &amp;#34;searchQueryState&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qs(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; params[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchQueryState&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[@data-zrr-shared-data-key=&amp;#34;mobileSearchPageStore&amp;#34;]/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; json_str &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;logger&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to find search query state&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;lt;!--&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--&amp;gt;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryState&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(search_query_state)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;generate_api_url_from_search_query_state(search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(api_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_json)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_list_json&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        search_results &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat2&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchResults&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;listResults&amp;#39;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; result &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; search_results:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            zpid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4c19ac196fd0f1e7e30ca7e7f9af85d5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            operation_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ForSaleDoubleScrollFullRenderQuery&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            url_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt;: zpid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderParameter&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryId&amp;#39;&lt;/span&gt;: query_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;operationName&amp;#39;&lt;/span&gt;: operation_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;clientVersion&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;home-details/6.0.11.5970.master.8d248cb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;operationName&amp;#39;&lt;/span&gt; : operation_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;queryId&amp;#39;&lt;/span&gt; : query_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;variables&amp;#39;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderParameter&amp;#39;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;isDoubleScroll&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;platform&amp;#39;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;desktop&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt; : zpid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zpid&amp;#39;&lt;/span&gt; : zpid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zillow.com/graphql/?&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; urlencode(url_params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; JsonRequest(url&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;api_url, data&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_payload, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_page_json)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cat2&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchList&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nextUrl&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_url &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            o &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urlparse(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            old_params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_qs(o&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            old_search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; old_params[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;searchQueryState&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(old_search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; search_query_state&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                search_query_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currentPage&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                search_query_state[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pagination&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;currentPage&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            search_query_state_json_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(search_query_state)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;generate_api_url_from_search_query_state(search_query_state_json_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request(api_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_property_list_json)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_property_page_json&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        property_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; data_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;property&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; property_dict &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Property()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hdpUrl&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; item&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;streetAddress&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;city&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;city&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;state&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;state&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zipcode&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zipcode&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zestimate&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zestimate&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;n_bedrooms&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bedrooms&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;n_bathrooms&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bathrooms&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;area&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;livingArea&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;latitude&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;latitude&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;longitude&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;longitude&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        contact_data_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contactFormRenderData&amp;#39;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        agent_module &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; contact_data_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;agent_module&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; agent_module &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phone_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; agent_module&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phone&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phone_dict &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;phone&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;) &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;areacode&amp;#39;&lt;/span&gt;), phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;prefix&amp;#39;&lt;/span&gt;), phone_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;number&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        huge_photos &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; property_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hugePhotos&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; huge_photos &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;image_urls&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; img: img&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;), huge_photos))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Understanding TLS fingerprinting</title>
      <link>https://www.trickster.dev/post/understanding-tls-fingerprinting/</link>
      <pubDate>Mon, 27 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-tls-fingerprinting/</guid>
      <description>&lt;p&gt;TLS (Transport Level Security) is a network protocol that sits between trasport layer (TCP)
and application layer protocols (HTTP, IMAP, SMTP and so on). It provides security features
such as encryption and authentication to TCP connections that merely deal with reliably
transferring streams of data. For example, a lot of URLs in modern web start with &lt;code&gt;https://&lt;/code&gt;
and you typically see a lock icon by the address bar on your web browser. This indicates
TLS being used to not only encrypt data against passive sniffing by various adversaries, but
also against active MITM attacks that involve someone hijacking connection between
client and server for credential stealing and other nefarious purposes.&lt;/p&gt;
&lt;p&gt;Exact details of TLS are fairly complex, but to encrypt data and authenticate the server it
has to perform a handshake. In TLS version 1.3 this is typically only one round trip:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Client send Client Hello message with a list of cipher suites it supports, proposed key
agreement protocol and some cryptographic materials to kickstart the encrypted communication
as soon as possible. Client Hello message may have a list of TLS extensions that provide
additional cryptographic capabilities and their parameters. Cipher suites are combinations
of cryptographic primitives (hashing and encryption functions) that will be very important
later on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Server sends Server Hello message with the chosen key agreement protocol and their share
of keys and certificates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When client receives the Server Hello message, it has everything to start encrypting
application-level protocol streams based on what it received in Server Hello message and
what it has computed before sending the Client Hello.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This description greatly oversimplifies all the details. The reader is encouraged to at least
skim &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc8446.txt&#34;&gt;RFC 8446&lt;/a&gt; to appreciate the complexity of
the protocol.&lt;/p&gt;
&lt;p&gt;TLS 1.2 used to have a fairly elaborate dance between client and server to establish the connection.
There we separate steps for sending the certificates, sending each half of Diffie-Hellman
and so on.&lt;/p&gt;
&lt;p&gt;So what is the TLS fingerprinting? Well, it turns out that due to large number of fields
in Client Hello message it can become fairly unique to the software that it comes from.
Consider the following parts of Client Hello message:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exact TLS version (1.3 or 1.2 in non-legacy cases).&lt;/li&gt;
&lt;li&gt;Ciphersuites.&lt;/li&gt;
&lt;li&gt;TLS Extensions.&lt;/li&gt;
&lt;li&gt;(If Elliptic Curve Cryptography is used) elliptic curves and their parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-27_15.31.57.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a lot of stuff and we have a bit of combinatorial explosion on the diversity
of Client Hello messages that could be created. Of course, not all combinations are
equally likely due to security reasons - some of the older ciphersuites are now
known to be insecure and should not longer be used.&lt;/p&gt;
&lt;p&gt;So the thing is, programs tend to choose some unique combination of the above things.
There&amp;rsquo;s one kind of Client Hello message that Google Chrome sends, another one for
Firefox, yet another one for curl and so on. Thus it is possible to develop a way
to fingerprint the Client Hello message and infer what kind of client that is.
A prominent implementation of TLS fingerprinting is &lt;a href=&#34;https://github.com/salesforce/ja3&#34;&gt;JA3&lt;/a&gt;
by Salesforce. In fact Salesforce even has a patent on TLS fingerprinting
(altough I have my doubts if they truly invented it). JA3 parses the Client
Hello message, extracts the fields that are relevant for fingerprinting,
encodes them into a string and computes a MD5 hash from this string.
Since so many things in the message are unique to program acting as TLS
client the resulting hash will be unique as well.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-27_15.26.05.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TLS fingerprinting is used not only to detect malware and network anomalies
but also as a countermeasure against automated traffic at CDN
level by CDN providers such as Akamai and Cloudflare (for enterprise
customer that have it enabled). So if there&amp;rsquo;s HTTP request coming with all the
right headers that claims to be from Google Chrome, but TLS fingerprint
matches Python HTTP client the CDN knows that something is off and
rejects the request. Systems that implement TLS fingerprinting can keep
blacklists of known malicious clients and whitelists of known
good clients and update them as traffic is being monitored.&lt;/p&gt;
&lt;p&gt;So what do we do about this as web scraper and automation developers?
Unfortunately Python requests module and standard libraries do not offer
much options to configure the low level TLS handshake details the way we
want. However there are some open source projects made for customising it
the way we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;curl-impersonate&#34;&gt;https://github.com/lwthiker/curl-impersonate&lt;/a&gt; -
a modified version of (lib)curl that is made to have the same TLS
fingerprints as many popular browsers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/refraction-networking/utls&#34;&gt;utls&lt;/a&gt; - a modified
version Golang&amp;rsquo;s &lt;code&gt;crypto/tls&lt;/code&gt; with extra customisability. You can set
the exact low-level details of Client Hello message when establishing
TLS connection.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Carcraftz/TLS-Fingerprint-API&#34;&gt;TLS-Fingerprint-API&lt;/a&gt; - a
proxy server developed to defeat TLS fingerprinting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These projects provide ways to pretend to be a whitelisted TLS client or
at least avoid being in a blacklist by manipulating the TLS fingerpint.
Some developers working in darker shades of software development also
implement Cipher Stunting - a technique that entails randomising
fingerprintable parts of Client Hello message to further thwart the countermeasures.&lt;/p&gt;
&lt;p&gt;If the target expects the traffic to come from a browser we could also
develop a solution based on browser automation by using something
like Playwright or Puppeteer.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to scrape Youtube view intensity time series</title>
      <link>https://www.trickster.dev/post/how-to-scrape-youtube-view-intensity-time-series/</link>
      <pubDate>Thu, 16 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-to-scrape-youtube-view-intensity-time-series/</guid>
      <description>&lt;p&gt;Recently Youtube has introduced a small graph on it&amp;rsquo;s user interface that visualises a
time series of video viewing intensity. Spikes in this graph indicate what parts of the
video tend to be replayed often, thus being most interesting or relevant to
watch. This requires quite some data to be accumulated and is only available on sufficiently
popular videos.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-15_15.11.40.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us try scraping this data as an exercise in web scraper development. Viewing page source
through the browser and scrolling through it reveals some deep JSON that includes parts
like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;heatMarkerRenderer&amp;#34;&lt;/span&gt;:{&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;timeRangeStartMillis&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;16080&lt;/span&gt;,&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;markerDurationMillis&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;8040&lt;/span&gt;,&lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ae81ff&#34;&gt;0.22363814666380694&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That looks very promising, as this means the time series data is available in the page HTML
and that no further requests are needed to get it. When we scroll higher up, we see that a large
JSON object is being assigned to &lt;code&gt;ytInitialData&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-15_15.17.54.png&#34;&gt;Screenshot 2&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-15_15.23.09.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For experimentation, we launch Python REPL and import some usual Python modules that we use
for web scraping and try fetching the page with &lt;code&gt;requests.get()&lt;/code&gt;.
Since the data is inside JavaScript code, we also import &lt;a href=&#34;https://github.com/scrapinghub/js2xml&#34;&gt;js2xml&lt;/a&gt;
to parse it.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.9.12 (main, Mar 26 2022, 15:51:15)
[Clang 13.1.6 (clang-1316.0.21.2)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; from lxml import html
&amp;gt;&amp;gt;&amp;gt; import js2xml
&amp;gt;&amp;gt;&amp;gt; resp = requests.get(&amp;#34;https://www.youtube.com/watch?v=SrsCEbi5N7Y&amp;amp;t=2s&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far, so good. We were to get the page HTML without running into any kind of blocking despite
not explicitly setting headers in our request and not managing cookies.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try to extract the JS statement we need.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; initial_data_js = tree.xpath(&amp;#39;//script[starts-with(text(), &amp;#34;var ytInitialData = &amp;#34;)]/text()&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; initial_data_js = initial_data_js[0]
&amp;gt;&amp;gt;&amp;gt; initial_data_js[:32]
&amp;#39;var ytInitialData = {&amp;#34;responseCo&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let us use js2xml to parse it into XML tree.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; parsed = js2xml.parse(initial_data_js)
&amp;gt;&amp;gt;&amp;gt; parsed
&amp;lt;Element program at 0x10f841e40&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Converting deep JSON dictionary into deep XML tree may not seem like much of an improvement. However,
now we can run XPath queries against it. Let us save the XML tree into a file for easier reading.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; out_f = open(&amp;#34;initial_data_js.xml&amp;#34;, &amp;#34;w&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; out_f.write(js2xml.pretty_print(parsed))
2411766
&amp;gt;&amp;gt;&amp;gt; out_f.close()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can open this with a text editor and find the part of tree that contains the data we need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;heatMarkers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;heatMarkerRenderer&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timeRangeStartMillis&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;markerDurationMillis&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8040&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;heatMarkerRenderer&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timeRangeStartMillis&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8040&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;markerDurationMillis&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8040&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.6048726840234977&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/object&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Based on this, we can write XPath queries for data extraction:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;)
[&amp;lt;Element property at 0x10f841d80&amp;gt;, &amp;lt;Element property at 0x10f83a3c0&amp;gt;, &amp;lt;Element property at 0x10f83a380&amp;gt;, &amp;lt;Element property at 0x10f83a100&amp;gt;, &amp;lt;Element property at 0x10db1a6c0&amp;gt;, &amp;lt;Element property at 0x10ec91d00&amp;gt;, &amp;lt;Element property at 0x110fd6f40&amp;gt;, &amp;lt;Element property at 0x110fd63c0&amp;gt;, &amp;lt;Element property at 0x110fd6ec0&amp;gt;, &amp;lt;Element property at 0x10ec81f00&amp;gt;, &amp;lt;Element property at 0x110fd6800&amp;gt;, &amp;lt;Element property at 0x110fd6c80&amp;gt;, &amp;lt;Element property at 0x110fd64c0&amp;gt;, &amp;lt;Element property at 0x110fd6dc0&amp;gt;, &amp;lt;Element property at 0x110fd6340&amp;gt;, &amp;lt;Element property at 0x110fd67c0&amp;gt;, &amp;lt;Element property at 0x110fd6440&amp;gt;, &amp;lt;Element property at 0x110fd6400&amp;gt;, &amp;lt;Element property at 0x110fd6fc0&amp;gt;, &amp;lt;Element property at 0x110fd66c0&amp;gt;, &amp;lt;Element property at 0x110fd6500&amp;gt;, &amp;lt;Element property at 0x110fd6cc0&amp;gt;, &amp;lt;Element property at 0x110fd6480&amp;gt;, &amp;lt;Element property at 0x110fd6d40&amp;gt;, &amp;lt;Element property at 0x110fd6380&amp;gt;, &amp;lt;Element property at 0x110fd6b80&amp;gt;, &amp;lt;Element property at 0x110fd6840&amp;gt;, &amp;lt;Element property at 0x110fd6d00&amp;gt;, &amp;lt;Element property at 0x110fd6e00&amp;gt;, &amp;lt;Element property at 0x110fd6740&amp;gt;, &amp;lt;Element property at 0x110fd6880&amp;gt;, &amp;lt;Element property at 0x110fd6c00&amp;gt;, &amp;lt;Element property at 0x110fd6a80&amp;gt;, &amp;lt;Element property at 0x110fd6f80&amp;gt;, &amp;lt;Element property at 0x110fd6540&amp;gt;, &amp;lt;Element property at 0x110fd69c0&amp;gt;, &amp;lt;Element property at 0x110fd6b40&amp;gt;, &amp;lt;Element property at 0x110fd6680&amp;gt;, &amp;lt;Element property at 0x110fd6980&amp;gt;, &amp;lt;Element property at 0x110fd68c0&amp;gt;, &amp;lt;Element property at 0x110fd6e80&amp;gt;, &amp;lt;Element property at 0x110fd6ac0&amp;gt;, &amp;lt;Element property at 0x110fd6b00&amp;gt;, &amp;lt;Element property at 0x110fd6c40&amp;gt;, &amp;lt;Element property at 0x110fd6a40&amp;gt;, &amp;lt;Element property at 0x110fd6a00&amp;gt;, &amp;lt;Element property at 0x110fd6bc0&amp;gt;, &amp;lt;Element property at 0x110fd6580&amp;gt;, &amp;lt;Element property at 0x110fd6780&amp;gt;, &amp;lt;Element property at 0x110fd6e40&amp;gt;, &amp;lt;Element property at 0x110fe0140&amp;gt;, &amp;lt;Element property at 0x110fe0740&amp;gt;, &amp;lt;Element property at 0x110fe09c0&amp;gt;, &amp;lt;Element property at 0x110fe0040&amp;gt;, &amp;lt;Element property at 0x110fe07c0&amp;gt;, &amp;lt;Element property at 0x110fe01c0&amp;gt;, &amp;lt;Element property at 0x110fe0180&amp;gt;, &amp;lt;Element property at 0x110fe0b80&amp;gt;, &amp;lt;Element property at 0x110fe05c0&amp;gt;, &amp;lt;Element property at 0x110fe0d00&amp;gt;, &amp;lt;Element property at 0x110fe0a00&amp;gt;, &amp;lt;Element property at 0x110fe0a80&amp;gt;, &amp;lt;Element property at 0x110fe0fc0&amp;gt;, &amp;lt;Element property at 0x110fe0840&amp;gt;, &amp;lt;Element property at 0x110fe0cc0&amp;gt;, &amp;lt;Element property at 0x110fe0100&amp;gt;, &amp;lt;Element property at 0x110fe0240&amp;gt;, &amp;lt;Element property at 0x110fe0f40&amp;gt;, &amp;lt;Element property at 0x110fe0a40&amp;gt;, &amp;lt;Element property at 0x110fe0600&amp;gt;, &amp;lt;Element property at 0x110fe06c0&amp;gt;, &amp;lt;Element property at 0x110fe0280&amp;gt;, &amp;lt;Element property at 0x110fe0540&amp;gt;, &amp;lt;Element property at 0x110fe0440&amp;gt;, &amp;lt;Element property at 0x110fe08c0&amp;gt;, &amp;lt;Element property at 0x110fe0e00&amp;gt;, &amp;lt;Element property at 0x110fe0ac0&amp;gt;, &amp;lt;Element property at 0x110fe0980&amp;gt;, &amp;lt;Element property at 0x110fe0940&amp;gt;, &amp;lt;Element property at 0x110fe0c80&amp;gt;, &amp;lt;Element property at 0x110fe0b00&amp;gt;, &amp;lt;Element property at 0x110fe0ec0&amp;gt;, &amp;lt;Element property at 0x110fe0c40&amp;gt;, &amp;lt;Element property at 0x110fe0680&amp;gt;, &amp;lt;Element property at 0x110fe0480&amp;gt;, &amp;lt;Element property at 0x110fe0400&amp;gt;, &amp;lt;Element property at 0x110fe0700&amp;gt;, &amp;lt;Element property at 0x110fe0f80&amp;gt;, &amp;lt;Element property at 0x110fe0080&amp;gt;, &amp;lt;Element property at 0x110fe0780&amp;gt;, &amp;lt;Element property at 0x110fe00c0&amp;gt;, &amp;lt;Element property at 0x110fe03c0&amp;gt;, &amp;lt;Element property at 0x110fe0dc0&amp;gt;, &amp;lt;Element property at 0x110fe0580&amp;gt;, &amp;lt;Element property at 0x110fe0880&amp;gt;, &amp;lt;Element property at 0x110fe0900&amp;gt;, &amp;lt;Element property at 0x110fe02c0&amp;gt;, &amp;lt;Element property at 0x110fe0d40&amp;gt;, &amp;lt;Element property at 0x110fe04c0&amp;gt;, &amp;lt;Element property at 0x110fe0200&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;)[0].xpath(&amp;#39;.//property[@name=&amp;#34;timeRangeStartMillis&amp;#34;]/number/@value&amp;#39;)
[&amp;#39;0&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;)[-1].xpath(&amp;#39;.//property[@name=&amp;#34;timeRangeStartMillis&amp;#34;]/number/@value&amp;#39;)
[&amp;#39;795960&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;)[-1].xpath(&amp;#39;.//property[@name=&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;]/number/@value&amp;#39;)
[&amp;#39;0.013456379693201089&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;)[0].xpath(&amp;#39;.//property[@name=&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;]/number/@value&amp;#39;)
[&amp;#39;1&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; for hmr in parsed.xpath(&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;):
...     start = hmr.xpath(&amp;#39;.//property[@name=&amp;#34;timeRangeStartMillis&amp;#34;]/number/@value&amp;#39;)
...     score = hmr.xpath(&amp;#39;.//property[@name=&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;]/number/@value&amp;#39;)
...     print(start, score)
...
[&amp;#39;0&amp;#39;] [&amp;#39;1&amp;#39;]
[&amp;#39;8040&amp;#39;] [&amp;#39;0.6048726840234977&amp;#39;]
[&amp;#39;16080&amp;#39;] [&amp;#39;0.22365224428850775&amp;#39;]
[&amp;#39;24120&amp;#39;] [&amp;#39;0.27094763489610235&amp;#39;]
[&amp;#39;32160&amp;#39;] [&amp;#39;0.14741887199269307&amp;#39;]
[&amp;#39;40200&amp;#39;] [&amp;#39;0.076784029975364834&amp;#39;]
[&amp;#39;48240&amp;#39;] [&amp;#39;0.085744019488653372&amp;#39;]
[&amp;#39;56280&amp;#39;] [&amp;#39;0.058312826881888297&amp;#39;]
[&amp;#39;64320&amp;#39;] [&amp;#39;0.036752924777072669&amp;#39;]
[&amp;#39;72360&amp;#39;] [&amp;#39;0.03365628640622368&amp;#39;]
[&amp;#39;80400&amp;#39;] [&amp;#39;0.050183479194471192&amp;#39;]
[&amp;#39;88440&amp;#39;] [&amp;#39;0.057593336089155567&amp;#39;]
[&amp;#39;96480&amp;#39;] [&amp;#39;0.034168240950240576&amp;#39;]
[&amp;#39;104520&amp;#39;] [&amp;#39;0.021520086067824129&amp;#39;]
[&amp;#39;112560&amp;#39;] [&amp;#39;0.0058667693397754829&amp;#39;]
[&amp;#39;120600&amp;#39;] [&amp;#39;0.023982917121602401&amp;#39;]
[&amp;#39;128640&amp;#39;] [&amp;#39;0.054083463495605&amp;#39;]
[&amp;#39;136680&amp;#39;] [&amp;#39;0.11079831293280626&amp;#39;]
[&amp;#39;144720&amp;#39;] [&amp;#39;0.14804064606274692&amp;#39;]
[&amp;#39;152760&amp;#39;] [&amp;#39;0.1617291352663259&amp;#39;]
[&amp;#39;160800&amp;#39;] [&amp;#39;0.22240811982443065&amp;#39;]
[&amp;#39;168840&amp;#39;] [&amp;#39;0.13413726751153177&amp;#39;]
[&amp;#39;176880&amp;#39;] [&amp;#39;0.088080834325529697&amp;#39;]
[&amp;#39;184920&amp;#39;] [&amp;#39;0.057483009795490349&amp;#39;]
[&amp;#39;192960&amp;#39;] [&amp;#39;0.040814160550204967&amp;#39;]
[&amp;#39;201000&amp;#39;] [&amp;#39;0.042385478042995778&amp;#39;]
[&amp;#39;209040&amp;#39;] [&amp;#39;0.067066323491578081&amp;#39;]
[&amp;#39;217080&amp;#39;] [&amp;#39;0.39149394113095093&amp;#39;]
[&amp;#39;225120&amp;#39;] [&amp;#39;0.16310203832771014&amp;#39;]
[&amp;#39;233160&amp;#39;] [&amp;#39;0.21357182135892602&amp;#39;]
[&amp;#39;241200&amp;#39;] [&amp;#39;0.19529301110503752&amp;#39;]
[&amp;#39;249240&amp;#39;] [&amp;#39;0.10639527729932123&amp;#39;]
[&amp;#39;257280&amp;#39;] [&amp;#39;0.28438246206017076&amp;#39;]
[&amp;#39;265320&amp;#39;] [&amp;#39;0.58298485358007635&amp;#39;]
[&amp;#39;273360&amp;#39;] [&amp;#39;0.50241718718906136&amp;#39;]
[&amp;#39;281400&amp;#39;] [&amp;#39;0.29319187203151548&amp;#39;]
[&amp;#39;289440&amp;#39;] [&amp;#39;0.40353326935938538&amp;#39;]
[&amp;#39;297480&amp;#39;] [&amp;#39;0.45796861838480818&amp;#39;]
[&amp;#39;305520&amp;#39;] [&amp;#39;0.30518139791969506&amp;#39;]
[&amp;#39;313560&amp;#39;] [&amp;#39;0.10516748863879144&amp;#39;]
[&amp;#39;321600&amp;#39;] [&amp;#39;0.051570373017044355&amp;#39;]
[&amp;#39;329640&amp;#39;] [&amp;#39;0.037088951461110936&amp;#39;]
[&amp;#39;337680&amp;#39;] [&amp;#39;0.050998202554842172&amp;#39;]
[&amp;#39;345720&amp;#39;] [&amp;#39;0.042643104793951211&amp;#39;]
[&amp;#39;353760&amp;#39;] [&amp;#39;0.025665097485547211&amp;#39;]
[&amp;#39;361800&amp;#39;] [&amp;#39;0.12987553061675877&amp;#39;]
[&amp;#39;369840&amp;#39;] [&amp;#39;0.086472314126850164&amp;#39;]
[&amp;#39;377880&amp;#39;] [&amp;#39;0.1022516073253201&amp;#39;]
[&amp;#39;385920&amp;#39;] [&amp;#39;0.0093151441911397924&amp;#39;]
[&amp;#39;393960&amp;#39;] [&amp;#39;0.0046690687650980112&amp;#39;]
[&amp;#39;402000&amp;#39;] [&amp;#39;0.016322905814324853&amp;#39;]
[&amp;#39;410040&amp;#39;] [&amp;#39;0.036336073624631912&amp;#39;]
[&amp;#39;418080&amp;#39;] [&amp;#39;0.095776905627377285&amp;#39;]
[&amp;#39;426120&amp;#39;] [&amp;#39;0.16380529268309721&amp;#39;]
[&amp;#39;434160&amp;#39;] [&amp;#39;0.10283409199925106&amp;#39;]
[&amp;#39;442200&amp;#39;] [&amp;#39;0.077177480387322694&amp;#39;]
[&amp;#39;450240&amp;#39;] [&amp;#39;0.021710680379162746&amp;#39;]
[&amp;#39;458280&amp;#39;] [&amp;#39;0.019724091656537029&amp;#39;]
[&amp;#39;466320&amp;#39;] [&amp;#39;0.036406494451724176&amp;#39;]
[&amp;#39;474360&amp;#39;] [&amp;#39;0.054331362295421265&amp;#39;]
[&amp;#39;482400&amp;#39;] [&amp;#39;0.034113380870322924&amp;#39;]
[&amp;#39;490440&amp;#39;] [&amp;#39;0.020638777415743645&amp;#39;]
[&amp;#39;498480&amp;#39;] [&amp;#39;0.018437309282101948&amp;#39;]
[&amp;#39;506520&amp;#39;] [&amp;#39;0.0063348338325342254&amp;#39;]
[&amp;#39;514560&amp;#39;] [&amp;#39;0.0044191130849080915&amp;#39;]
[&amp;#39;522600&amp;#39;] [&amp;#39;0&amp;#39;]
[&amp;#39;530640&amp;#39;] [&amp;#39;0.0099072475133881299&amp;#39;]
[&amp;#39;538680&amp;#39;] [&amp;#39;0.031019316084096041&amp;#39;]
[&amp;#39;546720&amp;#39;] [&amp;#39;0.022984068189618654&amp;#39;]
[&amp;#39;554760&amp;#39;] [&amp;#39;0.017848027959979786&amp;#39;]
[&amp;#39;562800&amp;#39;] [&amp;#39;0.030471162432826833&amp;#39;]
[&amp;#39;570840&amp;#39;] [&amp;#39;0.046978193818764453&amp;#39;]
[&amp;#39;578880&amp;#39;] [&amp;#39;0.097962445358898351&amp;#39;]
[&amp;#39;586920&amp;#39;] [&amp;#39;0.13967496580138281&amp;#39;]
[&amp;#39;594960&amp;#39;] [&amp;#39;0.14630756039370915&amp;#39;]
[&amp;#39;603000&amp;#39;] [&amp;#39;0.10187546650568548&amp;#39;]
[&amp;#39;611040&amp;#39;] [&amp;#39;0.087378990970205725&amp;#39;]
[&amp;#39;619080&amp;#39;] [&amp;#39;0.062368607450551332&amp;#39;]
[&amp;#39;627120&amp;#39;] [&amp;#39;0.024774647142917147&amp;#39;]
[&amp;#39;635160&amp;#39;] [&amp;#39;0.073689468358129825&amp;#39;]
[&amp;#39;643200&amp;#39;] [&amp;#39;0.082347861576243478&amp;#39;]
[&amp;#39;651240&amp;#39;] [&amp;#39;0.019156333053687032&amp;#39;]
[&amp;#39;659280&amp;#39;] [&amp;#39;0.076731087663138561&amp;#39;]
[&amp;#39;667320&amp;#39;] [&amp;#39;0.18014944299132968&amp;#39;]
[&amp;#39;675360&amp;#39;] [&amp;#39;0.19965675734239965&amp;#39;]
[&amp;#39;683400&amp;#39;] [&amp;#39;0.15902277752560293&amp;#39;]
[&amp;#39;691440&amp;#39;] [&amp;#39;0.13666601810346865&amp;#39;]
[&amp;#39;699480&amp;#39;] [&amp;#39;0.042585273664604947&amp;#39;]
[&amp;#39;707520&amp;#39;] [&amp;#39;0.048912346996792193&amp;#39;]
[&amp;#39;715560&amp;#39;] [&amp;#39;0.079598398777255164&amp;#39;]
[&amp;#39;723600&amp;#39;] [&amp;#39;0.090416556134188128&amp;#39;]
[&amp;#39;731640&amp;#39;] [&amp;#39;0.10795224631259727&amp;#39;]
[&amp;#39;739680&amp;#39;] [&amp;#39;0.09381178000430275&amp;#39;]
[&amp;#39;747720&amp;#39;] [&amp;#39;0.066739160272724848&amp;#39;]
[&amp;#39;755760&amp;#39;] [&amp;#39;0.16440586200572407&amp;#39;]
[&amp;#39;763800&amp;#39;] [&amp;#39;0.31834117943766299&amp;#39;]
[&amp;#39;771840&amp;#39;] [&amp;#39;0.2659790863359241&amp;#39;]
[&amp;#39;779880&amp;#39;] [&amp;#39;0.067401932837569511&amp;#39;]
[&amp;#39;787920&amp;#39;] [&amp;#39;0.04629072875281575&amp;#39;]
[&amp;#39;795960&amp;#39;] [&amp;#39;0.013456379693201089&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let us put all the needed steps into a Python script (and also get the data written
into CSV file):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; js2xml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;youtube_url&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;view_intensity.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;intensity&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;], lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    initial_data_js &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//script[starts-with(text(), &amp;#34;var ytInitialData = &amp;#34;)]/text()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    initial_data_js &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; initial_data_js[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parsed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; js2xml&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse(initial_data_js)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hmr &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; parsed&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//property[@name=&amp;#34;heatMarkerRenderer&amp;#34;]&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hmr&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.//property[@name=&amp;#34;timeRangeStartMillis&amp;#34;]/number/@value&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        score &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hmr&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.//property[@name=&amp;#34;heatMarkerIntensityScoreNormalized&amp;#34;]/number/@value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(start, score)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow({&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;: start, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;intensity&amp;#34;&lt;/span&gt;: score, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: url})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One can verify that scraped data is correct by plotting it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Understanding Abstract Syntax Trees</title>
      <link>https://www.trickster.dev/post/understanding-abstract-syntax-trees/</link>
      <pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/understanding-abstract-syntax-trees/</guid>
      <description>&lt;p&gt;First thing that happens when a program source code is parsed by compiler or interpreter
is tokenization - cutting of code into substrings that are later organised into parse tree.
However, this tree structure merely represents textual structure of the code. Further step
is syntactic analysis - an activity of converting parse tree (also known as CST - concrete
syntax tree) into another tree structure that represent logical (not textual) structure.
This new tree structure that emerges from syntactic analysis is known as
Abstract Syntax Tree (AST). AST is then further processed to yield some form of executable code,
possibly with some optimizations being applied on the way.&lt;/p&gt;
&lt;p&gt;AST represents code in terms of it&amp;rsquo;s logical building blocks and their relations to each other.
Program/module is topmost node that has nodes representing expression statements, functions calls,
variable declarations and many other things that collectively represent the code that
was parsed.&lt;/p&gt;
&lt;p&gt;One way to see what ASTs are like is to play with &lt;a href=&#34;https://astexplorer.net/&#34;&gt;ASTExplorer&lt;/a&gt; web
app. For example, we can choose Python and put some Hello World example into code editor:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello world&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By exploring the AST viewer on the right hand side we can see that &lt;code&gt;main()&lt;/code&gt; function is
represented by &lt;code&gt;FunctionDeclaration&lt;/code&gt; node with two children: &lt;code&gt;Identifier&lt;/code&gt; holding the name
for the function and &lt;code&gt;BlockStatement&lt;/code&gt; at property &lt;code&gt;body&lt;/code&gt;. &lt;code&gt;BlockStatement&lt;/code&gt; also has a &lt;code&gt;body&lt;/code&gt;
property with an array holding a single &lt;code&gt;ExpressionStatement&lt;/code&gt; node that points to some further
descendant nodes. I encourage everyone to play around with AST explorer to see what ASTs look like
for various programming languages. You will find that there is no single AST format out there
and that it can differ based on tooling even if the programming language is the same. However
all ASTs have top-down structure to them: at the top there is program/module/document node
that is being divided into smaller components as the tree goes deeper. Document formats
like HTML and JSON can also be parsed into Abstract Syntax Trees.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-13_19.28.30.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can also save the code into file named hello.py and try printing the AST with an
&lt;a href=&#34;https://docs.python.org/3/library/ast.html&#34;&gt;&lt;code&gt;ast&lt;/code&gt;&lt;/a&gt; module from vanilla Python
distribution:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python -m ast hello.py
Module(
   body=[
      FunctionDef(
         name=&amp;#39;main&amp;#39;,
         args=arguments(
            posonlyargs=[],
            args=[],
            kwonlyargs=[],
            kw_defaults=[],
            defaults=[]),
         body=[
            Expr(
               value=Call(
                  func=Name(id=&amp;#39;print&amp;#39;, ctx=Load()),
                  args=[
                     Constant(value=&amp;#39;Hello world&amp;#39;)],
                  keywords=[]))],
         decorator_list=[]),
      If(
         test=Compare(
            left=Name(id=&amp;#39;__name__&amp;#39;, ctx=Load()),
            ops=[
               Eq()],
            comparators=[
               Constant(value=&amp;#39;__main__&amp;#39;)]),
         body=[
            Expr(
               value=Call(
                  func=Name(id=&amp;#39;main&amp;#39;, ctx=Load()),
                  args=[],
                  keywords=[]))],
         orelse=[])],
   type_ignores=[])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;LLVM tooling can also dump AST for the supported programming languages. For this example,
let us implement Hello World in C:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; argc, &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;argv) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;)argc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;)argv;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, world!&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Command to dump the AST would be the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ clang -Xclang -ast-dump -fsyntax-only hello.c
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since we included stdio.h at the top it gets parsed as well, thus producing a fairly
excessive amount of output. The code we wrote ourselves would be represented at the
bottom of the output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;`-FunctionDecl 0x7fbc89941f60 &amp;lt;hello.c:3:1, line:10:1&amp;gt; line:3:5 main &amp;#39;int (int, char **)&amp;#39;
  |-ParmVarDecl 0x7fbc89941e00 &amp;lt;col:10, col:14&amp;gt; col:14 used argc &amp;#39;int&amp;#39;
  |-ParmVarDecl 0x7fbc89941e80 &amp;lt;col:20, col:27&amp;gt; col:27 used argv &amp;#39;char **&amp;#39;
  `-CompoundStmt 0x7fbc89942260 &amp;lt;col:33, line:10:1&amp;gt;
    |-CStyleCastExpr 0x7fbc89942058 &amp;lt;line:4:3, col:9&amp;gt; &amp;#39;void&amp;#39; &amp;lt;ToVoid&amp;gt;
    | `-ImplicitCastExpr 0x7fbc89942040 &amp;lt;col:9&amp;gt; &amp;#39;int&amp;#39; &amp;lt;LValueToRValue&amp;gt; part_of_explicit_cast
    |   `-DeclRefExpr 0x7fbc89942010 &amp;lt;col:9&amp;gt; &amp;#39;int&amp;#39; lvalue ParmVar 0x7fbc89941e00 &amp;#39;argc&amp;#39; &amp;#39;int&amp;#39;
    |-CStyleCastExpr 0x7fbc899420c8 &amp;lt;line:5:3, col:9&amp;gt; &amp;#39;void&amp;#39; &amp;lt;ToVoid&amp;gt;
    | `-ImplicitCastExpr 0x7fbc899420b0 &amp;lt;col:9&amp;gt; &amp;#39;char **&amp;#39; &amp;lt;LValueToRValue&amp;gt; part_of_explicit_cast
    |   `-DeclRefExpr 0x7fbc89942080 &amp;lt;col:9&amp;gt; &amp;#39;char **&amp;#39; lvalue ParmVar 0x7fbc89941e80 &amp;#39;argv&amp;#39; &amp;#39;char **&amp;#39;
    |-CallExpr 0x7fbc899421d8 &amp;lt;line:7:3, col:27&amp;gt; &amp;#39;int&amp;#39;
    | |-ImplicitCastExpr 0x7fbc899421c0 &amp;lt;col:3&amp;gt; &amp;#39;int (*)(const char *, ...)&amp;#39; &amp;lt;FunctionToPointerDecay&amp;gt;
    | | `-DeclRefExpr 0x7fbc899420f0 &amp;lt;col:3&amp;gt; &amp;#39;int (const char *, ...)&amp;#39; Function 0x7fbc89926548 &amp;#39;printf&amp;#39; &amp;#39;int (const char *, ...)&amp;#39;
    | `-ImplicitCastExpr 0x7fbc89942218 &amp;lt;col:10&amp;gt; &amp;#39;const char *&amp;#39; &amp;lt;NoOp&amp;gt;
    |   `-ImplicitCastExpr 0x7fbc89942200 &amp;lt;col:10&amp;gt; &amp;#39;char *&amp;#39; &amp;lt;ArrayToPointerDecay&amp;gt;
    |     `-StringLiteral 0x7fbc89942148 &amp;lt;col:10&amp;gt; &amp;#39;char [15]&amp;#39; lvalue &amp;#34;Hello, world!\n&amp;#34;
    `-ReturnStmt 0x7fbc89942250 &amp;lt;line:9:3, col:10&amp;gt;
      `-IntegerLiteral 0x7fbc89942230 &amp;lt;col:10&amp;gt; &amp;#39;int&amp;#39; 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Beyond compilation, what are ASTs used for? Many developer tools rely on ASTs to implement
things like linting, automatic refactoring, vulnerability detection and code obfuscation.
Some open source license compliance tools perform AST-level checking to detect code reuse
even if some variable names have been changed.&lt;/p&gt;
&lt;p&gt;So if code is obfuscated by manipulating the AST, can it work the other way around? If we
have some obfuscated JavaScript file that implements some countermeasures against automation
(e.g. PerimeterX JS SDK) can we parse it into AST, then manipulate the AST to undo the
obfuscations and regenerate JS code in more readable form? Turns out, that is possible
and being done by developers working on advanced scraping/automation projects (e.g.
sneaker bots and advanced proxy pools). This makes AST manipulation a valuable skill
and a differentiator in scraping/automation market. Furthermore, undoing JS code
obfuscation is also valuable for bug bounty hunting, as one may find things like API
endpoints and credentials being hardcoded in the obfuscated code that nobody is supposed
to read.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Notes from The Bug Hunters Methodology - Application Analysis v1</title>
      <link>https://www.trickster.dev/post/notes-from-the-bug-hunters-methodology-application-analysis-v1/</link>
      <pubDate>Tue, 14 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/notes-from-the-bug-hunters-methodology-application-analysis-v1/</guid>
      <description>&lt;p&gt;This post will consist of notes taken from &lt;a href=&#34;https://www.youtube.com/watch?v=HmDY7w8AbR4&#34;&gt;The Bug Hunter’s Methodology: Application Analysis v1&lt;/a&gt; -
a talk by Jason Haddix at Nahamcon 2022. These notes are mostly for my own future review, but hopefully
other people will find it useful as well.&lt;/p&gt;
&lt;p&gt;Many people have been teaching how to inject an XSS payload, but not how to systematically find
vulnerabilities in the first place. Jason has created an AppSec edition of his methodology
when it became large enough to be split into recon and AppSec parts.&lt;/p&gt;
&lt;p&gt;The following books are recommended:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Web-Application-Hackers-Handbook-Exploiting/dp/1118026470/ref=sr_1_1?crid=223GVK9BGXP70&amp;amp;keywords=web+application+hackers+handbook+2&amp;amp;qid=1655126465&amp;amp;sprefix=web+application+hackers+handbook+2%2Caps%2C186&amp;amp;sr=8-1&#34;&gt;The Web Application Hacker&amp;rsquo;s Handbook 2&lt;/a&gt; - read this at least twice!&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Real-World-Bug-Hunting-Field-Hacking/dp/1593278616/ref=sr_1_1?crid=3MSIL2826QZGD&amp;amp;keywords=Real+World+bug+hunting&amp;amp;qid=1655126510&amp;amp;sprefix=real+world+bug+hunting%2Caps%2C160&amp;amp;sr=8-1&#34;&gt;Real World Bug Hunting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/OWASP/wstg&#34;&gt;OWASP Web Security Testing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Bug-Bounty-Bootcamp-Reporting-Vulnerabilities/dp/1718501544&#34;&gt;Bug Bounty Bootcamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Hacker-Playbook-Practical-Penetration-Testing/dp/1980901759/ref=sr_1_1?crid=IPJY7MVTIQO8&amp;amp;keywords=the+hackers+playbook+3&amp;amp;qid=1655126695&amp;amp;s=books&amp;amp;sprefix=the+hackers+playbook+3%2Cstripbooks%2C156&amp;amp;sr=1-1&#34;&gt;The Hacker&amp;rsquo;s Playbook 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Breaking-into-Information-Security-Crafting-ebook/dp/B019K7CMMW/ref=sr_1_1?crid=AOOXS637SAU4&amp;amp;keywords=breaking+into+information+security&amp;amp;qid=1655126724&amp;amp;s=books&amp;amp;sprefix=breaking+into+information%2Cstripbooks%2C167&amp;amp;sr=1-1&#34;&gt;Breaking into information security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Hands-Hacking-Matthew-Hickey/dp/1119561450/ref=sr_1_1?crid=W4KBT61E7RQ5&amp;amp;keywords=Hands+on+hacking&amp;amp;qid=1655126763&amp;amp;s=books&amp;amp;sprefix=hands+on+hacking%2Cstripbooks%2C159&amp;amp;sr=1-1&#34;&gt;Hands on Hacking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://payhip.com/b/wAoh&#34;&gt;Bug Bounty Playbook&lt;/a&gt; - similar to Jason&amp;rsquo;s methodology.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following resources are recommended for practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pentesterlab.com/&#34;&gt;PentesterLab&lt;/a&gt; - paid&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://portswigger.net/web-security&#34;&gt;Web Security Academy&lt;/a&gt; - paid&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.hackthebox.com/&#34;&gt;Hack The Box&lt;/a&gt; - paid&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.vulnhub.com/&#34;&gt;Vulnhub&lt;/a&gt; - free&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://owasp.org/www-project-vulnerable-web-applications-directory/&#34;&gt;OWASP Vulnerable Web Application Directory&lt;/a&gt; - free&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The free ones also are good to practice some sysadmin stuff, as you&amp;rsquo;re supposed to know that as well for bounty hunting.&lt;/p&gt;
&lt;p&gt;Recommended security content creators to follow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@danielmiessler &lt;a href=&#34;https://twitter.com/danielmiessler&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://danielmiessler.com/podcast/&#34;&gt;Podcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@stok &lt;a href=&#34;https://twitter.com/stokfredrik&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/STOKfredrik&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@brutelogic &lt;a href=&#34;https://twitter.com/brutelogic&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://brutelogic.com.br/blog/&#34;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@InsiderPhD &lt;a href=&#34;https://twitter.com/InsiderPhD&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/InsiderPhD&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@infosec_au &lt;a href=&#34;https://twitter.com/infosec_au&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://blog.assetnote.io/&#34;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@Farah_Hawaa &lt;a href=&#34;https://twitter.com/Farah_Hawaa&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/farahhawa&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@zseano &lt;a href=&#34;https://twitter.com/zseano&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/zseano&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@hacker_ &lt;a href=&#34;https://twitter.com/hacker_&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://corben.io/&#34;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@hakluke &lt;a href=&#34;https://twitter.com/hakluke&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://hakluke.com/blog/&#34;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@albinowax &lt;a href=&#34;https://twitter.com/albinowax&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://skeletonscribe.net/&#34;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@tomnomnom &lt;a href=&#34;https://twitter.com/tomnomnom&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/user/TomNomNomDotCom/featured&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@_JohnHammond &lt;a href=&#34;https://twitter.com/_JohnHammond&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/johnhammond010&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@ippsec &lt;a href=&#34;https://twitter.com/ippsec&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/ippsec&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;@nahamsec &lt;a href=&#34;https://twitter.com/nahamsec&#34;&gt;Twitter&lt;/a&gt; &lt;a href=&#34;https://www.youtube.com/c/nahamsec&#34;&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally there&amp;rsquo;s a lot of bounty hunting community on Twitter.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t be intimidated by bug bounty programs by high profile companies as they have a lot of systems online that are bound
to have things you can find. More complexity and scale - more opportunity for bugs. The same applies to bounty programs
that involve pre-testing before they are publically available, open source projects and paid software products.
There are bugs in every application.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t merely touch the surface of the app - most of the good stuff is to be found beyond the authentication.&lt;/p&gt;
&lt;p&gt;There are following layers to security testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open ports and services&lt;/li&gt;
&lt;li&gt;Web hosting software&lt;/li&gt;
&lt;li&gt;Application framework&lt;/li&gt;
&lt;li&gt;Application: custom code&lt;/li&gt;
&lt;li&gt;Application libraries&lt;/li&gt;
&lt;li&gt;Integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need to do technology profiling first. The following browser extensions can be used to find
what technologies site is based on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.whatruns.com/&#34;&gt;Whatruns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.wappalyzer.com/&#34;&gt;Wappalyzer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also &lt;a href=&#34;https://github.com/rverton/webanalyze&#34;&gt;webalyze&lt;/a&gt; - a CLI tool that can be integrated into automated scanning pipelines.&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to check that no known vulns, default credentials, framework login pages and the like are
present on the site (related to non-custom code). This can be done with Nuclei or Nessus.
Nuclei has plenty of checks for CVEs, admin panels, information leaks and the like.
There&amp;rsquo;s other tools for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Static-Flow/gofingerprint&#34;&gt;Gofingerprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/1N3/Sn1per&#34;&gt;Sn1per&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://core.intrigue.io/&#34;&gt;Intrigue Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://vulners.com/plugins#burp&#34;&gt;Vulners&lt;/a&gt; (Burp extension)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jaeles-project/jaeles&#34;&gt;Jaeles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/retirejs/retire.js/&#34;&gt;retire.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, overreliance on Nuclei can be a competitive disadvantage if it is being used
to scan what everyone else is scanning - you are merely going to find dupes. But you
can still find vulnerabilities if you use Nuclei for fresh or obscure targets.
Nuclei templates are very easy to write which enables you to start using it to find
newly published vulnerabilities.&lt;/p&gt;
&lt;p&gt;You also want to do some port scanning. Jason recommends &lt;a href=&#34;https://github.com/projectdiscovery/naabu&#34;&gt;naabu&lt;/a&gt; by Project Discovery.
It can be integrated with Nmap for service scanning.&lt;/p&gt;
&lt;p&gt;Next phase is content discovery. This is very important part that has several sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tech-based discovery&lt;/li&gt;
&lt;li&gt;COTS / PAID / OSS&lt;/li&gt;
&lt;li&gt;Custom&lt;/li&gt;
&lt;li&gt;Historical&lt;/li&gt;
&lt;li&gt;Recursive&lt;/li&gt;
&lt;li&gt;Mobile APIs&lt;/li&gt;
&lt;li&gt;Change Detection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following tools are recommended for content discovery:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://portswigger.net/bappstore/9abaa233088242e8be252cd4ff534988&#34;&gt;Turbo Intruder&lt;/a&gt; (Burp extension)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/OJ/gobuster&#34;&gt;Gobuster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ffuf/ffuf&#34;&gt;ffuf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/epi052/feroxbuster&#34;&gt;Feroxbuster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/maurosoria/dirsearch&#34;&gt;dirsearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/xmendez/wfuzz&#34;&gt;Wfuzz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These will be used to bruteforce/fuzz directories on web servers to discover stuff.
Gobuster can also find subdomains and S3 buckets.&lt;/p&gt;
&lt;p&gt;It is also important to have pre-made lists for content discovery to look for common
things likes swagger.json file and various web-server or framework specific directories.
Sometimes this lets us find unprotected configuration files with database credentials
and other sensitive information. There are some lists available that are based on e.g.
scraping robots.txt files from most popular sites and compiling a big list of things
that sites don&amp;rsquo;t want bots to acesss/index. Some lists are technology-specific, i.e.
to be used against PHP or Java sites, some are generic. Make sure to you an up-to-date
list. See: &lt;a href=&#34;https://wordlists.assetnote.io/&#34;&gt;https://wordlists.assetnote.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If an application is built on open source stuff you can use
&lt;a href=&#34;https://github.com/danielmiessler/Source2URL/blob/master/Source2URL&#34;&gt;Source2URL&lt;/a&gt; -
a Bash script that goes through source code, harvests URLs and tries to access them through
proxy (e.g. Burp).&lt;/p&gt;
&lt;p&gt;But what if it&amp;rsquo;s a paid that that is being deployed (e.g. some CRM solution)? In that
case you may be able to get access by contacting the paid software vendor and get a
demo access by pretending to be interested in buying it. This will enable you to
explore the paid software and note down various API endpoints and other URL paths
to make your own wordlist for content discovery. This custom list would be then
used against an actual target.&lt;/p&gt;
&lt;p&gt;Custom content discovery lists can be also built by spidering sites and using
Scavenger in Burp to make the list for you.&lt;/p&gt;
&lt;p&gt;Content discovery lists can also be built from historical data.
&lt;a href=&#34;https://github.com/lc/gau&#34;&gt;gau&lt;/a&gt; is a tool that will access Open Threat Exchange,
Wayback Machine and CommonCrawl for a list of URLs to all known historical
pages on the site. Output of this tool can be fed into
&lt;a href=&#34;https://github.com/ameenmaali/wordlistgen&#34;&gt;wordlistgen&lt;/a&gt; to make a wordlist
based on list of URLs. For some sites gau will yield a lot of duplicate entries
that can be cleaned up with &lt;a href=&#34;https://github.com/michael1026/trashcompactor&#34;&gt;trashcompactor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recursive bruteforcing should be used on URL paths that yield HTTP 401 response,
as if you go deep enough you may find some level that does not enforce authentication
due to misconfiguration and then you&amp;rsquo;re in. Jason mentions finding a massive
amount of unprotected private data and access to SMS panel by doing recursive
content discovery.&lt;/p&gt;
&lt;p&gt;Many times same domain is used for mobile API as well, which means that mobile
API endpoints are covered by bug bounty program. They can be discovered by
parsing APK file with tool like &lt;a href=&#34;https://github.com/dwisiswant0/apkleaks&#34;&gt;APKLeaks&lt;/a&gt;.
This will also look for hardcoded API keys and other things that may be useful
for further analysis.&lt;/p&gt;
&lt;p&gt;Another tip is to monitor for website changes by using tools like
&lt;a href=&#34;https://github.com/dgtlmoon/changedetection.io&#34;&gt;ChangeDetection&lt;/a&gt;.
Another ways to find about changes is signing up for affiliate program,
watching the conference talks by company that develops the app in question,
reading their email newsletter. In bounty hunting it is of importance to be
the first to find new vulnerabilities ASAP after the code has changed.&lt;/p&gt;
&lt;p&gt;Next stage is application analysis.&lt;/p&gt;
&lt;p&gt;There are 6 highly important questions to ask when it comes to app analysis.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;How does it pass data? Is it by resource and URL parameters or is it
a combination of HTTP method and URL path? Not getting this right may derail
your further fuzzing/scanning efforts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Where and how does it reference users? How are they identified (UUID, user ID,
email, something else?) and where is the identifier communicated? Does it
enforce consistency between session and user?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Does it have multi-tenancy or multi-user functionality? This would have
potential for access, authentication and authorization bugs as it&amp;rsquo;s very
hard for developers to enforce boundaries properly in all cases. This
can  yield IDORs, information disclosure bugs and so forth. Are there
different levels of permissions within the app? Are there ways for lower
level user to view information that only admin is supposed to access?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Does the site have unique threat model? For example, securing
stream keys and private streamer information is very important for
Twitch, as leaking this data may lead to all kinds of trouble.
This also applies to medical sites.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Has there been prominent examples of vulnerabilities and attacks
against the system being analysed? Just Google for this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How does the app and/or framework handle XSS, CSRF and injection
attacks? What are countermeasures like and how can they be bypassed?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Spider the site to ensure coverage with something like Burp or ZAP.
You need to cover as many code paths as possible. Alternatively,
you can use &lt;a href=&#34;https://github.com/hakluke/hakrawler&#34;&gt;hakrawler&lt;/a&gt;
or &lt;a href=&#34;https://github.com/jaeles-project/gospider&#34;&gt;GoSpider&lt;/a&gt; for GUI-less automation.&lt;/p&gt;
&lt;p&gt;You may also want to parse JavaScript code to find sensitive API endpoints
and maybe even API keys with tool like
&lt;a href=&#34;https://github.com/xnl-h4ck3r/xnLinkFinder&#34;&gt;xnLinkFinder&lt;/a&gt;. There&amp;rsquo;s also Burp
extension for that.&lt;/p&gt;
&lt;p&gt;Sometimes JS code will be minified or obfuscated, but will still contain
things to be find there.&lt;/p&gt;
&lt;p&gt;Sometimes old JS libraries will be used and will contain exploitable
vulnerabilities. This can be found with retire.js.&lt;/p&gt;
&lt;p&gt;Heat mapping is an idea that there are certain places in the app
that are of particular interest or potential for bugs:
upload functions, integrations with 3rd parties, external
data storage (e.g. in S3 bucket), multipart forms, account sections,
APIs and error pages.&lt;/p&gt;
&lt;p&gt;Few years ago there was research project (HUNT) to determine what URL parameters
tend to statistically correlate with specific types of vulnerabilities.
This provides a good idea on what to check first and prioritise
parameters/routes in terms of their potential for bugs.
There&amp;rsquo;s a &lt;a href=&#34;https://github.com/bugcrowd/HUNT&#34;&gt;Burp extension&lt;/a&gt; that helps with this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Building higher-order automation workflows with n8n</title>
      <link>https://www.trickster.dev/post/building-higher-order-automation-workflows-with-n8n/</link>
      <pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/building-higher-order-automation-workflows-with-n8n/</guid>
      <description>&lt;p&gt;Automation systems tends to have a temporal aspect to them as some action or the entire flow
may need to be executed at specific times, at a certain intervals. Scrapers, vulnerability
scanners and social media bots are examples of things that you may want
to run at schedule. Those using web scraping for lead generation or price
intelligence need to relaunch the web scraper often enough to get up-to-date
snapshots of data. Bug bounty hunters are interesting in performing continuous
recon that entails periodically running bunch of tools that scan targets for changes
to quickly find new vulnerabilities. Social media bot operators also need to run
their campaigns at schedule.&lt;/p&gt;
&lt;p&gt;So we use cron(1), right? Yes, if our automations are simple enough and have a single
starting point. If you just need to launch a single shell script every night then
setting up an entry on crontab in your server is perfectly fine. However, that is
not always the case.&lt;/p&gt;
&lt;p&gt;When your automation flow grow large enough and start having multiple entry points (e.g. you
may want to run the entire flow at schedule, but some of it in response to a webhook)
it will start to have a graph-theorethic aspect to it. You will have multiple
interrelated tasks (vertices) that have temporal/informational dependencies (directed
edges) between them. For example, to run data enrichment step you first need to have
a scraped dataset, thus there would be a directed edge between node representing
scraping step to a node representing enrichment step. There will be a potential
for error conditions in some of the actions, but not all errors will be serious
enough to make everything fail. When things get complex enough, it becomes
harder and harder to run bunch of tools from a shell script or crontab entry,
especially if you need some flexibility to adjust the flow on as-needed basis.
This kind of setup becomes fragile, unmanageable mess over time.&lt;/p&gt;
&lt;p&gt;Thus we need something beyond cron. We need cron-on-steroids that will
automatically run our actions in the correct order and provides a mechanism
for error handling. I found that &lt;a href=&#34;https://n8n.io/&#34;&gt;n8n&lt;/a&gt; automation platform
fits these requirements when installed on Linux/Unix environment (this could be
a small, cheap VPS or Vagrant-managed virtual machine). For instructions on how
to prepare the environment, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.trickster.dev/post/reproducible-linux-environments-with-vagrant-and-terraform/&#34;&gt;Reproducible Linux environments with Vagrant and Terraform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this environment, we also need to install some Python modules for the sample
workflow we are going to build soon:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo apt-get install python3-pip
$ sudo pip3 install -U scrapy pandas openpyxl
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We are going to build a simple n8n workflow to run a Scrapy project, a small
script for converting CSV file to Excel-compatible spreadsheet and some extra
steps to send/upload the final file that is going to be an output of the workflow.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s clone a git repo containing a Scrapy project we want to run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd /vagrant
$ git clone https://github.com/rl1987/trickster.dev-code.git
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command to run Scrapy project of interest would be:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd /vagrant/trickster.dev-code/2022-02-13-introduction-to-scrapy-framework/books_to_scrape/ &amp;amp;&amp;amp; scrapy runspider books_to_scrape/spiders/books.py -o books.csv -s LOG_ENABLED=False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We use cd(1) to make this command invariant to the current working directory
that it is being executed from. We also disable logging to standard error
because n8n has only a fairly small buffer for storing an output. Emitting
too much logs cause an error in n8n flow. If we needed logging, we could redirect
logs to a file.&lt;/p&gt;
&lt;p&gt;At &lt;code&gt;/vagrant/csv_to_xlsx.py&lt;/code&gt; we put a very basic Python script that will
convert CSV file to Excel spreadsheet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pandas &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    df &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_csv(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    df&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_excel(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.xlsx&amp;#34;&lt;/span&gt;), index&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, header&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This script expects a single argument that is supposed to be a full path to CSV
file. We would run it like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 /vagrant/csv_to_xlsx.py /vagrant/trickster.dev-code/2022-02-13-introduction-to-scrapy-framework/books_to_scrape/books.csv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Opening the resulting file in &lt;a href=&#34;https://www.visidata.org/&#34;&gt;Visidata&lt;/a&gt; allows us to quickly
verify the correctness of the transformation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.38.34.png&#34;&gt;Screenshot 1&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.38.49.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Access the n8n web interface via web browser through port 5678. Once you go through
the onboarding steps (you can skip account creation if you are going to be only one
using your n8n instance) you will be presented with a blank workflow page with only
the Start node present (it cannot be removed, but can be disconnected or disabled
when no longer needed).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_18.59.43.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.00.46.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Press the &amp;ldquo;Add Node&amp;rdquo; (plus sign) button on the top-right corner. You will be presented
with searchable list of nodes (actions) that you can use to build your flow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.50.48.png&#34;&gt;Screenshot 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We need &amp;ldquo;Execute Command&amp;rdquo; node that we choose from the list.
Upon choosing this node we will be presented with modal screen to set it up. We put
the command to run the Scrapy spider into the text field for the command.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-12_15.19.09.png&#34;&gt;Screenshot 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click outside the modal view to dismiss it. Now we have new, currently disconnected
node on the screen.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.57.28.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this point we don&amp;rsquo;t worry about cronjobs and the like, so let&amp;rsquo;s connnect it to
Start node by dragging the line between them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.58.09.png&#34;&gt;Screenshot 8&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_19.58.22.png&#34;&gt;Screenshot 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is already a very simple n8n flow that we can launch by pressing &amp;ldquo;Execute Workflow&amp;rdquo;
button on the bottom.&lt;/p&gt;
&lt;p&gt;Let us add new node for file conversion step. If you do this with the first node highlighted
n8n will automatically connect it for you. To avoid confusion, we can rename the nodes
to something more descriptive than their initial names.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.16.23.png&#34;&gt;Screenshot 10&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Furthermore, we can click the little setting button and write down some notes about the node
that can optionally be shown in the flow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.21.17.png&#34;&gt;Screenshot 11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another thing we can enable in the node settings is retries on error. This is not needed
for the conversion step, but scraping step may fail due to the site being down or some
other reasons beyond our control. Thus you may want to enable retrying with number of
retries and delay between retries being some values that are sensible for your use case.
Some actions in your flow may not be critical and of little importance. You may want to
switch on the &amp;ldquo;Continue On Fail&amp;rdquo; switch for these actions. That way, an error in some
unimportant step does not bring down the entire workflow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.22.21.png&#34;&gt;Screenshot 12&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now let us send an email. We can use either generic Mail node or one of the nodes
for integrating into email provider (Gmail, Mailchimp, etc.). To demonstrate an
integration into external system, we pick SendGrid node.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.38.22.png&#34;&gt;Screenshot 13&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we need to add credentials. Click credential selection drop-down and choose
&amp;ldquo;Create New&amp;rdquo;. This will present you a new modal view to put your SendGrid API key into.
Once we save the API key, we can choose the SendGrid account in the drop down. Now
we can fill the form with details about email we want to be sending. Note that credentials
are stored separately from the workflows and can be edited by accessing menu option on the
side.  But one thing is missing - we need the binary data for the attachment. Thus we put this
node aside for a moment and add a &amp;ldquo;Read Binary File&amp;rdquo; node. We set it up to read XLSX file
from the conversion step, then wire it up between conversion and email sending nodes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.49.51.png&#34;&gt;Screenshot 14&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.51.33.png&#34;&gt;Screenshot 15&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we come back to SendGrid node, scroll to the bottom of the form, click &amp;ldquo;Add fields&amp;rdquo;
and pick &amp;ldquo;Attachments&amp;rdquo; from the list. New textfield will appear. We need to set up data transfer
here. Put the property name from previous node in here. By default, this will be &lt;code&gt;data&lt;/code&gt;.
Execute the node to test it - this will execute all the previous nodes in the flow.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_20.52.46.png&#34;&gt;Screenshot 16&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.11.54.png&#34;&gt;Screenshot 17&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We also want to upload our spreadsheet file to S3-compatible Digital Ocean Spaces bucket.
To set it up on Digital Ocean side, create Spaces bucket and follow the
&lt;a href=&#34;https://docs.digitalocean.com/reference/api/create-personal-access-token/&#34;&gt;procedure&lt;/a&gt;
to get API credentials for accessing it. n8n provides two kinds of nodes for S3 API -
AWS-specific one and generic variant. We choose the latter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.18.07.png&#34;&gt;Screenshot 18&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Like we did with SendGrid, we fill in API access credentials (endpoint and region
fields must match the DO region your bucket is created in). Then we make sure
that Resource is selected to be File and Operation is Upload. We put out bucket
and file names into their respective text fields. We set Binary Property to &lt;code&gt;data&lt;/code&gt;
as that is what is being set in previous node that reads XLSX file.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.24.08.png&#34;&gt;Screenshot 19&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.35.59.png&#34;&gt;Screenshot 20&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we can verify that file indeed gets uploaded by executing the node and checking
the bucket.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.40.39.png&#34;&gt;Screenshot 21&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We got the flow working, but we need it triggered based on time. Thus we add the Cron
node. For the sake of the example, we set trigger time to every midnight (we could
have more elaborate schedule if we wanted).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.43.45.png&#34;&gt;Screenshot 22&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We disconnect the Start node and put the Cron node in its place.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.46.08.png&#34;&gt;Screenshot 23&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.46.25.png&#34;&gt;Screenshot 24&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To tidy things up and to provide visual aid for finding where does the workflow
end, we connect the &amp;ldquo;No operation, do nothing&amp;rdquo; node to outputs of SendGrid and S3
nodes. This is technically not necessary and does not change the functionality in
any way, but will make bigger flows more readable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-12_15.44.14.png&#34;&gt;Screenshot 25&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But what if this entire thing fails due to some critical error? We would like to
know about this. We can set up another workflow that would be executed to notify
us about error condition. This can be done by entering workflow settings through side
pane by choosing &amp;ldquo;Settings&amp;rdquo; under &amp;ldquo;Workflows&amp;rdquo;. We can create a new workflow for
error handling (there&amp;rsquo;s some template ones from n8n communnity) and set it here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-06-11_21.55.40.png&#34;&gt;Screenshot 26&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we wanted the workflow to start based on some user action we could use other trigger
nodes than Cron. There are nodes for web hook, email being received and so on.&lt;/p&gt;
&lt;p&gt;We have shown how an interrelated collection of actions can be made more tractable
by using n8n system to rework into more visual form. Furthermore, error handling
mechanisms are provided at action level and at workflow level. We also have
pre-developed components for integrations. Thus n8n can be used as flexible
way to build higher order automation workflows that consist of launching
Python scripts or other tools and composing them into larger system.&lt;/p&gt;
&lt;p&gt;I do not suggest people to buy into No Code hype however. n8n alone would not be a right
tool for more fine-grained, lower level kind of engineering, such as reproducing
complex API flows to automate against automation-hostile systems. Many talkers
have been talking about how No Code solutions that you may not even need to host yourself
(at the cost of vendor lock-in) make it easier to build internal tools or even
SaaS apps. That is only true at sufficiently low levels of complexity and scale.
Once things get hard enough, it becomes necessary to think in computational terms.
Things like n8n do have their value, but overreliance on drag-and-dropping your
business logic on some GUI will constrain you to an environment and way of working
that is grossly suboptimal for dealing with complexity. There&amp;rsquo;s a reason why we
did not reproduce the stuff that goes on in the Scrapy project by reimplementing
it with n8n nodes despite n8n having some capability to scrape sites and our
Scrapy project being very basic.&lt;/p&gt;
&lt;p&gt;I cannot stress this enough: if you want to build software systems, learn to fucking code!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Importing Shopify product info programmatically</title>
      <link>https://www.trickster.dev/post/importing-shopify-product-info-programmatically/</link>
      <pubDate>Sat, 04 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/importing-shopify-product-info-programmatically/</guid>
      <description>&lt;p&gt;Sometimes one would scrape eCommerce product data for the purpose of reselling these
products. For example, a retail ecommerce company might be sourcing their products
from a distributor that does not provide an easy way to integrate into Shopify
store. This problem can be solved through web scraping. Once data is scraped,
it can be imported into Shopify store.&lt;/p&gt;
&lt;p&gt;One way to do that is to wrangle the product dataset into file(s) that heed
the &lt;a href=&#34;https://help.shopify.com/en/manual/products/import-export/using-csv&#34;&gt;Shopify product CSV schema&lt;/a&gt;
and import it via Shopify store admin dashboard. However this entails
human intervention and may be considered to be an inferior solution when
compared to something fully automatic.&lt;/p&gt;
&lt;p&gt;Good news is that Shopify does provide a public API for store administration
tasks, including product importing and updating. We can integrate this API with
our web scraping solution.&lt;/p&gt;
&lt;p&gt;We will be showing how the REST version of &lt;a href=&#34;https://shopify.dev/api/admin-rest&#34;&gt;Shopify Admin API&lt;/a&gt;
can be used to upload pre-scraped product dataset. But first, we need to get
some API credentials that we can use. The following assumes you having a
development store that you can play with.&lt;/p&gt;
&lt;p&gt;We need to create something called Custom App. In your Shopify admin dashboard, go to Apps tab
and press &amp;ldquo;Develop apps&amp;rdquo; button on the top. In the next page, press &amp;ldquo;Create an app&amp;rdquo; button on the top.
The modal view will ask for app name. Once app is created, we need to configure API scopes.
Press &amp;ldquo;Configure Admin API scopes&amp;rdquo; button in the new page. Bunch of checkboxes will be presented to you.
For the purpose of this task, we need to choose &lt;code&gt;read_products&lt;/code&gt; and &lt;code&gt;write_products&lt;/code&gt;. Now press the
&amp;ldquo;Save&amp;rdquo; button on the bottom of the page. Now press the &amp;ldquo;Install app&amp;rdquo; button on the top of the page.
You will be given a chance to see and copy the Admin API access token. Make sure you save it somewhere
else before clicking away from the page. Don&amp;rsquo;t worry about API key and secret - we won&amp;rsquo;t be needing
them here.&lt;/p&gt;
&lt;p&gt;Let us get familiar with the Shopify product data model. There are three entities of interest to us:
product, product variant and product image. Product is the parent object that may have one or more
variant associated to it and some images as well. Furthemore, product variants are allowed to make
secondary association to product images (e.g. product variant of specific color will point to
corresponding image). For the exact details, see the following pages on Shopify developer portal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product - &lt;a href=&#34;https://shopify.dev/api/admin-rest/2022-04/resources/product#top&#34;&gt;https://shopify.dev/api/admin-rest/2022-04/resources/product#top&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ProductVariant - &lt;a href=&#34;https://shopify.dev/api/admin-rest/2022-04/resources/product-variant#top&#34;&gt;https://shopify.dev/api/admin-rest/2022-04/resources/product-variant#top&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ProductImage - &lt;a href=&#34;https://shopify.dev/api/admin-rest/2022-04/resources/product-image#top&#34;&gt;https://shopify.dev/api/admin-rest/2022-04/resources/product-image#top&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will be importing product CSV file that has the following columns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt; - product title.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asin&lt;/code&gt; - Amazon product ID.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;price&lt;/code&gt; - price in GBP, with pound character included. Sometimes range of prices.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brand&lt;/code&gt; - name of brand that sometimes need a little cleanup.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;product_details&lt;/code&gt; - product description.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;breadcrumbs&lt;/code&gt; - slash-separated list of breadcrumb strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;images_list&lt;/code&gt; - JSON array containing URLs to product images&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us discuss the following code that implements the importing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;API_TOKEN &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;check_if_present&lt;/span&gt;(store_name, title, asin):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Shopify-Access-Token&amp;#34;&lt;/span&gt;: API_TOKEN}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.myshopify.com/admin/api/2022-04/products.json&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        store_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: title}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(api_url, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;products&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; len(json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;products&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; prod_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;products&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        variant &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; prod_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;variants&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; variant&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; asin:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;import_product&lt;/span&gt;(store_name, product):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Shopify-Access-Token&amp;#34;&lt;/span&gt;: API_TOKEN}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    api_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.myshopify.com/admin/api/2022-04/products.json&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        store_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;product&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;body_html&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;product_details&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vendor&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brand&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visit the &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; Store&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;breadcrumbs&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;published&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;options&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Size&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;product_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Shoes&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;variants&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;asin&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; - &amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;£&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;requires_shipping&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; img_url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(product&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images_list&amp;#34;&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        payload[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;product&amp;#34;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;: img_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;position&amp;#34;&lt;/span&gt;: i,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        i &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(api_url, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;payload, timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;201&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error: Import to shopify failed!&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; API_TOKEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    store_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enter store name (part of subdomain before .myshopify.com): &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_token.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    API_TOKEN &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; t_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amazon_uk_shoes_dataset.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_reader &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictReader(in_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; csv_reader:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; check_if_present(store_name, row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;), row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;asin&amp;#34;&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            import_product(store_name, row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; (&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;) already present - skipping...&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;), row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;asin&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &lt;code&gt;main()&lt;/code&gt; function we read CSV file line by line. We skips rows that do not have
price defined.&lt;/p&gt;
&lt;p&gt;Next, we call &lt;code&gt;check_if_present()&lt;/code&gt; function that will check if product matching title from
CSV with variant SKU matching &lt;code&gt;asin&lt;/code&gt; field is already present on the store. This is necessary
to prevent duplicate product listings from appearing on the store when script is launched
multiple times. If pre-existing product listing is not found, we call the &lt;code&gt;import_product()&lt;/code&gt;
function that will generate JSON payload describing the product in question (with some data
cleanup) and launch a HTTP POST to the corresponding API endpoint. In both cases we must
set &lt;code&gt;X-Shopify-Access-Token&lt;/code&gt; with API token we got from Shopify admin dashboard to
authorize the request. Since this is POST request, we must check that response status
code is 201 in case it&amp;rsquo;s all good.&lt;/p&gt;
&lt;p&gt;You may notice that &lt;code&gt;time.sleep()&lt;/code&gt; is called in the code to make it slower. That&amp;rsquo;s because
of Shopify &lt;a href=&#34;https://shopify.dev/api/admin-rest#rate_limits&#34;&gt;rate limits&lt;/a&gt;. We must not go
faster than two requests per seconds. That is indeed quite slow. Importing non-trivial number
of products will take some time.&lt;/p&gt;
&lt;p&gt;Shopify also has some &lt;a href=&#34;https://shopify.dev/api/admin-rest#client_libraries&#34;&gt;client libraries&lt;/a&gt;
for using it&amp;rsquo;s API in a few programming languages, including Python. Shopify &lt;a href=&#34;https://shopify.github.io/shopify_python_api/&#34;&gt;Python
SDK&lt;/a&gt; is based on &lt;a href=&#34;https://github.com/Shopify/pyactiveresource&#34;&gt;pyactiveresource&lt;/a&gt;
library that attempts to extend the concept of Object-Relational Mapping to RESTful APIs.
At surface, this seems like an improvement because now we have OOP API that would
allow dealing with API entities in cleaner way. Instead of constructing that JSON payload
dictionary we did before we could instantiate &lt;code&gt;Product&lt;/code&gt; object and set a property for each
field. However, this library is not without its own problems. Since it calls the very same
REST API we still need to deal with slowness of data importing. Furthermore, it does not
have a proper API session object and relies on global state fairly heavily. Currently the
documentation for this library is outdated and discusses now-deprecated way of setting
up Private App to access the API instead of the modern way we used in the code above.&lt;/p&gt;
&lt;p&gt;Shopify also has &lt;a href=&#34;https://shopify.dev/api/admin-graphql&#34;&gt;GraphQL API&lt;/a&gt; with
&lt;a href=&#34;https://shopify.dev/api/usage/bulk-operations/imports&#34;&gt;bulk import process&lt;/a&gt; that entails
converting data to JSONL file, uploading it to S3 bucket and calling GraphQL API to get it
processed. But that&amp;rsquo;s a topic for another day.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Turning Scrapy spider into API with ScrapyRT</title>
      <link>https://www.trickster.dev/post/turning-scrapy-spider-into-api-with-scrapyrt/</link>
      <pubDate>Sat, 04 Jun 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/turning-scrapy-spider-into-api-with-scrapyrt/</guid>
      <description>&lt;p&gt;Scrapy framework provides many benefits over regular Python scripts when it comes
to developing web scrapers of non-trivial complexity. However Scrapy by itself does
provide a direct way to integrate your web scraper into larger system that may need
some on-demand scraping (e.g. price comparison website).
&lt;a href=&#34;https://github.com/scrapinghub/scrapyrt&#34;&gt;ScrapyRT&lt;/a&gt; is a Scrapy extension
that was developed by Zyte to address this limitation. Just like Scrapy itself,
it is trivially installable through PIP. ScrapyRT exposes your Scrapy project
(not just spiders, but also pipelines, middlewares and extensions) through
HTTP API that you can integrate into your systems.&lt;/p&gt;
&lt;p&gt;To show how ScrapyRT can be used, we are going to rely on
&lt;a href=&#34;https://github.com/rl1987/trickster.dev-code/tree/main/2022-02-13-introduction-to-scrapy-framework/books_to_scrape&#34;&gt;&lt;code&gt;books_to_scrape&lt;/code&gt;&lt;/a&gt;
Scrapy project that was developed to demonstrate Scrapy project development.&lt;/p&gt;
&lt;p&gt;Launching ScrapyRT entails running a single command in Scrapy project root
directory:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd books_to_scrape/
$ scrapyrt
2022-06-04 10:33:17+0200 [-] Log opened.
2022-06-04 10:33:17+0200 [-] Site starting on 9080
2022-06-04 10:33:17+0200 [-] Starting factory &amp;lt;twisted.web.server.Site object at 0x10aa7fa90&amp;gt;
2022-06-04 10:33:17+0200 [-] Running with reactor: SelectReactor. 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default it binds the socket to local host interface. This will make it reachable
by software running on your local machine, but not by external clients that we may
want to expose the API to. Easy fix is to make it bind to wildcard IP address (0.0.0.0)
by providing the &lt;code&gt;--ip&lt;/code&gt; command line argument:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapyrt --ip 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running ScrapyRT like this make it accessible through all IP addresses the IP
host can accept incoming traffic at.&lt;/p&gt;
&lt;p&gt;Now let us use cURL to launch the spider:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;http://127.0.0.1:9080/crawl.json?spider_name=books&amp;amp;url=http://books.toscrape.com&amp;amp;callback=parse_categories&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We called the API at &lt;code&gt;/crawl.json&lt;/code&gt; endpoint with arguments for spider name,
URL to start scraping at and in this case we also need to provide callback
method name. Some seconds later we receive JSON response that can be piped
into jq(1) or &lt;code&gt;python3 -m json.tool&lt;/code&gt; for pretty-printing:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;http://127.0.0.1:9080/crawl.json?spider_name=books&amp;amp;url=http://books.toscrape.com&amp;amp;callback=parse_categories&amp;#34; | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1615k  100 1615k    0     0  54798      0  0:00:30  0:00:30 --:--:--  416k
{
  &amp;#34;status&amp;#34;: &amp;#34;ok&amp;#34;,
  &amp;#34;items&amp;#34;: [
    {
      &amp;#34;title&amp;#34;: &amp;#34;The Rise of Theodore Roosevelt (Theodore Roosevelt #1)&amp;#34;,
      &amp;#34;upc&amp;#34;: &amp;#34;1a5044d233936b1a&amp;#34;,
      &amp;#34;description&amp;#34;: &amp;#34;Selected by the Modern Library as one of the 100 best nonfiction books of all timeDescribed by the Chicago Tribune as \&amp;#34;a classic,\&amp;#34; The Rise of Theodore Roosevelt stands as one of the greatest biographies of our time. The publication of The Rise of Theodore Roosevelt on September 14th, 2001 marks the 100th anniversary of Theodore Roosevelt becoming president.&amp;#34;,
      &amp;#34;price_excl_tax&amp;#34;: &amp;#34;42.57&amp;#34;,
      &amp;#34;price_incl_tax&amp;#34;: &amp;#34;42.57&amp;#34;,
      &amp;#34;availability&amp;#34;: &amp;#34;3&amp;#34;,
      &amp;#34;n_reviews&amp;#34;: &amp;#34;0&amp;#34;
    },
...
    {
      &amp;#34;title&amp;#34;: &amp;#34;Trespassing Across America: One Man&amp;#39;s Epic, Never-Done-Before (and Sort of Illegal) Hike Across the Heartland&amp;#34;,
      &amp;#34;upc&amp;#34;: &amp;#34;2173a2ce35f6ace1&amp;#34;,
      &amp;#34;description&amp;#34;: &amp;#34;Told with sincerity, humor, and wit,Trespassing Across Americais both a fascinating account of one man’s remarkable journey along the Keystone XL pipeline and a meditation on climate change, the beauty of the natural world, and the extremes to which we can push ourselves—both physically and mentally. It started as a far-fetched idea—to hike the entire length of the propose Told with sincerity, humor, and wit, Trespassing Across America is both a fascinating account of one man’s remarkable journey along the Keystone XL pipeline and a meditation on climate change, the beauty of the natural world, and the extremes to which we can push ourselves—both physically and mentally.  It started as a far-fetched idea—to hike the entire length of the proposed route of the Keystone XL pipeline. But in the months that followed, it grew into something more for Ken Ilgunas. It became an irresistible adventure—an opportunity not only to draw attention to global warming but also to explore his personal limits. So in September 2012, he strapped on his backpack, stuck out his thumb on the interstate just north of Denver, and hitchhiked 1,500 miles to the Alberta tar sands. Once there, he turned around and began his 1,700-mile trek to the XL’s endpoint on the Gulf Coast of Texas, a journey he would complete entirely on foot, walking almost exclusively across private property.Both a travel memoir and a reflection on climate change, Trespassing Across America is filled with colorful characters, harrowing physical trials, and strange encounters with the weather, terrain, and animals of America’s plains. A tribute to the Great Plains and the people who live there, Ilgunas’s memoir grapples with difficult questions about our place in the world: What is our personal responsibility as stewards of the land? As members of a rapidly warming planet? As mere individuals up against something as powerful as the fossil fuel industry? Ultimately, Trespassing Across America is a call to embrace the belief that a life lived not half wild is a life only half lived. ...more&amp;#34;,
      &amp;#34;price_excl_tax&amp;#34;: &amp;#34;53.51&amp;#34;,
      &amp;#34;price_incl_tax&amp;#34;: &amp;#34;53.51&amp;#34;,
      &amp;#34;availability&amp;#34;: &amp;#34;3&amp;#34;,
      &amp;#34;n_reviews&amp;#34;: &amp;#34;0&amp;#34;
    }
  ],
  &amp;#34;items_dropped&amp;#34;: [],
  &amp;#34;stats&amp;#34;: {
    &amp;#34;downloader/request_bytes&amp;#34;: 416236,
    &amp;#34;downloader/request_count&amp;#34;: 1132,
    &amp;#34;downloader/request_method_count/GET&amp;#34;: 1132,
    &amp;#34;downloader/response_bytes&amp;#34;: 4427209,
    &amp;#34;downloader/response_count&amp;#34;: 1132,
    &amp;#34;downloader/response_status_count/200&amp;#34;: 1131,
    &amp;#34;downloader/response_status_count/404&amp;#34;: 1,
    &amp;#34;dupefilter/filtered&amp;#34;: 1051,
    &amp;#34;elapsed_time_seconds&amp;#34;: 30.023132,
    &amp;#34;finish_reason&amp;#34;: &amp;#34;finished&amp;#34;,
    &amp;#34;finish_time&amp;#34;: &amp;#34;2022-06-04 08:55:20&amp;#34;,
    &amp;#34;httpcompression/response_bytes&amp;#34;: 25118716,
    &amp;#34;httpcompression/response_count&amp;#34;: 1132,
    &amp;#34;item_scraped_count&amp;#34;: 1000,
    &amp;#34;log_count/DEBUG&amp;#34;: 2133,
    &amp;#34;log_count/INFO&amp;#34;: 9,
    &amp;#34;memusage/max&amp;#34;: 82591744,
    &amp;#34;memusage/startup&amp;#34;: 82591744,
    &amp;#34;request_depth_max&amp;#34;: 51,
    &amp;#34;response_received_count&amp;#34;: 1132,
    &amp;#34;robotstxt/request_count&amp;#34;: 1,
    &amp;#34;robotstxt/response_count&amp;#34;: 1,
    &amp;#34;robotstxt/response_status_count/404&amp;#34;: 1,
    &amp;#34;scheduler/dequeued&amp;#34;: 1131,
    &amp;#34;scheduler/dequeued/memory&amp;#34;: 1131,
    &amp;#34;scheduler/enqueued&amp;#34;: 1131,
    &amp;#34;scheduler/enqueued/memory&amp;#34;: 1131,
    &amp;#34;start_time&amp;#34;: &amp;#34;2022-06-04 08:54:50&amp;#34;
  },
  &amp;#34;spider_name&amp;#34;: &amp;#34;books&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not only we got all the scraped items, but we also received some statistics
on scope and duration of the scraping job.&lt;/p&gt;
&lt;p&gt;However, we may want to have more fine-grained control over scraping to have
only single page scraped. To achieve this, we need to pass JSON payload to
ScrapyRT API. Let me show how this can be done through Python REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.9.12 (main, Mar 26 2022, 15:51:15)
[Clang 13.1.6 (clang-1316.0.21.2)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; api_url = &amp;#39;http://localhost:9080/crawl.json&amp;#39;
&amp;gt;&amp;gt;&amp;gt; page_url = &amp;#39;https://books.toscrape.com/catalogue/throwing-rocks-at-the-google-bus-how-growth-became-the-enemy-of-prosperity_948/index.html&amp;#39;
&amp;gt;&amp;gt;&amp;gt; payload = { &amp;#39;request&amp;#39;: { &amp;#39;url&amp;#39;: page_url, &amp;#39;callback&amp;#39;: &amp;#39;parse_book_page&amp;#39; }, &amp;#39;spider_name&amp;#39;: &amp;#39;books&amp;#39;}
&amp;gt;&amp;gt;&amp;gt; resp = requests.post(api_url, json=payload)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;Response [200]&amp;gt;
&amp;gt;&amp;gt;&amp;gt; from pprint import pprint
&amp;gt;&amp;gt;&amp;gt; pprint(resp.json())
{&amp;#39;items&amp;#39;: [{&amp;#39;availability&amp;#39;: &amp;#39;16&amp;#39;,
            &amp;#39;description&amp;#39;: &amp;#39;Capital in the Twenty-First Century meets The &amp;#39;
                           &amp;#39;Second Machine Age in this stunning and optimistic &amp;#39;
                           &amp;#39;tour de force on the promise and peril of the &amp;#39;
                           &amp;#39;digital economy, from one of the most brilliant &amp;#39;
                           &amp;#39;social critics of our time. Digital technology was &amp;#39;
                           &amp;#39;supposed to usher in a new age of endless &amp;#39;
                           &amp;#39;prosperity, but so far it has been used to put &amp;#39;
                           &amp;#39;industrial capitalism on steroids, makin Capital &amp;#39;
                           &amp;#39;in the Twenty-First Century meets The Second &amp;#39;
                           &amp;#39;Machine Age in this stunning and optimistic tour &amp;#39;
                           &amp;#39;de force on the promise and peril of the digital &amp;#39;
                           &amp;#39;economy, from one of the most brilliant social &amp;#39;
                           &amp;#39;critics of our time. Digital technology was &amp;#39;
                           &amp;#39;supposed to usher in a new age of endless &amp;#39;
                           &amp;#39;prosperity, but so far it has been used to put &amp;#39;
                           &amp;#39;industrial capitalism on steroids, making it &amp;#39;
                           &amp;#39;harder for people and businesses to keep up. &amp;#39;
                           &amp;#39;Social networks surrender their original missions &amp;#39;
                           &amp;#39;to more immediately profitable data mining, while &amp;#39;
                           &amp;#39;brokerage houses abandon value investing for &amp;#39;
                           &amp;#39;algorithms that drain markets and our 401ks &amp;#39;
                           &amp;#39;alike--all tactics driven by the need to stoke &amp;#39;
                           &amp;#39;growth by any means necessary. Instead of taking &amp;#39;
                           &amp;#39;this opportunity to reprogram our economy for &amp;#39;
                           &amp;#39;sustainability, we have doubled down on growth as &amp;#39;
                           &amp;#39;its core command. We have reached the limits of &amp;#39;
                           &amp;#39;this approach. We must escape the growth trap, &amp;#39;
                           &amp;#39;once and for all.\xa0Media scholar and technology &amp;#39;
                           &amp;#34;author Douglas Rushkoff--one of today&amp;#39;s most &amp;#34;
                           &amp;#39;original and influential thinkers--argues for a &amp;#39;
                           &amp;#39;new economic program that utilizes the unique &amp;#39;
                           &amp;#39;distributive power of the internet while breaking &amp;#39;
                           &amp;#39;free of the winner-take-all system the growth trap &amp;#39;
                           &amp;#39;leaves in its wake. Drawing on sources both &amp;#39;
                           &amp;#39;contemporary and historical, Rushkoff pioneers a &amp;#39;
                           &amp;#39;new understanding of the old economic paradigm, &amp;#39;
                           &amp;#39;from central currency to debt to corporations and &amp;#39;
                           &amp;#39;labor.Most importantly, he offers a series of &amp;#39;
                           &amp;#39;practical steps for businesses, consumers, &amp;#39;
                           &amp;#39;investors, and policymakers to remake the economic &amp;#39;
                           &amp;#39;operating system from the inside out--and prosper &amp;#39;
                           &amp;#39;along the way. Instead of boycotting Wal-Mart or &amp;#39;
                           &amp;#39;overtaxing the wealthy, we simply implement &amp;#39;
                           &amp;#39;strategies that foster the creation of value by &amp;#39;
                           &amp;#39;stakeholders other than just ourselves. From our &amp;#39;
                           &amp;#39;currency to our labor to the corporation, every &amp;#39;
                           &amp;#39;aspect of the economy can be reprogrammed with &amp;#39;
                           &amp;#39;minimal disruption to create a more equitably &amp;#39;
                           &amp;#39;distributed prosperity for all.Inspiring and &amp;#39;
                           &amp;#39;challenging, Throwing Rocks at the Google Bus &amp;#39;
                           &amp;#39;provides a pragmatic, optimistic, and &amp;#39;
                           &amp;#39;human-centered model for economic progress in the &amp;#39;
                           &amp;#39;digital age. ...more&amp;#39;,
            &amp;#39;n_reviews&amp;#39;: &amp;#39;0&amp;#39;,
            &amp;#39;price_excl_tax&amp;#39;: &amp;#39;31.12&amp;#39;,
            &amp;#39;price_incl_tax&amp;#39;: &amp;#39;31.12&amp;#39;,
            &amp;#39;title&amp;#39;: &amp;#39;Throwing Rocks at the Google Bus: How Growth Became the &amp;#39;
                     &amp;#39;Enemy of Prosperity&amp;#39;,
            &amp;#39;upc&amp;#39;: &amp;#39;7858914fad68493c&amp;#39;}],
 &amp;#39;items_dropped&amp;#39;: [],
 &amp;#39;spider_name&amp;#39;: &amp;#39;books&amp;#39;,
 &amp;#39;stats&amp;#39;: {&amp;#39;downloader/request_bytes&amp;#39;: 565,
           &amp;#39;downloader/request_count&amp;#39;: 2,
           &amp;#39;downloader/request_method_count/GET&amp;#39;: 2,
           &amp;#39;downloader/response_bytes&amp;#39;: 4883,
           &amp;#39;downloader/response_count&amp;#39;: 2,
           &amp;#39;downloader/response_status_count/200&amp;#39;: 1,
           &amp;#39;downloader/response_status_count/404&amp;#39;: 1,
           &amp;#39;elapsed_time_seconds&amp;#39;: 0.843302,
           &amp;#39;finish_reason&amp;#39;: &amp;#39;finished&amp;#39;,
           &amp;#39;finish_time&amp;#39;: &amp;#39;2022-06-04 09:12:00&amp;#39;,
           &amp;#39;httpcompression/response_bytes&amp;#39;: 22447,
           &amp;#39;httpcompression/response_count&amp;#39;: 2,
           &amp;#39;item_scraped_count&amp;#39;: 1,
           &amp;#39;log_count/DEBUG&amp;#39;: 3,
           &amp;#39;log_count/INFO&amp;#39;: 9,
           &amp;#39;memusage/max&amp;#39;: 85594112,
           &amp;#39;memusage/startup&amp;#39;: 85594112,
           &amp;#39;response_received_count&amp;#39;: 2,
           &amp;#39;robotstxt/request_count&amp;#39;: 1,
           &amp;#39;robotstxt/response_count&amp;#39;: 1,
           &amp;#39;robotstxt/response_status_count/404&amp;#39;: 1,
           &amp;#39;scheduler/dequeued&amp;#39;: 1,
           &amp;#39;scheduler/dequeued/memory&amp;#39;: 1,
           &amp;#39;scheduler/enqueued&amp;#39;: 1,
           &amp;#39;scheduler/enqueued/memory&amp;#39;: 1,
           &amp;#39;start_time&amp;#39;: &amp;#39;2022-06-04 09:12:00&amp;#39;},
 &amp;#39;status&amp;#39;: &amp;#39;ok&amp;#39;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As expected, we got a single item with scraped data from page we pointed at in
our request. Some of the arguments we previously passed as URL parameters
are now part of JSON payload.&lt;/p&gt;
&lt;p&gt;One thing we may want to do is to run ScrapyRT in Docker container
to expose Scrapy project through API. Zyte provides an official &lt;a href=&#34;https://hub.docker.com/r/scrapinghub/scrapyrt&#34;&gt;ScrapyRT image&lt;/a&gt;
that is unfortunately neglected and still uses Python 2.7 at the &lt;code&gt;latest&lt;/code&gt; tag.
However, it is fairly simple to dockerize ScrapyRT without relying on it.
We can create the following Dockerfile at the same directory that
has scrapy.cfg file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; python:3-slim&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; pip3 install scrapy scrapyrt&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; mkdir /books_to_scrape&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ADD&lt;/span&gt; . /books_to_scrape&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;WORKDIR&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; /books_to_scrape&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;EXPOSE&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; 9080&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENTRYPOINT&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scrapyrt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-i&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To learn more about ScrapyRT, see the &lt;a href=&#34;https://scrapyrt.readthedocs.io/en/stable/&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Key limitation to keep in mind is that ScrapyRT is not designed to support
long crawls. It is meant to be used to help software outside Scrapy to scrape
a single page or small number of pages. This solution effectively turns web
sites into RESTful APIs. However if you need a way to launch large scraping
jobs through REST API you should consider &lt;a href=&#34;https://github.com/scrapy/scrapyd&#34;&gt;Scrapyd&lt;/a&gt;
API server that was designed to provide a way to manage scraping jobs from
outside Scrapy framework. To upload and run your Scrapy project in the
cloud you may want to consider either &lt;a href=&#34;https://www.zyte.com/scrapy-cloud/&#34;&gt;Scrapy Cloud&lt;/a&gt;
SaaS solution or &lt;a href=&#34;https://github.com/my8100/scrapydweb&#34;&gt;Scrapydweb&lt;/a&gt; for
self-hosted option.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scraping Instagram API with Instauto</title>
      <link>https://www.trickster.dev/post/scraping-instagram-api-with-instauto/</link>
      <pubDate>Mon, 23 May 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scraping-instagram-api-with-instauto/</guid>
      <description>&lt;p&gt;Instagram scraping is of interest to OSINT and growth hacking communities, but can be
rather challenging. If we proceed with using browser automation for this purpose we
risk triggering client-side countermeasures. Using private API is a safer, more performant
approach, but has it&amp;rsquo;s own challenges. Instagram implements complex API flows that involve
HMACs and other cryptographic techniques for extra security. When it comes to implementing
Instagram scraping code, one does not simply use mitmproxy or Chrome DevTools to intercept
API requests so that we could reproduce them programmatically. Significantly deeper reverse
engineering is needed: we would either try to make sense of the obfuscated frontend
JS code or decompiling/disassembling mobile app binaries to understand how exactly API
flows are implemented. Both of these approaches are labour intensive and require some fairly
deep, specialised knowledge.&lt;/p&gt;
&lt;p&gt;Luckily for us, there&amp;rsquo;s something called &lt;a href=&#34;https://instauto.readthedocs.io/en/latest/index.html&#34;&gt;Instauto&lt;/a&gt;.
Instauto is a Python module that implements an unofficial client library for private Instagram
API. It saves us a mountain of work that we would have to do if we wanted to tap into
private Instagram API for gray hat use cases.&lt;/p&gt;
&lt;p&gt;There are three major parts of Instauto:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;instauto.api&lt;/code&gt; - low level API client that we can use directly and that is used by rest of
the Instauto codebase.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;instauto.helpers&lt;/code&gt; - higher level API client that defines object-oriented API over raw Python
dictionaries/lists that it gets from &lt;code&gt;instauto.api&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;instauto.bot&lt;/code&gt; - high-level utility code for Instagram botting. It builds upon &lt;code&gt;instauto.helpers&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before we proceed with programming, we need to create or buy a scraper account that is meant
to be disposable. Running any kind of gray hat activities on an account that has any kind of value
is asking for trouble, as Instagram is known to crack down on accounts involved in sketchy, robotic
activities.&lt;/p&gt;
&lt;p&gt;The following example will entail scraping list of followers given the username of some target
account, including email addresses that are sometimes available through API, but are not necessarily
visible on Instagram profiles.&lt;/p&gt;
&lt;p&gt;We need to instantiate API client first and perform login step with username and password:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; instauto.api.client &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ApiClient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ApiClient(username&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;username, password&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;password)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;log_in()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point Instagram might ask for access confirmation code that Instauto will let you
enter via standard input.&lt;/p&gt;
&lt;p&gt;We want to avoid performing login step repeatedly, as this can trigger automation countermeasures.
To help us here, Instauto API client implements &lt;code&gt;save_to_disk()&lt;/code&gt; method that we can call with
a filepath:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save_to_disk(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;savefile.instauto&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create a JSON file with session data to be reused next time. To read this file
we will need to instantiate &lt;code&gt;ApiClient&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ApiClient&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;initiate_from_file(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;savefile.instauto&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assuming we have username of target account (without leading &lt;code&gt;@&lt;/code&gt; character) we can use
&lt;code&gt;get_user_id_from_username()&lt;/code&gt; from &lt;code&gt;instauto.helpers&lt;/code&gt; to get the numeric user ID that we will
need for further steps:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; instauto.helpers.search &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; get_user_id_from_username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_user_id_from_username(client, target_username)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point we could use &lt;code&gt;get_followers()&lt;/code&gt; from &lt;code&gt;instauto.helpers&lt;/code&gt;, but let&amp;rsquo;s proceed with
a slightly harder way to see how using stuff from &lt;code&gt;instauto.api&lt;/code&gt; looks like. To get the
list of followers, we need to instantiate a request object and use it on API client:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; instauto.api.actions.structs.friendships &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; fs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;obj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fs&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;GetFollowers(user_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;obj, response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;followers_get(obj)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;followers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;users&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instauto takes care of API pagination for us, but we would need to call &lt;code&gt;followers_get()&lt;/code&gt;
repeatedly until we either collected a required number of entries or ran out of data to scrape
(make sure to use the updated request object).  At this point we have a fairly sparse data
that does not include much beyond user names and IDs of the followers.&lt;/p&gt;
&lt;p&gt;To get more fields, we need to request for profile info based on user ID of each follower:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; instauto.api.actions.structs.profile &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;obj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pr&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Info(user_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fi &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;profile_info(obj)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete Python script that demonstrates follower info scraping is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; instauto.api.actions.structs.friendships &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; fs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; instauto.api.client &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ApiClient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; instauto.helpers.friendships &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; get_followers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; instauto.helpers.search &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; get_user_id_from_username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; instauto.api.actions.structs.profile &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;target_username&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    target_username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    target_username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; target_username&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isfile(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;savefile.instauto&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ApiClient&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;initiate_from_file(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;savefile.instauto&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Username: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        password &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Password: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ApiClient(username&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;username, password&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;password)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;log_in()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save_to_disk(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;savefile.instauto&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(client)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_user_id_from_username(client, target_username)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# followers = get_followers(client, user_id, 100)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# print(followers)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Based on:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# https://github.com/stanvanrooy/instauto/blob/master/examples/api/friendships/get_followers.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    obj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fs&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;GetFollowers(user_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    obj, response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;followers_get(obj)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    followers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;users&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Got &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; followers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(len(followers)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        obj, response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;followers_get(obj)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; response:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_followers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;users&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Got &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; followers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(len(new_followers)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        followers&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extend(new_followers)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(followers) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;followers.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(followers[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;keys()), lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; followers:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pprint(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linked_fb_info&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;del&lt;/span&gt; f[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linked_fb_info&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;follower_infos.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;biography&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_email&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_phone_number&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; followers:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Getting profile details for &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;...&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        obj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pr&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Info(user_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fi &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;profile_info(obj)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; type(fi) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: fi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;: fi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;biography&amp;#34;&lt;/span&gt;: fi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;biography&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_email&amp;#34;&lt;/span&gt;: fi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_email&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_phone_number&amp;#34;&lt;/span&gt;: fi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public_phone_number&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may want to use proxy with this script. Since Instauto uses the well-known requests
module for making HTTP requests, it heeds &lt;code&gt;HTTPS_PROXY&lt;/code&gt; environment variable. That&amp;rsquo;s an
easy way to tunnel traffic through proxy.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scapy: low level packet hacking toolkit for Python</title>
      <link>https://www.trickster.dev/post/scapy-low-level-packet-hacking-toolkit-for-python/</link>
      <pubDate>Sun, 08 May 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scapy-low-level-packet-hacking-toolkit-for-python/</guid>
      <description>&lt;p&gt;To make HTTP requests with Python we can use requests module. To send and receive things via TCP
connection we can use stream sockets. However, what if we want to go deeper than this? To parse
and generate network packets, we can use &lt;a href=&#34;https://docs.python.org/3/library/struct.html&#34;&gt;&lt;code&gt;struct&lt;/code&gt;&lt;/a&gt;
module. Depending on how deep in protocol stack are we working we may need to send/receive the
wire format buffer through raw sockets. This can be fun in a way, but if this kind of code is
being written for research purposes (e.g. to find and demonstrate vulnerabilities in networking software)
dealing with protocol wire formats and raw sockets will yield fairly low ROI on your efforts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://scapy.net/&#34;&gt;Scapy&lt;/a&gt; is a Python module and interactive program for low-level network
programming that attempts to make it easier without abstracting away the technical details.
This project is fairly prominent in cybersecurity space and used for things
like exploit development, data exfiltration, network recon, intrusion detection and
analysing captured traffic. Scapy integrates with data visualisation and report generation
tooling for presenting the results of your research to bug bounty program or during
the meeting with customer or management. The foundational idea for Scapy is proposing a
Python-based domain specific language for easy and quick wire format management.&lt;/p&gt;
&lt;p&gt;Scapy can be installed through PIP and in some cases through operating system package managers
(but make sure to check that version available through e.g. APT is not obsolete).&lt;/p&gt;
&lt;p&gt;Once installed, we can run Scapy REPL by simply running &lt;code&gt;scapy&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scapy
INFO: Can&amp;#39;t import matplotlib. Won&amp;#39;t be able to plot.
INFO: Can&amp;#39;t import PyX. Won&amp;#39;t be able to use psdump() or pdfdump().
WARNING: IPython not available. Using standard Python shell instead.
AutoCompletion, History are disabled.

                     aSPY//YASa
             apyyyyCY//////////YCa       |
            sY//////YSpcs  scpCY//Pp     | Welcome to Scapy
 ayp ayyyyyyySCP//Pp           syY//C    | Version 2.4.5
 AYAsAYYYYYYYY///Ps              cY//S   |
         pCCCCY//p          cSSps y//Y   | https://github.com/secdev/scapy
         SPPPP///a          pP///AC//Y   |
              A//A            cyP////C   | Have fun!
              p///Ac            sC///a   |
              P////YCpc           A//A   | What is dead may never die!
       scccccp///pSP///p          p//Y   |                     -- Python 2
      sY/////////y  caa           S//P   |
       cayCyayP//Ya              pY/Ya
        sY/PsY////YCc          aC//Yp
         sc  sccaCY//PCypaapyCP//YSs
                  spCPY//////YPSps
                       ccaacs

&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Simplest thing we can do with Scapy is reading a PCAP file. Let&amp;rsquo;s download the following sample PCAP
from Wireshark project:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://wiki.wireshark.org/uploads/1868d73fee89fce9d77c6ba21929e773/sip-rtp-opus-hybrid.pcap&#34;&gt;https://wiki.wireshark.org/uploads/1868d73fee89fce9d77c6ba21929e773/sip-rtp-opus-hybrid.pcap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We call &lt;code&gt;rdpcap()&lt;/code&gt; function with PCAP file path to read it&amp;rsquo;s contents:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; pkts = rdpcap(&amp;#34;sip-rtp-opus-hybrid.pcap&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; pkts
&amp;lt;sip-rtp-opus-hybrid.pcap: TCP:0 UDP:7 ICMP:0 Other:0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This reads all packets from PCAP file in one go. We may not want that if we have a large PCAP
file with a lot of packets captured. In that case, we would want to read packets iteratively -
one at a time. This is possible through &lt;code&gt;PcapReader&lt;/code&gt; object that can be instantiated from file
handle:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; in_f = open(&amp;#34;sip-rtp-opus-hybrid.pcap&amp;#34;, &amp;#34;rb&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; reader = PcapReader(in_f)
&amp;gt;&amp;gt;&amp;gt; reader
&amp;lt;scapy.utils.PcapReader at 0x1109daf40&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for pkt in reader:
...:     print(pkt)
...: 
WARNING: Calling str(pkt) on Python 3 makes no sense!
b&amp;#39;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x01\xee\x1b\\@\x00@\x11\x05\x81\n\x00\x02\x14\n\x00\x02\x0f\x13\xc4\x13\xc4\x01\xda\x1a\x0eINVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-4237-1-0\r\nFrom: &amp;#34;opus/48000/2&amp;#34; &amp;lt;sip:sipp@10.0.2.20:5060&amp;gt;;tag=1\r\nTo: test &amp;lt;sip:test@10.0.2.15:5060&amp;gt;\r\nCall-ID: 1-4237@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length:   128\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 99\r\na=rtpmap:99 opus/48000/2\r\na=recvonly\r\n&amp;#39;
WARNING: Calling str(pkt) on Python 3 makes no sense!
b&amp;#39;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x04}\xb6\xdc\x00\x00@\x11\xa7q\n\x00\x02\x0f\n\x00\x02\x14\x13\xc4\x13\xc4\x04i\x1c\x9dSIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-4237-1-0\r\nFrom: &amp;#34;opus/48000/2&amp;#34; &amp;lt;sip:sipp@10.0.2.20:5060&amp;gt;;tag=1\r\nTo: test &amp;lt;sip:test@10.0.2.15:5060&amp;gt;;tag=gm5D9rB47ggme\r\nCall-ID: 1-4237@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: &amp;lt;sip:test@10.0.2.15:5060;transport=udp&amp;gt;\r\nUser-Agent: FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit\r\nAccept: application/sdp\r\nAllow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE\r\nSupported: timer, path, replaces\r\nAllow-Events: talk, hold, conference, presence, as-feature-event, dialog, line-seize, call-info, sla, include-session-description, presence.winfo, message-summary, refer\r\nContent-Type: application/sdp\r\nContent-Disposition: session\r\nContent-Length: 283\r\nRemote-Party-ID: &amp;#34;test&amp;#34; &amp;lt;sip:test@10.0.2.15&amp;gt;;party=calling;privacy=off;screen=no\r\n\r\nv=0\r\no=FreeSWITCH 1480231472 1480231473 IN IP4 10.0.2.15\r\ns=FreeSWITCH\r\nc=IN IP4 10.0.2.15\r\nt=0 0\r\nm=audio 24196 RTP/AVP 99 101\r\na=rtpmap:99 opus/48000/2\r\na=fmtp:99 useinbandfec=1; minptime=10; maxptime=40\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=sendonly\r\na=ptime:20\r\n&amp;#39;
WARNING: more Calling str(pkt) on Python 3 makes no sense!
b&amp;#39;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00z\xb6\xdf@\x00@\x11kq\n\x00\x02\x0f\n\x00\x02\x14^\x84\x17p\x00f\x18\x9a\x80\xe3]%\x00\x00\x03\xc0\x04&amp;gt;\xee\x04x\x00\xb2g\xa8\x1f@N\xf7x`\x9a@r2\xaf\x14\xdf!\x8c-\xb4U\xcek\xc6\\p[\xc2\xcf\x0b\x8e\xa9\x18\xb1.V\xe2l\xbe\x1c\x13\x1bR\xb7\x1dT\xf2\x80\x8cS\x98r!\xec\x13\xc4\xbd\x11l\x86\xd7\x80&amp;lt;\x8dO\x8e\xd3\xb6\x9e-6\xa2\x03;\xeb\xa3E\x06Ui&amp;#39;
b&amp;#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xbd\xb6\xf2@\x00@\x11k\x1b\n\x00\x02\x0f\n\x00\x02\x14^\x84\x17p\x00\xa9\x18\xdd\x80c]-\x00\x00!\xc0\x04&amp;gt;\xee\x04y\xe3`\xcf\xf3\x9d\xf3\xd0#M\xf0\x1d@\xe87L\x90\x06:\xfc\xc8\x7f\x8b~\x9f\x80\xe8\x961av2iw-\xca\xc1]&amp;#39;p.s\xaf\xbcRZb\x8e4\x9eJ\xe4E\xbf\xf9\xd7\xc7\xdcT\x03\x0f\x8c\x8b7\xb5\x0evpq\x15\xcf\xa1\xf9,\x92\x0c\xf9\xdf@\x0cB\x93v\xce\xa1\xa8\x06\x13\xbbo\xd8\xb7,\x9e\x05K79\xc6h\\\xda\x80\x86*\x9bn\xa2\xd8$\x85\xb3\xd3sE\xcb\xc8\xa1\xb2\x85p\x9c`\x9f\xa2\xfb\x96\xebF\r|\x1cG6\xa2\xa0\x82Tv\x160Pw\xd7o\xe2\rV;&amp;#34;
b&amp;#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xc2\xb7\x10@\x00@\x11j\xf8\n\x00\x02\x0f\n\x00\x02\x14^\x84\x17p\x00\xae\x18\xe2\x80c];\x00\x00V@\x04&amp;gt;\xee\x04zd\x97\xde\xaf-\xae\\\xd8\xe3\x94\x10\x05\xab1\t\x91\xec\xd82(\x8d\xe1\xa1\xcb\xa8os\xdf\xc6\x8c\xff\xd9\xdb\x94`\xd7\xb8\xc8L\x95.af\x0b\xde\xe9\xfa\xb4\xe4\xc5\x00\xd3d\x01\xe8\x19\xf2\xb6\xcbw\x17\xd1\xf4X\xe2\xdb\xc9\xad\xd2&amp;amp;\xd2\x1f\xaa\xaaJ\xe9\x84Y\x16\x0b\x0f\xf8\xfe4\x17\x12\x16\x84@\xe4d\xbe!rn\x14\x9f(\x89\xdd\xdf\xfbQ&amp;#39; .9H-\x94\x9b\xf0\x86\xdbK\xf1\xc4o=\xb6\xdc\xa5\xed\xe5\xb6\x86s){&amp;lt;\xfb\xf4\x8cv\x1a\x12\xf6\x00(\xf9n\xaf\x98lO%\x91Z\xa5\x9a-\x11n&amp;#34;
b&amp;#34;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xc2\xb9\x0f@\x00@\x11h\xf9\n\x00\x02\x0f\n\x00\x02\x14^\x84\x17p\x00\xae\x18\xe2\x80c]\xe3\x00\x02\xcc@\x04&amp;gt;\xee\x04{B\x08\xb7\xc2\x92l&amp;lt;\xa8s\xde- ,c\x07\xe1\x0b7\xa3\xc3\xd6\xc3\xce\x8dA\xce\xb6\xc9\x85\xb3Y\xbeQi\xff\x8f\xd0!\xf1&amp;#39;\xb6bV\xa2\x80\xb2\xe7\x8fbsW\xc3\x18f\xbf_u8z\xbc\xf8\xcapM\x08\xed\xa8.3\x13\x98\xb9v\xd23\xc7\x18\x8eY\x10\xfc\x1c\x98\xb1\xbfaj\xbf\x02\xdb\x7f\xc4j\x85c\xddz\x8d\xd6vk`\x15\x14\x98\xcc\x81\x89\x13\x104\x00:l\x96\xa3\xb6\xe5\xad\xd1#\xd8\x04i\x99\x03\xe60\xe1\x8f(\r\x90\x89{\x9f\xf3\x16\x9cw\xc1\x9e\xb8\xeb\xb4&amp;amp;eU\xdd6\xcd\xca\xec&amp;#34;
b&amp;#39;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xc4\xba\x84@\x00@\x11g\x82\n\x00\x02\x0f\n\x00\x02\x14^\x84\x17p\x00\xb0\x18\xe4\x80c^`\x00\x04\xa1\x00\x04&amp;gt;\xee\x04{\xc2\x08\xcd\xab\x7f\x95\\&amp;#34;\xa8P@\xce\xa0\xf3\xe8\xa6_\x1fp\xb0G\xa9XgH\x180\xc2\xadzl\x8c\xd1q\x81\xf8\x9eY\x05lJ*s\xfc\x05\xacbC\xad\x8d?3?T\xa8lU26\xfd/\x9b\xc92\xc7\x93\xf1o/\x8c\xcbu\x1ew ~ed\xc75\xa2\xc5|\xba\xc4\x1a\x96\xecM6\x9e\xbb.\x1e\x0f\x85\xfbh\x04L\xf3\x91&amp;gt;\xa9\x19\x9d@\x08\xd9P\x1c&amp;gt;\x01\xf9\x12.\x7f%lH\x1c\x86\xd42\xdbq\x98\r\xc6_\xa0\x05\xe7\xe8Pz^K-\xfd8)F\x91\xda\x93{\xab\t3$\xe5\xa2\xbc\xbb&amp;#39;
&amp;gt;&amp;gt;&amp;gt; in_f.close()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As we can see, each packet is available in it&amp;rsquo;s wire format. Scapy treats each packet as stack of layers.
Scapy layers correspond to network protocols and their wire formats.&lt;/p&gt;
&lt;p&gt;Let us take the first packet and check if IP layer is available:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; first_pkt = pkts[0]
&amp;gt;&amp;gt;&amp;gt; first_pkt.haslayer(IP)
True
&amp;gt;&amp;gt;&amp;gt; IP in first_pkt
True
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To parse packet from a specific layer, we can index it by the layer we want and let Scapy print all the fields:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; first_pkt[Ether]
&amp;lt;Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |&amp;lt;IP  version=4 ihl=5 tos=0x0 len=494 id=7004 flags=DF frag=0 ttl=64 proto=udp chksum=0x581 src=10.0.2.20 dst=10.0.2.15 |&amp;lt;UDP  sport=sip dport=sip len=474 chksum=0x1a0e |&amp;lt;Raw  load=&amp;#39;INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-4237-1-0\r\nFrom: &amp;#34;opus/48000/2&amp;#34; &amp;lt;sip:sipp@10.0.2.20:5060&amp;gt;;tag=1\r\nTo: test &amp;lt;sip:test@10.0.2.15:5060&amp;gt;\r\nCall-ID: 1-4237@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length:   128\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 99\r\na=rtpmap:99 opus/48000/2\r\na=recvonly\r\n&amp;#39; |&amp;gt;&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; first_pkt[UDP]
&amp;lt;UDP  sport=sip dport=sip len=474 chksum=0x1a0e |&amp;lt;Raw  load=&amp;#39;INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-4237-1-0\r\nFrom: &amp;#34;opus/48000/2&amp;#34; &amp;lt;sip:sipp@10.0.2.20:5060&amp;gt;;tag=1\r\nTo: test &amp;lt;sip:test@10.0.2.15:5060&amp;gt;\r\nCall-ID: 1-4237@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length:   128\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 99\r\na=rtpmap:99 opus/48000/2\r\na=recvonly\r\n&amp;#39; |&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To print a packet in hexadecimal, we can use &lt;code&gt;hexdump()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; hexdump(first_pkt)
0000  00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00  ..............E.
0010  01 EE 1B 5C 40 00 40 11 05 81 0A 00 02 14 0A 00  ...\@.@.........
0020  02 0F 13 C4 13 C4 01 DA 1A 0E 49 4E 56 49 54 45  ..........INVITE
0030  20 73 69 70 3A 74 65 73 74 40 31 30 2E 30 2E 32   sip:test@10.0.2
0040  2E 31 35 3A 35 30 36 30 20 53 49 50 2F 32 2E 30  .15:5060 SIP/2.0
0050  0D 0A 56 69 61 3A 20 53 49 50 2F 32 2E 30 2F 55  ..Via: SIP/2.0/U
0060  44 50 20 31 30 2E 30 2E 32 2E 32 30 3A 35 30 36  DP 10.0.2.20:506
0070  30 3B 62 72 61 6E 63 68 3D 7A 39 68 47 34 62 4B  0;branch=z9hG4bK
0080  2D 34 32 33 37 2D 31 2D 30 0D 0A 46 72 6F 6D 3A  -4237-1-0..From:
0090  20 22 6F 70 75 73 2F 34 38 30 30 30 2F 32 22 20   &amp;#34;opus/48000/2&amp;#34;
00a0  3C 73 69 70 3A 73 69 70 70 40 31 30 2E 30 2E 32  &amp;lt;sip:sipp@10.0.2
00b0  2E 32 30 3A 35 30 36 30 3E 3B 74 61 67 3D 31 0D  .20:5060&amp;gt;;tag=1.
00c0  0A 54 6F 3A 20 74 65 73 74 20 3C 73 69 70 3A 74  .To: test &amp;lt;sip:t
00d0  65 73 74 40 31 30 2E 30 2E 32 2E 31 35 3A 35 30  est@10.0.2.15:50
00e0  36 30 3E 0D 0A 43 61 6C 6C 2D 49 44 3A 20 31 2D  60&amp;gt;..Call-ID: 1-
00f0  34 32 33 37 40 31 30 2E 30 2E 32 2E 32 30 0D 0A  4237@10.0.2.20..
0100  43 53 65 71 3A 20 31 20 49 4E 56 49 54 45 0D 0A  CSeq: 1 INVITE..
0110  43 6F 6E 74 61 63 74 3A 20 73 69 70 3A 73 69 70  Contact: sip:sip
0120  70 40 31 30 2E 30 2E 32 2E 32 30 3A 35 30 36 30  p@10.0.2.20:5060
0130  0D 0A 4D 61 78 2D 46 6F 72 77 61 72 64 73 3A 20  ..Max-Forwards:
0140  37 30 0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 65  70..Content-Type
0150  3A 20 61 70 70 6C 69 63 61 74 69 6F 6E 2F 73 64  : application/sd
0160  70 0D 0A 43 6F 6E 74 65 6E 74 2D 4C 65 6E 67 74  p..Content-Lengt
0170  68 3A 20 20 20 31 32 38 0D 0A 0D 0A 76 3D 30 0D  h:   128....v=0.
0180  0A 6F 3D 2D 20 34 32 20 34 32 20 49 4E 20 49 50  .o=- 42 42 IN IP
0190  34 20 31 30 2E 30 2E 32 2E 32 30 0D 0A 73 3D 2D  4 10.0.2.20..s=-
01a0  0D 0A 63 3D 49 4E 20 49 50 34 20 31 30 2E 30 2E  ..c=IN IP4 10.0.
01b0  32 2E 32 30 0D 0A 74 3D 30 20 30 0D 0A 6D 3D 61  2.20..t=0 0..m=a
01c0  75 64 69 6F 20 36 30 30 30 20 52 54 50 2F 41 56  udio 6000 RTP/AV
01d0  50 20 39 39 0D 0A 61 3D 72 74 70 6D 61 70 3A 39  P 99..a=rtpmap:9
01e0  39 20 6F 70 75 73 2F 34 38 30 30 30 2F 32 0D 0A  9 opus/48000/2..
01f0  61 3D 72 65 63 76 6F 6E 6C 79 0D 0A              a=recvonly..
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To fully parse and pretty-print a packet we can call &lt;code&gt;show()&lt;/code&gt; method on it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; first_pkt.show()
###[ Ethernet ]###
  dst= 00:00:00:00:00:00
  src= 00:00:00:00:00:00
  type= IPv4
###[ IP ]###
     version= 4
     ihl= 5
     tos= 0x0
     len= 494
     id= 7004
     flags= DF
     frag= 0
     ttl= 64
     proto= udp
     chksum= 0x581
     src= 10.0.2.20
     dst= 10.0.2.15
     \options\
###[ UDP ]###
        sport= sip
        dport= sip
        len= 474
        chksum= 0x1a0e
###[ Raw ]###
           load= &amp;#39;INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-4237-1-0\r\nFrom: &amp;#34;opus/48000/2&amp;#34; &amp;lt;sip:sipp@10.0.2.20:5060&amp;gt;;tag=1\r\nTo: test &amp;lt;sip:test@10.0.2.15:5060&amp;gt;\r\nCall-ID: 1-4237@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length:   128\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 99\r\na=rtpmap:99 opus/48000/2\r\na=recvonly\r\n&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The SIP payload was not parsed. That&amp;rsquo;s because Scapy mostly deals with binary protocols in
lower parts of network stack, which SIP is not. However, there are third party helper modules
to parse some of the application layer protocols, such as HTTP.&lt;/p&gt;
&lt;p&gt;So we can read PCAP files with pre-captured packets, but what if we want to do some packet sniffing?
Assuming we have our system ready to use network interface in promiscuous mode, we can can call
&lt;code&gt;sniff()&lt;/code&gt; function to get some packets from the network interface:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for pkt in sniff(count=5):
...:     pkt.show()
...:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also use the very same BPF syntax for filtering sniffed packets that Wireshark, tcpdump
and many other tools support:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for pkt in sniff(filter=&amp;#34;udp&amp;#34;, count=5):
...:     pkt.show()
...: 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To save captured packets into PCAP file for further analysis, we can use &lt;code&gt;wrpcap()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; capture = sniff(filter=&amp;#34;udp&amp;#34;, count=5)
&amp;gt;&amp;gt;&amp;gt; capture
&amp;lt;Sniffed: TCP:0 UDP:5 ICMP:0 Other:0&amp;gt;
&amp;gt;&amp;gt;&amp;gt; wrpcap(&amp;#34;udp.pcap&amp;#34;, capture)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can sniff (capture and parse) the network packets, but Scapy also supports packet generation for
doing all kinds of active trickery: network scanning, server probing, attacking systems by sending
malformed requests and so on.&lt;/p&gt;
&lt;p&gt;Let us try pinging a server. To send ICMP packet to a specific we need to make IP datagram
with ICMP payload:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; pkt = IP(dst=&amp;#34;8.8.8.8&amp;#34;) / ICMP()
&amp;gt;&amp;gt;&amp;gt; pkt.show()
###[ IP ]### 
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags= 
  frag= 0
  ttl= 64
  proto= icmp
  chksum= None
  src= 192.168.16.130
  dst= 8.8.8.8
  \options\
###[ ICMP ]### 
     type= echo-request
     code= 0
     chksum= None
     id= 0x0
     seq= 0x0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling &lt;code&gt;sr1()&lt;/code&gt; sends our packet on layer 3 and waits for single answer:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; sr1(pkt)
Begin emission:
Finished sending 1 packets.
...*
Received 4 packets, got 1 answers, remaining 0 packets
&amp;lt;IP  version=4 ihl=5 tos=0x0 len=28 id=0 flags= frag=0 ttl=118 proto=icmp chksum=0x63a7 src=8.8.8.8 dst=192.168.16.130 |&amp;lt;ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |&amp;lt;Padding  load=&amp;#39;\x00\x00\x00\x000000000000\x00\x00\x00\x00&amp;#39; |&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We got a proper ICMP reply, which means server is up.&lt;/p&gt;
&lt;p&gt;For sending multiple packets and receiving responses (e.g. to implement a ping sweep) we
could use &lt;code&gt;sr()&lt;/code&gt; function. To send multiple packets, but wait for single response
we can use &lt;code&gt;sr1_flood()&lt;/code&gt; function. For more on this, see:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://scapy.readthedocs.io/en/latest/api/scapy.sendrecv.html?highlight=sr1#module-scapy.sendrecv&#34;&gt;https://scapy.readthedocs.io/en/latest/api/scapy.sendrecv.html?highlight=sr1#module-scapy.sendrecv&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Scapy overrides the &lt;code&gt;/&lt;/code&gt; operator to implement layer stacking. It does not enforce the ordering
of layers to make sure it occurs in the same order as it should. Thus we can create some
pretty crazy packets:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; broken_pkt = ICMP() / UDP() / IP() / IP()
&amp;gt;&amp;gt;&amp;gt; broken_pkt
&amp;lt;ICMP  |&amp;lt;UDP  |&amp;lt;IP  frag=0 proto=ipencap |&amp;lt;IP  |&amp;gt;&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; broken_pkt.show()
###[ ICMP ]###
  type= echo-request
  code= 0
  chksum= None
  id= 0x0
  seq= 0x0
###[ UDP ]###
     sport= domain
     dport= domain
     len= None
     chksum= None
###[ IP ]###
        version= 4
        ihl= None
        tos= 0x0
        len= None
        id= 1
        flags=
        frag= 0
        ttl= 64
        proto= ipencap
        chksum= None
        src= 127.0.0.1
        dst= 127.0.0.1
        \options\
###[ IP ]###
           version= 4
           ihl= None
           tos= 0x0
           len= None
           id= 1
           flags=
           frag= 0
           ttl= 64
           proto= ip
           chksum= None
           src= 127.0.0.1
           dst= 127.0.0.1
           \options\

&amp;gt;&amp;gt;&amp;gt; hexdump(broken_pkt)
WARNING: No IP underlayer to compute checksum. Leaving null.
0000  08 00 F7 65 00 00 00 00 00 35 00 35 00 30 00 00  ...e.....5.5.0..
0010  45 00 00 28 00 01 00 00 40 04 7C CF 7F 00 00 01  E..(....@.|.....
0020  7F 00 00 01 45 00 00 14 00 01 00 00 40 00 7C E7  ....E.......@.|.
0030  7F 00 00 01 7F 00 00 01                          ........
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is by design, as we may want to generate arbitrarily broken packets
for the purposes of vulnerability research or exploitation. However we also
have to take care and make sure that layers are ordered properly when
normal packets are being created.&lt;/p&gt;
&lt;p&gt;For more Scapy usage examples, see: &lt;a href=&#34;https://scapy.readthedocs.io/en/latest/usage.html&#34;&gt;https://scapy.readthedocs.io/en/latest/usage.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What if you don&amp;rsquo;t want to use Scapy REPL exclusively, but would prefer to
develop Python scripts or Jupyter notebooks? Everything that Scapy REPL
provides can be used in a regular Python environment if you put the following
import statement on the top:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scapy.all &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Scapy has been used to unit-test network protocol implementations in Linux, OpenBSD
and RIOT projects. It has also been used to implement various offensive security
tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/calebmadrigal/trackerjacker&#34;&gt;trackerjacker&lt;/a&gt; - WiFi network mapper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/wifiphisher/wifiphisher&#34;&gt;wifiphisher&lt;/a&gt; - rogue access point creation tool.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/HynekPetrak/sshame&#34;&gt;sshame&lt;/a&gt; - SSH public key brute forcer.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/dark-lbp/isf&#34;&gt;ISF&lt;/a&gt; - exploitation framework for attacking industrial systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For an example of Scapy being used for bug bounty PoC, take a look at HackerOne
report &lt;a href=&#34;https://hackerone.com/reports/411519&#34;&gt;411519&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Strategies and patterns of gray hat social media automation</title>
      <link>https://www.trickster.dev/post/strategies-and-patterns-of-gray-hat-social-media-automation/</link>
      <pubDate>Tue, 26 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/strategies-and-patterns-of-gray-hat-social-media-automation/</guid>
      <description>&lt;h1 id=&#34;introduction-and-motivation&#34;&gt;Introduction and motivation&lt;/h1&gt;
&lt;p&gt;We live in the postmodern age of hyperreality. Vast majority of information most people receive through technological
means without having any direct access to it&amp;rsquo;s source or any way to verify or deny what is written or shown on the screen.&lt;/p&gt;
&lt;p&gt;A pretty girl sitting in a private jet might have paid a lot of money to fly somewhere warm or might have paid a &lt;a href=&#34;https://www.privatejetphotoshoot.com/&#34;&gt;company&lt;/a&gt;
that keeps the plane on the tarmac a smaller amount of money to do a photo shoot. Her fans on Instagram have no idea which of these
scenarios is true simply from seeing the picture of her relaxed in fancy seat, looking into distance through her sunglasses
and sipping wine. That&amp;rsquo;s hyperreality. The same applies to supposedly very successful twenty-something guy in an expensive car
that may or may not have been rented for a day to make an entire months worth of pictures.&lt;/p&gt;
&lt;p&gt;In modern information society, attention is money. There are multiple ways to financially benefit from peoples attention
through PPC advertisements, sponsorships, affiliate deals, promoting your own products or by simply selling your audiences
attention to the highest bidder (influencer marketing). Some people have become millionaires or even billionaires by
being able to play the social media game very well. Hyperreality is particularly prevalent on modern social media platforms
that are deliberately engineered to extract as much time and attention (engagement) from it&amp;rsquo;s users as possible, then
sell it through the integrated ad platform to the highest bidder. Objective truth be damned, Big Social executives will
do anything they can to keep the people reading, clicking, watching. Large scale virtual reality as it was imagined by science
fiction writers and technologists in 80s and 90s did not happen yet, but virtual &lt;em&gt;social&lt;/em&gt; reality is already here.
Lines betweeen true and fictional are increasingly getting blurry and muddled. In postmodern society, everyone is trying
to influence the consensus reality to their benefit. Those able to do it efficiently and scalably emerge as winners.&lt;/p&gt;
&lt;p&gt;If Big Social business objectives require, the organic reach is deliberately being limited to make more space for paid ads.
Therefore promoting your brand on major social media platforms by playing it straight and relying on organic reach only
works up to a point. It is still feasible on Tiktok, but less and less viable on Instagram and Facebook. The same applies
to buying peoples attention on social media ad platforms such as Facebook. When social media ad spend is overall increasing
auctions for human attention are getting more and more competitive, thus many business are finding that they are being priced out.
In 2022, it is very easy to burn bunch of money on e.g. Facebook ads only to end up net negative by the end of the quarter.&lt;/p&gt;
&lt;p&gt;So if we simply want to make some money, how do we proceed in this crazy world? If Big Social takes away the organic
reach with one hand and charges more and more money for paid ads with another how do we turn a profit?&lt;/p&gt;
&lt;p&gt;One of the feasible answers is growth hacking through gray hat social media automation. Let me elaborate what I mean by
that. Growth hacking is a kind of marketing that focuses on low-cost, experimental, unorthodox ways to get new customers
and retain them. Social media automation is the use of programmatic techniques and software tools to do things that
someone would be doing manually. In the order of increasing sketchiness, we classify the automation
techniques into three levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;White hat - fully legitimate and T&amp;amp;C-compliant automation, such as posting stuff on schedule through Buffer, ToS-compliant
chatbots and so on. Legit stuff in the eyes of social media platform that nobody would legitimately complain about.&lt;/li&gt;
&lt;li&gt;Gray hat - automations that explicitly break T&amp;amp;C of social media platform. Somewhat sketchy stuff.&lt;/li&gt;
&lt;li&gt;Black hat - automations for malicious and criminal purposes, such as phishing, harassment or mass-reporting social
media accounts on fraudulent basis. The evil stuff.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Assuming we don&amp;rsquo;t want to play the dangerous game of truly black hat automation on moral and risk management grounds, why
do we pick the gray hat option? Why not go with white hat automation and do everything in entirely clean way? Well, frankly
that&amp;rsquo;s because white hat automation has no teeth in 2022. We would be merely making an improvement over manual social
media management, but not going far beyond that. Altough Big Social is proactively working against non-whitehat automation
on technical and legal levels, gray hat automation at least gives us a fighting chance when trying to monetise these
massives audiences on modern massive social networks.&lt;/p&gt;
&lt;p&gt;Gray hat social media automation is not an easy thing to do in the post-Cambridge Analytica world, but may provide something
like a cheat code to those trying to make money online. The idea is to turn the tables against Big Social
by exploiting the very state of manipulative data-driven hyperreality that the Zuckerbergs of the world have created.&lt;/p&gt;
&lt;p&gt;Like in object-oriented programming, there are patterns - abstract principles of doing things that keep popping up when one
works in the field or is researching it. We will discuss the patterns of gray hat social media automation that are more
or less applicable to multiple platforms and can be thought of as theorethical concepts without scrutinizing how exactly
they would be implemented.&lt;/p&gt;
&lt;p&gt;But refrain from implementing some equivalent of &lt;a href=&#34;https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition&#34;&gt;FizzBuzz Enterprise Edition&lt;/a&gt;
if you can.&lt;/p&gt;
&lt;h1 id=&#34;two-major-approaches&#34;&gt;Two major approaches&lt;/h1&gt;
&lt;p&gt;Broadly speaking, scraping HTML pages and submitting forms via POST requests is not how social media automation is done
in modern times. There are two major technical approaches to implement gray hat automations for social media. Both of these
must deal with automation countermeasures that platforms implement.&lt;/p&gt;
&lt;h2 id=&#34;abusing-platform-apis&#34;&gt;(Ab)using platform APIs&lt;/h2&gt;
&lt;p&gt;One approach is automating against platform APIs. To some extend this can be possible through public APIs described on the
developer portal documentation (this was how &lt;a href=&#34;https://howitzer.co/&#34;&gt;Howitzer&lt;/a&gt; worked originally), but primarily we would
need to pretend to be a mobile app or frontend JavaScript code of social media platfrom and reproduce private API calls.
This may require reverse engineering API calls with tools like mitmproxy and perhaps even reverse engineering binaries
of mobile apps if API security techniques such as request signing with HMAC is used. We may need to deal with complex
API flows, such as Instagram &lt;a href=&#34;https://stackoverflow.com/questions/62436766/cant-login-to-instagram-using-requests&#34;&gt;using&lt;/a&gt;
AES256 in GCM mode to encrypt the password during login. However the code implementing this approach would be fairly
lightweight and performant.&lt;/p&gt;
&lt;h2 id=&#34;browser-automation&#34;&gt;Browser automation&lt;/h2&gt;
&lt;p&gt;Another approach is running an entire browser and controlling it programmatically. In some cases browser is running in
headless mode - without the GUI window. There&amp;rsquo;s several ways to do this. One is implementing basic bots that automation
user could install as browser extensions. Another is using browser automation technologies such as Selenium, Playwright
or Puppeteer to develop scripts implementing the automations. On surface this may seem to be easier than API-based automation,
but it entails the risk of detection due to some tell-tale signs in the JS environment, as browser being automated will not
behave exactly the same way as browser being user by a real user. &lt;a href=&#34;https://github.com/InstaPy/InstaPy&#34;&gt;InstaPy&lt;/a&gt; is a
prominent open source project to implement IG automations by controlling the browser with Selenium.&lt;/p&gt;
&lt;h1 id=&#34;account-creation&#34;&gt;Account creation&lt;/h1&gt;
&lt;p&gt;Being able to quickly and automatically create social media accounts is highly beneficial for growth hacking. It entails
automating registration flow with the help of proxies, captcha solvers and - if phone verification is requeired -
SMS reception services such as &lt;a href=&#34;https://smspva.com/&#34;&gt;SMSPVA&lt;/a&gt; or &lt;a href=&#34;https://valar-sms.com/&#34;&gt;Valar SMS&lt;/a&gt; or your own setup,
such as phone farm or product like &lt;a href=&#34;https://proxidize.com/&#34;&gt;Proxidize&lt;/a&gt; that can also be used to receive text messages for
verification.&lt;/p&gt;
&lt;h1 id=&#34;scraping-and-data-analysis&#34;&gt;Scraping and data analysis&lt;/h1&gt;
&lt;h2 id=&#34;social-media-scraping&#34;&gt;Social media scraping&lt;/h2&gt;
&lt;p&gt;Social media platforms can be scraped just like any other website, but it is generally more difficult due to automation
countermeasures and many pages generally being behind login. It is advisable to take a look into Network tab in Chrome
DevTools as sometimes information not visible on the frontend is available in API responses, such as email addresses
for some business Instagram accounts.&lt;/p&gt;
&lt;p&gt;Scraping social media pages can yield not only some contact information to be used later, but can also help to map out
communities in graph-theorethical way. Speaking in graph theory terms, a social media profile is a vertex and connections
between them can be either undirected edges (e.g. Facebook friends) or directed edges (one account following or mentioning
another).&lt;/p&gt;
&lt;h2 id=&#34;social-network-analysis&#34;&gt;Social network analysis&lt;/h2&gt;
&lt;p&gt;From information gathered via social media scraping, we can model communities or audiences as graphs. By doing graph-theory
computations, we can get insights and ideas on how further growth hacking operations should be performed. Perhaps it is not desirable
to target major influencers or even their direct followers, but people connected indirectly to major influencer in a social graph
(e.g. followers of direct followers, but not direct followers themselves) would be the best audience to hit with automated
outreach. Merely plotting it with &lt;a href=&#34;https://networkx.org/&#34;&gt;NetworkX&lt;/a&gt; and matplotlib might provide some insight.&lt;/p&gt;
&lt;p&gt;See the following examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Datalux/Osintgram&#34;&gt;Osintgram&lt;/a&gt; - tool to scrape Instagram for OSINT purposes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://youtube-dl.org/&#34;&gt;youtube-dl&lt;/a&gt; - tool to download videos from many sources, including social media platforms.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/twintproject/twint&#34;&gt;TWINT&lt;/a&gt; - tool to scrape Twitter for OSINT.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In broader picture, social network analysis is entire branch of social science that deals with investigating communities
through the lens of graph theory. If we have social media footprint of some community scraped and stored in a database in
a structured way we can try to reverse engineer the social dynamics and use them to our benefit.&lt;/p&gt;
&lt;h1 id=&#34;automating-content-creation&#34;&gt;Automating content creation&lt;/h1&gt;
&lt;p&gt;Automated social media accounts should have at least some content. There are are ways to automate content generation and/or
posting.&lt;/p&gt;
&lt;h2 id=&#34;recycling-content&#34;&gt;Recycling content&lt;/h2&gt;
&lt;p&gt;If you have content created for one purpose or platform you can use automation to repurpose it for other platform, for example
by cutting up a longer video into shorter segments and uploading it to Tiktok for viewers with short attention span.&lt;/p&gt;
&lt;p&gt;Another approach is develop code to read RSS feeds of prominent news sources, use something like
&lt;a href=&#34;https://developers.bannerbear.com/#introduction&#34;&gt;Bannerbear API&lt;/a&gt; to turn them
into headline images and post them with link to original source.&lt;/p&gt;
&lt;p&gt;Yet another way is to simply repost well performing content from other accounts. Many Instagram theme pages were build by
doing exactly this - a practice known as &amp;ldquo;cash cow&amp;rdquo; pages.&lt;/p&gt;
&lt;h2 id=&#34;ai-generated-content&#34;&gt;AI-generated content&lt;/h2&gt;
&lt;p&gt;At this point, AI systems such as GPT-3 can help with content creation, but not replace a human working at reasonable quality.
If someone is not supervising it, the text generation can easily go off the rails and produce complete nonsense.
One can use GPT-3 to generate Buzzfeed-style listicles, but to go beyond that you would need to get good
at prompt engineering - a practice of crafting good prompts that instruct the language model. That is a skill in itself.
However, AI systems can definitely make the content creation easier.&lt;/p&gt;
&lt;h1 id=&#34;spamming-and-manufacturing-engagement&#34;&gt;Spamming and manufacturing engagement&lt;/h1&gt;
&lt;h2 id=&#34;mass-direct-message-sending&#34;&gt;Mass direct message sending&lt;/h2&gt;
&lt;p&gt;One spammy tactic is to send mass direct/private message to a pre-scraped list of potential customers. This is popular in NFT-related
communities on Discord by using tools like &lt;a href=&#34;https://github.com/V4NSH4J/discord-mass-DM-GO&#34;&gt;discord-dm-go&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Whether or not it will annoy them is another matter.&lt;/p&gt;
&lt;h2 id=&#34;spammy-engagement&#34;&gt;Spammy engagement&lt;/h2&gt;
&lt;p&gt;There are multiple way to nudge social media users into seeing our account:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Liking their post.&lt;/li&gt;
&lt;li&gt;Commenting on their post.&lt;/li&gt;
&lt;li&gt;Mentioning their account.&lt;/li&gt;
&lt;li&gt;In some cases viewing their content (watching stories on IG or taking a look at LinkedIN profile of user with paid plan).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these tactics can be done with automation.&lt;/p&gt;
&lt;p&gt;Instapy &lt;a href=&#34;https://github.com/InstaPy/InstaPy/blob/master/quickstart.py&#34;&gt;quickstart script&lt;/a&gt; provides a following example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; InstaPy()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; smart_run(session):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# general settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_dont_include([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;friend1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;friend2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;friend3&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# activity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;like_by_tags([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;natgeo&amp;#34;&lt;/span&gt;], amount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;follow-unfollow&#34;&gt;Follow-unfollow&lt;/h2&gt;
&lt;p&gt;If we want to collect a following of real people in our target niche it might be desirable to do follow-unfollow tactic that
entails automated following bunch of people within the niche every day, giving them a chance to follow our account and unfollowing
them if they don&amp;rsquo;t reciprocate. This is fairly low cost way of not only bringing some traffic to the page, but also building
an audience over time. However, we cannot be too agressive with this as platform have rate limits on how many actions we can
perform daily (although they change over time). Furthermore, it is desirable to keep your following-to-follower ratio as low
as possible, as it may become very obvious over time what we are doing here.&lt;/p&gt;
&lt;h1 id=&#34;risk-management&#34;&gt;Risk management&lt;/h1&gt;
&lt;h2 id=&#34;using-proxies&#34;&gt;Using proxies&lt;/h2&gt;
&lt;p&gt;Generally speaking, one should use mobile 4G proxies for social media automation. Yes, that&amp;rsquo;s expensive, but there are ways to
set them up with off-the-shelf hardware. In some cases residential proxies of sufficient quality are also enough.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t always want to use one proxy per account, but the amount of accounts sharing single exit IP should not be high.&lt;/p&gt;
&lt;h2 id=&#34;account-pre-warming&#34;&gt;Account pre-warming&lt;/h2&gt;
&lt;p&gt;If you create or purchase an account you may not want to or be able to use it at full capacity from the beginning. You may need
to let it go through a pre-warming sequence, which entails gradually increasing activity on the account to make it less likely
to get banned and also to make social media platform increase the rate limits over time. This depends on exact social media platform,
account standing and what kind of automations are planned for the account.&lt;/p&gt;
&lt;h2 id=&#34;scraper-accounts&#34;&gt;Scraper accounts&lt;/h2&gt;
&lt;p&gt;API scraping entails making a lot of API calls to backend systems to extract information. If social media platform is prone to
cracking down on account based on this thing alone, it is desirable to have some accounts that are easily replaceable and meant
to act as cannon fodder while gathering information for further parts of automated workflow. These are called scraper accounts.&lt;/p&gt;
&lt;h2 id=&#34;mother-child-method&#34;&gt;Mother-child method&lt;/h2&gt;
&lt;p&gt;Due to automation countermeasures, it is highly desirable to refrain from running any sketchy automations on valuable accounts that
we are trying to establish as assets to make money long term. Yet it is still possible to use automation to grow these accounts
through a technique called mother-child method. It is primarily used for growth on Instagram and uses 3 level-system of multiple
accounts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mother account that is not running any gray hat automations directly, but is used to build a primary audience and sell stuff.&lt;/li&gt;
&lt;li&gt;Child accounts that perform things like follow-unfollow, mass DMs, etc. to bring new people to the mother account and maybe to
build secondary assets (that may be difficult because platform is likely to shut them down eventually).&lt;/li&gt;
&lt;li&gt;Scraper accounts that gather audience data for child accounts to act on. These are meant to be disposable and replaced when burned.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;abusing-social-media-ads&#34;&gt;Abusing social media ads&lt;/h1&gt;
&lt;h2 id=&#34;targetting-scraped-audiences&#34;&gt;Targetting scraped audiences&lt;/h2&gt;
&lt;p&gt;PPC platforms such as Facebook ads allow you to import list of people (e.g. CSV file with column for emails) that are supposed to
be your customers. This can be abused to run ads towards a scraped list of people and possibly save some money on advertisment.&lt;/p&gt;
&lt;h2 id=&#34;nanotargetting&#34;&gt;Nanotargetting&lt;/h2&gt;
&lt;p&gt;Nanotargeting is a practice of using PPC ad platform to narrow down the ad targetting to very small number of people that we want
to influence, perhaps as little as one person. There&amp;rsquo;s a &lt;a href=&#34;https://arxiv.org/pdf/2110.06636.pdf&#34;&gt;research&lt;/a&gt; finding that
4 rarest Facebook interests of a person makes them unique in the user base with 90% probability.&lt;/p&gt;
&lt;h1 id=&#34;manufacturing-social-proof&#34;&gt;Manufacturing social proof&lt;/h1&gt;
&lt;h2 id=&#34;fake-followers&#34;&gt;Fake followers&lt;/h2&gt;
&lt;p&gt;Bot or clickworker accounts can be used to artificially inflate the seeming popularity of social media account and fake social proof.&lt;/p&gt;
&lt;h2 id=&#34;fake-engagement&#34;&gt;Fake engagement&lt;/h2&gt;
&lt;p&gt;Fake likes, comments and views can also be used to fake social proof.&lt;/p&gt;
&lt;h2 id=&#34;astroturfing&#34;&gt;Astroturfing&lt;/h2&gt;
&lt;p&gt;Astroturfing is a practice that attempts to influence public discourse on product or public issue by having paid shills or
bots post on public forums and social media platforms as if they were honest, concerned citizens or members of the community.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>You probably don&#39;t need AWS and are better off without it</title>
      <link>https://www.trickster.dev/post/you-probably-dont-need-aws-and-are-better-off-without-it/</link>
      <pubDate>Wed, 20 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/you-probably-dont-need-aws-and-are-better-off-without-it/</guid>
      <description>&lt;p&gt;Amazon Web Services (AWS) is the most prominent large cloud provider in the world, offering what seems to
be practically unlimited scalability and flexibility. It is heavily marketed towards startups and big companies
alike. There&amp;rsquo;s even a version of AWS meant for extra-secure governmental use (AWS for Government). There&amp;rsquo;s an
entire branch of DevOps industry that helps setting up software systems on AWS and a lot of people are
making money by using AWS in some capacity. AWS isn&amp;rsquo;t just a VPS provider or web hosting service -
there&amp;rsquo;s entire periodic table of things they offer, ranging from commodity stuff such as virtual private
servers (EC2) or data storage (S3) to cutting edge experimental technologies such as quantum computation
(Amazon Braket) and artificial intelligence (SageMaker, Rekognition). It is simply too significant to be ignored.&lt;/p&gt;
&lt;p&gt;However, is it actually that great? Is it actually good as default choice to host a software system of any scale?
Let us go through some arguments against going all-in with AWS and why many companies and individual side-project
developers might be better off using something else.&lt;/p&gt;
&lt;h2 id=&#34;pricing-complexity-and-billing-surprises&#34;&gt;Pricing complexity and billing surprises&lt;/h2&gt;
&lt;p&gt;If you read AWS discussions on Hacker News or other developer communities you will find that the issue of billing
keeps popping up. AWS billing is a lot like taxes in USA - it depends on so many things and you can never be sure
how much you will be paying when the month is over. Furthermore, AWS billing is uncapped, which has significant
potential for trouble.&lt;/p&gt;
&lt;p&gt;If you are using something that is billed based on traffic or resource usage (Lambda, Step Functions) you may have
a very bad surprise in otherwise good situation of your project getting viral traction. Imagine a sudden wave of
new users becoming the very reason of SaaS startup going bankrupt.&lt;/p&gt;
&lt;p&gt;There are also ways to mess up your AWS Lambda &amp;ldquo;serverless&amp;rdquo; functions in ways that can rack up huge bills. Certain
bugs in code, such as infinite loop or bottomless recursion can make you go broke in a single monday morning.&lt;/p&gt;
&lt;p&gt;Another way to get a billing surprise is to provision some resources and forget to take them down. A HN user
&lt;a href=&#34;https://news.ycombinator.com/item?id=22719573&#34;&gt;reports&lt;/a&gt; a story of someone who burned 80 grand overnight
by provisioning bunch of EC2 instances for testing and leaving them on. If you have non-trivial setup on AWS
(e.g. VPC with multiple EC2 instances, some of which exposed to public internet) and are relying on AWS
Dashboard or AWS CLI to tear it down when no longer needed it is very easy to forget to remove some part
of it (e.g. NAT gateway) and be billed for it even if it&amp;rsquo;s no longer used.&lt;/p&gt;
&lt;p&gt;Yet another way to lose a lot of money is to get your AWS credentials stolen by bad people that will
use your account for their own purposes, such as cryptocurrency mining. There&amp;rsquo;s multiple ways this can
happen. Someone might exploit a vulnerability in your app to access source code or configuration file
with AWS secrets. If you publish your code on public Github repo you might accidentally leak it yourself.&lt;/p&gt;
&lt;p&gt;Now you could say that the above things are accidents, not unlike a car crash or fire in the office.
However, AWS pricing is significantly complex for specialist AWS cost optimisation consultants to
exist in the market. Depending on how exactly you are using the AWS infra, you may be be overpaying
significantly for what you are getting, especially if you consider options to host your systems
outside AWS.&lt;/p&gt;
&lt;p&gt;One might say that it is possible to use CloudWatch alerts to fire on billing thresholds and get alerted.
However, the problem is that billing data is updated &lt;em&gt;after&lt;/em&gt; resources are consumed and it might be too
late at that point. In fact, AWS has no reliable way to shut down your systems in the event of runaway
costs.&lt;/p&gt;
&lt;p&gt;Also one may respond that at least in some cases you can contact AWS support and have them write off
the surprise charges, even if they occured entirely due to your own mistake. That is indeed true to
my own experience. I was able to get $100+ written off when I forgot to remove a Private CA after
being done with it. Some people report being able not to pay multiple thousands of dollars in surprise
charges. However, this is entirely dependent on the goodwill of a very large corporation that is
not known to treat their employees well and there might an explicit limit on how many times you can
ask for it. It is not something to rely on.&lt;/p&gt;
&lt;p&gt;You might be saying that AWS is actually free up to a point due to the presence of free tier,
free trials for some services and generous credits being given out to startups. But here&amp;rsquo;s the thing -
AWS is not providing free services out of goodness of their hearts. AWS giving out some tens of
thousands of free credits to every single startup in certain accelerators is not unlike to a drug
dealer giving out free hits in a community susceptible to drug abuse. It is done in a calculated
manner so that customer life time value is very worthwhile on average case, even if some services
are provided for free initially. By taking up the free AWS offer you might be saving money short
term only to overpay in long term.&lt;/p&gt;
&lt;h2 id=&#34;vendor-lock-in&#34;&gt;Vendor lock-in&lt;/h2&gt;
&lt;p&gt;AWS provides a seeming flexibility to develop cloud-native software. However, if you rely on AWS
APIs and services too much (for example by implementing a bulk of your codebase in Lambda functions)
AWS will become a hard, critical dependency to your systems. Problems with AWS infra can quickly
spread to your side. This was particularly evident during the outages in recent years that disrupted
significant portions of the web. You will not be able to address these problems quickly if you
rely on AWS to work properly all the time (which it does not), unless you have a multi-cloud
setup that is deliberately designed to be resilient. Furthermore, AWS may decide that they hate
your app/company/project for whatever reason and cut you off, even if it is based on a false
positive in their anti-fraud analytics. If your code is written in such way
that it works on AWS or not at all this can be fatal to the project.&lt;/p&gt;
&lt;p&gt;This can be largely avoided by limiting the code and infra dependencies to things that can be
replaced with acceptable cost, such as basic EC2 instances with mainstream Linux distributions
and open source equivalents to AWS stuff e.g. PostgreSQL instead of DynamoDB,
MinIO instead of S3 and so on. The flipside of this approach is that by avoiding AWS-specific services
and tools we maybe also be not getting the maximum value from AWS. For example, if all you use
are vanilla servers with Debian Linux you don&amp;rsquo;t truly need AWS - there are simpler and
cheaper ways to set them up.&lt;/p&gt;
&lt;h2 id=&#34;devops-complexity&#34;&gt;Devops complexity&lt;/h2&gt;
&lt;p&gt;Last but not least, there&amp;rsquo;s devops complexity. Web interface of AWS is not exactly known for it&amp;rsquo;s usability.
Developers and technical users would probably prefer AWS CLI. For things like automated CI/CD pipelines
we can use client libraries (such as boto3 for Python) or infrastructure-as-code tools such as Ansible,
Terraform, Puppet, Chef. This makes things easier in some ways as we only need to get the configuration
or script right once and we can benefit from it in the future without having to remember how to perform tedious manual
steps through a browser. However, using Python or Terraform for has its own challenges and may turn out
to be harder than it looks.&lt;/p&gt;
&lt;p&gt;For example, consider setting up a Kubernetes cluster on AWS. It used to be that we would need to use
solution like &lt;a href=&#34;https://kubernetes.io/docs/setup/production-environment/tools/kops/&#34;&gt;kops&lt;/a&gt; to do it, but
now AWS offers EKS - a managed Kubernetes service. So it should be fairly easy, right? Well, not so quick.
Setting up EKS cluster with actual compute is far from one-click (or one Unix command) process. Configuring
VPC for EKS cluster is harder than one expect it should be. We can do this through Terraform, as AWS
is officially supported through Terraform adapter. This cuts down the waste of effort, but when setting up
VPC for EKS cluster I had to make a compromise and use &lt;code&gt;aws_cloudformation_stack&lt;/code&gt; resource to pull in
the example CloudFormation configuration and create VPC from that. This worked fairly well, but there was
a degree of impendance mismatch between Terraform stuff and CloudFormation stuff. This manifested during
infrastructure teardown - some networking resources (NAT gateways, subnets) could not always be cleaned up
due to what appeared to Terraform to be circular dependencies. Running &lt;code&gt;terraform destroy&lt;/code&gt; would fail with
an error and I would be left with the task of fighting some errors through AWS console to make sure everything
is cleanly removed. After some fiddling, this was solved by using &lt;code&gt;depends_on&lt;/code&gt; statements in Terraform configuration.&lt;/p&gt;
&lt;p&gt;Digital Ocean also offers a managed Kubernetes service that you can set up within minutes by going through
a simple process consisting of few steps. It&amp;rsquo;s not exactly one-click, but close to that. At the end, you
also end up with a functioning Kubernetes cluster, but there&amp;rsquo;s far less hassle getting there. Compare
and contrast this with AWS EKS, which may easily eat up a day of your time, especially if you&amp;rsquo;re not
upfront familiar with it. There&amp;rsquo;s a tool called &lt;a href=&#34;https://eksctl.io/&#34;&gt;eksctl&lt;/a&gt; to make it easier, but
setting up EKS cluster is generally much harder than using DOKS.&lt;/p&gt;
&lt;p&gt;People may be saying that a company using AWS is saving money as the alternative would be getting a server
that would not only incur capital expenses of buying the server hardware, but also operational expenses
to have a systems administrator employed so that the server would be properly managed. One may question
if that is true simply because using AWS at scale is not exactly cheap operationally. Furthermore,
chances are the company is not saving money on employees as managing stuff on AWS is not easy; it&amp;rsquo;s just
that the effort is spread across multiple people in the development team.&lt;/p&gt;
&lt;h2 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;So if we don&amp;rsquo;t want to use AWS, what are the alternatives?&lt;/p&gt;
&lt;p&gt;One option is to purchase a hardware server and keep it on premises. Managing server hardware is becoming
an increasingly specialised skillset and thus it might be challenging to find someone able to do this
professionally. Fortunately, companies like Hetzner will do it for us for a monthly fee and will provide
a dedicated server for installing anything we like. We can install Kubernetes or Proxmox or anything else
and make our own cloud based on rented server(s). Remember: there is no cloud, just the other peoples
computers.&lt;/p&gt;
&lt;p&gt;Furthermore, there are many smaller and cheaper cloud providers that are more than enough for small-medium
scale projects (Digital Ocean, Vultr, Linode, etc.). Some consumers of AWS kool aid think that to start your
own SaaS you need 100k in AWS credits, but the reality is that many web apps can be developed to be
fairly simple and lightweight. There are indie developers spending less than $100 monthly to run a SaaS
app or website that makes them fairly good money.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Compiling Python programs with Pyinstaller</title>
      <link>https://www.trickster.dev/post/compiling-python-programs-with-pyinstaller/</link>
      <pubDate>Tue, 19 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/compiling-python-programs-with-pyinstaller/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://pyinstaller.org/en/stable/&#34;&gt;Pyinstaller&lt;/a&gt; is a CLI tool that compiles Python scripts into executable binaries,
installable through PIP. Let us go through a couple of examples of using this tool.&lt;/p&gt;
&lt;p&gt;SMTP enumeration script from &lt;a href=&#34;https://www.trickster.dev/post/smtp-user-enumeration-for-fun-and-profit/&#34;&gt;previous post&lt;/a&gt; can be trivially compiled
with this tool by running the following commnd:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pyinstaller smtp_enum.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This creates two directories - build/ for intermediate files and dist/ for the results of compilation. However we
find that dist/ contains multiple binary files, whereas it is generally more convenient to compile it into single
file that statically links all the dependencies. Adding &lt;code&gt;--onefile&lt;/code&gt; argument to our command tells the Pyinstaller
to do so:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pyinstaller --onefile smtp_enum.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On Windows this create an .exe file; on Linux and macOS it will create a platform specific binary. Cross-compilation
is not supported.&lt;/p&gt;
&lt;p&gt;However things will not always be so simple. We may want to compile something like the following code that imports
third party modules and has a GUI with image to be loaded from the file. This code is largely based on someone
elses &lt;a href=&#34;https://github.com/flatplanet/Intro-To-TKinter-Youtube-Course/blob/master/bitcoin.py&#34;&gt;educational example&lt;/a&gt;
that I took the liberty to update and clean up a little. It will scrape Bitcoin price from Coindesk and show it
in a GUI window.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; tkinter &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;previous &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Grab the bitcoin price&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Update&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; previous
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.coindesk.com/price/bitcoin&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_large &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//span[contains(@class, &amp;#34;briNjb&amp;#34;)]&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_large &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; price_large&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(price_large)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Update our bitcoin label&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bit_label&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;price_large)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Set timer to 30 seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 1 second = 1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;after(&lt;span style=&#34;color:#ae81ff&#34;&gt;30000&lt;/span&gt;, Update)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Get Current Time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    now &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;now()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    current_time &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; now&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%I:%M:%S %p&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Update the status bar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status_bar&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Last Updated: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;current_time&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Determine Price Change&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# grab current Price&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    current &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; price_large
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# remove the comma&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    current &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; current&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; previous &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; float(previous) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; float(current):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            latest_price&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Price Down &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;round(float(previous)&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;float(current), &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;red&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; float(previous) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; float(current):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            latest_price&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Price Unchanged&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grey&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            latest_price&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Price Up &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;round(float(current)&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;float(previous), &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;green&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        previous &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; current
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        latest_price&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Price Unchanged&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grey&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; root
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; bit_label
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; status_bar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; previous
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;global&lt;/span&gt; latest_price
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Tk()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;title(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bitcoin Price Grabber&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;geometry(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;550x210&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config(bg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    now &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;now()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    current_time &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; now&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%I:%M:%S %p&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my_frame &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Frame(root, bg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my_frame&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pack(pady&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logo &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; PhotoImage(file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/bitcoin.png&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logo_label &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Label(my_frame, image&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;logo, bd&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logo_label&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grid(row&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, column&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, rowspan&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bit_label &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Label(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        my_frame, text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TEST&amp;#34;&lt;/span&gt;, font&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Helvetica&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;45&lt;/span&gt;), bg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;green&amp;#34;&lt;/span&gt;, bd&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bit_label&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grid(row&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, column&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, padx&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, sticky&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    latest_price &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Label(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        my_frame, text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;move test&amp;#34;&lt;/span&gt;, font&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Helvetica&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;), bg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;, fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grey&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    latest_price&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grid(row&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, column&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, sticky&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Create status bar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status_bar &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Label(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        root,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        text&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Last Updated &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;current_time&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bd&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        anchor&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;E,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grey&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status_bar&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pack(fill&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;X, side&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;BOTTOM, ipady&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# On program start, run update function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Update()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mainloop()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As our first attempt, we run the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pyinstaller --onefile bitcoin.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compilation succeeds, but the program crashes due to not being able to find an image file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cd dist/
$ ./bitcoin 
Traceback (most recent call last):
  File &amp;#34;bitcoin.py&amp;#34;, line 113, in &amp;lt;module&amp;gt;
  File &amp;#34;bitcoin.py&amp;#34;, line 80, in main
  File &amp;#34;tkinter/__init__.py&amp;#34;, line 4064, in __init__
  File &amp;#34;tkinter/__init__.py&amp;#34;, line 4009, in __init__
_tkinter.TclError: couldn&amp;#39;t open &amp;#34;images/bitcoin.png&amp;#34;: no such file or directory
[26587] Failed to execute script &amp;#39;bitcoin&amp;#39; due to unhandled exception!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is because we have a hardcoded relative path to the file that the program is not generally
able to resolve. It would be best if the image file was compiled into executable as well.
Turn out, according to a certain helpful &lt;a href=&#34;https://stackoverflow.com/a/54926684&#34;&gt;StackOverflow answer&lt;/a&gt;
there are two simple fixes we need to do (altough the question was about compiling PyGame-based
game that includes graphical and sound assets, the answer can be applied for the scripts
like the one we are dealing with).&lt;/p&gt;
&lt;p&gt;The first fix is to introduce a following function to resolve a relative path to an
asset file, then making sure that all code uses it instead of trying to access hardcoded paths
directly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resource_path&lt;/span&gt;(relative_path):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# PyInstaller creates a temp folder and stores path in _MEIPASS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        base_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_MEIPASS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        base_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;abspath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(base_path, relative_path)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the script in question, we only need to update the &lt;code&gt;PhotoImage()&lt;/code&gt; constructor call to use
&lt;code&gt;resource_path()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logo &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; PhotoImage(file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;resource_path(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/bitcoin.png&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we rerun the command from before:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pyinstaller --onefile bitcoin.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, the binary executable file still crashes when we try to run it. The second fix we have to
do is to update bitcoin.spec file that originally looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# -*- mode: python ; coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;block_cipher &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Analysis(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bitcoin.py&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pathex&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    binaries&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    datas&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hiddenimports&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hookspath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hooksconfig&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    runtime_hooks&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    excludes&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    win_no_prefer_redirects&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    win_private_assemblies&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cipher&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;block_cipher,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    noarchive&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pyz &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; PYZ(a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pure, a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;zipped_data, cipher&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;block_cipher)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exe &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; EXE(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pyz,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;scripts,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;binaries,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;zipfiles,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;datas,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bitcoin&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    debug&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bootloader_ignore_signals&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    strip&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    upx&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    upx_exclude&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    runtime_tmpdir&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    disable_windowed_traceback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    argv_emulation&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    target_arch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    codesign_identity&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    entitlements_file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Think of this file as a build configuration file. Like we can see, it specifies a script name and various build options.
We need to update &lt;code&gt;datas&lt;/code&gt; line with a list of tuples for each asset (1 entry in our case). First element in the tuple
is source path of the asset. Second element is destination directory path. We want these to be consistent, as
launching the Python script from binary version and from the source should load exactly the same file. Thus we update
this line as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    datas&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/bitcoin.png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;images/&amp;#34;&lt;/span&gt;)],
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can compile the script based on the spec file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pyinstaller bitcoin.spec
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This generates a functional executable we can run. On macOS, it even creates an .app package we can copy into /Applications
directory.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-04-19_20.31.10.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Notes on TBHM v4 recon edition</title>
      <link>https://www.trickster.dev/post/notes-on-tbhm-v4-recon-edition/</link>
      <pubDate>Thu, 14 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/notes-on-tbhm-v4-recon-edition/</guid>
      <description>&lt;p&gt;This post will summarize &lt;a href=&#34;https://www.youtube.com/watch?v=qLTe6Z10vj8&#34;&gt;The Bug Hunter&amp;rsquo;s Methodology v4.01: Recon edition&lt;/a&gt; -
a talk at h@activitycon 2020 by Jason Haddix, a prominent hacker in the bug bounty community. Taking notes is important when
learning new things and therefore notes were taken for future reference of this material. This methodology represents
breadth-first approach to bounty hunting and is meant to provide a reproducible strategy to discover as many assets
related to the target as possible (but make sure to heed scope!). It will involve both OSINT and some active scanning
of web/IT systems.&lt;/p&gt;
&lt;p&gt;Jason starts his talk by saying that you should takes notes during your recon activities as well. He uses &lt;a href=&#34;https://www.xmind.net/&#34;&gt;XMind&lt;/a&gt;
mind mapping tool to organise information being gathered, but pretty much anything that allows you to take notes and
organise them in a tree structure will do (Vim, spreadsheets, etc.). This way of taking notes mirrors the way
target recon is conducted - we will have some initial questions that will branch out into further things
we will need to look into to broaden our knowledge about the target.&lt;/p&gt;
&lt;p&gt;The root element of tree for each project (e.g.  bounty program we are working on) should be marked with project name. This
root element will have two child nodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Root/seed domains&lt;/li&gt;
&lt;li&gt;Recon&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under &amp;ldquo;Root/seed domains&amp;rdquo; we are going to put the initial domain names for bounty program that we find on e.g.
HackerOne program page when starting the process. Note that some bug bounty programs also include wildcard
entries such as &lt;code&gt;*.tesla.com&lt;/code&gt; and may even say that any host verifiably owned by the company is in scope.
Thus we may need to do further research to enumerate an exact list of (sub)domains covered by a bounty
program.&lt;/p&gt;
&lt;p&gt;Under &amp;ldquo;Recon&amp;rdquo; element we have some initial research questions that we will use
OSINT and other recon techniques to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ASNs - what autonomous systems does the company have?&lt;/li&gt;
&lt;li&gt;Acquisitions - what companies were acquired by company of interest?&lt;/li&gt;
&lt;li&gt;Linked discovery - what other assets can be found that are linked by and to company being looked into?&lt;/li&gt;
&lt;li&gt;Reverse whois - what domains belonging to a company can be found through a reverse whois tool?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Acquisitions are of interest to find new assets and seed/root domains as the systems formerly
belonging to acquired company now belong to the acquiring company, thus possibly falling into
the scope of a bug bounty program. Information on acquisitions can be found on sources such
as &lt;a href=&#34;https://www.crunchbase.com/&#34;&gt;Crunchbase&lt;/a&gt;, Wikipedia or by simply Googling around.&lt;/p&gt;
&lt;p&gt;Autonomous systems are large chunks or internet that are identified by Autonomous System
Numbers (ASNs) and are administered by large organisations. Huricane Electric provides
&lt;a href=&#34;https://bgp.he.net/&#34;&gt;BGP toolkit&lt;/a&gt; portal that we can use to try and find ASNs belonging to
a company we are looking into. Technically this can be scraped automatically but due to
similarities between company names it&amp;rsquo;s best to use human intelligence for this step.
Each AS we find will have IP address ranges associated to it. However, if you do want
to automate it Jason recommends the following tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/yassineaboukir/Asnlookup&#34;&gt;ASNlookup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/j3ssie/metabigor&#34;&gt;metabigor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we have some ASNs we can run &lt;a href=&#34;https://github.com/OWASP/Amass&#34;&gt;Amass&lt;/a&gt; in intel mode
to discover more seed domains.&lt;/p&gt;
&lt;p&gt;Another way to discover more seed domains is to do reverse whois through something like
&lt;a href=&#34;https://whoxy.com/&#34;&gt;Whoxy&lt;/a&gt;. We can do it manually through the web interface, but it
also has an API for intergration with our automations. &lt;a href=&#34;https://github.com/vysecurity/DomLink&#34;&gt;DOMLink&lt;/a&gt;
is one of the tools we can use to query Whoxy API for domain OSINT purposes.&lt;/p&gt;
&lt;p&gt;Yet another thing that may help us discover assets related to a target is by linking
assets through ad pixels/analytics tracking codes. This can be done through
BuiltWith relationships pages, e.g.: &lt;a href=&#34;https://builtwith.com/relationships/twitch.tv&#34;&gt;https://builtwith.com/relationships/twitch.tv&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;We can also use Google search with unique parts of legal pages (copyrights text, etc.)
to find more hosts/domains. Furthermore, searching for company name or primary domain
on &lt;a href=&#34;https://shodan.io/&#34;&gt;Shodan&lt;/a&gt; will reveal various assets related to the target.&lt;/p&gt;
&lt;p&gt;Jason also recommends to use Burp Suite or other tool to discover (sub)domains by
recursively crawling sites in a way that links are extracted not just from
HTML pages but from JavaScript code as well. We would start with site(s) we already know
from seed domains and traverse links until we have no new subdomains left.
If you don&amp;rsquo;t want GUI tool this can be done with &lt;a href=&#34;https://github.com/jaeles-project/gospider&#34;&gt;GoSpider&lt;/a&gt;
or &lt;a href=&#34;https://github.com/hakluke/hakrawler&#34;&gt;Hakrawler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/nsonaniya2010/SubDomainizer&#34;&gt;Subdomainizer&lt;/a&gt;
will extract not only HTML and JS links from the page, but will also compute
Shannon entropy to find things that look like passwords or API keys. It will also
find things like S3 buckets being linked from the page. However this tool only works
on single pages. &lt;a href=&#34;https://github.com/Cillian-Collins/subscraper&#34;&gt;Subscraper&lt;/a&gt; is a similar
tool that also implements the crawling aspect.&lt;/p&gt;
&lt;p&gt;There are plenty other sources to scrape target subdomains from. For example, to
enumerate Twitch subdomains with Google one might search for pages on initial domain
first, then use minus operator to exclude each new domain until there are no
new domains found:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;site:twitch.tv 
site:twitch.tv -www.twitch.tv
site:twitch.tv -www.twitch.tv -watch.twitch.tv
site:twitch.tv -www.twitch.tv -watch.twitch.tv -dev.twitch.tv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Furthermore, subdomains can be scraped from various sources by tools
like &lt;a href=&#34;https://github.com/OWASP/Amass&#34;&gt;Amass&lt;/a&gt; and &lt;a href=&#34;https://github.com/projectdiscovery/subfinder&#34;&gt;Subfinder&lt;/a&gt;.
Amass can also link the subdomains it finds with Autonomous Systems,
which may provide new IP ranges to scan.&lt;/p&gt;
&lt;p&gt;You may also scrape Github API with script like &lt;a href=&#34;https://github.com/gwen001/github-search&#34;&gt;github-search&lt;/a&gt;
to dig up subdomains or source them from Shodan API with tool like
&lt;a href=&#34;https://github.com/incogbyte/shosubgo&#34;&gt;shosubgo&lt;/a&gt;. This approach will require
you to get the relevant API keys and heed rate limits.&lt;/p&gt;
&lt;p&gt;Lastly, one might monitor IP ranges of public cloud providers, looks
for servers that talk HTTPS and get domain names from their certificates. This
approach can be implemented by writing script that utilizes Masscan, but
Jason recommends &lt;a href=&#34;https://tls.bufferover.run/&#34;&gt;tls.bufferover.run&lt;/a&gt;
SaaS API that does this for you.&lt;/p&gt;
&lt;p&gt;Besides subdomain scraping, there&amp;rsquo;s subdomain bruteforcing, which is an activity
of automatically guessing subdomain names based on wordlist or by simply
iterating across character permutations. When bruteforcing, we check subdomain
guesses against real DNS servers. This can also be done with
Amass. Another tool to do this is &lt;a href=&#34;https://github.com/blechschmidt/massdns.git&#34;&gt;Massdns&lt;/a&gt;.
If you use a wordlist it is important to make sure that it is large enough.
There are multiple wordlists available on Github from red team/pentesting/
bug bounty space.&lt;/p&gt;
&lt;p&gt;Sometimes subdomains you come across will have a pattern in their name, e.g.:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dev.company.com
dev1.company.com
dev2.company.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/infosec-au/altdns&#34;&gt;Altdns&lt;/a&gt; is a tool developed to scan
for this kind of subdomains. Jason calls this technique &amp;ldquo;alteration scanning&amp;rdquo;.
It can be used to find ways to bypass Web Application Firewall or the origin
server that is meant to be hidden behind the CDN.&lt;/p&gt;
&lt;p&gt;When we have enumerated all the subdomains and Autonomous Systems, it is time to
do some port scannning. Jason recommends the following approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run masscan to discover open ports on IP ranges. You may want to use wrapper
script to resolve domain names and scan IPs based on DNS responses, as
masscan does not implement this on it&amp;rsquo;s own.&lt;/li&gt;
&lt;li&gt;Run Nmap service detection on masscan results. Output nmap results in a greppable
format.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://github.com/x90skysn3k/brutespray&#34;&gt;brutespray&lt;/a&gt; to check the above
for remote admin services that use default credentials.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Another thing we want to check is information leaks on Github. Like Google
and Shodan, Github can be searched with clever search queries to find things
relevant to security. Jason provides a simple &lt;a href=&#34;https://gist.github.com/jhaddix/1fb7ab2409ab579178d2a79959909b33&#34;&gt;shell script&lt;/a&gt;
that prints bunch of Github search links that you can check and also
recommends watching &lt;a href=&#34;https://www.youtube.com/watch?v=l0YsEk_59fQ&#34;&gt;GitHub Recon and Sensitive Data Exposure&lt;/a&gt;
from Bugcrowd University.&lt;/p&gt;
&lt;p&gt;If you have a bunch of domains that may or may not have HTTP(S) services exposed
you may want to iterate across them and take screenshots of their front pages.
There are several tools for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/michenriksen/aquatone&#34;&gt;Aquatone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/breenmachine/httpscreenshot&#34;&gt;HTTPScreenshot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/FortyNorthSecurity/EyeWitness&#34;&gt;Eyewitness&lt;/a&gt; - also provides server header info and some other intel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This practice is known as &amp;ldquo;domain flyover&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Last thing to check are subdomain takeover opportunities. Subdomain takeover involves
finding a still valid CNAME DNS record for a subdomain that used to point to a third
party service, but no longer does and re-registering that service with the old subdomain can be done
to cause some shenanigans (or to demonstrate the impact of security issue).  There&amp;rsquo;s a Github
repo called &lt;a href=&#34;https://github.com/EdOverflow/can-i-take-over-xyz&#34;&gt;can-i-take-over-xyz&lt;/a&gt;
that you may want to look into. &lt;a href=&#34;https://github.com/projectdiscovery/nuclei&#34;&gt;Nuclei&lt;/a&gt;
vulnerability scanner provides some automations (templates) to demonstrate this
issue.&lt;/p&gt;
&lt;p&gt;We have covered a lot of tools and techiques. Target recon can be a rather extensive process
and therefore it is highly desirable to automate as much of it as possible.
Many of the tools we have discussed are single threaded and do not take CIDR ranges or
file globs directly. &lt;a href=&#34;https://github.com/codingo/Interlace&#34;&gt;Interlace&lt;/a&gt; is a general
purpose wrapper script that wraps simpler tools to make them effectively
multithreaded in order to use them for larger scale recon operations.&lt;/p&gt;
&lt;p&gt;When discussing further automation of recon process, Jason introduces the
four levels of recon frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C-level: basic Bash or Python scripts that glue together bunch of CLI tools.&lt;/li&gt;
&lt;li&gt;B-level: somewhat modular automation systems that runs point-in-time and
saves results into flat files. May have some GUI.&lt;/li&gt;
&lt;li&gt;A-level: fully modular automation systems with GUI that can continuously
monitor target systems. Has GUI and integrates a proper database.&lt;/li&gt;
&lt;li&gt;S-level: advanced automation systems that have all features in A-level with
scalability across multiple servers and AI/ML-based recon techniques.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://drive.google.com/file/d/1aG_qqRvNW-s5_8vvPk5rJiMSMeNL2uY9/view&#34;&gt;Presentation slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ceos3c.com/wp-content/uploads/2020/06/Bug-Hunter-Methodology-V4-Visualization.pdf&#34;&gt;Visualisation by ceos3c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=uKWu6yhnhbQ&#34;&gt;DefCon Red Team Village workshop&lt;/a&gt; - Jason goes through key steps of methodology and demonstrates the tooling.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=-FAjxUOKbdI&#34;&gt;How to shot web&lt;/a&gt; - a presentation at DEFCON back in 2015 that is predecessor of this methodology.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Writing web scrapers in Go with Colly framework</title>
      <link>https://www.trickster.dev/post/writing-web-scrapers-in-go-with-colly-framework/</link>
      <pubDate>Sat, 09 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/writing-web-scrapers-in-go-with-colly-framework/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;http://go-colly.org/&#34;&gt;Colly&lt;/a&gt; is a web scraping framework for Go programming language. The
feature set of Colly largely overlaps with that of Scrapy framework from Python ecosystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in concurrency.&lt;/li&gt;
&lt;li&gt;Cookie handling.&lt;/li&gt;
&lt;li&gt;Caching of HTTP response data.&lt;/li&gt;
&lt;li&gt;Automatic heeding of robots.txt rules.&lt;/li&gt;
&lt;li&gt;Automatic throttling of outgoing traffic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, Colly supports distributed scraping out-of-the-box through a Redis-based task queue
and can be integrated with Google App Engine. This makes it a viable choice for large-scale
web scraping projects.&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&#34;introduction-to-scrapy-framework/&#34;&gt;previous post&lt;/a&gt; we have shown how to scrape
&lt;a href=&#34;https://books.toscrape.com/&#34;&gt;books.toscrape.com&lt;/a&gt; with Scrapy framework. We will develop
a similar scraper with Colly. The following assumes that you have a working and up-to-date
Go development environment.&lt;/p&gt;
&lt;p&gt;In a new project directory we run &lt;code&gt;go mod init&lt;/code&gt; and &lt;code&gt;go get github.com/gocolly/colly&lt;/code&gt;.
Then we create main.go file with Colly being imported as dependency:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gocolly/colly&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s get some crawling going. A central Colly object that we have to interact with is
called collector. It is similar to Scrapy spider. Therefore the
first thing we do in our &lt;code&gt;main()&lt;/code&gt; function is instantiating it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewCollector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllowedDomains&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have to register some callbacks that Colly will call during scraping. By providing
code that will react to various things that happen during scraping/crawling we will implement
our scraper in event-driven way. Events we can hook into include request being sent,
HTTP error being detected, response headers being received, entire HTTP response being
received, elements matching CSS selector being detected on HTML document and
elements matching XPath query being detected on HTML/XML document.&lt;/p&gt;
&lt;p&gt;Let us start simple by printing an URL of each HTTP request just before it is sent to the
server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnRequest&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visiting %s...\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also, let us have a similar callback to all HTTP requests that we receive:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnResponse&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visited %s\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These are good for debuggability, but implement no real functionality. Let us register a
callback for CSS selector that matches category links on right-hand pane and Next page
button on the bottom. Callback functions will merely extract the URL and generate a
follow up request. We don&amp;rsquo;t need to worry about URLs possibly being relative to the
current page - Colly will fix them for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;div.side_categories * &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;li.next &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This crawls the category lists, but we also need to go into product pages. Thus we register
one more callback based on XPath query that will match a product link on category pages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnXML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//article[@class=\&amp;#34;product_pod\&amp;#34;]/h3/a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To activate Colly for crawling, we call &lt;code&gt;Visit()&lt;/code&gt; method on collector after we have finished
setting everything up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://books.toscrape.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The main.go file at this point looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gocolly/colly&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewCollector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllowedDomains&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnRequest&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visiting %s...\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;div.side_categories * &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;li.next &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnXML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//article[@class=\&amp;#34;product_pod\&amp;#34;]/h3/a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnResponse&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visited %s\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://books.toscrape.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we compile and run it we notice that crawling is performed without concurrency - just
a single page at a time. That&amp;rsquo;s because we did not enable asynchronous mode. We fix it by
updating a call to &lt;code&gt;NewCollector&lt;/code&gt; with one more argument:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewCollector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllowedDomains&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Async&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also need to call &lt;code&gt;Wait()&lt;/code&gt; method on collector at the end. Otherwise, the code will quit immediately
without scraping anything.&lt;/p&gt;
&lt;p&gt;Let us define a Go struct for the book data - this will be an equivalent for Scrapy item
class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inspection of HTML code of web pages reveal that product pages always have the following element
in them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;article&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;product_page&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Start of product page --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Category pages do not have this element. Furthemore, this &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; thing has all the elements
with data we want to extract as children. Therefore, it is desirable to make a callback with
XPath query for this element and provide a callback function that will extract all the data into
fields of Go struct we just defined:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnXML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//article[.//div[@id=\&amp;#34;product_description\&amp;#34;]]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//h1&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./p&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;upc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;UPC\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Price (excl. tax)\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Replace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;£&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceExclTax&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Price (incl. tax)\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Replace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;£&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceInclTax&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;availability&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Availability\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;nReviews&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Atoi&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Number of reviews\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;:        &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;:          &lt;span style=&#34;color:#a6e22e&#34;&gt;upc&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;priceExclTax&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;priceInclTax&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;availability&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;nReviews&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point we are not exporting scraped data anywhere and just printing it into the standard output.
We can verify that our code is correctly scraping the data:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Visited https://books.toscrape.com/catalogue/category/books/sports-and-games_17/index.html
Visiting https://books.toscrape.com/catalogue/naturally-lean-125-nourishing-gluten-free-plant-based-recipes-all-under-300-calories_479/index.html...
Visiting https://books.toscrape.com/catalogue/the-book-of-basketball-the-nba-according-to-the-sports-guy_232/index.html...
Visiting https://books.toscrape.com/catalogue/friday-night-lights-a-town-a-team-and-a-dream_158/index.html...
Visiting https://books.toscrape.com/catalogue/icing-aces-hockey-2_25/index.html...
Visiting https://books.toscrape.com/catalogue/settling-the-score-the-summer-games-1_50/index.html...
Visiting https://books.toscrape.com/catalogue/sugar-rush-offensive-line-2_108/index.html...
Visited https://books.toscrape.com/catalogue/the-genius-of-birds_843/index.html
Visited https://books.toscrape.com/catalogue/the-bridge-to-consciousness-im-writing-the-bridge-between-science-and-our-old-and-new-beliefs_840/index.html
{The Genius of Birds Birds are astonishingly intelligent creatures. In fact, according to revolutionary new research, some birds rival primates and even humans in their remarkable forms of intelligence. Like humans, many birds have enormous brains relative to their size. Although small, bird brains are packed with neurons that allow them to punch well above their weight. InThe Genius of Birds, Birds are astonishingly intelligent creatures. In fact, according to revolutionary new research, some birds rival primates and even humans in their remarkable forms of intelligence.  Like humans, many birds have enormous brains relative to their size. Although small, bird brains are packed with neurons that allow them to punch well above their weight. In The Genius of Birds, acclaimed author Jennifer Ackerman explores the newly discovered brilliance of birds and how it came about. As she travels around the world to the most cutting-edge frontiers of research— the distant laboratories of Barbados and New Caledonia, the great tit communities of the United Kingdom and the bowerbird habitats of Australia, the ravaged mid-Atlantic coast after Hurricane Sandy and the warming mountains of central Virginia and the western states—Ackerman not only tells the story of the recently uncovered genius of birds but also delves deeply into the latest findings about the bird brain itself that are revolutionizing our view of what it means to be intelligent.Consider, as Ackerman does, the Clark’s nutcracker, a bird that can hide as many as 30,000 seeds over dozens of square miles and remember where it put them several months later; the mockingbirds and thrashers, species that can store 200 to 2,000 different songs in a brain a thousand times smaller than ours; the well-known pigeon, which knows where it’s going, even thousands of miles from familiar territory; and the New Caledonian crow, an impressive bird that makes its own tools. But beyond highlighting how birds use their unique genius in technical ways, Ackerman points out the impressive social smarts of birds. They deceive and manipulate. They eavesdrop. They display a strong sense of fairness. They give gifts. They play keep-away and tug-of-war. They tease. They share. They cultivate social networks. They vie for status. They kiss to console one another. They teach their young. They blackmail their parents. They alert one another to danger. They summon witnesses to the death of a peer. They may even grieve. This elegant scientific investigation and travelogue weaves personal anecdotes with fascinating science. Ackerman delivers an extraordinary story that will both give readers a new appreciation for the exceptional talents of birds and let them discover what birds can reveal about our changing world. Incredibly informative and beautifully written, The Genius of Birds richly celebrates the triumphs of these surprising and fiercely intelligent creatures. ...more 42d0d7c92b75fc1c 17.24 17.24 In stock (15 available) 0}
Visited https://books.toscrape.com/catalogue/the-death-of-humanity-and-the-case-for-life_932/index.html
Visited https://books.toscrape.com/catalogue/love-lies-and-spies_622/index.html
Visited https://books.toscrape.com/catalogue/the-stranger_861/index.html
Visited https://books.toscrape.com/catalogue/proofs-of-god-classical-arguments-from-tertullian-to-barth_538/index.html
{The Death of Humanity: and the Case for Life Do you believe human life is inherently valuable? Unfortunately, in the secularized age of state-sanctioned euthanasia and abortion-on-demand, many are losing faith in the simple value of human life. To the disillusioned, human beings are a cosmic accident whose intrinsic value is worth no more than other animals.The Death of Humanity explores our culture&amp;#39;s declining respe Do you believe human life is inherently valuable? Unfortunately, in the secularized age of state-sanctioned euthanasia and abortion-on-demand, many are losing faith in the simple value of human life. To the disillusioned, human beings are a cosmic accident whose intrinsic value is worth no more than other animals.The Death of Humanity explores our culture&amp;#39;s declining respect for the sanctity of human life, drawing on philosophy and history to reveal the dark road ahead for society if we lose our faith in human life. ...more bd261725b99f5983 58.11 58.11 In stock (16 available) 0}
{Love, Lies and Spies Juliana Telford is not your average nineteenth-century young lady. She’s much more interested in researching ladybugs than marriage, fashionable dresses, or dances. So when her father sends her to London for a season, she’s determined not to form any attachments. Instead, she plans to secretly publish their research.Spencer Northam is not the average young gentleman of lei Juliana Telford is not your average nineteenth-century young lady. She’s much more interested in researching ladybugs than marriage, fashionable dresses, or dances. So when her father sends her to London for a season, she’s determined not to form any attachments. Instead, she plans to secretly publish their research.Spencer Northam is not the average young gentleman of leisure he appears. He is actually a spy for the War Office, and is more focused on acing his first mission than meeting eligible ladies. Fortunately, Juliana feels the same, and they agree to pretend to fall for each other. Spencer can finally focus, until he is tasked with observing Juliana’s traveling companions . . . and Juliana herself. ...more 9546d537fbf99eb6 20.55 20.55 In stock (12 available) 0}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We miss the data exporting part, so let us implement writing book data to CSV files. We open a new
file for writing and instantiate CSV writer object from &lt;code&gt;encoding/csv&lt;/code&gt; package:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;outF&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OpenFile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDWR&lt;/span&gt;|&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_CREATE&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0755&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;csv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewWriter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;outF&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this CSV writer only takes slices of string for each row we have to explicitly write out
the header row:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Write&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price_excl_tax&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price_incl_tax&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;availability&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n_reviews&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, we have &lt;code&gt;Book&lt;/code&gt; struct that we instantiate for each product page we have scraped. Let us
implement a helper method that will return CSV field values in the order that match the columns:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;ToCSVRow&lt;/span&gt;() []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One more thing we need to set up is a mutex that will prevent multiple writes to the file in parallel
that may result in some messed up CSV lines. We just need to declare a variable for it during preparation
to scrape.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mutex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We update the final part of callback function to lock the mutex, write out CSV row and then unlock the mutex:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Lock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Write&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ToCSVRow&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unlock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The entire scraper code we have implemented is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;encoding/csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;strconv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gocolly/colly&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;ToCSVRow&lt;/span&gt;() []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%.2f&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewCollector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AllowedDomains&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.toscrape.com&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Async&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnRequest&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visiting %s...\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;div.side_categories * &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;li.next &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnHTML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;article.product_pod &amp;gt; h3 &amp;gt; a&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HTMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Attr&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;outF&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OpenFile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;books.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_RDWR&lt;/span&gt;|&lt;span style=&#34;color:#a6e22e&#34;&gt;os&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;O_CREATE&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0755&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mutex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;csv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewWriter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;outF&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Write&lt;/span&gt;([]&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;upc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price_excl_tax&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price_incl_tax&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;availability&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n_reviews&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnXML&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//article[@class=\&amp;#34;product_page\&amp;#34;]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLElement&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//h1&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./p&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;upc&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;UPC\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Price (excl. tax)\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Replace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;£&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceExclTax&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrExclTax&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Price (incl. tax)\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Replace&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;£&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;priceInclTax&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ParseFloat&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;priceStrInclTax&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;availability&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Availability\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;nReviews&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strconv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Atoi&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ChildText&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//tr[./th[contains(text(), \&amp;#34;Number of reviews\&amp;#34;)]]/td&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;:        &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Description&lt;/span&gt;:  &lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;UPC&lt;/span&gt;:          &lt;span style=&#34;color:#a6e22e&#34;&gt;upc&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceExclTax&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;priceExclTax&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;PriceInclTax&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;priceInclTax&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;Availability&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;availability&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;NReviews&lt;/span&gt;:     &lt;span style=&#34;color:#a6e22e&#34;&gt;nReviews&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Lock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Write&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;book&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ToCSVRow&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;mtx&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unlock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;OnResponse&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;colly&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Visited %s\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Visit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://books.toscrape.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Wait&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;csvWriter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Flush&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;outF&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Creating DC proxies on cloud providers</title>
      <link>https://www.trickster.dev/post/creating-dc-proxies-on-cloud-providers/</link>
      <pubDate>Sun, 03 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/creating-dc-proxies-on-cloud-providers/</guid>
      <description>&lt;p&gt;Although many proxy providers offer data center (DC) proxies fairly cheaply, sometimes it is desirable
to make our own. In this post we will discuss how to set up &lt;a href=&#34;http://www.squid-cache.org/&#34;&gt;Squid&lt;/a&gt; proxy
server on cheap Virtual Private Servers from &lt;a href=&#34;https://www.vultr.com/&#34;&gt;Vultr&lt;/a&gt;. We will be using Debian 11
Linux environment on virtual &amp;ldquo;Cloud Compute&amp;rdquo; servers.&lt;/p&gt;
&lt;p&gt;Let us go through the steps to install Squid through Linux shell with commands that will be put into provisioning script.&lt;/p&gt;
&lt;p&gt;First, we need to install the required APT packages:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ apt-get update
$ apt-get install -y squid apache2-utils
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Besides Squid, we also install Apache utils package to get a htpasswd(1) tool that will create file for Basic HTTP authentication.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s enable squid service to make sure it gets re-launched during server reboot:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ systemctl enable squid
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to edit configuration file at /etc/squid/squid.conf. Since we want to all incoming HTTP traffic we remove
line that disallows it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sed -i &amp;#39;s/http_access deny all//&amp;#39; /etc/squid/squid.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This used sed(1) to remove line &lt;code&gt;http_access deny all&lt;/code&gt;. Now let us configure HTTP proxying with username/password authentication.
We add several lines to configuration file for this purpose:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwords&amp;#34; &amp;gt;&amp;gt; /etc/squid/squid.conf
$ echo &amp;#34;auth_param basic realm proxy&amp;#34; &amp;gt;&amp;gt; /etc/squid/squid.conf
$ echo &amp;#34;acl authenticated proxy_auth REQUIRED&amp;#34; &amp;gt;&amp;gt; /etc/squid/squid.conf
$ echo &amp;#34;http_access allow authenticated&amp;#34; &amp;gt;&amp;gt; /etc/squid/squid.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This configures Squid to accept incoming requests with HTTP Basic Auth. Let&amp;rsquo;s restart the squid to get it to read modified
configuration:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ systemctl reload squid
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point Squid is reachable from within the server, but not from outside. This is because by default Vultr Debian servers
have Netfilter firewall enabled that only allows SSH traffic to go in. To disable the firewall, we need to run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ufw disable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can use the proxy server from some other program or script to relay traffic.&lt;/p&gt;
&lt;p&gt;The entire provisioning script is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get install -y squid apache2-utils
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl enable squid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Based on: https://stackoverflow.com/questions/3297196/how-to-set-up-a-squid-proxy-with-basic-username-and-password-authentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sed -i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s/http_access deny all//&amp;#39;&lt;/span&gt; /etc/squid/squid.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwords&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/squid/squid.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;auth_param basic realm proxy&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/squid/squid.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;acl authenticated proxy_auth REQUIRED&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/squid/squid.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http_access allow authenticated&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/squid/squid.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;htpasswd -bc /etc/squid/passwords user trust_no_1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl reload squid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ufw disable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that you should probably change the hardcoded password in the script to a more secure one.&lt;/p&gt;
&lt;p&gt;Once we boot up a Vultr VPS with Debian 11 Linux we can upload this script through SFTP and launch it there. However what if we
want to set up multiple proxy servers? Luckily for us, Vultr has it&amp;rsquo;s own official
&lt;a href=&#34;https://registry.terraform.io/providers/vultr/vultr/latest/docs&#34;&gt;Terraform provider&lt;/a&gt; and we can use Terraform
infrastructure-as-code tool for automated server provisioning. We need to write some code in HCL - Hashicorp Configuration
Language in main.tf file.&lt;/p&gt;
&lt;p&gt;Before we proceed with developing Terraform configuration, we need to get Vultr API key from &lt;a href=&#34;https://my.vultr.com/settings/#settingssshkeys&#34;&gt;SSH keys&lt;/a&gt;
tab in Account Settings part of Vultr client area.&lt;/p&gt;
&lt;p&gt;First, let us import and configure the Vultr provider:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;terraform&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;required_providers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vultr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      source  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr/vultr&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.10.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;provider&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr&amp;#34;&lt;/span&gt; {&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  # Setting api_key is not required if VULTR_API_KEY environment variable is present with API key value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  #api_key = &amp;#34;[REDACTED]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make server troubleshooting more convenient, let us set up SSH key resource with our public SSH key. This will let us
establish SSH connection from the current development machine without worrying about SSH username/password.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr_ssh_key&amp;#34; &amp;#34;my_ssh_key&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name    &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-ssh-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;~/.ssh/id_rsa.pub&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is not strictly necessary as we can simply access Linux shell through Vultr console, but will make things nicer to debug
if the need arises.&lt;/p&gt;
&lt;p&gt;Next, we set a variable for proxy count with default value of 8.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;variable&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy_count&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  default &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s the most important part - setting up resources for the actual proxy servers. The code that declaratively specifies
the server configuration is the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr_instance&amp;#34; &amp;#34;proxy&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;proxy_count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  plan        &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vc2-1c-1gb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  region      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sea&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  os_id       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;477&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_key_ids &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;vultr_ssh_key&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;my_ssh_key&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;id&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user_data   &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We set proxy server count by referencing the value of variable we created earlier. We set &lt;code&gt;plan&lt;/code&gt;, &lt;code&gt;region&lt;/code&gt; and &lt;code&gt;os_id&lt;/code&gt; to values
we got from the following Vultr APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.vultr.com/api/#operation/list-plans&#34;&gt;List Plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.vultr.com/api/#operation/list-regions&#34;&gt;List Regions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.vultr.com/api/#operation/list-os&#34;&gt;List OS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;plan&lt;/code&gt; value &lt;code&gt;vc2-1c-1gb&lt;/code&gt; corresponds to small VPS that costs $5/month, &lt;code&gt;sea&lt;/code&gt; corresponds to Seattle region and 477 is for
Debian Linux 11. We set &lt;code&gt;ssh_key_ids&lt;/code&gt; with UUID of SSH key that was configured earlier to make the server allow incoming
SSH connections from our machine. Lastly we set &lt;code&gt;user_data&lt;/code&gt; with the contents of our provisioning script. This might seem to
be puzzling. The thing is, Vultr and many cloud provider install Linux servers with a program called
&lt;a href=&#34;https://cloudinit.readthedocs.io/en/latest/&#34;&gt;cloud-init&lt;/a&gt; that is meant to provision and configure newly launched server.
It takes either a YAML file or a script that it will execute.&lt;/p&gt;
&lt;p&gt;Last thing we do in our Terraform configuration file is declaring an output for IP addresses of newly created servers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy_ip&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${vultr_instance.proxy.*.main_ip}&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete main.tf file is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;terraform&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;required_providers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vultr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      source  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr/vultr&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.10.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;provider&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr&amp;#34;&lt;/span&gt; {&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  # Setting api_key is not required if VULTR_API_KEY environment variable is present with API key value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  #api_key = &amp;#34;[REDACTED]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr_ssh_key&amp;#34; &amp;#34;my_ssh_key&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name    &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-ssh-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;~/.ssh/id_rsa.pub&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;variable&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy_count&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  default &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vultr_instance&amp;#34; &amp;#34;proxy&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;proxy_count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  plan        &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vc2-1c-1gb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  region      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sea&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  os_id       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;477&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_key_ids &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;vultr_ssh_key&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;my_ssh_key&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;id&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user_data   &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proxy_ip&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${vultr_instance.proxy.*.main_ip}&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When using the common Terraform CLI commands note that &lt;code&gt;terraform apply&lt;/code&gt; finishes sooner than the actual
provisioning of the server. Few minutes later, we can verify that proxies indeed work:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl --proxy-basic --proxy-user user:trust_no_1 --proxy http://66.42.65.58:3128 http://ifconfig.me -v
*   Trying 66.42.65.58:3128...
* Connected to 66.42.65.58 (66.42.65.58) port 3128 (#0)
* Proxy auth using Basic with user &amp;#39;user&amp;#39;
&amp;gt; GET http://ifconfig.me/ HTTP/1.1
&amp;gt; Host: ifconfig.me
&amp;gt; Proxy-Authorization: Basic dXNlcjp0cnVzdF9ub18x
&amp;gt; User-Agent: curl/7.77.0
&amp;gt; Accept: */*
&amp;gt; Proxy-Connection: Keep-Alive
&amp;gt;
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 OK
&amp;lt; access-control-allow-origin: *
&amp;lt; Content-Type: text/plain; charset=utf-8
&amp;lt; Content-Length: 11
&amp;lt; Date: Sun, 03 Apr 2022 18:38:08 GMT
&amp;lt; x-envoy-upstream-service-time: 1
&amp;lt; X-Cache: MISS from vultr
&amp;lt; X-Cache-Lookup: MISS from vultr:3128
&amp;lt; Via: 1.1 google, 1.1 vultr (squid/4.13)
&amp;lt; Connection: keep-alive
&amp;lt;
* Connection #0 to host 66.42.65.58 left intact
66.42.65.58
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>SMTP user enumeration for fun and profit</title>
      <link>https://www.trickster.dev/post/smtp-user-enumeration-for-fun-and-profit/</link>
      <pubDate>Sat, 02 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/smtp-user-enumeration-for-fun-and-profit/</guid>
      <description>&lt;p&gt;Sometimes it is desirable to enumerate all (or as much as possible) email recipients at given
domain. This can be done by establishing SMTP connection to corresponding mail server that
can be found via DNS MX record and getting the server to verify your guesses for email usernames
or addresses.&lt;/p&gt;
&lt;p&gt;There are three major approaches to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using VRFY request that is meant to verify if there is user with corresponding mailbox on the server.
Server would respond with status code 250 if address is valid, 251 if it&amp;rsquo;s not, 252 if it cannot be
determined.&lt;/li&gt;
&lt;li&gt;Using EXPN request that is meant to give a list of subscribers to a mailing list. Some email servers
consider every user to be in a list of single member, others do not. On success, server would respond
with status code 250 and a list of subscribers in implementation-specific format.&lt;/li&gt;
&lt;li&gt;Using RCPT request that is designed to add one more recipient to the email being sent out. On success
the SMTP server would respond with status code 250 or 251. Note that the SMTP protocol
disallows more than 100 recipients being sent the same email message and that for some servers the limit may be
lower.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will be primarily focusing on recipients that use Gmail/GSuite/Google Workspaces for their email service.
Let us use some Unix tools to manually check-and-verify some email addresses on domain peopledatalabs.com.&lt;/p&gt;
&lt;p&gt;As a first step, we need to find out a hostname of mail server for this domain. We use dig(1) for this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ dig -t MX peopledatalabs.com

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; -t MX peopledatalabs.com
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 44234
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;peopledatalabs.com.		IN	MX

;; ANSWER SECTION:
peopledatalabs.com.	3600	IN	MX	1 aspmx.l.google.com.
peopledatalabs.com.	3600	IN	MX	10 alt3.aspmx.l.google.com.
peopledatalabs.com.	3600	IN	MX	10 alt4.aspmx.l.google.com.
peopledatalabs.com.	3600	IN	MX	5 alt1.aspmx.l.google.com.
peopledatalabs.com.	3600	IN	MX	5 alt2.aspmx.l.google.com.

;; Query time: 66 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sat Apr 02 13:50:38 EEST 2022
;; MSG SIZE  rcvd: 162
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can use NetCat to establish SMTP connection to SMTP server:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ nc aspmx.l.google.com 25
220 mx.google.com ESMTP i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us start a SMTP transaction by sending HELO and MAIL FROM messages:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;HELO test.example.org
250 mx.google.com at your service
MAIL FROM: &amp;lt;a@example.org&amp;gt;
250 2.1.0 OK i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far, so good. We&amp;rsquo;re getting valid responses to our requests. Let us try sending VRFY
request to check an email address we know to be valid:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;VRFY &amp;lt;support@peopledatalabs.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We get a response that is inconclusive and fails to give us an answer whether this address is valid:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;252 2.1.5 Send some mail, I&amp;#39;ll try my best i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We get the same response if we try to verify a made up email address:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;VRFY &amp;lt;fakefakefake@peopledatalabs.com&amp;gt;
252 2.1.5 Send some mail, I&amp;#39;ll try my best i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thus we conclude that VRFY method is not applicable to enumerate addresses on Google-hosted email domains.&lt;/p&gt;
&lt;p&gt;But what about EXPN method? Let us try it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;EXPN &amp;lt;support@peopledatalabs.com&amp;gt;
502 5.5.1 Unimplemented command. i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This seems to be explicitly unsupported. Only option left is trying RCPT method, which succeeds:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RCPT TO: &amp;lt;support@peopledatalabs.com&amp;gt;
250 2.1.5 OK i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
RCPT TO: &amp;lt;fakefakefake@peopledatalabs.com&amp;gt;
550-5.1.1 The email account that you tried to reach does not exist. Please try
550-5.1.1 double-checking the recipient&amp;#39;s email address for typos or
550-5.1.1 unnecessary spaces. Learn more at
550 5.1.1  https://support.google.com/mail/?p=NoSuchUser i23-20020a2ea237000000b002495e9ab777si3826740ljm.545 - gsmtp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By using RCPT verb, we can conclusively verify if email address at Google-managed email domain is valid
or not.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s try doing the same step in Python REPL with &lt;a href=&#34;https://www.dnspython.org/&#34;&gt;dnspython&lt;/a&gt; and smtplib modules:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import dns.resolver
&amp;gt;&amp;gt;&amp;gt; answers = dns.resolver.resolve(&amp;#39;peopledatalabs.com&amp;#39;, &amp;#39;MX&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; answers
&amp;lt;dns.resolver.Answer object at 0x109101910&amp;gt;
&amp;gt;&amp;gt;&amp;gt; answers[0]
&amp;lt;DNS IN MX rdata: 1 aspmx.l.google.com.&amp;gt;
&amp;gt;&amp;gt;&amp;gt; str(answers[0].exchange)
&amp;#39;aspmx.l.google.com.&amp;#39;
&amp;gt;&amp;gt;&amp;gt; smtp_hostname = str(answers[0].exchange)
&amp;gt;&amp;gt;&amp;gt; conn = smtplib.SMTP(smtp_hostname)
&amp;gt;&amp;gt;&amp;gt; conn.helo(&amp;#39;test.example.org&amp;#39;)
(250, b&amp;#39;mx.google.com at your service&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; conn.mail(&amp;#39;FROM: &amp;lt;a@example.org&amp;gt;&amp;#39;)
(250, b&amp;#39;2.1.0 OK u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; conn.vrfy(&amp;#39;support@peopledatalabs.com&amp;#39;)
(252, b&amp;#34;2.1.5 Send some mail, I&amp;#39;ll try my best u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; conn.vrfy(&amp;#39;fakefakefake@peopledatalabs.com&amp;#39;)
(252, b&amp;#34;2.1.5 Send some mail, I&amp;#39;ll try my best u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; conn.expn(&amp;#39;support@peopledatalabs.com&amp;#39;)
(502, b&amp;#39;5.5.1 Unimplemented command. u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; conn.rcpt(&amp;#39;support@peopledatalabs.com&amp;#39;)
(250, b&amp;#39;2.1.5 OK u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; conn.rcpt(&amp;#39;fakefakefake@peopledatalabs.com&amp;#39;)
(550, b&amp;#34;5.1.1 The email account that you tried to reach does not exist. Please try\n5.1.1 double-checking the recipient&amp;#39;s email address for typos or\n5.1.1 unnecessary spaces. Learn more at\n5.1.1  https://support.google.com/mail/?p=NoSuchUser u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; conn.quit()
(221, b&amp;#39;2.0.0 closing connection u30-20020ac25bde000000b00449fff281b7si4479810lfn.313 - gsmtp&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A simple Python script that takes a wordlist and domain via CLI arguments to enumerate SMTP users at given domain
would look like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; smtplib
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; dns.resolver  &lt;span style=&#34;color:#75715e&#34;&gt;# requires dnspython&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;make_smtp_conn&lt;/span&gt;(smtp_hostname):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; smtplib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;SMTP(smtp_hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;helo(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gmail.com&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mail(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FROM: &amp;lt;a@gmail.com&amp;gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; conn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Usage:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;wordlist&amp;gt; &amp;lt;domain&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    domain &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    answers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dns&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;resolver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;resolve(domain, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MX&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(answers) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Error: no MX records found for &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(domain))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    smtp_hostname &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(answers[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exchange)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; make_smtp_conn(smtp_hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; line &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; in_f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        line &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; line&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        email_addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; line &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; domain
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        status, _ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;rcpt(email_addr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; status &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;250&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(email_addr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        n &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;99&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;quit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; make_smtp_conn(smtp_hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;quit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us create a very basic wordlist with some entries from RFC 2142 and other common words found in email addresses:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;abuse
noc
security
postmaster
hostmaster
usenet
news
webmaster
www
uucp
ftp
support
info
sales
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running the above script with this wordlist on peopledatalabs.com domain prints the following valid email addresses
to standard output:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3 smtp_enum.py basic_wordlist.txt peopledatalabs.com
abuse@peopledatalabs.com
security@peopledatalabs.com
postmaster@peopledatalabs.com
support@peopledatalabs.com
sales@peopledatalabs.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we wish to be more serious about enumerating a significant portion of email addresses at any given domain we need
to take a proper care to acquire or generate a proper wordlist. Quality of the wordlist largely makes or breaks
the success of SMTP user enumeration. If you know that email addresses at certain company follow a certain pattern and
are based on names and surnames of employees (e.g. &lt;code&gt;john.smith@example.org&lt;/code&gt;) you would want to generate your wordlist
based on this pattern. You may also want to look into public wordlists (e.g. from &lt;a href=&#34;https://github.com/danielmiessler/SecLists&#34;&gt;danielmiessler/SecLists&lt;/a&gt;
repo) to use them as starting point for creating your own.&lt;/p&gt;
&lt;p&gt;Furthermore, email providers do have some countermeasures against SMTP enumeration. For example, I found that Google
starts reporting all email addresses as non-existent if the above script launches bunch of checks repeatedly from single
IP address in a short timeframe - probably due to triggerring some rate limits. This
should be addressable by introducing delays into code (SMTP connection may need to be re-established) or by spreading
the traffic across multiple connections tunneled through SOCKS proxies. I would not recommend doing this sort of
shenanigans at non-trivial scale from home IP adddress. Some email providers also reject the SMTP
transaction if source domain address is weird (e.g. example.org). This can be fixed by simply using a domain with a
popular email service in HELO and MAIL messages in the beginning.&lt;/p&gt;
&lt;p&gt;What are the possible applications of SMTP user enumeration? Growth hackers can use it to source email addresses for
cold outreach or ad targeting. OSINT investigators can use it to cover more ground in their research. Penetration testers
and social engineers can leverage email list gathered via SMTP enumeration for further attacks. When it comes to bug
bounties, SMTP username enumeration is generally not considered to be a vulnerability by itself as RCPT method
cannot truly be prevented due to RCPT request being an integral part of a regular email sending flow, but one could
argue that it&amp;rsquo;s a security problem if it is too easy to perform. For example, someone received $300
bounty for reporting that VRFY method is possible on a certain server, besides other problems with SMTP (see
HackerOne report &lt;a href=&#34;https://hackerone.com/reports/138315&#34;&gt;#138315&lt;/a&gt;).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Running GUI apps within Docker containers</title>
      <link>https://www.trickster.dev/post/running-gui-apps-within-docker-containers/</link>
      <pubDate>Sat, 26 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/running-gui-apps-within-docker-containers/</guid>
      <description>&lt;p&gt;Typically Docker is used to encapsulate server-side software in reproducible packages - containers.
A certain degree of isolation is ensured between containers. Furthermore, containers can be used
as building blocks for systems consisting of multiple software servers. For example, a web app
can consist of backend server, database server, frontend server, load balancer, redis instance for
caching and so on.&lt;/p&gt;
&lt;p&gt;However, what if we want to run desktop GUI apps within Docker containers to use them as components
within larger systems? For example, if we run Firefox within Docker we can have an explicit separation
of browser state between containers. This is beneficial for things like social media management,
growth hacking (either via social media automation or manual labour done by VAs) or
OSINT investigations. For example, one container would be configured with Firefox instance that uses
a single dedicated mobile proxy for just one social media account. This would provide a degree of
protection against social media platform cracking down on sock puppet accounts being used from single
setup because traffic is kept separate for each account and cookie cross-contamination is being
prevented.&lt;/p&gt;
&lt;p&gt;Suppose we have Docker installed on macOS or other Unix/Linux system. How do we run Firefox within
Docker container? For a first attempt, let us consider the following Dockerfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; ubuntu:latest&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; apt-get update &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get install -y firefox x11vnc xvfb&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;exec firefox&amp;#34;&lt;/span&gt; &amp;gt; ~/.xinitrc &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; chmod +x ~/.xinitrc&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CMD&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x11vnc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-create&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-noxrecord&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-noxfixes&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-noxdamage&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-forever&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-passwd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;trustno1&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We use &lt;code&gt;ubuntu&lt;/code&gt; base image and install Firefox with two more things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;x11vnc - VNC server for remote access of X11-based desktop environments. Think of it as poor-mans TeamViewer.&lt;/li&gt;
&lt;li&gt;xvfb - a version of X11 server that does not require any actual display hardware and renders video into RAM instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We configure X11 to run Firefox at startup and launch x11vnc with the exact arguments that allow interoperability
with macOS on the client side.&lt;/p&gt;
&lt;p&gt;Building and running this container is rather simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ docker build -t firefox-test-1 .
$ docker run -p 5900:5900 --rm firefox-test-1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can use VNC feature in macOS Finder to access it by choosing &amp;ldquo;Go -&amp;gt; Connect to server&amp;rdquo;, putting in
&lt;code&gt;vnc://127.0.0.1:5900&lt;/code&gt; into text field at the top and pressing Connect. It will ask for password - enter &lt;code&gt;trustno1&lt;/code&gt;.
This is a minor inconvenience we have to deal on macOS to make it work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-25_19.59.00.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is easy to get working, but relies on VNC capability on client side. It would be nice to avoid it. Assuming we
have X11 server installed on the host system, let us remove x11vnc from container and install just the bare minimum of
extra packages to make Firefox work in X11 client environment that will create an outgoing connection. This results
in the following simplified Dockerfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; ubuntu:latest&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; apt-get update &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get install -y libxext-dev libxrender-dev libxtst-dev firefox&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CMD&lt;/span&gt; firefox &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us put it into separate directory and build a container from it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ docker build -t firefox .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to set up X11 server with incoming connections enabled. On Linux you may need to just run &lt;code&gt;xhost +&lt;/code&gt;, but
on macOS you need to install &lt;a href=&#34;https://www.xquartz.org/&#34;&gt;XQuartz&lt;/a&gt;, launch it, go to Preferences and check the box
that says &amp;ldquo;Allow connections from network clients&amp;rdquo;, then run &lt;code&gt;xhost +&lt;/code&gt; in terminal. Now we are ready to run the Firefox
container that we have built before:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ docker run --rm -e DISPLAY=&amp;#34;$(ifconfig en0 | grep inet | awk &amp;#39;$1==&amp;#34;inet&amp;#34; {print $2}&amp;#39;)&amp;#34;:0 firefox
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we set &lt;code&gt;DISPLAY&lt;/code&gt; environment variable to a value that includes IP address of host system, and &lt;code&gt;0&lt;/code&gt;, separated by
colon. This tells the X11 client part within container to reach out to X11 server at that particular IP address and use zeroth
display for new windows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-25_20.13.02.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This does simplify environment within Docker container, but makes requirements for host machine more complicated,
especially if it&amp;rsquo;s a system that does not natively support X11. Furthermore, if we wanted to run this kind of setup
on a server environment we would need to have desktop environment running there already, which has its own problems.
There has to be a better way.&lt;/p&gt;
&lt;p&gt;Turns out, there is a web app called &lt;a href=&#34;https://novnc.com/info.html&#34;&gt;novnc&lt;/a&gt; that bridges the gap between x11vnc and web
browser. It works as a client for x11vnc, but as a server to the end user that is connecting remotely via web browser.
Although it does not have an official Dockerfile or Docker Hub image, there are some unofficial ones available.
For this example, we will use &lt;a href=&#34;https://hub.docker.com/r/theasp/novnc&#34;&gt;theasp/novnc&lt;/a&gt; image from Docker Hub.&lt;/p&gt;
&lt;p&gt;We keep the Firefox container as-is, but we create a docker-compose.yml file that brings both parts together (based on
example from theasp/novnc Github repo):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;firefox&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;DISPLAY=novnc:0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;depends_on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;novnc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;x11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;novnc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;theasp/novnc:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# Adjust to your screen size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;DISPLAY_WIDTH=1600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;DISPLAY_HEIGHT=968&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;RUN_XTERM=no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8080:8080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;x11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;networks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;x11&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running &lt;code&gt;docker-compose up&lt;/code&gt; builds and launches both containers and we can access the Dockerized Firefox via regular
web browser at: http://localhost:8080/vnc.html&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-25_20.35.24.png&#34;&gt;Screenshot 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With this setup, we have one static part (x11vnc + novnc container) and one part that we would replace if we wanted to
run some other GUI program within Docker environment. Furthermore, we no longer require any client side software other
than web browser. We can also run this kind of configuration on cloud deployment (e.g. Docker server from Digital
Ocean marketplace) if we solved the security issue of no access control being enforced. These are significant improvements
over our first attempt.&lt;/p&gt;
&lt;p&gt;Let us also look into some open source projects dedicated to running GUI apps within Docker containers.&lt;/p&gt;
&lt;h2 id=&#34;x11docker&#34;&gt;x11docker&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mviereck/x11docker&#34;&gt;x11docker&lt;/a&gt; is a solution for running GUI apps within Linux host system that
is more advanced in some ways (GPU access, peripheral support, security improvements), but is constrained to an existing
Linux desktop environment and does not properly support operating systems other than Linux.&lt;/p&gt;
&lt;p&gt;To launch a GUI app through x11docker, one would have to build or pull Docker container for that app written in a way that
is compatible with x11docker (some examples are on Github and Docker Hub) and launch it through x11docker CLI tool.&lt;/p&gt;
&lt;h2 id=&#34;kasm-workspaces&#34;&gt;Kasm Workspaces&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.kasmweb.com/&#34;&gt;Kasm Workspaces&lt;/a&gt; is an open source platform that takes this idea quite a bit further and even
offers a SaaS version that they host for you. It is also available through Digital Ocean marketplace at:
&lt;a href=&#34;https://marketplace.digitalocean.com/apps/kasm-workspaces&#34;&gt;https://marketplace.digitalocean.com/apps/kasm-workspaces&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Like x11docker, it provides a bunch of pre-developed Docker images for common apps like Firefox (and other browsers),
Slack, Discord, VS Code and others. Entire Linux desktop environments are available through one-click install on
management interface. Like with out previous example, remote desktops are made available through web browser. However
Kasm Workspaces use KasmVNC - a VNC solution that is developed by the same company.&lt;/p&gt;
&lt;h2 id=&#34;docker-android&#34;&gt;docker-android&lt;/h2&gt;
&lt;p&gt;So far we talked about running Linux GUI apps inside containers. But what about running Android apps? Turns out, it is
possible to run an entire Android emulator within Docker container and expose it through novnc interface by using
solution called &lt;a href=&#34;https://github.com/budtmo/docker-android&#34;&gt;docker-android&lt;/a&gt;. Even though it warns about nested virtualization
not being completely supported, I was able to run it through the following command on Digital Ocean VPS:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ docker run --privileged -d -p 6080:6080 -p 5554:5554 -p 5555:5555 -e DEVICE=&amp;#34;Samsung Galaxy S6&amp;#34; --name android-container budtmo/docker-android-x86-8.1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Android emulator requires some CPU and RAM thus making it infeasible to run it on $5/month droplets - it simply was too slow.
However the performace was acceptable on some of the larger servers - it could even play Youtube videos without sound.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-26_10.56.51.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since we also have port 5554 and 5555 exposed, we could also use ADB to side-load APK files and do some debugging.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Reproducible Linux environments with Vagrant and Terraform</title>
      <link>https://www.trickster.dev/post/reproducible-linux-environments-with-vagrant-and-terraform/</link>
      <pubDate>Fri, 25 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/reproducible-linux-environments-with-vagrant-and-terraform/</guid>
      <description>&lt;p&gt;When developing and operating scraping/automation solutions, we don&amp;rsquo;t exactly want to focus
on systems administration part of things. If we are writing code to achieve a particular
objective, making that code run on the VPS or local system is merely a supporting side-objective
that is required to make the primary thing happen. Thus it is undesirable to spend too much time
on it, especially if we can use automation to avoid repetitive, error-prone activity of installing
the required tooling in disposable virtual machines or virtual private servers. Luckily for us,
there are DevOps tools developed for this exact purpose.&lt;/p&gt;
&lt;p&gt;For the sake of this example, we will be setting up Node.js with &lt;a href=&#34;https://n8n.io/&#34;&gt;n8n&lt;/a&gt;
workflow automation platform.&lt;/p&gt;
&lt;p&gt;To reproduce virtual machine with Linux OS on a development machine, we will use
&lt;a href=&#34;https://www.vagrantup.com/&#34;&gt;Vagrant&lt;/a&gt; VM management software with &lt;a href=&#34;https://www.virtualbox.org/&#34;&gt;Virtualbox&lt;/a&gt;
backend.&lt;/p&gt;
&lt;p&gt;To reproduce the same environment in Digital Ocean VPS, we will use &lt;a href=&#34;https://www.terraform.io/&#34;&gt;Terraform&lt;/a&gt;
infrastructure-as-code tool.&lt;/p&gt;
&lt;h2 id=&#34;vagrant&#34;&gt;Vagrant&lt;/h2&gt;
&lt;p&gt;First, we need to set up Vagrant. If you use any of the major desktop operating systems (Windows,
macOS, Linux) you should be able to find an installer at &lt;a href=&#34;https://www.vagrantup.com/downloads&#34;&gt;Vagrant Downloads page&lt;/a&gt;.
You may also want to check if it is available through a package manager on your system.&lt;/p&gt;
&lt;p&gt;Likewise, Virtualbox installers are available on &lt;a href=&#34;https://www.virtualbox.org/wiki/Downloads&#34;&gt;Virtualbox downloads page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once both are installed, we are ready to start setting up a configuration for our VM. We go to
&lt;a href=&#34;https://app.vagrantup.com/boxes/search&#34;&gt;Vagrant boxes search&lt;/a&gt; - a searchable directory for pre-made
virtual machine images that we can use. Since Vagrant works best with Linux, most of these will be
Linux-based. For the sake of this example we choose &lt;code&gt;generic/debian11&lt;/code&gt; as this provides a fairly
vanilla Debian 11 system that is also readily available on cloud providers.&lt;/p&gt;
&lt;p&gt;We need to create Vagrantfile now. This will be a central configuration file for the Vagrant
environment we will be setting up. Technically Vagrantfiles are written in Ruby, but don&amp;rsquo;t
worry if you don&amp;rsquo;t know Ruby - it&amp;rsquo;s not actually required in all but most advanced use
cases of Vagrant.&lt;/p&gt;
&lt;p&gt;On the &lt;a href=&#34;https://app.vagrantup.com/generic/boxes/debian11&#34;&gt;Vagrant box page&lt;/a&gt;,
there is a very basic example snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Vagrant&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;box &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;generic/debian11&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However this is not enough for us. Thus we will create new Vagrantfile with more extensive
template by running &lt;code&gt;vagrant init generic/debian11&lt;/code&gt; in a root directory of our project.
This creates a following Vagrantfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# -*- mode: ruby -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# vi: set ft=ruby :&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# All Vagrant configuration is done below. The &amp;#34;2&amp;#34; in Vagrant.configure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# configures the configuration version (we support older styles for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# backwards compatibility). Please don&amp;#39;t change it unless you know what&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# you&amp;#39;re doing.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Vagrant&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# The most common configuration options are documented and commented below.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# For a complete reference, please see the online documentation at&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# https://docs.vagrantup.com.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Every Vagrant development environment requires a box. You can search for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# boxes at https://vagrantcloud.com/search.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;box &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;generic/debian11&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Disable automatic box update checking. If you disable this, then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# boxes will only be checked for updates when the user runs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# `vagrant box outdated`. This is not recommended.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.box_check_update = false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Create a forwarded port mapping which allows access to a specific port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# within the machine from a port on the host machine. In the example below,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# accessing &amp;#34;localhost:8080&amp;#34; will access port 80 on the guest machine.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# NOTE: This will enable public access to the opened port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.network &amp;#34;forwarded_port&amp;#34;, guest: 80, host: 8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Create a forwarded port mapping which allows access to a specific port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# within the machine from a port on the host machine and only allow access&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# via 127.0.0.1 to disable public access&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.network &amp;#34;forwarded_port&amp;#34;, guest: 80, host: 8080, host_ip: &amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Create a private network, which allows host-only access to the machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# using a specific IP.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.network &amp;#34;private_network&amp;#34;, ip: &amp;#34;192.168.33.10&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Create a public network, which generally matched to bridged network.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Bridged networks make the machine appear as another physical device on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# your network.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.network &amp;#34;public_network&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Share an additional folder to the guest VM. The first argument is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# the path on the host to the actual folder. The second argument is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# the path on the guest to mount the folder. And the optional third&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# argument is a set of non-required options.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.synced_folder &amp;#34;../data&amp;#34;, &amp;#34;/vagrant_data&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Provider-specific configuration so you can fine-tune various&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# backing providers for Vagrant. These expose provider-specific options.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Example for VirtualBox:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.provider &amp;#34;virtualbox&amp;#34; do |vb|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   # Display the VirtualBox GUI when booting the machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   vb.gui = true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   # Customize the amount of memory on the VM:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   vb.memory = &amp;#34;1024&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# View the documentation for the provider you are using for more&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# information on available options.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Enable provisioning with a shell script. Additional provisioners such as&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# documentation for more information about their specific syntax and use.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# config.vm.provision &amp;#34;shell&amp;#34;, inline: &amp;lt;&amp;lt;-SHELL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   apt-get update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;#   apt-get install -y apache2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# SHELL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the initial configuration, only &lt;code&gt;config.vm.box&lt;/code&gt; is set and rest is commented out.
Read the comments to get an idea of what can be configured for the virtual machine.&lt;/p&gt;
&lt;p&gt;First, we want to perform provisioning with a shell script. However, we will not be
using inline syntax shown in the comments and will be creating a separate shell
script file instead. This is because we want to be prepared to reuse provisioning
code in Terraform config that we will work on later without introducing code
duplication. Thus we replace the provisioning section with the following line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;provision &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shell&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have to develop the shell script that will install Node.js, n8n and all the
dependencies we will need to run n8n and also launch n8n as daemon.&lt;/p&gt;
&lt;p&gt;The contents of provision.sh file are fairly straightforward:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set -x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -fsSL https://deb.nodesource.com/setup_17.x -o /tmp/install_node.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bash /tmp/install_node.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get install -y gcc g++ make nodejs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg &amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main&amp;#34;&lt;/span&gt; | sudo tee /etc/apt/sources.list.d/yarn.list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get update 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get install -y yarn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install n8n -g
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install pm2 -g
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pm2 start n8n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pm2 startup
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pm2 save
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, we run &lt;code&gt;apt-get update&lt;/code&gt; so that APT system would fetch up-to-date information about Debian packages.
Next, we proceed with Node.js installation. Since Debian package of Node.JS is not very well maintained, we
use an alternative approach of installing &lt;a href=&#34;https://github.com/nodesource/distributions&#34;&gt;NodeSource binary distribution&lt;/a&gt;
that involves fetching a shell script to update APT configuration, running it and installing NodeJS from
third party APT repository. We use similar approach to install Yarn package manager. There is no explicit step to
install NPM, as it is installed from &lt;code&gt;nodejs&lt;/code&gt; APT package.&lt;/p&gt;
&lt;p&gt;Now we can use NPM to install n8n that we do through command &lt;code&gt;npm install n8n -g&lt;/code&gt;. Since we want to run n8n as daemon
we also install &lt;a href=&#34;https://pm2.keymetrics.io/&#34;&gt;PM2&lt;/a&gt; process manager and run the relevant commands to set up n8n as
background service process. PM2 makes sure that n8n will be relaunched when Linux system is restarted.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;vagrant up&lt;/code&gt; in the directory with Vagrantfile creates a virtual machine with Debian Linux 11 and runs our
provisioning script. Running &lt;code&gt;vagrant ssh&lt;/code&gt; will give us a shell access to Linux environment that was provisioned.
We can verify that n8n is indeed running:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vagrant@debian11:~$ ps aux | grep n8n
root         907  7.5  9.9 11538924 201832 ?     Ssl  12:28   0:08 node /usr/bin/n8n
vagrant     1592  0.0  0.0   6152   708 pts/0    S+   12:30   0:00 grep --color=auto n8n
vagrant@debian11:~$ curl http://127.0.0.1:5678 -v
*   Trying 127.0.0.1:5678...
* Connected to 127.0.0.1 (127.0.0.1) port 5678 (#0)
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: 127.0.0.1:5678
&amp;gt; User-Agent: curl/7.74.0
&amp;gt; Accept: */*
&amp;gt;
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 OK
&amp;lt; X-Powered-By: Express
&amp;lt; Access-Control-Allow-Origin: http://localhost:8080
&amp;lt; Access-Control-Allow-Credentials: true
&amp;lt; Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE
&amp;lt; Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, sessionid
&amp;lt; Content-Type: text/html; charset=utf-8
&amp;lt; Content-Length: 1201
&amp;lt; ETag: W/&amp;#34;4b1-/hUzA+Pj4gDyZovqj340mKmXXjQ&amp;#34;
&amp;lt; Vary: Accept-Encoding
&amp;lt; Date: Fri, 25 Mar 2022 12:35:55 GMT
&amp;lt; Connection: keep-alive
&amp;lt; Keep-Alive: timeout=5
&amp;lt;
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&amp;#34;utf-8&amp;#34;&amp;gt;&amp;lt;meta http-equiv=&amp;#34;X-UA-Compatible&amp;#34; content=&amp;#34;IE=edge&amp;#34;&amp;gt;&amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width,initial-scale=1&amp;#34;&amp;gt;&amp;lt;link rel=&amp;#34;icon&amp;#34; href=&amp;#34;/favicon.ico&amp;#34;&amp;gt;&amp;lt;script&amp;gt;window.BASE_PATH = &amp;#34;/&amp;#34;;&amp;lt;/script&amp;gt;&amp;lt;title&amp;gt;n8n.io - Workflow Automation&amp;lt;/title&amp;gt;&amp;lt;link href=&amp;#34;/js/chunk-2d2073c1.f0370751.js&amp;#34; rel=&amp;#34;prefetch&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/js/chunk-2d22d3e6.388c0a4c.js&amp;#34; rel=&amp;#34;prefetch&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/js/chunk-4301fce8.d032a636.js&amp;#34; rel=&amp;#34;prefetch&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/js/chunk-b1e1f7c0.92d036f0.js&amp;#34; rel=&amp;#34;prefetch&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/css/app.287bd9f8.css&amp;#34; rel=&amp;#34;preload&amp;#34; as=&amp;#34;style&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/css/chunk-vendors.29499615.css&amp;#34; rel=&amp;#34;preload&amp;#34; as=&amp;#34;style&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/js/app.070a8ea4.js&amp;#34; rel=&amp;#34;preload&amp;#34; as=&amp;#34;script&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/js/chunk-vendors.39458c07.js&amp;#34; rel=&amp;#34;preload&amp;#34; as=&amp;#34;script&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/css/chunk-vendors.29499615.css&amp;#34; rel=&amp;#34;stylesheet&amp;#34;&amp;gt;&amp;lt;link href=&amp;#34;/css/app.287bd9f8.css&amp;#34; rel=&amp;#34;stylesheet&amp;#34;&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;noscript&amp;gt;&amp;lt;strong&amp;gt;We&amp;#39;re sorry but the n8n Editor-UI doesn&amp;#39;t work properly without JavaScript enabled. Pl* Connection #0 to host 127.0.0.1 left intact
ease enable it to continue.&amp;lt;/strong&amp;gt;&amp;lt;/noscript&amp;gt;&amp;lt;div id=&amp;#34;app&amp;#34;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script src=&amp;#34;/js/chunk-vendors.39458c07.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;script src=&amp;#34;/js/app.070a8ea4.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also verify presence of Node.js tooling:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vagrant@debian11:~$ node --version
v17.8.0
vagrant@debian11:~$ yarn --version
1.22.18
vagrant@debian11:~$ npm --version
8.5.5
vagrant@debian11:~$ node
Welcome to Node.js v17.8.0.
Type &amp;#34;.help&amp;#34; for more information.
&amp;gt; .exit
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we cannot access n8n through a desktop web browser from host system. That&amp;rsquo;s because we did not configure port forwarding
in our Vagrantfile.&lt;/p&gt;
&lt;p&gt;We edit port forwarding section to add the following line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;network &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;forwarded_port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;guest&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5678&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;host&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5678&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To reboot the VM and make this change working, we run &lt;code&gt;vagrant reload&lt;/code&gt;. After doing so, we can access n8n frontend via http://localhost:5678/.&lt;/p&gt;
&lt;p&gt;There is one more thing we need to set up in Vagrant configuration. You may want to edit your source code through VIM or other
text editor in host machine and run it in a guest machine without going through the hassle of using SFTP to transfer files.
Vagrant supports shared directories that enable us to mirror a source code directory between host and guest systems. Edit the part
of Vagrantfile that mentions shared folders to add the following line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;synced_folder &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make current source code directory on host machine mirrored to /vagrant in guest machine.
To make this change work, we need to run &lt;code&gt;vagrant reload&lt;/code&gt; again. However, depending on exact circumstances
this may not work right away. To fix it, you may need to install &lt;code&gt;vbguest&lt;/code&gt; plugin and activate by running the following
commands:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ vagrant plugin install vagrant-vbguest
$ vagrant vbguest
$ vagrant reload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To temporarily shut down your Vagrant VM, run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ vagrant halt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To delete the VM, run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ vagrant destroy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Complete Vagrantfile with comments removed for brevity is merely few lines long:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Vagrant&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;box &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;generic/debian11&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;network &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;forwarded_port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;guest&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5678&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;host&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5678&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;synced_folder &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;vm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;provision &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shell&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With less than 30 lines of code were able to set up an entire environment for experimenting with n8n automation workflows
in a reproducible way. This enables us to delete the VM when no longer needed, but quickly recreate it from Vagrantfile
and provisioning script when we need it again.&lt;/p&gt;
&lt;h2 id=&#34;terraform&#34;&gt;Terraform&lt;/h2&gt;
&lt;p&gt;Terraform is another DevOps tool created by Hashicorp - the company that developed Vagrant. It reads declarative configuration
files written in HCL (Hashicorp Configuration Language) and performs infrastructure setup for us. We will use it to provision
a Digital Ocean VPS with the same environment we created in Vagrant VM. The following steps will use Terraform CLI tool
that can be found at &lt;a href=&#34;https://www.terraform.io/downloads&#34;&gt;Terraform Downloads page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are some prerequisites before we start. You will need to have a Digital Ocean account with payment method added.
Furthermore, you will need to register your SSH key on your Digital Ocean account if you haven&amp;rsquo;t already (this is generally
convenient thing to have, as you won&amp;rsquo;t need to type a password in when logging into servers from your local machine).
Lastly, you will need to generate Digital Ocean API token and save it to do_token.txt (make sure there&amp;rsquo;s no whitespace!).&lt;/p&gt;
&lt;p&gt;When these matters are take care of, we can start writing our main.tf file that will contain the configuration for
the server. We start by setting up &lt;a href=&#34;https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs&#34;&gt;Digital Ocean Terraform provider&lt;/a&gt;
(an equivalent of software library that abstract away an external API) with the following lines:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;terraform&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;required_providers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    digitalocean &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      source  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean/digitalocean&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;~&amp;gt; 2.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;provider&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;do_token.txt&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we create a resource (an equivalent of object) for Digital Ocean droplet we want created:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean_droplet&amp;#34; &amp;#34;n8n&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  image     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;debian-11-x64&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n8n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  region    &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sfo3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  size      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s-1vcpu-1gb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_keys  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are several things of interest here. We set &lt;code&gt;image&lt;/code&gt; argument to &lt;code&gt;debian-11-x64&lt;/code&gt;
as this is the same Linux distribution that we had in our Vagrant machine. Thus it is
very reasonable to expect that our provisioning script will work here as well.
We set &lt;code&gt;size&lt;/code&gt; to value that corresponds to $5/month droplet as we don&amp;rsquo;t expect to
use much resources. I find that in many cases $5/month droplets are more than enough
for computational needs of many scraping/automation scripts that I develop.
Next, we set up SSH key fingerprint that will be needed to access the droplet.
We make sure to use the same SSH keypair that is configured in Digital
Ocean account and our local machine. Also we set &lt;code&gt;user_data&lt;/code&gt; with contents of our
provisioning script so that it will be launched when server setup is complete.&lt;/p&gt;
&lt;p&gt;Lastly, we create an output for getting an IP address of server after setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;server_ip&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;digitalocean_droplet&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;n8n&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;ipv4_address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To actually install our environment on the cloud, there are three steps. First, we
run &lt;code&gt;terraform init&lt;/code&gt; to initialize local state with TF providers. Next, we launch
&lt;code&gt;terraform plan -out=tf.plan&lt;/code&gt; to set up an action plan that will be shown for us
and saved into file tf.plan. Lastly, we run &lt;code&gt;terraform apply tf.plan&lt;/code&gt; to actually
proceed with the installation. Few minutes later, we can access n8n via HTTP
protocol at port 5678 on IP address that is printed at the end of the installation
process.&lt;/p&gt;
&lt;p&gt;Conveniently for us, Digital Ocean does not set up any default firewall
rules that would limit the ingress traffic. However if you work on equivalent
setup with AWS EC2 you would need to make sure that a security group is configured
accordingly. Furthermore, you may want to set up HTTP authentication for n8n
by setting up &lt;code&gt;N8N_BASIC_AUTH_ACTIVE&lt;/code&gt; environment variable to &lt;code&gt;true&lt;/code&gt; in
provision.sh file and setting username/password to &lt;code&gt;N8N_BASIC_AUTH_USER&lt;/code&gt; \
&lt;code&gt;N8N_BASIC_AUTH_PASSWORD&lt;/code&gt; variables if you are going to run it on the cloud for
non-trivial amounts of time (this has been skipped in above code for simplicity).&lt;/p&gt;
&lt;p&gt;When we no longer need this environment, we can run &lt;code&gt;terraform destroy&lt;/code&gt; to remove
it.&lt;/p&gt;
&lt;p&gt;The complete main.tf file is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-hcl&#34; data-lang=&#34;hcl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;terraform&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;required_providers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    digitalocean &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      source  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean/digitalocean&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;~&amp;gt; 2.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;provider&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;do_token.txt&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;digitalocean_droplet&amp;#34; &amp;#34;n8n&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  image     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;debian-11-x64&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n8n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  region    &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sfo3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  size      &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s-1vcpu-1gb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;file&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;provision.sh&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ssh_keys  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;server_ip&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;resource&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;digitalocean_droplet&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;n8n&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;ipv4_address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since the new environment is now running on the remote server it is not very feasible to make
a shared directory with it. However, if we wanted to upload some files to the server
we could have used &lt;a href=&#34;https://www.terraform.io/language/resources/provisioners/file&#34;&gt;&lt;code&gt;file&lt;/code&gt; provisioner&lt;/a&gt;
to do so.&lt;/p&gt;
&lt;p&gt;$5/month Digital Ocean droplet costs $0.007 per hour. If we launch some automation workflow
in the evening and let it run overnight for 10 hours, we have merely spent $0.07!
Since Terraform enables us to prepare configuration once and setup/destroy the environment
as many times as we like, we can save money that we would otherwise spending on keeping
the unused infra running in the cloud. Furthermore, we save time and effort that we would
be spending when recreating the environment repeatedly and possibly making mistakes.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Decrypting your own HTTPS traffic with Wireshark</title>
      <link>https://www.trickster.dev/post/decrypting-your-own-https-traffic-with-wireshark/</link>
      <pubDate>Sun, 20 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/decrypting-your-own-https-traffic-with-wireshark/</guid>
      <description>&lt;p&gt;HTTP messages are typically are not sent in plaintext in the post-Snowden world. Instead, TLS protocol is used
to provide communications security against tampering and surveillance of communications based on HTTP protocol.
TLS itself is fairly complex protocol consisting of several sub-protocols, but let us think of it as encrypted
and authenticated layer on top of TCP connection that also does some server (and optionally client) verification through
public key cryptography.&lt;/p&gt;
&lt;p&gt;In this blog, we have previously discussed setting up mitmproxy to intercept HTTPS communications between
mobile apps and their backend systems. However, we may also want to see what desktop apps are communicating.
Furthermore, we may want to go deeper into reverse engineering private APIs for web apps and would like to
surpass limitations of Chrome DevTools. This is where we turn to &lt;a href=&#34;https://www.wireshark.org/&#34;&gt;Wireshark&lt;/a&gt; -
a GUI tool for packet sniffing and analysis. We can use this tool for deep dive into what exactly
an application is doing on the network.&lt;/p&gt;
&lt;p&gt;However if we try to sniff HTTPS without any preparations we will not be able to go far, as TLS protocol
is doing it&amp;rsquo;s job to prevent adversaries from reading communication contents by sniffing the network.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-20_14.32.05.png&#34;&gt;Screenshot 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, if we control one of the endpoints (i.e. desktop system with a web browser) we can set
&lt;code&gt;SSLKEYLOGFILE&lt;/code&gt; environment variable to a path of textfile we can access. Software that implements
TLS typically (altough not always) will write keys and other TLS secrets to this file. This applies
to curl, Chrome, Firefox and many desktop apps that use NSS/OpenSSL libraries. Wireshark, if configured
correctly, will be able to read this file and decrypt the intercepted TLS packets. This is a client-side
capture of session keys.&lt;/p&gt;
&lt;p&gt;How exactly do we set the environent variable? This is dependant on operating system and whether or not
we want to cover all apps that are installed. On Linux/Unix that uses Bash we could simply put the
following line in ~/.bashrc or ~/.bash_profile file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export SSLKEYLOGFILE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;~/.sslkeyfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, this will only cover program launched directly from the shell. On Linux, we could edit
/etc/environment (or /etc/profile) and reboot to set the environment variable globally.&lt;/p&gt;
&lt;p&gt;On macOS it is somewhat more complicated. First, we need to check if ~/Library/LaunchAgents directory
exists and create it under our regular user if it does not. Then we create a Property List file
(e.g. tlskeylogger.plist) with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;version=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;sslkeylogger&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;sh&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-c&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    launchctl setenv SSLKEYLOGFILE ~/.sslkeyfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To get it loaded on next restart, we run the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ launchctl load ~/Library/LaunchAgents/tlskeylogger.plist
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make it immediate, we run the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ launchctl start ~/Library/LaunchAgents/tlskeylogger.plist
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On Windows, you can set &lt;code&gt;SSLKEYLOGFILE&lt;/code&gt; environment variable via GUI by finding
&amp;ldquo;Edit environment variables for your account&amp;rdquo; in Windows menu and creating a new environment
variable for the current user.&lt;/p&gt;
&lt;p&gt;Once this is done, we should restart browser and/or other software that communicates over HTTPS.
If everything is okay, the file should start to get populated with lines similar to the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# SSL/TLS secrets log file, generated by NSS
CLIENT_HANDSHAKE_TRAFFIC_SECRET 7f6e75485ed674e16c83481963f9d65a072c113069d3349639018cdebe5ec154 8cc7626c1516d379e7ba10334b7161b1b95ee2b4455513289ee031d78c007038
SERVER_HANDSHAKE_TRAFFIC_SECRET 7f6e75485ed674e16c83481963f9d65a072c113069d3349639018cdebe5ec154 9cfb00bc3004427de3bbfe005fb32d835cf56ff3c24c3299f5c817f8c8774f0f
CLIENT_RANDOM bea906bf19bf67d0ef1ff7de604f470a73e4ad00a89f62c51adc903c78836342 aadd7d3defb78946d1a22992c9ede2b00a7a4f3add8ce2a3252000818c031887a044358e40665807d6ec813f2fb8d173
CLIENT_TRAFFIC_SECRET_0 7f6e75485ed674e16c83481963f9d65a072c113069d3349639018cdebe5ec154 da25a93b3167179088ebf35b8415bd4f48f0e558ee7db442ed83fba1e31693b9
SERVER_TRAFFIC_SECRET_0 7f6e75485ed674e16c83481963f9d65a072c113069d3349639018cdebe5ec154 4de128f1ab06148bb9faee99e3be590a1c9fd27261dc255de8ec7d59bfa2eccd
EXPORTER_SECRET 7f6e75485ed674e16c83481963f9d65a072c113069d3349639018cdebe5ec154 b1f5242bfe9853f78c41358e602ab6417864ca7fb05f3f5715a641bf263102bd
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We are ready to configure Wireshark now. This is fairly simple. All we have to do is go to
(Edit -&amp;gt;) Preferences -&amp;gt; Protocols -&amp;gt; TLS and put the value of &lt;code&gt;SSLKEYLOGFILE&lt;/code&gt; into
&amp;ldquo;(Pre-)Master Secret Log filename&amp;rdquo;.  You should also tick checkboxes about reassembling
TLS records and application data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-20_15.40.02.png&#34;&gt;Screenshot 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That is one way. Another way is to start sniffing, right click on a TLS packet, then
choosing &amp;ldquo;Protocol Preferences -&amp;gt; Transport Layer Security -&amp;gt; (Pre-)Master Secret Log
filename&amp;rdquo; and clicking that. Wireshark will open a textfield on the top to let you input a path
to the file that it needs to read for decryption.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-20_15.40.55.png&#34;&gt;Screenshot 3&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-03-20_15.41.07.png&#34;&gt;Screenshot 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we can see HTTPS messages decrypted. This includes some API calls that Google Chrome
is making when phoning back to the mothership, as well requests to Discord backend by a
desktop Discord client.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-20_14.40.27.png&#34;&gt;Screenshot 5&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-03-20_14.58.31.png&#34;&gt;Screenshot 6&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-03-20_14.45.42.png&#34;&gt;Screenshot 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This opens up possibilities to not only reverse engineer web app private APIs in a deeper way, but
also to do the same kind of research against desktop apps for purposes such as data scraping,
automation, vulnerability research and privacy analysis.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Scrapy framework architecture</title>
      <link>https://www.trickster.dev/post/scrapy-framework-architecture/</link>
      <pubDate>Sun, 20 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scrapy-framework-architecture/</guid>
      <description>&lt;p&gt;In this post we will take a deeper look into architecture of not just Scrapy projects, but
Scrapy framework itself. We will go through some key components of Scrapy and will look into
how data is flowing through the system.&lt;/p&gt;
&lt;p&gt;Let us look into the following picture from Scrapy documentation:
&lt;a href=&#34;https://docs.scrapy.org/en/latest/_images/scrapy_architecture_02.png&#34;&gt;https://docs.scrapy.org/en/latest/_images/scrapy_architecture_02.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Engine is the central switchboard for all data that is transferred inside Scrapy when it is running.&lt;/li&gt;
&lt;li&gt;Spiders are components that traverse links and parse data to generate new requests and items (units of
scraped data).&lt;/li&gt;
&lt;li&gt;Spider middlewares (between spiders and Engine) to customise spider functionality by hooking into requests, responses
and error conditions. Some default spider middlewares are integrated into each new Scrapy project and deal with things
like filling &lt;code&gt;Referer&lt;/code&gt; header, tracking crawling depth, filtering out unsuccessful responses, filtering out requests
to outside the domain of site being scraped.&lt;/li&gt;
&lt;li&gt;Downloader is where actual HTTP requests are made and responses are received.&lt;/li&gt;
&lt;li&gt;Downloader middlewares (between Engine and Downloader) can be implemented for for lower-level customisation of requests
and responses. It can also override some requests with a response that was prepared beforehand. Default downloader
middlewares deal with cookie management, HTTP headers, timeouts, retries, caching, heeding robots.txt rules and some other
aspects of HTTP protocol.&lt;/li&gt;
&lt;li&gt;Scheduler keeps track of requests to be made (in a priority queue) and when requested gives one to the Engine.&lt;/li&gt;
&lt;li&gt;Item pipelines can be implemented for post-processing of scraped data, such as cleaning, reformatting and exporting to
external stores.&lt;/li&gt;
&lt;li&gt;Optionally, there can be Extensions which are Python classes that implement further customisations to Scrapy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Out of these, one or more spiders are implemented in each Scrapy project. Depending on specifics of the project, you may
also need to implement one or more of spider/downloader middlewares and item pipelines. You are unlikely to deal with
Extension development unless you work with deep customisation of Scrapy framework.&lt;/p&gt;
&lt;p&gt;Now let us go through data flow in the picture.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Spider is launched and generates some initial requests. They are passed into Engine through zero or more spider middlewares.
Each spider middleware is given opportunity to modify or drop any of the requests.&lt;/li&gt;
&lt;li&gt;Engine passes requests into Scheduler, which puts them into a queue.&lt;/li&gt;
&lt;li&gt;Engine asks Scheduler for next request to be processed. Scheduler gives it to Engine (as long as queue is not empty; if it is,
scraping job is finished).&lt;/li&gt;
&lt;li&gt;Engine sends the request to Downloader through a sequence of one or more downloader middlewares. Like spider middlewares,
each of the downloader middlewares are given opportunity to modify or drop the request.&lt;/li&gt;
&lt;li&gt;Downloader performs HTTP request and generates response object. It is returned through donwloader middleware to engine.
Like with the request, they can drop or modify the response.&lt;/li&gt;
&lt;li&gt;Engine sends the response through spider middlewares to the spider.&lt;/li&gt;
&lt;li&gt;Spider parses the response and generates zero or more items and zero or more new requests. These are sent to Engine
via spider middlewares.&lt;/li&gt;
&lt;li&gt;Engine sends items to item pipelines and requests to scheduler. At this point, the process loops back to step 3 and is
repeated until there are no new requests to be processed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that despite Scrapy being able to launch multiple requests concurrently, no multithreading is used anywhere is Scrapy.
Instead, asynchronous network event handling is done through &lt;a href=&#34;https://twistedmatrix.com/trac/&#34;&gt;Twisted&lt;/a&gt; network programming framework.
Just like Scrapy provides foundations for your web scraping project, Twisted provides a network programming machinery that is
used to build Scrapy. This has some ramifications for web scraper developers. First, we should avoid doing things
that block unless they are really quick and/or rare as otherwise they would slow down the entire system (as could be seen
by trying to use openpyxl in a pipeline to export very large amounts of data). Second, we have to take care to use &lt;code&gt;yield&lt;/code&gt; (not &lt;code&gt;return&lt;/code&gt;) keyword
when new items/requests are being created in the spider, as that allows deferring the entire code path until the moment the
result is needed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to scrape pages behind login with Python</title>
      <link>https://www.trickster.dev/post/how-to-scrape-pages-behind-login-with-python/</link>
      <pubDate>Sun, 13 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-to-scrape-pages-behind-login-with-python/</guid>
      <description>&lt;p&gt;Many websites do not provide proper access to information for unauthenticated users. Instead,
the data is provided on some client area that is accessible only after the user goes through
login flow, possibly by signing up for a paid plan beforehand. How can such websites be scraped
with Python?&lt;/p&gt;
&lt;p&gt;There are two additional things we have to do when scraping data behind login:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up &lt;code&gt;requests.Session&lt;/code&gt; object with proper cookies by reproducing the login flow programmatically.&lt;/li&gt;
&lt;li&gt;Using the session from previous step to scrape data behind login.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Depending on the specifics of the site being scraped, we may also need to set up Authorization header
with API access token.&lt;/p&gt;
&lt;p&gt;To exemplify scraping behind login, we will develop a simple company information scraper for
&lt;a href=&#34;https://www.apollo.io/&#34;&gt;Apollo.io&lt;/a&gt; - a SaaS tool for sales intelligence and outreach automation.
This site is heavily reliant on API calls made by client-side JS code, but the approach demonstrated
here is applicable for pure HTML sites as well.&lt;/p&gt;
&lt;h1 id=&#34;reproducing-login-flow&#34;&gt;Reproducing login flow&lt;/h1&gt;
&lt;p&gt;The following assumes that we have a free account at Apollo.io.&lt;/p&gt;
&lt;p&gt;Let us explore login flow of the site. We need to go to login page of the site and switch on Chrome DevTools. Make
sure to clear request history in Network tab. Then perform the login with credentials you have. You will find that
HTTP POST request is being sent to &lt;code&gt;https://app.apollo.io/api/v1/auth/login&lt;/code&gt; with JSON payload containing the following
key-value pairs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; - email and password for your account.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timezone_offset&lt;/code&gt; - timezone offset in minutes; does not seem to matter much.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cacheKey&lt;/code&gt; - UNIX time of request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will also find that the request has some cookies from before. It also has &lt;code&gt;x-csrf-token&lt;/code&gt; header. At first glance it
may seem that these things make it more complicated to reproduce the login step, but a quick experiment with curl snippet
copied from Chrome DevTools shows that we can safely remove them and still have a working request. However, it is important
that request looks like it came from actual browser and thus we should keep rest of the headers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-12_15.05.58.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us write a function that creates a session object based on what we have discovered:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;(username, password):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;authority&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app.apollo.io&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34; Not A;Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;98&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;98&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-mobile&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;?0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-ch-ua-platform&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*/*&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://app.apollo.io&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-site&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;same-origin&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-mode&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cors&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sec-fetch-dest&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;empty&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;referer&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://app.apollo.io/&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en-US;q=0.9,en;q=0.8&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;: username,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;: password,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;timezone_offset&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cacheKey&amp;#39;&lt;/span&gt;: int(time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;time()),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://app.apollo.io/api/v1/auth/login&amp;#39;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The session object keeps track of headers and cookies between requests and enables us to set up the initial state once and
not to worry about it later. Later in the code we will be making further API requests through this object.&lt;/p&gt;
&lt;h1 id=&#34;using-the-session-with-cookies&#34;&gt;Using the session with cookies&lt;/h1&gt;
&lt;p&gt;Let us explore further. In the client area, choose Companies tab and search for something. You will find that further API
requests are made. We find that some of them are made to &lt;code&gt;/api/v1/mixed_companies/search&lt;/code&gt; with JSON payload containing
our search query at key &lt;code&gt;q_organization_name&lt;/code&gt;. We also have &lt;code&gt;page&lt;/code&gt; key-value pair for page number (starting from 1) and
something called &lt;code&gt;display_mode&lt;/code&gt; - this has to be set to &lt;code&gt;explorer_mode&lt;/code&gt; to let us retrieve all data search API gives us,
not just metadata.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-12_15.10.28.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also something called &lt;code&gt;finder_view_id&lt;/code&gt; which we can also find at various points earlier in the flow. The easiest
and earliest way to get this value is to dig it out of API response that we get at the login step.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-12_15.03.44.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thus we modify &lt;code&gt;create_session()&lt;/code&gt; to do so and return it as a tuple with session object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    view_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bootstrapped_data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;finder_views&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session, view_id
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No we are ready to reproduce company search API calls, but we need to ask the user for search query and output file path.
The complete script that logs in and performs Apollo.io private API scraping for company data (name, phone number, some URLs)
is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; random
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;(username, password):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authority&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;app.apollo.io&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34; Not A;Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;98&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;98&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-mobile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-ch-ua-platform&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;macOS&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*/*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;origin&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://app.apollo.io&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;same-origin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cors&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sec-fetch-dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;empty&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;referer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://app.apollo.io/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en-US;q=0.9,en;q=0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: username,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;: password,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;timezone_offset&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cacheKey&amp;#34;&lt;/span&gt;: int(time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;time()),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pprint(json_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://app.apollo.io/api/v1/auth/login&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    view_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bootstrapped_data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;finder_views&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session, view_id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_company_data&lt;/span&gt;(session, view_id, query, output_csv_path):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;finder_view_id&amp;#34;&lt;/span&gt;: view_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q_organization_name&amp;#34;&lt;/span&gt;: query,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;: page,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;display_mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;explorer_mode&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;per_page&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open_factor_names&amp;#34;&lt;/span&gt;: [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;num_fetch_result&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;companies-index-page&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;show_suggestions&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Based on:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# https://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ui_finder_random_seed&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            random&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;choice(string&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ascii_lowercase &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; string&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;digits) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; _ &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cacheKey&amp;#34;&lt;/span&gt;: int(time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;time()),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(output_csv_path, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linkedin_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;primary_domain&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://app.apollo.io/api/v1/mixed_companies/search&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;json_data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; org_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;organizations&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; org_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            linkedin_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; org_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linkedin_url&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            website_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; org_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website_url&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            primary_domain &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; org_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;primary_domain&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; org_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linkedin_url&amp;#34;&lt;/span&gt;: linkedin_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;website_url&amp;#34;&lt;/span&gt;: website_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;primary_domain&amp;#34;&lt;/span&gt;: primary_domain,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;phone&amp;#34;&lt;/span&gt;: phone,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pagination_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total_pages &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pagination_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;total_pages&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; total_pages &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; page:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_data[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; page
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Username: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    password &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Password: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Search query: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    output_csv_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Output CSV path: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session, view_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session(username, password)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scrape_company_data(session, view_id, query, output_csv_path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Using Scrapy pipelines to export scraped data</title>
      <link>https://www.trickster.dev/post/using-scrapy-pipelines-to-export-scraped-data/</link>
      <pubDate>Sun, 13 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/using-scrapy-pipelines-to-export-scraped-data/</guid>
      <description>&lt;p&gt;By default, Scrapy framework provides a way to export scraped data into CSV, JSON, JSONL,
XML files with a possibility to store them remotely. However, we may need more flexibility in
how and where the scraped data will be stored. This is the purpose of Scrapy item pipelines.
Scrapy pipeline is a component of Scrapy project for implementing post-processing and exporting
of scraped data. We are going to discuss how to implement data export code in pipelines and
provide a couple of examples.&lt;/p&gt;
&lt;p&gt;At code level, pipeline is a Python class that implements one or more of the following methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;open_spider()&lt;/code&gt; - called when scraping starts.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;process_item()&lt;/code&gt; - called when new item was scraped in the spider.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;close_spider()&lt;/code&gt; called when scraping ends.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;from_crawler()&lt;/code&gt; (class method) - factory method that is called from Scrapy crawler object to
create an instance of pipeline class, possibly with some customizations based on project settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will do our preparations to export data in &lt;code&gt;open_spider()&lt;/code&gt;, process data one item at a time in
&lt;code&gt;process_item()&lt;/code&gt; and finalise data export in &lt;code&gt;close_spider()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;For the sake of simplicity, we will use &lt;a href=&#34;https://github.com/scrapy/quotesbot&#34;&gt;quotesbot&lt;/a&gt; Scrapy
project. We will edit pipelines.py file to implement new pipelines and will also make minor changes
to settings.py file to enable newly developed pipelines in the project.&lt;/p&gt;
&lt;h1 id=&#34;using-openpyxl-to-write-data-to-excel-file&#34;&gt;Using openpyxl to write data to Excel file&lt;/h1&gt;
&lt;p&gt;Let us implement &lt;code&gt;open_spider()&lt;/code&gt; first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; openpyxl&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Workbook()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;active
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(FIELDNAMES)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We instantiated &lt;code&gt;openpyxl.Workbook&lt;/code&gt; object into instance variable and also saved a reference to
active work sheet (page within spreadsheet). Lastly, we created a header row in the spreadsheet
by appending list of fieldnames.&lt;/p&gt;
&lt;p&gt;Now we can process items. Let us implement &lt;code&gt;process_item()&lt;/code&gt; method.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemAdapter(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append([adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;), adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;))])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, we wrapped the item we have received into &lt;code&gt;ItemAdapter&lt;/code&gt; object that provides unified API for all kinds
of items that we can come across. For such a trivial example this may not be necessary, but it might prove to be useful
in larger, more complex Scrapy project and is a generally recommended thing to do. Next, we are calling &lt;code&gt;append()&lt;/code&gt; on
openpyxl worksheet object with a list of strings to be appended, ordered in such a way that they line up with
order of fieldnames. This ensures that values of each field are lining up with the header row in the final document.
Note that we cannot write tags directly due to it being a list of strings. To address this issue, we convert it to
a single string by using comma character as separator.&lt;/p&gt;
&lt;p&gt;Finally, we need to implement &lt;code&gt;close_spider()&lt;/code&gt; method where we will call &lt;code&gt;save()&lt;/code&gt; on openpyxl Workbook to perform
an actual writing to file system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;close_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save(XLSX_PATH)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The entire code for this pipeline is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define your item pipelines here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Don&amp;#39;t forget to add your pipeline to the ITEM_PIPELINES setting&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; itemadapter &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ItemAdapter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; openpyxl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; quotesbot.settings &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; XLSX_PATH
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;author&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;XLSXPipeline&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; openpyxl&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Workbook()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;active
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(FIELDNAMES)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemAdapter(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append([adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;), adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;))])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;close_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wb&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save(XLSX_PATH)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need to enable this pipeline in settings.py and also set the &lt;code&gt;XLSX_PATH&lt;/code&gt; configuration setting to some value.
We uncomment &lt;code&gt;ITEM_PIPELINES&lt;/code&gt; dictionary and edit with a complete class name or our pipeline:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ITEM_PIPELINES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;quotesbot.pipelines.XLSXPipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also set the &lt;code&gt;XLSX_PATH&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;XLSX_PATH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;quotes.xlsx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running any of the two spiders creates an Excel spreadsheet with scraped data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-13_11.10.33.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;saving-scraped-data-to-sqlite&#34;&gt;Saving scraped data to SQLite&lt;/h1&gt;
&lt;p&gt;We have a two tiered structure to our data: at one level, there&amp;rsquo;s quotes and at another we have lists of tags, associated with
each quote. In our openpyxl-based code we squashed the both levels into one by forcing each list of tags to became a comma-separated
string. It would be nice to keep the initial structure when saving the data for storage. This is where a database comes in.
For simplicity, we will use a database management system that is truly serverless in the most literal sense of the world: SQLite.
We will be setting up our tables in a single file in local system and writing our data there.&lt;/p&gt;
&lt;p&gt;Let us go through the same steps we did in our previous pipeline, but do them for SQLite now.&lt;/p&gt;
&lt;p&gt;Implementing &lt;code&gt;open_spider()&lt;/code&gt; involves creating a database connection object and two tables in the new database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sqlite3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect(SQLITE_PATH)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CREATE TABLE quote(id INTEGER PRIMARY KEY, text TEXT, author TEXT);&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CREATE TABLE tag(id INTEGER PRIMARY KEY, text TEXT, quote_id, FOREIGN KEY(quote_id) REFERENCES quote(id));&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One thing to note here is that &lt;code&gt;tag&lt;/code&gt; table references &lt;code&gt;quote&lt;/code&gt; table via foreign key, which represents a parent-child relationship
between quote an tag. Calling &lt;code&gt;commit()&lt;/code&gt; on database connection object saves the changes into file. Generally &lt;code&gt;execute()&lt;/code&gt; is not
directly called on DB connection directly like we do here - cursor object is used instead. We will be using the cursor in our next
method - &lt;code&gt;process_item()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemAdapter(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        author &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tags &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cursor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cursor()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INSERT INTO quote(text, author) VALUES (?, ?);&amp;#34;&lt;/span&gt;, (text, author))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quote_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;lastrowid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; t &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tags:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INSERT INTO tag(text, quote_id) VALUES (?, ?);&amp;#34;&lt;/span&gt;, (t, quote_id))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this method, we use SQLite Python API from vanilla Python installation to insert rows into both tables. We make sure that
&lt;code&gt;tag&lt;/code&gt; row properly points to a corresponding &lt;code&gt;quote&lt;/code&gt; row via the foreign key field.&lt;/p&gt;
&lt;p&gt;Lastly, we need to implement &lt;code&gt;close_spider()&lt;/code&gt; method that merely closes the DB connection:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;close_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Last thing we need to do is to update settings.py file so that part about pipelines is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Configure item pipelines&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ITEM_PIPELINES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;quotesbot.pipelines.XLSXPipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;quotesbot.pipelines.SQLitePipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;301&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;XLSX_PATH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;quotes.xlsx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SQLITE_PATH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;quotes.db&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, the priority value of first pipeline is lower, thus making it be executed first. That does not matter in such a trivial
example, but it might be important depending on the exact specifics of your project. If you have additional pipelines performing
data cleaning, filtering or other changes you want them to be executed before the data export pipelines.&lt;/p&gt;
&lt;p&gt;Running the Scrapy project again creates the quotes.db file that we can access via SQLite program:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sqlite3 quotes.db
-- Loading resources from /Users/[REDACTED]/.sqliterc
SQLite version 3.32.2 2020-06-04 12:58:43
Enter &amp;#34;.help&amp;#34; for usage hints.
sqlite&amp;gt; .schema
CREATE TABLE quote(id INTEGER PRIMARY KEY, text TEXT, author TEXT);
CREATE TABLE tag(id INTEGER PRIMARY KEY, text TEXT, quote_id, FOREIGN KEY(quote_id) REFERENCES quote(id));
sqlite&amp;gt; SELECT * FROM quote LIMIT 10;
id          text                                                                                                                 author
----------  -------------------------------------------------------------------------------------------------------------------  ---------------
1           “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”  Albert Einstein
2           “It is our choices, Harry, that show what we truly are, far more than our abilities.”                                J.K. Rowling
3           “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everythin  Albert Einstein
4           “The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”             Jane Austen
5           “Imperfection is beauty, madness is genius and it&amp;#39;s better to be absolutely ridiculous than absolutely boring.”      Marilyn Monroe
6           “Try not to become a man of success. Rather become a man of value.”                                                  Albert Einstein
7           “It is better to be hated for what you are than to be loved for what you are not.”                                   André Gide
8           “I have not failed. I&amp;#39;ve just found 10,000 ways that won&amp;#39;t work.”                                                    Thomas A. Ediso
9           “A woman is like a tea bag; you never know how strong it is until it&amp;#39;s in hot water.”                                Eleanor Rooseve
10          “A day without sunshine is like, you know, night.”                                                                   Steve Martin
sqlite&amp;gt; SELECT * FROM tag WHERE quote_id = 1;
id          text        quote_id
----------  ----------  ----------
1           change      1
2           deep-thoug  1
3           thinking    1
4           world       1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In case you are wondering about formatting of output being nicer than it typically is: it can be configured to show data in columns
by putting the following two lines into ~/.sqliterc:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.mode column on
.headers on
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To sum it up, the code for entire pipeline that saves data to SQLite DB is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlite3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; quotesbot.settings &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; SQLITE_PATH
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; itemadapter &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ItemAdapter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SQLitePipeline&lt;/span&gt;(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    db_conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sqlite3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect(SQLITE_PATH)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CREATE TABLE quote(id INTEGER PRIMARY KEY, text TEXT, author TEXT);&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CREATE TABLE tag(id INTEGER PRIMARY KEY, text TEXT, quote_id, FOREIGN KEY(quote_id) REFERENCES quote(id));&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemAdapter(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        author &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tags &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cursor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cursor()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INSERT INTO quote(text, author) VALUES (?, ?);&amp;#34;&lt;/span&gt;, (text, author))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        quote_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;lastrowid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; t &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tags:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            cursor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INSERT INTO tag(text, quote_id) VALUES (?, ?);&amp;#34;&lt;/span&gt;, (t, quote_id))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;close_spider&lt;/span&gt;(self, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;db_conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>How to use Scrapy Cloud</title>
      <link>https://www.trickster.dev/post/how-to-use-scrapy-cloud/</link>
      <pubDate>Thu, 10 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-to-use-scrapy-cloud/</guid>
      <description>&lt;p&gt;Scrapy is a prominent Python framework for web scraping that provides a certain kind of
project template structure for easier development. However, once Scrapy project is developed
it may be necessary to deploy it into cloud for long running scraping jobs.
&lt;a href=&#34;https://www.zyte.com/scrapy-cloud/&#34;&gt;Scrapy Cloud&lt;/a&gt; is a SaaS solution for hosting your
Scrapy projects, developed by the same company that created Scrapy framework. Let us go through
an example for using it with already developed scraper.&lt;/p&gt;
&lt;p&gt;First we need to sign up at Scrapy Cloud. This can be done with Github account via OAuth flow.
Once you&amp;rsquo;re in the client area, press &amp;ldquo;Start a new project&amp;rdquo; button. Enter the name for your project
(e.g. &amp;ldquo;quotes&amp;rdquo;) and press &amp;ldquo;Start&amp;rdquo;. You will be given shell commands to install CLI tool for
interacting with Scrapy Cloud (&lt;code&gt;shub&lt;/code&gt;), log in into the system from shell session and and deploy
your project.&lt;/p&gt;
&lt;p&gt;Now let us clone a git repository containing an example Scrapy project:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git clone https://github.com/scrapy/quotesbot.git
$ cd quotesbot
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go through the steps from Scrapy Cloud dashboard:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pip3 install shub
$ shub login
$ shub deploy [REDACTED]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you should have an URL for running your spiders through web interface. Open it in the browser.
Press Run button. In the modal view choose which spider(s) you want to run (our example has two),
priority and maybe some arguments for the spider (that can be ingested via keyword arguments in
the initializer of your &lt;code&gt;scrapy.Spider&lt;/code&gt; subclass). If you have a paid plan of Scrapy cloud you will
be able to increase Job Units as well. Press &amp;ldquo;Run&amp;rdquo; in the modal dialog to launch scraping.
Now your spider will be listed in the Running Jobs part of the page when it&amp;rsquo;s running and will
be in Completed Jobs page when it gets completed. Either way, next to spider name there will
a number of items scraped so far, number of requests made, number of errors detected and number
of log lines generated. Clicking either of these will provide a page with detailed listing of
corresponding data. You can export your scraped data in CSV, JSON, JSONL or XML formats from
the page that presents scraped items. It&amp;rsquo;s as simple as that.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-03-08_18.35.28.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To get notified about scraping jobs being finished you can enable email notifications by ticking
the checkbox under &amp;ldquo;Watching&amp;rdquo; in a project of interest.&lt;/p&gt;
&lt;p&gt;To schedule periodic rescraping of data, you can create a periodic job in the Periodic Jobs tab
of the dashboard. However, this requires a paid plan.&lt;/p&gt;
&lt;p&gt;You may want to have your Scrapy project running in the cloud, but control it from command line.
That is possible with &lt;code&gt;shub&lt;/code&gt; CLI. We have already deployed a Scrapy project using it, but we can
also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Schedule a scraping job for a given spider: &lt;code&gt;$ shub schedule toscrape-xpath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Watch logs of in-progress job: &lt;code&gt;$ shub log -f 2/2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Print items as they are being scraped: &lt;code&gt;$ shub items -f 2/2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Print request/response statuses: &lt;code&gt;$ shub requests -f 2/2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can also build custom Docker images to deploy our Scrapy projects that need more customised
environment, but that is a rather advanced use case and not needed that often.
To learn more about custom images, see: &lt;a href=&#34;https://shub.readthedocs.io/en/stable/deploy-custom-image.html&#34;&gt;https://shub.readthedocs.io/en/stable/deploy-custom-image.html&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sending notifications programmatically: let me count the ways</title>
      <link>https://www.trickster.dev/post/sending-notifications-programmatically/</link>
      <pubDate>Wed, 09 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/sending-notifications-programmatically/</guid>
      <description>&lt;p&gt;You may want to get notified about certain events happening during your scraping/botting operations. Examples
might be an outages of external systems that your setup depends on, fatal error conditions, scraping jobs
being finished, and so on. If you are implementing automations for bug bounty hunting, you certaintly want to get notified
about new vulnerabilities being found in target systems being scanned. You may also want to get periodic status
updates on long running tasks. This requires integrating one or more messaging channels into your code.&lt;/p&gt;
&lt;h2 id=&#34;sending-emails&#34;&gt;Sending emails&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with the least urgent kind of notification - a simple email message. SMTP (Simple Mail Transfer Protocol)
is a standard way to send email messages and is implemented in many programming languages. For example, vanilla
Python installation includes module called &lt;a href=&#34;https://docs.python.org/3/library/smtplib.html&#34;&gt;&lt;code&gt;smtplib&lt;/code&gt;&lt;/a&gt; that can be used
to send email messages. This requires an email account with functional SMTP interface. It is supported by most email
providers, altough you may need to adjust your account settings. Alternatively, you can use email sending SaaS
vendor such as &lt;a href=&#34;https://sendgrid.com/&#34;&gt;SendGrid&lt;/a&gt; or &lt;a href=&#34;https://aws.amazon.com/ses/&#34;&gt;AWS SES&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A very basic example of using Python SMTP API is provided by the &lt;a href=&#34;https://docs.python.org/3/library/smtplib.html#smtp-example&#34;&gt;official documentation&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; smtplib
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;prompt&lt;/span&gt;(prompt):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; input(prompt)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fromaddr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; prompt(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;From: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;toaddrs  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; prompt(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;To: &amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enter message, end with ^D (Unix) or ^Z (Windows):&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Add the From: and To: headers at the start!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;msg &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;From: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;To: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (fromaddr, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(toaddrs)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        line &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;EOFError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; line:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    msg &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; msg &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; line
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Message length is&amp;#34;&lt;/span&gt;, len(msg))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; smtplib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;SMTP(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_debuglevel(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sendmail(fromaddr, toaddrs, msg)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;quit()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This sample code assumes that you have a local SMTP server running and that you are not using TLS for your SMTP connections.
That will not always be the case. Let us try sending a simple email message via Sendgrid. Now we are connecting to non-standard
SMTP port, wrapping SMTP communications in the TLS connection for security purposes and using SMTP authentication with username
&lt;code&gt;apikey&lt;/code&gt; and password being the API key we are getting from SendGrid dashboard. Even so, sending an email from Python REPL is
rather simple:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import smtplib
&amp;gt;&amp;gt;&amp;gt; smtp = smtplib.SMTP(&amp;#34;smtp.sendgrid.com&amp;#34;, port=2525)
&amp;gt;&amp;gt;&amp;gt; smtp.starttls()
(220, b&amp;#39;Begin TLS negotiation now&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; smtp.login(&amp;#34;apikey&amp;#34;, &amp;#34;[REDACTED]&amp;#34;)
(235, b&amp;#39;Authentication successful&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; from email.message import EmailMessage
&amp;gt;&amp;gt;&amp;gt; msg = EmailMessage()
&amp;gt;&amp;gt;&amp;gt; msg.set_content(&amp;#34;Hello via SendGrid&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; msg[&amp;#39;Subject&amp;#39;] = &amp;#34;Test&amp;#34;
&amp;gt;&amp;gt;&amp;gt; msg[&amp;#39;From&amp;#39;] = &amp;#34;noreply@keyspace.lt&amp;#34;
&amp;gt;&amp;gt;&amp;gt; msg[&amp;#39;To&amp;#39;] = &amp;#34;noreply@keyspace.lt&amp;#34;
&amp;gt;&amp;gt;&amp;gt; msg.as_string()
&amp;#39;Content-Type: text/plain; charset=&amp;#34;utf-8&amp;#34;\nContent-Transfer-Encoding: 7bit\nMIME-Version: 1.0\nSubject: Test\nFrom: noreply@keyspace.lt\nTo: noreply@keyspace.lt\n\nHello via SendGrid\n&amp;#39;
&amp;gt;&amp;gt;&amp;gt; smtp.send_message(msg)
{}
&amp;gt;&amp;gt;&amp;gt; smtp.close()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we used &lt;a href=&#34;https://docs.python.org/3/library/email.html#module-email&#34;&gt;&lt;code&gt;email&lt;/code&gt;&lt;/a&gt; module to construct the exact textual
structure of message we are sending, which is generally more desirable
over having your own code generate it, like it was done in &lt;code&gt;smtplib&lt;/code&gt; sample code.&lt;/p&gt;
&lt;p&gt;Note however that you would need to go through a procedure of verifying your sender email address with SendGrid or AWS SES.&lt;/p&gt;
&lt;p&gt;However it might be that you prefer using REST APIs for everything. SendGrid also supports sending email via
&lt;a href=&#34;https://docs.sendgrid.com/api-reference/mail-send/mail-send&#34;&gt;REST API&lt;/a&gt; and even has some client libraries in multiple
languages, including Python.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/sendgrid/sendgrid-python&#34;&gt;SendGrid Python module&lt;/a&gt; enables us to send emails via API that largely abstracts
away network protocol details:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import sendgrid
&amp;gt;&amp;gt;&amp;gt; from sendgrid.helpers.mail import *
&amp;gt;&amp;gt;&amp;gt; client = sendgrid.SendGridAPIClient(&amp;#34;[REDACTED]&amp;#34;) # &amp;lt;-- API key
&amp;gt;&amp;gt;&amp;gt; from_email = &amp;#34;noreply@keyspace.lt&amp;#34;
&amp;gt;&amp;gt;&amp;gt; to_email = &amp;#34;noreply@keyspace.lt&amp;#34;
&amp;gt;&amp;gt;&amp;gt; subject = &amp;#34;Test #2&amp;#34;
&amp;gt;&amp;gt;&amp;gt; content = Content(mime_type=&amp;#34;text/plain&amp;#34;, content=&amp;#34;Hello via SendGrid API&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; mail = Mail(from_email, to_email, subject, content)
&amp;gt;&amp;gt;&amp;gt; resp = client.send(mail)
&amp;gt;&amp;gt;&amp;gt; resp
&amp;lt;python_http_client.client.Response object at 0x106a35eb0&amp;gt;
&amp;gt;&amp;gt;&amp;gt; resp.status_code
202
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However this would introduce a vendor-specific library in our code base, which is not always desirable, as switching to another
vendor for sending emails would be harder than simply changing SMTP server settings.&lt;/p&gt;
&lt;p&gt;It should be noted that GMail also has REST API with some client libraries that allow sending emails programmatically, but
requires implementing OAuth flow, which makes it somewhat harder to integrate into scraping/automation scripts.&lt;/p&gt;
&lt;h2 id=&#34;sending-telegram-notifications&#34;&gt;Sending Telegram notifications&lt;/h2&gt;
&lt;p&gt;Perhaps you don&amp;rsquo;t use email that much nowadays and reserve it for important communications, such as clueless recruiter or HR
person asking you if you have five years of experience in a technology that emerged one year ago. Or perhaps you need to be
notified in a way that enables more urgent response. This is where messenger notifications come into picture. Modern IM platforms
typically have mobile apps, which enables you to receive push notifications when you receive direct (private?) messages that your
code will be sending. One of these platforms is Telegram.&lt;/p&gt;
&lt;p&gt;To register for API access with Telegram, you will need to contact &lt;code&gt;@BotFather&lt;/code&gt; via direct message and answer some questions
regarding your bot. Once the registration procedure is finished, you will get an API key for Telegram HTTP API. You may
have heard about &lt;a href=&#34;https://docs.telethon.dev/en/stable/&#34;&gt;Telethon&lt;/a&gt; - a Python module for building Telegram bots that largely
relies on asynchronuous programming techniques. Telethon is not needed for simply sending notifications, as REST API will
be enough.&lt;/p&gt;
&lt;p&gt;To start receiving notifications, you would need to DM your bot with message &lt;code&gt;/start&lt;/code&gt;. This will register you as having initiated
a conversation with the bot. Bots are not allowed to initiate chats with regular users.&lt;/p&gt;
&lt;p&gt;To send a message, we have to get a list of chat IDs first:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;find_chat_ids&lt;/span&gt;(token):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chat_ids &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://api.telegram.org/bot&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/getUpdates&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;, [])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; rd &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; results:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chat_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chat_ids&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(chat_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; list(chat_ids)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have a list of chat IDs you can message all subscribers by posting chat ID and message contents to the API:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; chat_id &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; find_chat_ids(token):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;chat_id&amp;#34;&lt;/span&gt;: chat_id, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;: message}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://api.telegram.org/bot&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/sendMessage&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(url, data&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;sending-discord-notifications&#34;&gt;Sending Discord notifications&lt;/h2&gt;
&lt;p&gt;Similarly to Telegram, Discord also provides an &lt;a href=&#34;https://discord.com/developers/docs/intro&#34;&gt;API&lt;/a&gt; for bot development that
requires you to register your bot at developer portal to get credentials for API access.
&lt;a href=&#34;https://discordpy.readthedocs.io/en/stable/&#34;&gt;discord-py&lt;/a&gt; is a prominent Python module for implementing Discord chat bots,
To send a DM programmatically you don&amp;rsquo;t need a registration step from the user, but the user has to be present in the same
&amp;ldquo;server&amp;rdquo;/guild as the bot. Alternatively, your bot could send chat messages to a regular channel on Discord.&lt;/p&gt;
&lt;p&gt;A gray hat approach would be what&amp;rsquo;s called &amp;ldquo;self-botting&amp;rdquo; - automating against private Discord API and pretending to be a
regular user when sending messages. However Discord is deploying countermeasures against things like that.&lt;/p&gt;
&lt;h2 id=&#34;sending-sms-message-via-twilio&#34;&gt;Sending SMS message via Twilio&lt;/h2&gt;
&lt;p&gt;Perhaps you don&amp;rsquo;t want to rely on some IM platform to notify you and don&amp;rsquo;t mind spending a bit of money to set up notifications
in a way that makes them reach you even if you don&amp;rsquo;t have TCP/IP connectivity on your smartphone. This is where goold old
Short Message Service (SMS) comes in. To start sending SMS messages programmatically, we first need to sign up at
&lt;a href=&#34;https://www.twilio.com/&#34;&gt;Twilio&lt;/a&gt; - a prominent SaaS vendor that exposes telecommunications features via REST API for easy
integration. Then we would purchase a phone number and get an API credentials (account SID and auth token).&lt;/p&gt;
&lt;p&gt;Now we have 3 ways to send an SMS message:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calling REST API directly:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -X POST https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json \
--data-urlencode &amp;#34;Body=Hello there!&amp;#34; \
--data-urlencode &amp;#34;From=[REDACTED]&amp;#34; \
--data-urlencode &amp;#34;To=[REDACTED]&amp;#34; \
-u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;twilio&lt;/code&gt; Python module (or any of the other client libraries that Twilio provides):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; twilio.rest &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;account_sid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auth_token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Client(account_sid, auth_token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;message &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;messages&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create(body&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello there!&amp;#34;&lt;/span&gt;, from_&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[REDACTED]&amp;#39;&lt;/span&gt;, to&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[REDACTED]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Using &lt;a href=&#34;https://www.twilio.com/docs/twilio-cli/quickstart&#34;&gt;Twilio CLI&lt;/a&gt; tool:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ twilio api:core:messages:create --from &amp;#34;[REDACTED]&amp;#34; --to &amp;#34;[REDACTED]&amp;#34; --body &amp;#34;Hello there!&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All phone numbers have to in E.164 format, e.g. &amp;ldquo;+14155552671&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;making-a-phone-call-via-twilio&#34;&gt;Making a phone call via Twilio&lt;/h2&gt;
&lt;p&gt;Sometimes things are getting complicated and may require waking you or a support engineer in the middle of the night
to immediately address issues that have emerged. Thus we need to make a phone call, that we can also do via Twilio in all
3 ways that applies for sending SMS. However there is one key difference - when making a phone call via Twilio API
we have to pass a &lt;a href=&#34;https://www.twilio.com/docs/voice/twiml&#34;&gt;TwiML&lt;/a&gt; payload that will define interaction with the user
that picks up the phone. This can be a rather complex flow, but for the sake of simplicity let us make Twilio use
text-to-speech synthesis to pass a message for us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Data center is on fire!&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us make a phone call from Python REPL:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from twilio.rest import Client
&amp;gt;&amp;gt;&amp;gt; client = Client(&amp;#34;[REDACTED]&amp;#34;, &amp;#34;[REDACTED]&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; call = client.calls.create(twiml=&amp;#39;&amp;lt;Response&amp;gt;&amp;lt;Say&amp;gt;Data center is on fire!&amp;lt;/Say&amp;gt;&amp;lt;/Response&amp;gt;&amp;#39;, to=&amp;#34;[REDACTED]&amp;#34;, from_=&amp;#34;[REDACTED]&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;theres-more-ways-to-get-notified&#34;&gt;There&amp;rsquo;s more ways to get notified&amp;hellip;&lt;/h2&gt;
&lt;p&gt;You may also want to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/web/fundamentals/push-notifications&#34;&gt;Web Push notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Sending Whatsapp messages via &lt;a href=&#34;https://www.twilio.com/docs/whatsapp/api&#34;&gt;Twilio Whatsapp Business API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Setting up Slack chatbot via &lt;a href=&#34;https://api.slack.com/&#34;&gt;Slack API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Integrating PagerDuty or similar solution if uptime is truly critical.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Evaluating MS Playwright for gray hat automation</title>
      <link>https://www.trickster.dev/post/evaluating-ms-playwright-for-gray-hat-automation/</link>
      <pubDate>Sat, 05 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/evaluating-ms-playwright-for-gray-hat-automation/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://playwright.dev/&#34;&gt;MS Playwright&lt;/a&gt; is a framework for web testing and automation.
Playwright supports programmatic control of Chromium and Firefox browser and also integrates WebKit engine.
Headless mode is supported and enabled by default, thus making it possible to run your automations in
environments that have no GUI support (lightweight virtual private servers, Docker containers and so on).
Playwright is written in JavaScript, but has official bindings for Python, C#, TypeScript and Java.
Think of Playwright as more modern equivalent of things like Selenium and Puppetteer that solve some of the
problems associated with these technologies (e.g. crashes in your code due to race conditions).
Since Python is commonly used for gray hat automation, we will experiment with Python version of Playwright
API with the need to evade automation countermeasures in mind.&lt;/p&gt;
&lt;p&gt;Let us install Playwright first in Python development enviromnent. This is fairly simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;pip3 install playwright&lt;/code&gt; to install Playwright Python module with CLI tool.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;playwright install&lt;/code&gt; to install instances of browsers that Playwright manages for you.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;playwright install-deps&lt;/code&gt; to install other dependencies that Playwright needs to run. Depending on your enviroment this
might not be needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With these 3 steps I was able to get Playwright working on $5 Digital Ocean server within minutes.&lt;/p&gt;
&lt;p&gt;Now let us do our first experiment. Let&amp;rsquo;s modify some sample code to try loading two sites with all 3
browsers Playwright supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ifconfig.me&#34;&gt;ifconfig.me&lt;/a&gt; - gives us page with source IP address and headers within HTTP request.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.stockx.com&#34;&gt;StockX&lt;/a&gt; - prominent sneaker marketplace that is protected by PerimeterX and
Cloudflare anti-botting technologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.sync_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sync_playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; sync_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; p:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.stockx.com&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://ifconfig.me&amp;#34;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; browser_type &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; [p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium, p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;firefox, p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;webkit]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser_type&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;launch()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;title())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            domain &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; url&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//&amp;#34;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;screenshot(path&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;example-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;domain&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;browser_type&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/example-ifconfig.me-chromium.png&#34;&gt;example-ifconfig.me-chromium.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/example-www.stockx.com-chromium.png&#34;&gt;example-www.stockx.com-chromium.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We see that string &lt;code&gt;Headless&lt;/code&gt; is included into User-Agent header and that an attempt to load StockX page instantly
fails due to be being detected by PerimeterX.&lt;/p&gt;
&lt;p&gt;However, we can set &lt;code&gt;user_agent&lt;/code&gt; &lt;a href=&#34;https://playwright.dev/python/docs/api/class-browser#browser-new-page&#34;&gt;argument&lt;/a&gt;
when calling &lt;code&gt;new_page()&lt;/code&gt; method and pass a string with User-Agent value from a proper browser, which prevents getting
blocked on the very first page by PerimeterX.&lt;/p&gt;
&lt;p&gt;We can also set &lt;code&gt;proxy&lt;/code&gt; argument to dictionary with proxy address and credentials. Unfortunately, it does not support putting proxy
credentials and address in the same way as &lt;code&gt;requests&lt;/code&gt; module does. Instead, we would need to create a dictionary with following
key-value pairs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;server&lt;/code&gt; - server URL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt; - proxy username.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;password&lt;/code&gt; - proxy password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Playwright also has a code generator feature that we can try by launching &lt;code&gt;playwright codegen&lt;/code&gt;. Running this command
launches a Playwright-managed browser (Chromium by default) with an Inspector window next to it. When user actions are performed
in the browser window, code that reproduces these actions is automatically generated in inspector window. For instance, going
through the happy path of Instagram login flow generates the following code that you can use as starting point for your automation
script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.sync_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Playwright, sync_playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(playwright: Playwright) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; playwright&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;launch(headless&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_context()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Open new page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Go to https://www.instagram.com/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.instagram.com/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Click text=Only allow essential cookies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text=Only allow essential cookies&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;click()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Click [aria-label=&amp;#34;Phone\ number\,\ username\,\ or\ email&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[aria-label=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Phone&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; number&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; username&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; or&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; email&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;]&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;click()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Fill [aria-label=&amp;#34;Phone\ number\,\ username\,\ or\ email&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[aria-label=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Phone&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; number&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; username&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; or&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; email&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;]&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fill(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Click [aria-label=&amp;#34;Password&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[aria-label=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Password&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;]&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;click()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Fill [aria-label=&amp;#34;Password&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[aria-label=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Password&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;]&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fill(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Click button:has-text(&amp;#34;Log In&amp;#34;) &amp;gt;&amp;gt; nth=0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# with page.expect_navigation(url=&amp;#34;https://www.instagram.com/&amp;#34;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;expect_navigation():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;button:has-text(&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Log In&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;first&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;click()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Click text=Not Now&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locator(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text=Not Now&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;click()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ---------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; sync_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; playwright:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run(playwright)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We see that the code is fairly linear and does not have explicit waiting steps or any kind of exception handling. This is because
of Playwright locators - every time you call a &lt;code&gt;locator()&lt;/code&gt; method Playwright makes sure that any waiting for element you are
interacting with is done automatically. String argument that is being passed into this method is called a selector and can be a
CSS selector, XPath query, React selector, or Vue selector.&lt;/p&gt;
&lt;p&gt;A word of warning: it is generally advised to refrain from running automations against Instagram accounts that are considered
valueable, as they can get suspended or banned due to triggering automation countermeasures at IG. For experimentation, you can get
some disposable accounts from &lt;a href=&#34;https://accfarm.com/&#34;&gt;Accfarm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When doing social media automation, it might be desirable to save cookies and reuse them to avoid performing login flow excessively.
Calling &lt;code&gt;cookies&lt;/code&gt; on browser context object (&lt;code&gt;context&lt;/code&gt; in the above code) gives us list of dictionaries with cookie data that we
can serialize into JSON and save:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cookies.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dump(cookies, c_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To reuse user session next time, we would call &lt;code&gt;add_cookies()&lt;/code&gt; on browser context object with cookies that we have saved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; playwright.sync_api &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Playwright, sync_playwright
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(playwright: Playwright) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; playwright&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chromium&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;launch(headless&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_context()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cookies.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cookies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(c_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_cookies(cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Open new page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new_page()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Go to https://www.instagram.com/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;goto(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.instagram.com/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ---------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    browser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; sync_playwright() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; playwright:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run(playwright)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Previously, we have used Python API to make page screenshots. However, Playwright CLI tool provides a quicker way to save
screenshot:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ playwright screenshot https://ifconfig.me ifconfig.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Likewise, page content can be quickly saved into PDF document:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ playwright pdf https://ifconfig.me ifconfig.pdf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have barely scratched the surface of what Playwright can do, but what we have seen so far is promising. Playwright supports less
browsers than Selenium does, but looks like a far superior choice when it comes to stability and developer experience.
Some developers working in web scraping and gray hat automation say that it also has an additional benefit of making it easier
to evade automation countermeasures, as there are less tell-tale signs in JS environment that betray browser being controlled
programmatically. Our quick experiments with StockX and Instagram seem to support that.&lt;/p&gt;
&lt;p&gt;You may also want to try &lt;a href=&#34;https://github.com/AtuboDad/playwright_stealth&#34;&gt;Playwright_stealth&lt;/a&gt; - a Python module for additional
blocking evasion features.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Automating spreadsheet tasks with openpyxl</title>
      <link>https://www.trickster.dev/post/automating-spreadsheet-tasks-with-openpyxl/</link>
      <pubDate>Thu, 24 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/automating-spreadsheet-tasks-with-openpyxl/</guid>
      <description>&lt;p&gt;Spreadsheets are a mainstay tool for information processing in many domains and widely used by people in many walks
of life. It is fairly common for developers working in scraping and automation to be dealing with spreadsheets for
inputting data into custom code, report generation and other tasks. &lt;a href=&#34;https://foss.heptapod.net/openpyxl/openpyxl&#34;&gt;Openpyxl&lt;/a&gt;
is a prominent Python module for reading and writing a spreadsheet files in Open Office XML format that is compatible
with many spreasheet programs (MS Excel 2010 and later, Google Docs, Open Office, Libre Office, Apple Numbers, etc.).&lt;/p&gt;
&lt;p&gt;First, let us download a spreadsheet file for experimentation. In this example we are going to read medical service pricing
data for a certain hospital in USA.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ wget https://28g1xh366uy0x9ort41hns72-wpengine.netdna-ssl.com/wp-content/uploads/2021/12/Albuquerque-Price-Transparency-01-2022.xlsx
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Spreadsheet document is opened into workbook (openpyxl object for entire spreadsheet document) by calling &lt;code&gt;load_workbook()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import openpyxl
&amp;gt;&amp;gt;&amp;gt; wb = openpyxl.load_workbook(&amp;#34;Albuquerque-Price-Transparency-01-2022.xlsx&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Argument to &lt;code&gt;load_workbook()&lt;/code&gt; can also be a file handle or some object similar to it (e.g. &lt;code&gt;BytesIO&lt;/code&gt; buffer from &lt;code&gt;io&lt;/code&gt; module).&lt;/p&gt;
&lt;p&gt;Now we need to get a work sheet object for a primary (active) sheet. We can get it by reading &lt;code&gt;active&lt;/code&gt; property of the work book.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; ws = wb.active
&amp;gt;&amp;gt;&amp;gt; ws
&amp;lt;Worksheet &amp;#34;Albuquerque Jan 2019&amp;#34;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We may want to read other sheets as well, or read a certain sheet by it&amp;rsquo;s name. For this purpose, &lt;code&gt;sheetnames&lt;/code&gt; property
provides a list of names of individual sheets in the document:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; wb.sheetnames
[&amp;#39;Albuquerque Jan 2019&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Indexing work book object by a sheet name gives a work sheet object for that sheet, or raises an exception if it does not
exist:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; wb[&amp;#39;Albuquerque Jan 2019&amp;#39;]
&amp;lt;Worksheet &amp;#34;Albuquerque Jan 2019&amp;#34;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; wb[&amp;#39;...&amp;#39;]
Traceback (most recent call last):
  File &amp;#34;&amp;lt;stdin&amp;gt;&amp;#34;, line 1, in &amp;lt;module&amp;gt;
  File &amp;#34;/usr/local/lib/python3.9/site-packages/openpyxl/workbook/workbook.py&amp;#34;, line 288, in __getitem__
    raise KeyError(&amp;#34;Worksheet {0} does not exist.&amp;#34;.format(key))
KeyError: &amp;#39;Worksheet ... does not exist.&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One way to read data from work sheet is to get individual cells (by row and column) and access their &lt;code&gt;value&lt;/code&gt; property:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; ws.cell(row=1, column=1)
&amp;lt;Cell &amp;#39;Albuquerque Jan 2019&amp;#39;.A1&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ws.cell(row=1, column=1).value
&amp;#39;Albuquerque&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that row and column indices start at 1, not at 0.&lt;/p&gt;
&lt;p&gt;Another way is to use row generator at &lt;code&gt;rows&lt;/code&gt; property that will lazily read and return lists of cells on a give work sheet.
Iterating across &lt;code&gt;rows&lt;/code&gt; is an easy way to get all data within the work sheet:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for row in ws.rows:
...     row = list(map(lambda cell: cell.value, row))
...     print(row)
... 
[&amp;#39;Albuquerque&amp;#39;, None, None, None]
[&amp;#39;Revenue Code&amp;#39;, &amp;#39;Description&amp;#39;, &amp;#39;Procedure Number&amp;#39;, &amp;#39;Standard Amount&amp;#39;]
[111, &amp;#39;PRIVATE ROOM - LTAC&amp;#39;, &amp;#39;PRVL&amp;#39;, 1750]
[118, &amp;#39;PRIVATE ROOM - REHAB&amp;#39;, &amp;#39;PRIVR&amp;#39;, 1345]
[121, &amp;#39;SEMI-PRIVATE ROOM LTAC&amp;#39;, &amp;#39;SEMIL&amp;#39;, 1750]
[128, &amp;#39;SEMI-PRIVATE ROOM REHAB&amp;#39;, &amp;#39;SEMIR&amp;#39;, 13.45]
[190, &amp;#39;PRIVATE ROOM SUB-ACUTE&amp;#39;, &amp;#39;PRSUB&amp;#39;, 1200]
[190, &amp;#39;SEMI-PRIVATE ROOM SUB-ACUTE&amp;#39;, &amp;#39;SEMSUB&amp;#39;, 1200]
[202, &amp;#39;ICU ROOM PRIVATE&amp;#39;, &amp;#39;ICUP&amp;#39;, 2135]
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let us create a new workbook object to exemplify ways of writing data into spreadsheets through openpyxl:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; wb = openpyxl.Workbook()
&amp;gt;&amp;gt;&amp;gt; wb
&amp;lt;openpyxl.workbook.workbook.Workbook object at 0x116f93190&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like with Excel, this newly created work book comes with a single empty work sheet:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; wb.sheetnames
[&amp;#39;Sheet&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; ws = wb.active
&amp;gt;&amp;gt;&amp;gt; ws
&amp;lt;Worksheet &amp;#34;Sheet&amp;#34;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One way set value of a cell is to use indexing with Excel notation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; ws[&amp;#39;A1&amp;#39;] = &amp;#39;N&amp;#39;
&amp;gt;&amp;gt;&amp;gt; ws[&amp;#39;A2&amp;#39;] = 25
&amp;gt;&amp;gt;&amp;gt; ws[&amp;#39;A1&amp;#39;]
&amp;lt;Cell &amp;#39;Sheet&amp;#39;.A1&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ws[&amp;#39;A1&amp;#39;].value
&amp;#39;N&amp;#39;
&amp;gt;&amp;gt;&amp;gt; ws[&amp;#39;A2&amp;#39;].value
25
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another way is to call &lt;code&gt;cell&lt;/code&gt; method on work sheet object with &lt;code&gt;value&lt;/code&gt; keyword argument being set:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; _ = ws.cell(row=2, column=1, value=&amp;#39;x&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; _ = ws.cell(row=2, column=2, value=3.5)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you want to append an entire row (e.g. when doing row-by-row writing of scraped data) you can call &lt;code&gt;append()&lt;/code&gt;
method on work sheet:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; ws.append([&amp;#39;f&amp;#39;, 8000])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that cell values can also be formulas that spreadsheet programs will run once.&lt;/p&gt;
&lt;p&gt;We may want to add new work sheet to a workbook. This can be done by calling &lt;code&gt;create_sheet()&lt;/code&gt; method with name of new sheet:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; wb.create_sheet(&amp;#39;test&amp;#39;)
&amp;lt;Worksheet &amp;#34;test&amp;#34;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; wb.sheetnames
[&amp;#39;Sheet&amp;#39;, &amp;#39;test&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To delete a sheet, we use the &lt;code&gt;del&lt;/code&gt; operator on it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; del wb[&amp;#39;test&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; wb.sheetnames
[&amp;#39;Sheet&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling &lt;code&gt;save()&lt;/code&gt; method on a workbook with path of output XLSX file will save it to a file and write out all contents from the
memory.&lt;/p&gt;
&lt;p&gt;Openpyxl also allows us to control visual style of cells (fonts, colors, borders, cell alignments. See
&lt;a href=&#34;https://openpyxl.readthedocs.io/en/stable/styles.html&#34;&gt;Working with styles&lt;/a&gt; page of the official documentation.&lt;/p&gt;
&lt;p&gt;If you are automating spreadsheet tasks with Python, you may also want to check out the following modules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pandas.pydata.org/&#34;&gt;Pandas&lt;/a&gt; - Swiss army knife for handling tabular data across multiple formats including,
but not limited to, spreadsheets. Think of it as a more programmable version of Excel inside Python module.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://xlsxwriter.readthedocs.io/&#34;&gt;XlsxWriter&lt;/a&gt; - Python module for writing data to older format that is compatible with
Excel 2007+.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/python-excel/xlrd&#34;&gt;xlrd&lt;/a&gt; - Python module for reading legacy .xls files.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/sheets/api/quickstart/python&#34;&gt;Google client libraries for Google Sheets/Drive/Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Scrapy framework tips and tricks</title>
      <link>https://www.trickster.dev/post/scrapy-framework-tips-and-tricks/</link>
      <pubDate>Sun, 20 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/scrapy-framework-tips-and-tricks/</guid>
      <description>&lt;h2 id=&#34;use-scrapy-shell-for-interactive-experimentation&#34;&gt;Use Scrapy shell for interactive experimentation&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;scrapy shell&lt;/code&gt; gives you an interactive environment for experimenting with
the site being scraped. For example, running &lt;code&gt;fetch()&lt;/code&gt; with URL of page
fetches the page and creates a &lt;code&gt;response&lt;/code&gt; variable with &lt;code&gt;scrapy.Response&lt;/code&gt; object for that page.
&lt;code&gt;view(response)&lt;/code&gt; opens the browser to let you see the HTML that the Scrapy spider would fetch.
This bypasses some of the client-side rendering and also lets us detect if the site has some
countermeasures against scraping.&lt;/p&gt;
&lt;p&gt;Furthermore, calling &lt;code&gt;css()&lt;/code&gt; or &lt;code&gt;xpath()&lt;/code&gt; methods on response object is a convenient way to
refine your CSS or XPath queries before putting them into actual Python code.&lt;/p&gt;
&lt;p&gt;To learn more about Scrapy shell, see: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/shell.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/shell.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;use-imagefile-pipeline-for-file-downloading&#34;&gt;Use image/file pipeline for file downloading&lt;/h2&gt;
&lt;p&gt;Sometimes your web scraping project will involve downloading images or other kinds of files.
Scrapy provides some official pipelines for this exact task.&lt;/p&gt;
&lt;p&gt;To download any kind of files, we can integrate &lt;code&gt;FilesPipeline&lt;/code&gt; by adding
it into &lt;code&gt;ITEM_PIPELINES&lt;/code&gt; map in settings.py file like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ITEM_PIPELINES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;scrapy.pipelines.files.FilesPipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You would also need to set the directory path for files to be downloaded:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FILES_STORE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/path/to/valid/dir&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Furthermore, your items will need to include &lt;code&gt;file_urls&lt;/code&gt; field with a list of file URLs.
When files pipeline processes the item, it will download each file into directory you
have configured and will set a &lt;code&gt;files&lt;/code&gt; property with list of dictionaries of original URLs and
paths of downloaded files. To convert file URL into file name, SHA1 hash is computed on
URL and prepended to the original file extension.&lt;/p&gt;
&lt;p&gt;Integration of images pipeline is rather similar. You would edit settings.py to add
&lt;code&gt;scrapy.pipelines.files.FilesPipeline&lt;/code&gt; into &lt;code&gt;ITEM_PIPELINES&lt;/code&gt; dictionary and set
&lt;code&gt;IMAGES_STORE&lt;/code&gt; to path of directory that will be used for storing files. However,
images pipeline provides some image processing capabilities, thus requiring that
&lt;a href=&#34;https://pillow.readthedocs.io/en/stable/&#34;&gt;Pillow&lt;/a&gt; module is installed. By default,
images pipeline automatically converts all downloaded images to JPEG format.&lt;/p&gt;
&lt;p&gt;Images pipeline expects &lt;code&gt;image_urls&lt;/code&gt; item property to be filled with list of image
URLs and will set &lt;code&gt;images&lt;/code&gt; property in a a way equivalent to &lt;code&gt;files&lt;/code&gt; property of
files pipeline.&lt;/p&gt;
&lt;p&gt;Both pipelines allow storing downloaded files on stores external to local file system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS S3 buckets&lt;/li&gt;
&lt;li&gt;Remote FTP servers&lt;/li&gt;
&lt;li&gt;Google Cloud storage buckets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn more about this, see: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/media-pipeline.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/media-pipeline.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;use-automatic-throttling-for-scraping-rate-limited-sites&#34;&gt;Use automatic throttling for scraping rate-limited sites&lt;/h2&gt;
&lt;p&gt;Some sites implement rate-limiting and will refuse to give you responses with proper pages
if you are generating too many requests too quickly from single IP. A simple way to slow
down your Scrapy project is to decrease the following values in settings.py:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONCURRENT_REQUESTS_PER_DOMAIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONCURRENT_REQUESTS_PER_IP&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These variables control upper limit of how many concurrent requests spider is allowed to
launch per domain/target IP address - only one of them should be set.&lt;/p&gt;
&lt;p&gt;A more advanced way to slow down is to use &lt;code&gt;AutoThrottle&lt;/code&gt; extension by uncommenting the following
parts of settings.py file and experimenting with values to reach a point of no requests being dropped:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Enable and configure the AutoThrottle extension (disabled by default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See https://docs.scrapy.org/en/latest/topics/autothrottle.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#AUTOTHROTTLE_ENABLED = True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# The initial download delay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#AUTOTHROTTLE_START_DELAY = 5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# The maximum download delay to be set in case of high latencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#AUTOTHROTTLE_MAX_DELAY = 60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# The average number of requests Scrapy should be sending in parallel to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# each remote server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Enable showing throttling stats for every response received:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#AUTOTHROTTLE_DEBUG = False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will enable an adaptive throttling algorithm that will take into account a load of
both your Scrapy instance and that of remote server(s) being scraped.&lt;/p&gt;
&lt;p&gt;To learn more about automatic throttling, see: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/autothrottle.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/autothrottle.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;deploy-scrapy-projects-to-the-cloud&#34;&gt;Deploy Scrapy projects to the cloud&lt;/h2&gt;
&lt;p&gt;Once you have your Scrapy project running properly, you may want to avoid running it on your local machine to
perform scraping. There are several ways to get it running in the cloud environment.&lt;/p&gt;
&lt;p&gt;The simplest way is to get a cheap VPS (e.g. $5/month Digital Ocean droplet), install Scrapy there, upload your
Scrapy project via SFTP and run it in tmux session.&lt;/p&gt;
&lt;p&gt;Another way is to self-host a solution like &lt;a href=&#34;https://github.com/my8100/scrapydweb&#34;&gt;Scrapydweb&lt;/a&gt; that will provide
you with web interface to upload your Scrapy project and monitor scraping progress on the dashboard.&lt;/p&gt;
&lt;p&gt;Yet another way is to sign up for &lt;a href=&#34;https://www.zyte.com/scrapy-cloud/&#34;&gt;Scrapy Cloud&lt;/a&gt; - the official service from
creators of Scrapy. This service has its own CLI tool for uploading your Scrapy project and launching it in the
cloud environment, which means you can integrate it into your CI/CD pipeline.&lt;/p&gt;
&lt;h2 id=&#34;use-item-loader-to-streamline-item-creation&#34;&gt;Use Item Loader to streamline item creation&lt;/h2&gt;
&lt;p&gt;Scrapy lets you create Item Loaders to simplify and streamline item creation based on CSS selectors and XPath
queries. Furthermore, it allows you to include extra steps such as whitespace stripping and other parsing tasks.&lt;/p&gt;
&lt;p&gt;Scrapy documentation provides the following example on how Item Loader could be used in the spider:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; scrapy.loader &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ItemLoader
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; myproject.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Product
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemLoader(item&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Product(), response&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;response)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@class=&amp;#34;product_name&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@class=&amp;#34;product_title&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//p[@id=&amp;#34;price&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stock&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;p#stock]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_value(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_updated&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;today&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# you can also use literal values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load_item()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To learn more about Item Loaders, see: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/loaders.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/loaders.html&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Email harvesting from Github profiles</title>
      <link>https://www.trickster.dev/post/email-harvesting-from-github-profiles/</link>
      <pubDate>Sat, 19 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/email-harvesting-from-github-profiles/</guid>
      <description>&lt;p&gt;You may have some reason to automatically gather (harvest) software developer emails. That might be SaaS marketing
objectives, recruitment or community building. One place online that has a lot of developers is Github. Some
of the developers have a public email address listed on their Github profile. Turns out, this information is available
through Github&amp;rsquo;s &lt;a href=&#34;https://docs.github.com/en/rest&#34;&gt;REST API&lt;/a&gt; and can be extracted with just a bit of scripting.&lt;/p&gt;
&lt;p&gt;First we need an API token. Log in to your Github account and go to &lt;a href=&#34;https://github.com/settings/tokens&#34;&gt;Personal Access Tokens&lt;/a&gt;
page. Press &amp;ldquo;Generate new token&amp;rdquo; button - Github will ask for your password. Now type in some name, choose expiration date (or
no expiration) and tick the following checkboxes under &amp;ldquo;Select scopes&amp;rdquo;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public_repo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user:email&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Press &amp;ldquo;Generate token&amp;rdquo; button on the bottom of the page. Now it gives you an API token to be used in the code without going through
the OAuth flow. Make sure to copy it somewhere, as this token will not be shown again. We will be setting &lt;code&gt;Authorization&lt;/code&gt; header
with this token when making API calls.&lt;/p&gt;
&lt;p&gt;Our API scraping strategy will be as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Scrape &lt;code&gt;/search/repositories&lt;/code&gt; API for repos matching language and query (we can use &lt;code&gt;language:&lt;/code&gt; operator to get repositories
developed in a specific programming language). We extract repo name, description, web URL and contributors URL. These fields are saved
into CSV file that next script will read.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The second script reads repo CSV file and iterates across contributor URLs to get a list of contributors for each repository.
In the inner loop, it iterates across contributors, fetches their profiles from API, which gives us their emails. Names, usernames,
account creation dates and email addresses are saved into CSV file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For API documentation, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.github.com/en/rest/reference/search#search-repositories&#34;&gt;https://docs.github.com/en/rest/reference/search#search-repositories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.github.com/en/rest/reference/repos#list-repository-contributors&#34;&gt;https://docs.github.com/en/rest/reference/repos#list-repository-contributors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.github.com/en/rest/reference/users#get-a-user&#34;&gt;https://docs.github.com/en/rest/reference/users#get-a-user&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first script that scrapes API for matching repos is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; configparser
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;html_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contributors_url&amp;#34;&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_session&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; configparser&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ConfigParser()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;harvest.ini&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GH&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Token&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application/vnd.github.v3+json&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;token &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; token,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User-Agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GithubEmailHarvest&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    language &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Language [Python]: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Query: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; language &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        language &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Python&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    q &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; language:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(query, language)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;repos.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w+&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;q&amp;#39;&lt;/span&gt;: q,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;page&amp;#39;&lt;/span&gt; : str(page),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.github.com/search/repositories&amp;#39;&lt;/span&gt;, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        items &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;items&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; items &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; len(items) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(json_dict)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; item_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; items:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; : item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; : item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;html_url&amp;#34;&lt;/span&gt; : item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;html_url&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contributors_url&amp;#34;&lt;/span&gt; : item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contributors_url&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        page &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1.2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second script that reads repository CSV file and scrapes developer names/emails is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; search_repos &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; create_session
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joined_at&amp;#34;&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;repos.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contacts.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_reader &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictReader(in_f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    next(csv_reader)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; create_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; in_row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; csv_reader:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_row&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contributors_url&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_arr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; contrib_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_arr:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; contrib_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; : json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;login&amp;#39;&lt;/span&gt; : json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;login&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;: json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;joined_at&amp;#39;&lt;/span&gt; : json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;created_at&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that Github API does some rate limiting, especially when it comes to search requests. This is why
there are some delays made with &lt;code&gt;time.sleep()&lt;/code&gt; in the code.&lt;/p&gt;
&lt;p&gt;What if you don&amp;rsquo;t want your Github account email publically available and harvestable? There is a simple
way to hide it: go to &lt;a href=&#34;https://github.com/settings/emails&#34;&gt;Emails&lt;/a&gt; page of your account settings
and make sure that &amp;ldquo;Keep my email addresses private&amp;rdquo; checkbox is ticked.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>CAPTCHA solver services for scraping and automation</title>
      <link>https://www.trickster.dev/post/captcha-solver-services-for-scraping-and-automation/</link>
      <pubDate>Sun, 13 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/captcha-solver-services-for-scraping-and-automation/</guid>
      <description>&lt;p&gt;CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) is a type of automation
countermeasure. It is based on challenge-response approach that involves asking user to perform an action
that only human is supposed to be able to perform, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing down characters or words from distorted image.&lt;/li&gt;
&lt;li&gt;Performing based mathematical operations that are presented in distorted image.&lt;/li&gt;
&lt;li&gt;Recognizing specific object within a set of images, possibly with distortion.&lt;/li&gt;
&lt;li&gt;Solving simple puzzles.&lt;/li&gt;
&lt;li&gt;Hearing a word on audio and writing it down.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the user passes the challenge, access is granted.&lt;/p&gt;
&lt;p&gt;CAPTCHAs can be used for security purposes (e.g. to prevent password bruteforcing) or to fight any kind of automation
(scraping, auto-purchasing eCommerce products, etc.).&lt;/p&gt;
&lt;p&gt;One prominent example of CAPTCHA you have seen on the web is &lt;a href=&#34;https://developers.google.com/recaptcha/docs/display&#34;&gt;Google reCaptcha version 2&lt;/a&gt;.
It works by having the user to tick a checkbox to indicate that they are not a robot. Depending on risk assessment that is
running in background, it may also ask the user to perform a basic image recognition task. For example, they may be asked
to make images with cars. Once user completes the task, client side JS SDK encodes the response and submits it to Google API.
Google API gives a token in return. This token is then submitted to the website via a hidden input in a form or via API
parameter. Website then has to verify it on server side with Google. Google also allows web developers to integrate reCaptcha
in invisible mode. In this case, only the most suspicious traffic is asked to integrate with the CAPTCHA.&lt;/p&gt;
&lt;p&gt;Another prominent CAPTCHA service is &lt;a href=&#34;https://docs.hcaptcha.com/&#34;&gt;hCaptcha&lt;/a&gt;. It is meant to be a drop-in replacement for
reCaptcha that does not belong to Google and has a different business model. It works pretty much the same way as reCaptcha,
but may also allow including users IP address into API request as additional security measure.&lt;/p&gt;
&lt;p&gt;As developer working in web scraping and automation, you should try to avoid the situation of your code having to directly
face CAPTCHAs. For example, you may be able to use proxy pool to avoid the site or app asking for
CAPTCHA solution due to detecting excessive requests from a single IP address. Using CAPTCHA solving services introduces
significant delays into your code that can be avoided in many cases.&lt;/p&gt;
&lt;p&gt;With that being said, let us discuss CAPTCHA solving services that you may need to use in some cases. The foundational idea
behind most of the CAPTCHA solving services is that if CAPTCHA solving requires humans, then it can be outsourced by giving
the work to cheap clickworkers somewhere far away. Although AI models can solve some CAPTCHAs in some cases, the big picture
of CAPTCHA solving services is that cheap labour is used for this purpose most of the time.&lt;/p&gt;
&lt;p&gt;You typically interface to CAPTCHA solvers by using their REST API. In somes cases a client library will be available to make
this easier.&lt;/p&gt;
&lt;p&gt;Let us try using &lt;a href=&#34;https://anti-captcha.com/&#34;&gt;Anti-captcha&lt;/a&gt; service through the &lt;a href=&#34;https://github.com/AdminAnticaptcha/anticaptcha-python&#34;&gt;official Python module&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To solve a basic image CAPTCHA, you would post a &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/ImageToTextTask&#34;&gt;task&lt;/a&gt;
Anti-captcha API containing the Base64-encoded image data. Then you would wait for task to be completed. If you are directly
talking to REST API, you would need to poll it every few seconds for status report. If you are using a client library that
would be done automatically. Once solution is available, &lt;code&gt;status&lt;/code&gt; in API response would be &lt;code&gt;ready&lt;/code&gt; and &lt;code&gt;solution&lt;/code&gt; would contain
the text in the image.&lt;/p&gt;
&lt;p&gt;However, image captchas are dying and it&amp;rsquo;s more interesting to solve reCaptcha. To solve reCaptcha, we need to get a site key.
This is unique ID that is used for identifying a site within Google systems for integration purposes. It can be found in API
requests via Chrome DevTools. Some sites also have it somewhere in the HTML document, which means you can scrape it from there.
Once we have site key we can create &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/RecaptchaV2TaskProxyless&#34;&gt;&lt;code&gt;RecaptchaV2TaskProxyless&lt;/code&gt;&lt;/a&gt; via
Anti-Captcha API. We need to include page URL and site key in the API request. Some seconds later we will receive a reCaptcha
token that we can submit to the site as if we have solved the CAPTCHA ourselves manually.&lt;/p&gt;
&lt;p&gt;The following code exemplifies solving reCaptcha v2 with Anti-Captcha service:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; anticaptchaofficial.recaptchav2proxyless &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.google.com/recaptcha/api2/demo&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sitekey &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@id=&amp;#34;recaptcha-demo&amp;#34;]/@data-sitekey&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; recaptchaV2Proxyless()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_verbose(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_key(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_website_url(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_website_key(sitekey)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    g_response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; solver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;solve_and_return_solution()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; g_response &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    form_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;g-recaptcha-response&amp;#39;&lt;/span&gt;: g_response
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.google.com/recaptcha/api2/demo&amp;#39;&lt;/span&gt;, data&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;form_data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To solve hCaptcha, you would need to proceed in more or less the same way, but depending on IP address check being used you
would need to submit either &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/HCaptchaTask&#34;&gt;&lt;code&gt;HCaptchaTask&lt;/code&gt;&lt;/a&gt; with IP and credentials
of your proxy or &lt;a href=&#34;https://anti-captcha.com/apidoc/task-types/HCaptchaTaskProxyless&#34;&gt;&lt;code&gt;HCaptchaTaskProxyless&lt;/code&gt;&lt;/a&gt; that is meant
to be used when IP check is disabled.&lt;/p&gt;
&lt;p&gt;Some other proxy solving services you may want to check out are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://2captcha.com/&#34;&gt;2captcha&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.deathbycaptcha.com/&#34;&gt;DeathByCaptcha&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://bestcaptchasolver.com/&#34;&gt;BestCaptchaSolver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://azcaptcha.com/&#34;&gt;AZcaptcha&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Introduction to Scrapy framework</title>
      <link>https://www.trickster.dev/post/introduction-to-scrapy-framework/</link>
      <pubDate>Sun, 13 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/introduction-to-scrapy-framework/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://scrapy.org/&#34;&gt;Scrapy&lt;/a&gt; is a Python framework for web scraping. One might ask: why would we need an entire framework
for web scraping if we can simply code up some simple scripts in Python using Beautiful Soup, lxml, requests and the like?
To be fair, for simple scrapers you don&amp;rsquo;t. However when you are developing and running web scraper of non-trivial complexity the
following problems will arise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Error handling. If you have an unhandled exception in your Python code, it can bring down an entire script, which can cause
to time and data being lost. Furthermore, you will need to make sure your requests are running with timeouts as sometimes
remote servers fail to respond at all. This is not a major issue in small web scraping operations, but at large enough scale
low probability events are bound to happen. For example, you may be scraping one million pages and have one of 100 000 requests
fail. You need to make sure that this small fraction of failed requests do not bring down the entire script.&lt;/li&gt;
&lt;li&gt;Concurrency. Generally speaking, it is not efficient to do one request at a time, as your code will spend most of the time
waiting for response from the server. To speed up the scraping, it is highly desirable to use multithreading or asynchronuous
code so that multiple requests are running in parallel.&lt;/li&gt;
&lt;li&gt;Separation of concerns between scraping the data and further processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scrapy framework addresses these problems as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scrapy is designed in such a way that unhandled exceptions are logged for troubleshooting, but does not crash the entire
scraping job. Furthermore, it provides infrastructure for handling some of the HTTP-level error conditions.&lt;/li&gt;
&lt;li&gt;Scrapy is based on &lt;a href=&#34;https://www.twistedmatrix.com/trac/&#34;&gt;Twisted&lt;/a&gt; framework for asynchronuous network programming and
supports concurrent requests out of the box.&lt;/li&gt;
&lt;li&gt;Scrapy is architected to allow decoupling of scraping part from further processing of scraped data and provides easy to use
boilerplate code for development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The primary way to install Scrapy is to use PIP command &lt;code&gt;pip3 install scrapy&lt;/code&gt;. Some Linux distributions may also ship it through
their package managers (e.g. APT on Ubuntu), but the version might be lagging behind the last official release from the Scrapy
project.&lt;/p&gt;
&lt;h2 id=&#34;scrapy-shell-and-selectors&#34;&gt;Scrapy shell and selectors&lt;/h2&gt;
&lt;p&gt;Once you install Scrapy you can access it&amp;rsquo;s own REPL by running &lt;code&gt;scrapy shell&lt;/code&gt;. This provides an interactive environment for
exploration and experimentation. For example, &lt;code&gt;fetch&lt;/code&gt; command downloads a given page into response object:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [1]: fetch(&amp;#39;https://books.toscrape.com/&amp;#39;)
2022-02-13 14:41:22 [scrapy.core.engine] INFO: Spider opened
2022-02-13 14:41:23 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET https://books.toscrape.com/&amp;gt; (referer: None)

In [2]: response
Out[2]: &amp;lt;200 https://books.toscrape.com/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that you can also launch Scrapy shell from the command line of your OS with URL of the page you want fetched:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy shell https://books.toscrape.com/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can try running some XPath or CSS queries on Scrapy response object. However, doing so will not return string with text
or HTML snippet. It will return an object called selector list:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [1]: response.xpath(&amp;#34;//title&amp;#34;)
Out[1]: [&amp;lt;Selector xpath=&amp;#39;//title&amp;#39; data=&amp;#39;&amp;lt;title&amp;gt;\n    All products | Books to S...&amp;#39;&amp;gt;]

In [2]: type(response.xpath(&amp;#34;//title&amp;#34;))
Out[2]: scrapy.selector.unified.SelectorList
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unsurprisingly, selector list is a list of selector objects:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [3]: type(response.xpath(&amp;#34;//title&amp;#34;)[0])
Out[3]: scrapy.selector.unified.Selector
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scrapy selector object encapsulates XPath/CSS query and provides methods to get matching parts of HTML document. Calling &lt;code&gt;.get()&lt;/code&gt;
method on selector or selector list gives you the contents of the first matched element:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [4]: response.xpath(&amp;#34;//title&amp;#34;).get()
Out[4]: &amp;#39;&amp;lt;title&amp;gt;\n    All products | Books to Scrape - Sandbox\n&amp;lt;/title&amp;gt;&amp;#39;

In [5]: response.xpath(&amp;#34;//title&amp;#34;)[0].get()
Out[5]: &amp;#39;&amp;lt;title&amp;gt;\n    All products | Books to Scrape - Sandbox\n&amp;lt;/title&amp;gt;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see that we are getting the HTML string of matched element, but not the contents of the tag. Scrapy framework expects us
to adjust the query accordingly if we want just the contents:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [6]: response.xpath(&amp;#34;//title/text()&amp;#34;).get()
Out[6]: &amp;#39;\n    All products | Books to Scrape - Sandbox\n&amp;#39;

In [7]: response.xpath(&amp;#34;string(//title)&amp;#34;).get()
Out[7]: &amp;#39;\n    All products | Books to Scrape - Sandbox\n&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can also chain the &lt;code&gt;.xpath()&lt;/code&gt; or &lt;code&gt;.css()&lt;/code&gt; calls on the selectors to perform extraction in step-by-step fashion:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [8]: response.xpath(&amp;#34;/html&amp;#34;).css(&amp;#39;title&amp;#39;).xpath(&amp;#39;text()&amp;#39;).get()
Out[8]: &amp;#39;\n    All products | Books to Scrape - Sandbox\n&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Selector object also has a &lt;code&gt;.re()&lt;/code&gt; method for running regular expressions. However, this does not return a new selector and gives
you a list of strings instead:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [9]: response.selector.re(&amp;#39;\d{1,}\.\d{2}&amp;#39;)
Out[9]:
[&amp;#39;51.77&amp;#39;,
 &amp;#39;53.74&amp;#39;,
 &amp;#39;50.10&amp;#39;,
 &amp;#39;47.82&amp;#39;,
 &amp;#39;54.23&amp;#39;,
 &amp;#39;22.65&amp;#39;,
 &amp;#39;33.34&amp;#39;,
 &amp;#39;17.93&amp;#39;,
 &amp;#39;22.60&amp;#39;,
 &amp;#39;52.15&amp;#39;,
 &amp;#39;13.99&amp;#39;,
 &amp;#39;20.66&amp;#39;,
 &amp;#39;17.46&amp;#39;,
 &amp;#39;52.29&amp;#39;,
 &amp;#39;35.02&amp;#39;,
 &amp;#39;57.25&amp;#39;,
 &amp;#39;23.88&amp;#39;,
 &amp;#39;37.59&amp;#39;,
 &amp;#39;51.33&amp;#39;,
 &amp;#39;45.17&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scrapy selector does not provide the HTML element text directly. However, we can get attributes through &lt;code&gt;.attrib&lt;/code&gt; property:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [10]: response.xpath(&amp;#39;//a&amp;#39;)[0].attrib
Out[10]: {&amp;#39;href&amp;#39;: &amp;#39;index.html&amp;#39;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get all results from the selector call the &lt;code&gt;.getall()&lt;/code&gt; method on it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In [11]: response.xpath(&amp;#39;//a/@href&amp;#39;).getall()
Out[11]:
[&amp;#39;index.html&amp;#39;,
 &amp;#39;index.html&amp;#39;,
 &amp;#39;catalogue/category/books_1/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/travel_2/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/mystery_3/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/historical-fiction_4/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/sequential-art_5/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/classics_6/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/philosophy_7/index.html&amp;#39;,
 &amp;#39;catalogue/category/books/romance_8/index.html&amp;#39;,
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scrapy shell is very useful tool for trying out and refining your XPath/CSS queries even if the final code you are developing
will not be based on Scrapy.&lt;/p&gt;
&lt;h2 id=&#34;scrapy-spider&#34;&gt;Scrapy Spider&lt;/h2&gt;
&lt;p&gt;Now we&amp;rsquo;re getting close to develop some actual code, but we need to get familiar with one of the central concepts in Scrapy:
the spider. Spider is an object that performs link traversal (crawling) and data extraction from web pages. Developing custom
spiders (that is, your own subclasses of &lt;code&gt;scrapy.Spider&lt;/code&gt; object) is most of the work you will be doing when using Scrapy.
The front page of Scrapy website provides an example of very basic spider that traverses blog pages and extracts post titles:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BlogSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;blogspider&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://www.zyte.com/blog/&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; title &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.oxy-post-title&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;: title&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;::text&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; next_page &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;css(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a.next&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;follow(next_page, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each spider has to have &lt;code&gt;name&lt;/code&gt; property filled with it&amp;rsquo;s name and &lt;code&gt;start_urls&lt;/code&gt; property set to list of initial URLs that scraping
will start from. By default, spider will start by iterating across this list and scheduling requests for each URL in the list.
For each valid response, &lt;code&gt;parse()&lt;/code&gt; method will be called (this is by default and can be overriden). 404 erros and other invalid
responses will be filtered out. Callback method (&lt;code&gt;parse()&lt;/code&gt; in this example) is called for each valid response that is received.
This is where response parsing and data extraction happens. In this example, a CSS query to extract blog titles is implemented.
For each title, the code yields an item dictionary.&lt;/p&gt;
&lt;p&gt;We have two important concepts here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Item is an unit of scraped data that spider generates. It&amp;rsquo;s a scraped data point, basically.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yield&lt;/code&gt; keyword in Python means &amp;ldquo;lazy return&amp;rdquo;. In async code it is used to postpone the execution of all preceding code until
the exact moment when result is needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This spider also performs another CSS query to find links to further pages to be scraped. For each of these links, new request
is being generated. This is how you schedule the scraping of further pages based on the links you have found on the current page.&lt;/p&gt;
&lt;p&gt;Let us use Scrapy CLI to generate a new Scrapy project with a single spider to scrape &lt;a href=&#34;https://books.toscrape.com&#34;&gt;books.toscrape.com&lt;/a&gt; -
a simple to site built for people to practice their web scraping skills.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy startproject books_to_scrape
New Scrapy project &amp;#39;books_to_scrape&amp;#39;, using template directory &amp;#39;/usr/local/lib/python3.9/site-packages/scrapy/templates/project&amp;#39;, created in:
    /Users/[REDACTED]/src/tmp/books_to_scrape

You can start your first spider with:
    cd books_to_scrape
    scrapy genspider example example.com
$ cd books_to_scrape/
$ scrapy genspider books books.toscrape.com
Created spider &amp;#39;books&amp;#39; using template &amp;#39;basic&amp;#39; in module:
  books_to_scrape.spiders.books
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives us the following directory structure:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tree .
.
├── books_to_scrape
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   └── settings.cpython-39.pyc
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── __pycache__
│       │   └── __init__.cpython-39.pyc
│       └── books.py
└── scrapy.cfg

4 directories, 11 files
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In books.py we have the following boilerplate code that we will use as a starting point:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksSpider&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;books&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowed_domains &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;books.toscrape.com&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_urls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://books.toscrape.com/&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, first we need to take a look at the site we are going to scrape and do some planning on the overall scraping strategy.
On the side there&amp;rsquo;s a list of book categories. For each category there&amp;rsquo;s a paginated list of books that we can iterate across.
You may notice that entire list of books is also available from the front page. This does indeed make things easier for us, but
that won&amp;rsquo;t be the case for all eCommerce sites. Thus we will be proceeding with slightly harder two-tiered approach of going through
categories first and scraping the book (product) pages for each category. For each product (book) page we will be running XPath
queries to extract product details.&lt;/p&gt;
&lt;p&gt;Let us write code that implements the crawling aspect first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start_requests&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start_urls[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_categories)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_categories&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; category_link &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@class=&amp;#34;side_categories&amp;#34;]//a&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            category_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, category_link&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;href&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(category_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_book_list)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_book_list&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; book_link &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//article[@class=&amp;#34;product_pod&amp;#34;]/h3/a&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            book_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, book_link&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;href&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(book_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_book_page)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//li[@class=&amp;#34;next&amp;#34;]/a&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; next_page_link &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            next_page_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urljoin(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url, next_page_link&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;attrib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;href&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Request(next_page_url, callback&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_book_list)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_book_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we have three custom callback methods (one for front page, another for product list page, and yet another for
product details page) we override the &lt;code&gt;start_requests()&lt;/code&gt; method to generate a request for front page with the
corresponding callback. This callback method, &lt;code&gt;parse_categories()&lt;/code&gt; extracts category links grom left hand side of
the page and generates requests for scraping them. We use &lt;code&gt;urljoin()&lt;/code&gt; from &lt;code&gt;urllib.parse&lt;/code&gt; module to convert relative URLs
into absolute form and we yield a new request for each category link with &lt;code&gt;parse_book_list()&lt;/code&gt; method being the callback for
all new requests.&lt;/p&gt;
&lt;p&gt;Next callback method, &lt;code&gt;parse_book_list()&lt;/code&gt;, deals with book list page. This time, we are primarily concerned with links to product
pages. We use the same approach to extract them and to schedule a new request. Since the site implements pagination, we may also
have a link to next page. If it is found, one more request is generated with the same callback method that we&amp;rsquo;re discussing now.&lt;/p&gt;
&lt;p&gt;Lastly, there&amp;rsquo;s one more callback method that will be used to parse the product page. It&amp;rsquo;s empty for now.&lt;/p&gt;
&lt;p&gt;To check that everything goes well, we can run &lt;code&gt;scrapy runspider&lt;/code&gt; command with the path to spider file and check the log lines.
We can see that it starts by visiting front page, then goes across category links. We can also see that duplicate(s) has been
filtered out:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy runspider spiders/books.py 
2022-02-13 16:09:30 [scrapy.utils.log] INFO: Scrapy 2.5.1 started (bot: books_to_scrape)
2022-02-13 16:09:30 [scrapy.utils.log] INFO: Versions: lxml 4.7.1.0, libxml2 2.9.12, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 20.3.0, Python 3.9.10 (main, Jan 15 2022, 11:48:04) - [Clang 13.0.0 (clang-1300.0.29.3)], pyOpenSSL 21.0.0 (OpenSSL 1.1.1l  24 Aug 2021), cryptography 36.0.0, Platform macOS-12.1-x86_64-i386-64bit
2022-02-13 16:09:30 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor
2022-02-13 16:09:30 [scrapy.crawler] INFO: Overridden settings:
{&amp;#39;BOT_NAME&amp;#39;: &amp;#39;books_to_scrape&amp;#39;,
 &amp;#39;EDITOR&amp;#39;: &amp;#39;vim&amp;#39;,
 &amp;#39;NEWSPIDER_MODULE&amp;#39;: &amp;#39;books_to_scrape.spiders&amp;#39;,
 &amp;#39;ROBOTSTXT_OBEY&amp;#39;: True,
 &amp;#39;SPIDER_LOADER_WARN_ONLY&amp;#39;: True,
 &amp;#39;SPIDER_MODULES&amp;#39;: [&amp;#39;books_to_scrape.spiders&amp;#39;]}
2022-02-13 16:09:30 [scrapy.extensions.telnet] INFO: Telnet Password: d4e8f3a322620d99
2022-02-13 16:09:30 [scrapy.middleware] INFO: Enabled extensions:
[&amp;#39;scrapy.extensions.corestats.CoreStats&amp;#39;,
 &amp;#39;scrapy.extensions.telnet.TelnetConsole&amp;#39;,
 &amp;#39;scrapy.extensions.memusage.MemoryUsage&amp;#39;,
 &amp;#39;scrapy.extensions.logstats.LogStats&amp;#39;]
2022-02-13 16:09:30 [scrapy.middleware] INFO: Enabled downloader middlewares:
[&amp;#39;scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.useragent.UserAgentMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.retry.RetryMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.redirect.RedirectMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.cookies.CookiesMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware&amp;#39;,
 &amp;#39;scrapy.downloadermiddlewares.stats.DownloaderStats&amp;#39;]
2022-02-13 16:09:30 [scrapy.middleware] INFO: Enabled spider middlewares:
[&amp;#39;scrapy.spidermiddlewares.httperror.HttpErrorMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.offsite.OffsiteMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.referer.RefererMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.urllength.UrlLengthMiddleware&amp;#39;,
 &amp;#39;scrapy.spidermiddlewares.depth.DepthMiddleware&amp;#39;]
2022-02-13 16:09:30 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2022-02-13 16:09:30 [scrapy.core.engine] INFO: Spider opened
2022-02-13 16:09:30 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2022-02-13 16:09:30 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6025
2022-02-13 16:09:30 [scrapy.core.engine] DEBUG: Crawled (404) &amp;lt;GET http://books.toscrape.com/robots.txt&amp;gt; (referer: None)
2022-02-13 16:09:31 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/&amp;gt; (referer: None)
2022-02-13 16:09:31 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/category/books_1/index.html&amp;gt; (referer: http://books.toscrape.com/)
2022-02-13 16:09:31 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/category/books/sports-and-games_17/index.html&amp;gt; (referer: http://books.toscrape.com/)
2022-02-13 16:09:31 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/category/books/religion_12/index.html&amp;gt; (referer: http://books.toscrape.com/)
2022-02-13 16:09:31 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/category/books/childrens_11/index.html&amp;gt; (referer: http://books.toscrape.com/)
2022-02-13 16:09:31 [scrapy.dupefilters] DEBUG: Filtered duplicate request: &amp;lt;GET http://books.toscrape.com/catalogue/category/books/sports-and-games_17/index.html&amp;gt; - no more duplicates will be shown (see DUPEFILTER_DEBUG to show all duplicates)
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Duplicate filtering is yet another helpful thing that Scrapy does for us so that we don&amp;rsquo;t have to.&lt;/p&gt;
&lt;p&gt;Soon enough it reaches product pages by traversing the links. Let us implement that &lt;code&gt;parse_book_page()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_book_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//h1/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;upc&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./th[contains(text(), &amp;#34;UPC&amp;#34;)]]/td/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//article[@class=&amp;#34;product_page&amp;#34;]/p/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_excl_tax&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./th[contains(text(), &amp;#34;Price (excl. tax)&amp;#34;)]]/td/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_incl_tax&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./th[contains(text(), &amp;#34;Price (incl. tax)&amp;#34;)]]/td/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;availability&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./th[contains(text(), &amp;#34;Availability&amp;#34;)]]/td/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;n_reviews&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./th[contains(text(), &amp;#34;Number of reviews&amp;#34;)]]/td/text()&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First we instantiate the item dictionary, then we run a few of XPath queries to extract several pieces of information about product:
title, UPC, description, price, availability, number of reviews - the typical stuff that customers in eCommerce industry are
interested in. Lastly, we yield the item dictionary.&lt;/p&gt;
&lt;p&gt;Let us run the spider again. This time, we expect some actual data to be scraped. Thus we specify output CSV file path through
Scrapy CLI:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ scrapy runspider spiders/books.py -o books.csv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we start seeing some product information being logged into the terminal, which shows that data extraction is working:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;...
2022-02-13 16:15:32 [scrapy.core.engine] DEBUG: Crawled (200) &amp;lt;GET http://books.toscrape.com/catalogue/the-last-girl-the-dominion-trilogy-1_70/index.html&amp;gt; (referer: http://books.toscrape.com/catalogue/category/books/science-fiction_16/index.html)
2022-02-13 16:15:33 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/choosing-our-religion-the-spiritual-lives-of-americas-nones_14/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#34;Choosing Our Religion: The Spiritual Lives of America&amp;#39;s Nones&amp;#34;, &amp;#39;upc&amp;#39;: &amp;#39;a812f6969ddf3e39&amp;#39;, &amp;#39;description&amp;#39;: &amp;#39;To the dismay of religious leaders, study after study has shown a steady decline in affiliation and identification with traditional religions in America. By 2014, more than twenty percent of adults identified as unaffiliated--up more than seven percent just since 2007. Even more startling, more than thirty percent of those under the age of thirty now identify as &amp;#34;Nones&amp;#34;--a To the dismay of religious leaders, study after study has shown a steady decline in affiliation and identification with traditional religions in America. By 2014, more than twenty percent of adults identified as unaffiliated--up more than seven percent just since 2007. Even more startling, more than thirty percent of those under the age of thirty now identify as &amp;#34;Nones&amp;#34;--answering &amp;#34;none&amp;#34; when queried about their religious affiliation. Is America losing its religion? Or, as more and more Americans choose different spiritual paths, are they changing what it means to be religious in the United States today? In Choosing Our Religion, Elizabeth Drescher explores the diverse, complex spiritual lives of Nones across generations and across categories of self-identification as &amp;#34;Spiritual-But-Not-Religious,&amp;#34; &amp;#34;Atheist,&amp;#34; &amp;#34;Agnostic,&amp;#34; &amp;#34;Humanist,&amp;#34; &amp;#34;just Spiritual,&amp;#34; and more. Drawing on more than one hundred interviews conducted across the United States, Drescher opens a window into the lives of a broad cross-section of Nones, diverse with respect to age, gender, race, sexual orientation, and prior religious background. She allows Nones to speak eloquently for themselves, illuminating the processes by which they became None, the sources of information and inspiration that enrich their spiritual lives, the practices they find spiritually meaningful, how prayer functions in spiritual lives not centered on doctrinal belief, how morals and values are shaped outside of institutional religions, and how Nones approach the spiritual development of their own children. These compelling stories are deeply revealing about how religion is changing in America--both for Nones and for the religiously affiliated family, friends, and neighbors with whom their lives remain intertwined. ...more&amp;#39;, &amp;#39;price_excl_tax&amp;#39;: &amp;#39;£28.42&amp;#39;, &amp;#39;price_incl_tax&amp;#39;: &amp;#39;£28.42&amp;#39;, &amp;#39;availability&amp;#39;: &amp;#39;In stock (1 available)&amp;#39;, &amp;#39;n_reviews&amp;#39;: &amp;#39;0&amp;#39;}
2022-02-13 16:15:33 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/having-the-barbarians-baby-ice-planet-barbarians-75_23/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#34;Having the Barbarian&amp;#39;s Baby (Ice Planet Barbarians #7.5)&amp;#34;, &amp;#39;upc&amp;#39;: &amp;#39;6717a70913b3db79&amp;#39;, &amp;#39;description&amp;#39;: &amp;#39;Megan’s ready to give birth, but she’s not ready to let her mate leave her side. When Cashol must go hunting to feed the tribe, they’re separated for the first time since resonance. Not a problem, except the baby’s ready to be born and there’s a storm brewing… This is a short story set in the ICE PLANET BARBARIANS world. It does not stand alone, and is intended to be read Megan’s ready to give birth, but she’s not ready to let her mate leave her side. When Cashol must go hunting to feed the tribe, they’re separated for the first time since resonance. Not a problem, except the baby’s ready to be born and there’s a storm brewing…\u2029\u2029 This is a short story set in the ICE PLANET BARBARIANS world. It does not stand alone, and is intended to be read after BARBARIAN’S MATE. It’s a little bit of sweetness for those that can’t get enough of the big blue aliens! Happy reading! ...more&amp;#39;, &amp;#39;price_excl_tax&amp;#39;: &amp;#39;£34.96&amp;#39;, &amp;#39;price_incl_tax&amp;#39;: &amp;#39;£34.96&amp;#39;, &amp;#39;availability&amp;#39;: &amp;#39;In stock (1 available)&amp;#39;, &amp;#39;n_reviews&amp;#39;: &amp;#39;0&amp;#39;}
2022-02-13 16:15:33 [scrapy.core.scraper] DEBUG: Scraped from &amp;lt;200 http://books.toscrape.com/catalogue/the-book-of-mormon_571/index.html&amp;gt;
{&amp;#39;title&amp;#39;: &amp;#39;The Book of Mormon&amp;#39;, &amp;#39;upc&amp;#39;: &amp;#39;3f25a2fedae191cc&amp;#39;, &amp;#39;description&amp;#39;: &amp;#39;The Book of Mormon is a volume of scripture comparable to the Bible. It is a record of God\&amp;#39;s dealings with the ancient inhabitants of the Americas and contains, as does the Bible, the fullness of the everlasting gospel.The book was written by many ancient prophets by the spirit of prophecy and revelation. Their words, written on gold plates, were quoted and abridged by a p The Book of Mormon is a volume of scripture comparable to the Bible. It is a record of God\&amp;#39;s dealings with the ancient inhabitants of the Americas and contains, as does the Bible, the fullness of the everlasting gospel.The book was written by many ancient prophets by the spirit of prophecy and revelation. Their words, written on gold plates, were quoted and abridged by a prophet-historian named Mormon. The record gives an account of two great civilizations. One came from Jerusalem in 600 B.C., and afterward separated into two nations, known as the Nephites and the Lamanites. The other came much earlier when the Lord confounded the tongues at the Tower of Babel. This group is known as the Jaredites. After thousands of years, all were destroyed except the Lamanites, and they are among the ancestors of the American Indians. The crowning event recorded in the Book of Mormon is the personal ministry of the Lord Jesus Christ among the Nephites soon after his resurrection. It puts forth the doctrines of the gospel, outlines the plan of salvation, and tells men what they must do to gain peace in this life and eternal salvation in the life to come. After Mormon completed his writings, he delivered the account to his son Moroni, who added a few words of his own and hid up the plates in the hill Cumorah. On September 21, 1823, the same Moroni, then a glorified, resurrected being, appeared to the Prophet Joseph Smith and instructed him relative to the ancient record and its destined translation into the English language. In due course the plates were delivered to Joseph Smith, who translated them by the gift and power of God. The record is now published in many languages as a new and additional witness that Jesus Christ is the Son of the living God and that all who will come unto him and obey the laws and ordinances of his gospel may be saved. Concerning this record the Prophet Joseph Smith said: &amp;#34;I told the brethren that the Book of Mormon was the most correct of any book on earth, and the keystone of our religion, and a man would get nearer to God by abiding by its precepts, than by any other book.&amp;#34; In addition to Joseph Smith, the Lord provided for eleven others to see the gold plates for themselves and to be special witnesses of the truth and divinity of the Book of Mormon. Their written testimonies are included herewith as &amp;#34;The Testimony of Three Witnesses&amp;#34; and &amp;#34;The Testimony of Eight Witnesses.&amp;#34; We invite all men everywhere to read the Book of Mormon, to ponder in their hearts the message it contains, and the to ask God, the Eternal Father, in the name of Christ if the book is true. Those who pursue this course and ask in faith will gain a testimony of its truth and divinity by the power of the Holy Ghost. (See Moroni 10:3-5.) ...more&amp;#39;, &amp;#39;price_excl_tax&amp;#39;: &amp;#39;£24.57&amp;#39;, &amp;#39;price_incl_tax&amp;#39;: &amp;#39;£24.57&amp;#39;, &amp;#39;availability&amp;#39;: &amp;#39;In stock (9 available)&amp;#39;, &amp;#39;n_reviews&amp;#39;: &amp;#39;0&amp;#39;}
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once this finishes, you will have a CSV file with the scraped data. However, if you launch the spider again it will not delete
previously scraped data - new data will be appended to the file. This may not be desirable and you may want to take steps to
prevent it.&lt;/p&gt;
&lt;h2 id=&#34;scrapy-items&#34;&gt;Scrapy items&lt;/h2&gt;
&lt;p&gt;So far regular Python dictionaries were used for items. However, there is more powerful way to create them. We can subclass
&lt;code&gt;scrapy.Item&lt;/code&gt; class to create items in more object-oriented way. Scrapy project boilerplate includes items.py file with the
following template code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define here the models for your scraped items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See documentation in:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# https://docs.scrapy.org/en/latest/topics/items.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksToScrapeItem&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# define the fields for your item here like:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# name = scrapy.Field()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us update the code to use item objects. In items.py file we create item subclass with all the fields we need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Book&lt;/span&gt;(scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    upc &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_excl_tax &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price_incl_tax &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    availability &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n_reviews &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrapy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Field()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since item API is made to be very similar to Python dictionary we won&amp;rsquo;t be doing much changes on the spider code.&lt;/p&gt;
&lt;p&gt;First change to spider is to import the &lt;code&gt;Book&lt;/code&gt; item class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; books_to_scrape.items &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Book
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next change is to instantiate a &lt;code&gt;Book&lt;/code&gt; object instead of &lt;code&gt;dict&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_book_page&lt;/span&gt;(self, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Book()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we run the spider again we see that items are logged in more readable way. Furthermore, we are more prepared to implement
further post-processing of scraped data in Scrapy pipelines.&lt;/p&gt;
&lt;h2 id=&#34;scrapy-pipelines&#34;&gt;Scrapy pipelines&lt;/h2&gt;
&lt;p&gt;After scraping the data, you may want to peform data cleanup, filtering, enrichment and exporting. Item pipeline is a component
of Scrapy project that you would implement for one or more of post-processing tasks. Multiple pipelines may be implemented in a
single Scrapy project and executed sequentially - output of pipeline earlier in sequence would be the input of next next pipeline.
Newly created Scrapy project comes with a pipelines.py file with some basic template code for pipeline development:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Define your item pipelines here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Don&amp;#39;t forget to add your pipeline to the ITEM_PIPELINES setting&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# useful for handling different item types with a single interface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; itemadapter &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ItemAdapter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksToScrapePipeline&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us develop a basic pipeline to exemplify a simple cleanup of book data we are scraping:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BooksToScrapePipeline&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_item&lt;/span&gt;(self, item, spider):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ItemAdapter(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_excl_tax&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_excl_tax&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;£&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_incl_tax&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;price_incl_tax&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;£&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;availability&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; adapter[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;availability&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;In stock (&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; available)&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We develop the &lt;code&gt;process_item()&lt;/code&gt; method that Scrapy will call with our item. First, we wrap the item in the item adapter object
that provides an unified interface to various kinds of items (Python dictionaries, &lt;code&gt;scrapy.Item&lt;/code&gt;-based objects, etc.) to
enable them to be processed the same way. Next, we remove the pound character from price fields and clean the extra whitespace
from the availability field. Then we return the modified item.&lt;/p&gt;
&lt;p&gt;To enable this pipeline, we need to edit settings.py. Since we kept the defautl pipeline class only we only need to uncomment
the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ITEM_PIPELINES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;books_to_scrape.pipelines.BooksToScrapePipeline&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, if wanted to implement more pipelines we would need to add more key-value pairs into &lt;code&gt;ITEM_PIPELINES&lt;/code&gt; dictionary.
Key in this dictioary is pipeline class name, prefixed by module name. Value is a number used for pipeline ordering - pipelines
with lower number get executed earlier.&lt;/p&gt;
&lt;p&gt;Pipelines can also be used to extract data to databases or other systems external to Scrapy project. They can also be used
to drop some of the items.&lt;/p&gt;
&lt;p&gt;To see some more examples of Scrapy pipelines, take a look at: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/item-pipeline.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/item-pipeline.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;scrapy-middlewares&#34;&gt;Scrapy middlewares&lt;/h2&gt;
&lt;p&gt;Scrapy also provides ways to hooking into HTTP requests that spider generates and hooking into responses before they reach the
spider through components called middlewares. There are two kinds of middlewares that you can implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Downloader middlewares hook into various points of HTTP request and response processing. They can be used for proxy pool
integration, lower level handling of HTTP messages, monitoring and debugging.&lt;/li&gt;
&lt;li&gt;Spider middlewares can hook into responses just before they reach spider. They can also hook into requests and items generation
from the spider. This can be used to enforce scraping limits and to implement other customisations for scraping/crawling.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scrapy ships with several default middlewares for heeding robots.txt rules, managing cookies, controlling HTTP request headers
and other common tasks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/downloader-middleware.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/downloader-middleware.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/spider-middleware.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/spider-middleware.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;scrapy-cli&#34;&gt;Scrapy CLI&lt;/h2&gt;
&lt;p&gt;We are already familiar with Scrapy CLI commands to launch Scrapy shell and to generate project/spider. However there are few
more useful commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scrapy view&lt;/code&gt; command downloads the page and opens it in the browser. This can be used for debugging to see the page in the
same way as spider receives it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scrapy crawl&lt;/code&gt; is similar to &lt;code&gt;scrapy runspider&lt;/code&gt;, but it takes spider name instead of file path.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scrapy bench&lt;/code&gt; runs a quick benchmark.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn more about Scrapy CLI, see: &lt;a href=&#34;https://docs.scrapy.org/en/latest/topics/commands.html&#34;&gt;https://docs.scrapy.org/en/latest/topics/commands.html&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tools of the trade for scraping and automation</title>
      <link>https://www.trickster.dev/post/tools-of-the-trade-for-scraping-and-automation/</link>
      <pubDate>Sun, 06 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/tools-of-the-trade-for-scraping-and-automation/</guid>
      <description>&lt;p&gt;To get things done, one needs a set of tools appropriate to the task. We will discuss several open source
tools that are highly valuable for developers working in scraping and automation.&lt;/p&gt;
&lt;h2 id=&#34;chrome-devtools&#34;&gt;Chrome DevTools&lt;/h2&gt;
&lt;p&gt;Let us start with the basics. Chrome has a &lt;a href=&#34;https://developer.chrome.com/docs/devtools/&#34;&gt;developer tool panel&lt;/a&gt;
that you can open by right-clicking on something in the web page and choose &amp;ldquo;Inspect Element&amp;rdquo; or by going to
View -&amp;gt; Developer -&amp;gt; Developer Tools. There are following tabs in the DevTools panel:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Elements - allows you to inspect and modify the DOM tree that exists &lt;em&gt;after&lt;/em&gt; client-side rendering (to view the page source
&lt;em&gt;before&lt;/em&gt; client side rendering, you may want to go to View -&amp;gt; Developer -&amp;gt; View Source).&lt;/li&gt;
&lt;li&gt;Console - a client-side JS shell that prints JS warnings/errors and also lets you type in your own JavaScript to be executed in
the current page.&lt;/li&gt;
&lt;li&gt;Sources - provides you with an interface to read HTML, JavaScript and CSS source files that browser has downloaded when
rendering the page.&lt;/li&gt;
&lt;li&gt;Network - provides visibility into network communications originating from browser when page is being load and also when
user actions are being performed. Very useful for reverse engineering.&lt;/li&gt;
&lt;li&gt;Performance - provides tooling to measure page performance in terms of where time is being spent in the browser.&lt;/li&gt;
&lt;li&gt;Memory - provides tooling to measure memory performance and also to inspect memory contents of JS environment of the current page.&lt;/li&gt;
&lt;li&gt;Application - provides access to client side data being stored in the browser. We may want to take cookies from here if it is
not feasible or desirable to implement automated code to go through steps to get them automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending on the version of your Chrome instance and extensions you have installed, you may have more tabs in you DevTools.
As web scraper developer I am spending most time in Elements and Network tabs and barely use other ones.&lt;/p&gt;
&lt;p&gt;In Network tab you can choose an HTTP request you want to reproduce programmatically, right-click on it and choose Copy -&amp;gt;
Copy as cURL. Which brings us to&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;curl&#34;&gt;cURL&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://curl.se/&#34;&gt;cURL&lt;/a&gt; is a command line program that is essentially a Swiss army knife of application-level networking.
It supports a number of application level protocols, but mostly we are interested in HTTP(S). If you have a cURL snippet
copied from Network tab of Chrome DevTools, you can paste it into small shell script and do further experimentation.
For example, you can add &lt;code&gt;--verbose&lt;/code&gt; option to make cURL print some protocol-level details about request-response flow you are
reproducing. If you want to go extra deep, try running it with &lt;code&gt;--trace-ascii -&lt;/code&gt;: &lt;code&gt;curl https://www.trickster.dev --trace-ascii -&lt;/code&gt;.
You may also want to remove some headers/cookies/parameters to see which parts of request are necessary for it to succeed.&lt;/p&gt;
&lt;p&gt;cURL project also ships libcurl - a C library that has many wrappers in many programming languages. However it is fairly low-level
and not the best choice for a typical scraping or automation project.&lt;/p&gt;
&lt;h2 id=&#34;curlconverter&#34;&gt;curlconverter&lt;/h2&gt;
&lt;p&gt;But eventually we want to reproduce the requests in some actual programming language. This is where
&lt;a href=&#34;https://curlconverter.com/&#34;&gt;curlconverter&lt;/a&gt; comes in. It&amp;rsquo;s a small webapp that takes cURL commands and converts them into
snippets in one of several programming languages: Python, Dart, Golang, Rust and a few more. This makes it quick to go from
finding a key request-response flow in DevTools to reproducing it in your own code.&lt;/p&gt;
&lt;h2 id=&#34;mitmproxy&#34;&gt;mitmproxy&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; is a small proxy server that is used to tap into communications of networked software,
mostly HTTP-based API calls. This is similar to Network tab in DevTools, except that you are not limited to reverse engineering
requests your browser makes, but can also see what mobile apps are doing over the network. Furthermore, once you are doing
MITM attacks you can modify or replay requests of interest, which opens various possibilities for security work, such as looking
for security vulnerabilities or privacy leaks. mitmproxy also provides a &lt;a href=&#34;https://docs.mitmproxy.org/stable/addons-overview/&#34;&gt;Python API&lt;/a&gt;
for developing your own addons.&lt;/p&gt;
&lt;p&gt;Using mitmproxy allows one to discover and tap into private APIs of mobile apps, which we have discussed earlier. However it is
required that X.509 certificate validation is not being enforced too strictly - if an app you want to hack is implementing
certificate pinning you will need to find a way to defeat it.&lt;/p&gt;
&lt;h2 id=&#34;tmux&#34;&gt;tmux&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/tmux/tmux&#34;&gt;tmux&lt;/a&gt; is a terminal multiplexer program that provides a simple way to run long-running tasks
(e.g. massive scraping jobs or long-term automations) or remote Linux/Unix server. You would need to start a tmux session by
launching tmux with no CLI arguments, launch your stuff in the tmux session and press &lt;code&gt;Ctrl-b d&lt;/code&gt; to detach it. Now it&amp;rsquo;s safe
to close your SSH connection - your automation will keep running in a detached session. When you reconnect, launch
&lt;code&gt;tmux attach&lt;/code&gt; to attach to your last session. If you have multiple sessions running in the background, you may need to use
&lt;code&gt;-t&lt;/code&gt; option with name or number of session you want to go back to, such as: &lt;code&gt;tmux attach -t 1&lt;/code&gt;. Furthermore, tmux provides
more functionality, such as splitting terminal window into multiple subwindows (panes, tabs) and so forth.&lt;/p&gt;
&lt;p&gt;I find it very cheap and convenient to run my long-running scripts inside tmux sessions on disposable $5/month virtual private
servers from Digital Ocean.&lt;/p&gt;
&lt;h2 id=&#34;crontab-ui&#34;&gt;Crontab UI&lt;/h2&gt;
&lt;p&gt;You or your customer may want to run automation at some fixed time-based schedule. One way to get it done is to configure
cronjobs on your Unix/Linux server of Kubernetes cluster. However, you may want to have web-based dashboard for managing
your cronjobs that is at least somewhat comprehensible to your not-so-technical customer who just wants to adjust the schedule
once in a while. This is where &lt;a href=&#34;https://github.com/alseambusher/crontab-ui&#34;&gt;Crontab UI&lt;/a&gt; comes in. It&amp;rsquo;s a Node.js-based webapp
that provides a simple web interface for cronjob management. It can be installed through NPM and also has an
official &lt;a href=&#34;https://hub.docker.com/r/alseambusher/crontab-ui&#34;&gt;Docker image&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;n8n&#34;&gt;n8n&lt;/h2&gt;
&lt;p&gt;Sometimes simply running some Unix commands at schedule will not be enough and more complex automation flow will be necessary.
&lt;a href=&#34;https://n8n.io/&#34;&gt;n8n&lt;/a&gt; is extendable workflow automation system that enables people to build automations with little to no
programming in some cases. Think of this as open-source equivalent of famous &amp;ldquo;no code&amp;rdquo; tools like Zapier and Integromat. However
I don&amp;rsquo;t recommend that people buy into No Code hype too much as this sort of graphical workflows can turn into it&amp;rsquo;s own kind of
spaghetti code once you reach some non-trivial complexity. However, this can be still useful when connecting various actions
(e.g. execution of scripts you coded yourself) into some greater automated workflow. Like Crontab UI, n8n can be installed
via NPM or Docker.&lt;/p&gt;
&lt;h2 id=&#34;vagrant&#34;&gt;Vagrant&lt;/h2&gt;
&lt;p&gt;Dealing with differences in development environment can be pain. &lt;a href=&#34;https://www.vagrantup.com/&#34;&gt;Vagrant&lt;/a&gt; is a virtual machine
management program that enables you to have a consistent development environment based from a single source of truth (your
Vagrantfile, possibly with some provisioning scripts). It&amp;rsquo;s essentially infrastructure-as-code, but for Virtualbox/VMWare
virtual machines that can be created and destroyed on as-needed basis.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Harvesting emails from Google search results</title>
      <link>https://www.trickster.dev/post/harvesting-emails-from-google-search-results/</link>
      <pubDate>Fri, 04 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/harvesting-emails-from-google-search-results/</guid>
      <description>&lt;p&gt;Automated gathering of email addresses is known as email harvesting. Suppose we want to gather the
email addresses of certain kinds of individuals, such as influencers or content creators. This
can be accomplished through certain less-known features of Google. For example, &lt;code&gt;site:&lt;/code&gt; operator
limits search results to a given domain. Double-quoting something forces Google to provide
exact matches on the quoted text. Boolean operators (&lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;OR&lt;/code&gt;) are also supported.&lt;/p&gt;
&lt;p&gt;Google caps the number of results to about 300 per query, but we can mitigate this limitation
by breaking down search space into smaller segments across multiple search queries. In this
example, we are going to search for people not only by keyword but also by location - for
each search, we will be using a combination of keywords with a Boolean operator to tell the search
engine to match on a combination of strings.&lt;/p&gt;
&lt;p&gt;We will be implementing email harvesting from social media profiles that can be found via Google.
We won&amp;rsquo;t be scraping social media portals, but we will be extracting email addresses from
Google SERPs. This enables us to avoid the difficulties of social media scraping.&lt;/p&gt;
&lt;p&gt;One more thing to address is Google&amp;rsquo;s rate-limiting on excessive searching. If we launch too many
search engines in a short timeframe Google will start asking for captcha solutions, which we want
to avoid. For this purpose, we will be sending the requests through a proxy pool.&lt;/p&gt;
&lt;p&gt;Let us prepare the search query components first. In sites.txt, we put the list of domain names
we want to find the results in:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;instagram.com
tiktok.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In q1.txt we put list of domain names we want to find emails in, prefixed by &lt;code&gt;@&lt;/code&gt; character:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@gmail.com
@yahoo.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In q2.txt we put a list of locations we want to find people in:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;San Francisco
New York
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In q3.txt we put a list of occupational keywords:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;influencer
model
content creator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our code will be doing nested loops across all permutations of the above things, scraping Google
search result pages, and applying regular expressions to extract email addresses.&lt;/p&gt;
&lt;p&gt;Depending on your exact needs, you may want to use simpler or more complex regular expression
to match email addresses in the result snippet text. For some, examples see the following
Stack Overflow pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/201323/how-can-i-validate-an-email-address-using-a-regular-expression&#34;&gt;How can I validate an email address using a regular expression?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/42407785/regex-extract-email-from-strings&#34;&gt;regex extract email from strings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get HTML page with search results, we are going to send HTTP GET requests to &lt;code&gt;https://www.google.com/search&lt;/code&gt; with
the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; - search query.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt; - initial index of start results.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;num&lt;/code&gt; - number of search results per page (we will be using max value: 100).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also set the &lt;code&gt;User-Agent&lt;/code&gt; header to match that of a regular browser, as Google would block
our requests otherwise.&lt;/p&gt;
&lt;p&gt;The complete script for harvesting email is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; re
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_result_page&lt;/span&gt;(query, start, num):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.google.com/search&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: query,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start&amp;#34;&lt;/span&gt;: start,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;num&amp;#34;&lt;/span&gt;: num,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params, verify&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rows &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result_divs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@class=&amp;#34;jtfYYd&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; result_div &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; result_divs:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        result_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_div&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//a/@href&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(result_url) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            result_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_url[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            result_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_div&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.//h3/text()&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(title) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; title[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            title &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        snippet_div &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; result_div&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.//div[@style=&amp;#34;-webkit-line-clamp:2&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(snippet_div) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            snippet &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; snippet_div[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text_content()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            snippet &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_url&amp;#34;&lt;/span&gt;: result_url,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: title,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;: snippet,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# https://stackoverflow.com/questions/17681670/extract-email-sub-strings-from-large-document&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; re&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;search(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(?:\.?)([\w\-_+#~!$&amp;amp;\&amp;#39;\.]+(?&amp;lt;!\.)(@|[ ]?\(?[ ]?(at|AT)[ ]?\)?[ ]?)(?&amp;lt;!\.)[\w]+[\w\-\.]*\.[a-zA-Z-]{2,3})(?:[^\w])&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            snippet &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; title,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; m &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;groups()[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rows&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; rows, len(result_divs) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; num
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_results&lt;/span&gt;(query):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rows, done &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scrape_result_page(query, start, num)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; rows:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; done:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; num
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sites.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sites &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q1.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queries1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q2.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queries2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q3.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queries3 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;emails.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; site &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; sites:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; q1 &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; queries1:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; q2 &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; queries2:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; q3 &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; queries3:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;site:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AND &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; AND &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(site, q1, q2, q3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape_results(query):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code expects &lt;code&gt;HTTPS_PROXY&lt;/code&gt; environment variable with proxy URL. For this purpose, we can
create a SERP proxy zone on Bright Data.&lt;/p&gt;
&lt;p&gt;Note that Google is constantly updating it&amp;rsquo;s frontend, which may cause the code to break.
For example, you may need to update XPath queries for parts of page being extracted.
To sidestep this issue, you may want to check out some SaaS tools that provide an API for Google
result scraping:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://brightdata.com/products/search-engine-crawler&#34;&gt;Bright Data Search Engine Crawler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://serpapi.com/&#34;&gt;SerpAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zenserp.com/&#34;&gt;ZenSERP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://serpstack.com/&#34;&gt;SERPStack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>The very basics of XPath for web scraping: less than 1% of XPath language to scrape more than 90% of pages</title>
      <link>https://www.trickster.dev/post/the-very-basics-of-xpath/</link>
      <pubDate>Mon, 31 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/the-very-basics-of-xpath/</guid>
      <description>&lt;p&gt;When scraping HTML pages, many developers are using CSS selectors to find the right elements
for data extraction. However, modern HTML is largely based on XML format, which means there
is a better, more powerful way to find the exact elements one needs: XPath. XPath is an entire
language created for traversing XML (and by extension HTML) element trees. The size and complexity
of &lt;a href=&#34;https://www.w3.org/TR/xpath-31/&#34;&gt;XPath 3.1 specification&lt;/a&gt; might seem daunting, but the good news
is that you need to know very little of it as web scraper developer.&lt;/p&gt;
&lt;p&gt;One way to use XPath in web scraping is to use within Scrapy projects by calling &lt;code&gt;xpath()&lt;/code&gt; method
on &lt;code&gt;scrapy.Response&lt;/code&gt; object. Another is to use &lt;a href=&#34;https://lxml.de/&#34;&gt;lxml&lt;/a&gt; Python module that provides
a Pythonic API to the functionality of libxml2 library. Either way, you will get a list of
result(s) that can be empty if none was found.&lt;/p&gt;
&lt;p&gt;Let us go through several examples to demonstrate some key features of XPath that will prove to be
very useful for web scraping.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with a very simple example. We want to extract &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; from the following HTML
document:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;SCRAPE ME&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One way to get it is to use XPath like directory/file path: &lt;code&gt;/html/body/h1&lt;/code&gt;. Another way is to
use two slashes in the beginning of the query to ask for a recursive search within DOM tree:
&lt;code&gt;//h1&lt;/code&gt;. Yet another way is to go step-by-step across the three levels in the tree: &lt;code&gt;/html&lt;/code&gt;
gives you the topmost &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element, on which you call &lt;code&gt;./body&lt;/code&gt; to get the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;
element in the middle (notice the dot character at the start of query - it denotes that we
want a child element). Lastly, calling &lt;code&gt;./h1&lt;/code&gt; on the latter gives us the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;It is very common for HTML elements to have attributes for styling, links and other purposes.
That is very useful when writing XPath queries. As an example, consider the following HTML
document:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&amp;gt;SCRAPE ME&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secondary&amp;#34;&lt;/span&gt;&amp;gt;...&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to get the first &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element and don&amp;rsquo;t need the second one. &lt;code&gt;//div&lt;/code&gt; would return
both of them, but we don&amp;rsquo;t want that. What we do is use brackets and define a predicate:
&lt;code&gt;//div[@id=&amp;quot;main&amp;quot;]&lt;/code&gt;. This specifies that we want to find a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;id&lt;/code&gt; exactly matching
value &lt;code&gt;main&lt;/code&gt;. When specifying a predicate, we can use Boolean operations and write something like:
&lt;code&gt;//*[@id=&amp;quot;main&amp;quot; and not(@id=&amp;quot;secondary&amp;quot;)]&lt;/code&gt;. This time, we use asterisk character as wildcard to
refrain from specifying exact tag name and forcing the matching exclusively on the predicate.&lt;/p&gt;
&lt;p&gt;XPath queries can call functions that XPath language specifies for the use in predicates.
For example, consider the following HTML document:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;ignore&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a b&amp;#34;&lt;/span&gt;&amp;gt;ignore&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a b c&amp;#34;&lt;/span&gt;&amp;gt;SCRAPE ME&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to get the last &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;class&lt;/code&gt; attribute having three values. Since this element
is the only having &lt;code&gt;c&lt;/code&gt; in &lt;code&gt;class&lt;/code&gt; we can use XPath &lt;code&gt;contains()&lt;/code&gt; function in the following
query: &lt;code&gt;//div[contains(@class, &amp;quot;c&amp;quot;)]/text()&lt;/code&gt;. Note that we&amp;rsquo;re also calling &lt;code&gt;text()&lt;/code&gt; at the
end of the query to extract the text from the element. Another way to is to use &lt;code&gt;contains()&lt;/code&gt;
function to match substring within text of the element we want to match:
&lt;code&gt;//div[contains(text(), &amp;quot;ME&amp;quot;)]/text()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can also use XPath to match an element based on presence of a child element. Consider the
following HTML&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;ignore&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;SCRAPE ME&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;!&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;strong&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We specifically want the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that has &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; child element with a single exclamation mark
in it. The XPath query would be: &lt;code&gt;//div[./strong[text()=&amp;quot;!&amp;quot;]]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, we may want to extract a specific cell from HTML table from document like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;Data 1&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;Data 2&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;th&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;ignore&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;...&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;scrape&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;SCRAPE ME&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;td&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;tr&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;table&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The query to get a &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; with &lt;code&gt;SCRAPE ME&lt;/code&gt; text would be: &lt;code&gt;//tr[./td[text()=&amp;quot;scrape&amp;quot;]]/td[2]&lt;/code&gt;.
This query uses all the XPath features we discussed with one more thing: indexing. We say &lt;code&gt;[2]&lt;/code&gt;
at the end of the query to get the second element from ones it finds. Note that unlike a lot
of programming languages, XPath starts the indices at 1 - beware of off-by-one bugs in your
queries!&lt;/p&gt;
&lt;p&gt;The complete example script that demonstrates all these queries is the following - feel free
to modify and play with it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example1&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 1&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;h1&amp;gt;SCRAPE ME&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/html/body/h1&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//h1&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/html&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./body&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; body&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;./h1&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(h1[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example2&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 2&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;div id=&amp;#34;main&amp;#34;&amp;gt;SCRAPE ME&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;div id=&amp;#34;secondary&amp;#34;&amp;gt;...&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main_div &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@id=&amp;#34;main&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(main_div)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(main_div[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main_div &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//*[@id=&amp;#34;main&amp;#34; and not(@id=&amp;#34;secondary&amp;#34;)]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(main_div)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(main_div[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example3&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 3&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;a href=&amp;#34;https://lxml.de&amp;#34; label=&amp;#34;SCRAPE ME&amp;#34;&amp;gt;lxml&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    label &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//a/@label&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(label)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example4&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 4&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;div&amp;gt;ignore&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;div class=&amp;#34;a b&amp;#34;&amp;gt;ignore&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;div class=&amp;#34;a b c&amp;#34;&amp;gt;SCRAPE ME&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[contains(@class, &amp;#34;c&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    text &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[contains(text(), &amp;#34;ME&amp;#34;)]/text()&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example5&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 5&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;div&amp;gt;ignore&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;div&amp;gt;SCRAPE ME&amp;lt;strong&amp;gt;!&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[./strong[text()=&amp;#34;!&amp;#34;]]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(d)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(d[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text_content())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;example6&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Example 6&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;table&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;th&amp;gt;Data 1&amp;lt;/th&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;th&amp;gt;Data 2&amp;lt;/th&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;td&amp;gt;ignore&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;td&amp;gt;...&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;td&amp;gt;scrape&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;lt;td&amp;gt;SCRAPE ME&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(html_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    td &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//tr[./td[text()=&amp;#34;scrape&amp;#34;]]/td[2]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(td)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(td[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example1()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example2()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example3()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example4()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example5()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example6()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice that sometimes &lt;code&gt;text_content()&lt;/code&gt; is called on the element and sometimes value
is being read from the &lt;code&gt;text&lt;/code&gt; property. The difference is that calling &lt;code&gt;text_content()&lt;/code&gt; makes
lxml recursively gather all text from all child elements of given tree, whereas reading
&lt;code&gt;text&lt;/code&gt; property merely gives you the text that is immediately within the element.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using proxies for web scraping and automation</title>
      <link>https://www.trickster.dev/post/using-proxies-for-web-scraping-and-automation/</link>
      <pubDate>Mon, 31 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/using-proxies-for-web-scraping-and-automation/</guid>
      <description>&lt;p&gt;When it comes to scraping and automation operations, it might be important to control where remote systems see the traffic
coming from to evade rate-limiting, captchas and IP bans. This is what we need to use proxies for.
Let us talk about individual proxy servers and proxy pools.&lt;/p&gt;
&lt;p&gt;Proxy server is server somewhere in the network that acts as middleman for network communications. One way this can
work is connection-level proxying via SOCKS protocol. The client would make a connection to SOCKS server and ask it to
make another connection to the real destination address. Once this is done, the remote server sees the second connection
coming from proxy IP address and the resulting two-legged connection can be used to make requests to the server, thus hiding
their true source. Another way is HTTP request forwarding. HTTP proxy would listen to incoming requests and forward them to
their true destination. Third way is a hybrid approach of client establishing TCP connection to proxy server, sending HTTP
CONNECT request to make it connect to remote destination server and then using it for further HTTP-based communication with the
true destination server. This is known as HTTP tunneling.&lt;/p&gt;
&lt;p&gt;Proxy pool is a geographically distributed network of proxy servers that accept incoming traffic at single IP or DNS address,
and spread out the outgoing traffic through large number of exit IP addresses.&lt;/p&gt;
&lt;p&gt;There are the following main categories of proxies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Datacenter proxies that reroute traffic through datacenter and public cloud systems. Good enough to evade simple rate-limiting
and IP-based blocking, but insufficient when dealing with more advanced countermeasures. However they are very cheap.&lt;/li&gt;
&lt;li&gt;ISP proxies from providers that have deals with ISPs and can use IP addresses from residential ranges. More expensive
and more capable to evade blocking.&lt;/li&gt;
&lt;li&gt;Residential proxies reroute traffic through homes of real people. One may ask: how is this possible? The answer is that
certain companies are either paying home users for their bandwidth or incentivise traffic sharing through things like free
VPN service. On a darker end of spectrum, users of some freeware apps may be sharing their traffic unknowingly or even be
infected with malware as was the case with &lt;a href=&#34;https://threatpost.com/google-glupteba-botnet-lawsuit/176826/&#34;&gt;Glupteba&lt;/a&gt; botnet.
However, note that not all proxies marketed as residential are truly residential. Some are actually ISP proxies. Depending
on your proxy provider, you may need to go through more complicated verification procedure to get access.&lt;/li&gt;
&lt;li&gt;Most expensive are mobile proxies that have exit IPs from mobile networks. This does not necessarily mean that traffic is
coming from real phone of real user. It might be solution similar to &lt;a href=&#34;https://proxidize.com/&#34;&gt;Proxidize&lt;/a&gt; that entails some
setup with cellular modems and SIM cards. Mobile proxies provide significant blocking evasion capacity and are used for social media
automation in a post-Cambridge Analytica world.&lt;/li&gt;
&lt;li&gt;Lastly, there&amp;rsquo;s advanced proxies like &lt;a href=&#34;https://brightdata.com/products/web-unlocker&#34;&gt;Bright Data Web Unlocker&lt;/a&gt; and &lt;a href=&#34;https://www.zyte.com/smart-proxy-manager/&#34;&gt;Zyte
Smart Proxy Manager&lt;/a&gt; (formerly known as Crawlera). These proxy services implement
proprietary advanced blocking evasion techniques and can be used to scrape sites protected by anti-botting solutions like
PerimeterX. However there is one drawback - for the purpose of blocking evasion they might overtake cookie management, which
means you may not be able to do things that require you to set up session with cookies (e.g. to scrape behind login).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You may also have heard the term &amp;ldquo;dedicated proxy&amp;rdquo;. Dedicated proxy is a single proxy server that proxy provider rents to one
customer at a time. By default, proxy pools provide what is called rotating proxies - a network of proxies that load balance
the outgoing traffic by rotating exit IPs for you. Sometimes they provide a way to lock into a single exit IP for a limited
duration of session - this is called &amp;ldquo;sticky sessions&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;You may have seen some sites with long list of free proxy addresses. These are no good for serious web scraping operations
as many of them do not even work and the ones that work are unreliable. Furthermore, they may betray the real source IP address
through HTTP headers.&lt;/p&gt;
&lt;p&gt;Some proxy providers you may want to check out are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://brightdata.com/&#34;&gt;Bright Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://oxylabs.io/&#34;&gt;OxyLabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://packetstream.io/&#34;&gt;PacketStream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.scraperapi.com/&#34;&gt;ScraperAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.geosurf.com/&#34;&gt;Geosurf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://oculusproxies.com/index&#34;&gt;Oculus Proxies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://thesocialproxy.com/&#34;&gt;The Social Proxy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s also many more. Due your research to find the best option for your budget and use case.&lt;/p&gt;
&lt;p&gt;How would we use proxies in our code? Bright Data provides the following Python snippet for simple use case of their proxy service.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;If you get error &amp;#34;ImportError: No module named &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;six&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; install six:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;$ sudo pip install six&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;To enable your free eval account and get CUSTOMER, YOURZONE and &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YOURPASS, please contact sales@brightdata.com&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version_info[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; six
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; six.moves.urllib &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    opener &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;build_opener(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ProxyHandler(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://[REDACTED]:[REDACTED]@zproxy.lum-superproxy.io:22225&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://[REDACTED]:[REDACTED]@zproxy.lum-superproxy.io:22225&amp;#39;&lt;/span&gt;}))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(opener&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://lumtest.com/myip.json&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version_info[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; urllib.request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    opener &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; urllib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;build_opener(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        urllib&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ProxyHandler(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://[REDACTED]:[REDACTED]@zproxy.lum-superproxy.io:22225&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://[REDACTED]:[REDACTED]@zproxy.lum-superproxy.io:22225&amp;#39;&lt;/span&gt;}))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(opener&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://lumtest.com/myip.json&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are using requests module for making HTTP requests (as you should) you can simply copy the proxy URL from the snippet
Bright Data provides and either set &lt;code&gt;HTTPS_PROXY&lt;/code&gt; environment variable with that, or create a proxies dictionary and use it for
requests like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;proxies &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;: PROXY_URL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https&amp;#39;&lt;/span&gt;: PROXY_URL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(url, proxies&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;proxies)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the &lt;a href=&#34;https://2.python-requests.org/en/master/user/advanced/#proxies&#34;&gt;official documentation&lt;/a&gt; to read more about this.&lt;/p&gt;
&lt;p&gt;There is one more snippet on Bright Data dashboard that is of interest to us - the &amp;ldquo;High-perf parallel requests&amp;rdquo; example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;If you get error &amp;#34;ImportError: No module named &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;six&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; install six:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;$ sudo pip install six&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;If you get error &amp;#34;ImportError: No module named &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;eventlet&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; install&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;eventlet:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$ sudo pip install eventlet&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;To enable your free eval account and get CUSTOMER, YOURZONE and &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;YOURPASS, please contact sales@brightdata.com&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; eventlet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version_info[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; six
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; six.moves.urllib &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version_info[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; eventlet.green.urllib &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; random
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; socket
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;super_proxy &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; socket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;gethostbyname(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;zproxy.lum-superproxy.io&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SingleSessionRetriever&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-session-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;super_proxy&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    port &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;22225&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, username, password, requests_limit, failures_limit):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_password &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; password
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_requests_limit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests_limit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_failures_limit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; failures_limit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_reset_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_reset_session&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        session_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;random()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SingleSessionRetriever&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_username, session_id, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_password,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                              SingleSessionRetriever&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;port)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_handler &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ProxyHandler({&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;: proxy, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https&amp;#39;&lt;/span&gt;: proxy})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_opener &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;build_opener(proxy_handler)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_requests &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_failures &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;retrieve&lt;/span&gt;(self, url, timeout):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_requests &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_requests_limit:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_reset_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_requests &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                timer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; eventlet&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Timeout(timeout)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_opener&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;open(url)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                timer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cancel()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                timer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cancel()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_failures &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_failures &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_failures_limit:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_reset_session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MultiSessionRetriever&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, username, password, session_requests_limit, session_failures_limit):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_password &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; password
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_sessions_stack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_session_requests_limit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session_requests_limit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_session_failures_limit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session_failures_limit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;retrieve&lt;/span&gt;(self, urls, timeout, parallel_sessions_limit, callback):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pool &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; eventlet&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;GreenPool(parallel_sessions_limit)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url, body &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; pool&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;imap(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; url: self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_retrieve_single(url, timeout), urls):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            callback(url, body)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_retrieve_single&lt;/span&gt;(self, url, timeout):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_sessions_stack:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_sessions_stack&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SingleSessionRetriever(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_username, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_password,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                             self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_session_requests_limit, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_session_failures_limit)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        body &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;retrieve(url, timeout)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_sessions_stack&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(session)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; url, body
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt;(url, body):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(body)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n_total_req &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;req_timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n_parallel_exit_nodes &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;switch_ip_every_n_req &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;max_failures &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MultiSessionRetriever(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[REDACTED]&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[REDACTED]&amp;#39;&lt;/span&gt;, switch_ip_every_n_req, max_failures)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;retrieve(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://lumtest.com/myip.json&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; n_total_req, req_timeout, n_parallel_exit_nodes, output)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This uses &lt;a href=&#34;https://eventlet.net/&#34;&gt;eventlet&lt;/a&gt; module to implement concurrent networking, which is highly useful
for web scraping, as most the time web scraping code spends goes into waiting for HTTP responses to come from the servers.
Running multiple requests concurrently can speed up web scraping job by orders of magnitude, as long as we don&amp;rsquo;t run into
rate-limiting and other countermeasures against scraping that might be implemented by website operators. Launching concurrent
requests through the proxy pool enables us to benefit from performance improvements that come from concurrent requests while
still being able to evade blocking.&lt;/p&gt;
&lt;p&gt;Furthermore, I would like to direct attention to &lt;code&gt;SingleSessionRetriever._reset_session()&lt;/code&gt; method. We see that a random session
ID value is being generated and appended to proxy user name, which is Bright Data&amp;rsquo;s way to let us have sticky sessions. However,
in this case it is also being used to force exit IP randomisation every 10 requests. That might be necessary to avoid getting
rate-limited or other forms of blocking when scraping some sites.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up mitmproxy with Android</title>
      <link>https://www.trickster.dev/post/setting-up-mitmproxy-with-android/</link>
      <pubDate>Sun, 23 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/setting-up-mitmproxy-with-android/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://www.trickster.dev/post/setting-up-mitmproxy-with-ios15/&#34;&gt;previous post&lt;/a&gt;, instructions were provided on
how to set up &lt;a href=&#34;https://mitmproxy.org&#34;&gt;mitmproxy&lt;/a&gt; with iOS device. In this one, we will be going
through setting up mitmproxy with Android device or emulator. If you would prefer to use Android
emulator for hacking mobile apps I would recommend &lt;a href=&#34;https://www.genymotion.com/&#34;&gt;Genymotion&lt;/a&gt;
software which lets you create pre-rooted virtual devices. The following steps were reproduced with
Android 10 system running in Genymotion emulator with Google Chrome installed through ADB from
some sketchy APK file that was found online.&lt;/p&gt;
&lt;p&gt;Make sure your Android device or emulator is running in the same subnet as your computer. Install
and launch mitmproxy on your local computer. Find out the IP address of your local computer &lt;em&gt;in the
local subnet&lt;/em&gt; and note it down. These things were discussed on greater detail in my previous post.&lt;/p&gt;
&lt;p&gt;Now let us set things up on Android side. Open the Settings app and go to &amp;ldquo;Network &amp;amp; internet&amp;rdquo; -&amp;gt;
&amp;ldquo;Wi-Fi&amp;rdquo;. Now press the settings (cog) icon next to the name of Wi-Fi network you are using. Press the
Edit (pen) icon on the top of the new screen. Explode the &amp;ldquo;Advanced options&amp;rdquo; entry in the modal window
and press the &amp;ldquo;None&amp;rdquo; text below &amp;ldquo;Proxy&amp;rdquo; label. Out of the 3 options, choose &amp;ldquo;Manual&amp;rdquo;. Now you will be
given some textfields for proxy address and port. Under &amp;ldquo;Proxy hostname&amp;rdquo;, enter the IP address of your
computer. In the &amp;ldquo;Proxy port&amp;rdquo; field, enter 8080. Leave everything else as-is and press Save.&lt;/p&gt;
&lt;p&gt;Open Chrome in your Android system and go to &lt;a href=&#34;https://mitm.it&#34;&gt;mitm.it&lt;/a&gt;. Press the green button under
Android label. A modal dialog will pop up and ask for certificate name - enter &amp;ldquo;mitmproxy&amp;rdquo; and press
&amp;ldquo;OK&amp;rdquo; button. Go back to Settings app and go to &amp;ldquo;Security&amp;rdquo; -&amp;gt; &amp;ldquo;Encryption and credentials&amp;rdquo; -&amp;gt;
&amp;ldquo;Trusted credentials&amp;rdquo;. Verify that &amp;ldquo;mitmproxy&amp;rdquo; certificate is available in the User tab.&lt;/p&gt;
&lt;p&gt;At this point, you should be seeing HTTP request intercepted and displayed on mitmproxy user interface.
The mitmproxy server is now performing the MITM attack by being in the middle of communications between
apps in Android device and remote backend server. This enables us to passively monitor the traffic
or actively mess with it.&lt;/p&gt;
&lt;p&gt;If you would like this setup process to be automated, you may want to check out
&lt;a href=&#34;https://gist.github.com/PhilippeBoisney/26eb5885668259325fd7bfe4dcda61b9&#34;&gt;mitmproxy.sh&lt;/a&gt; shell
script that uses Android Debug Bridge tool to push the CA certificate into Android system and set up
the proxy settings. Since it has some macOS-specific paths hardcoded it may not be useful right away,
but will provide you with a starting point.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up mitmproxy with iOS 15</title>
      <link>https://www.trickster.dev/post/setting-up-mitmproxy-with-ios15/</link>
      <pubDate>Sun, 23 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/setting-up-mitmproxy-with-ios15/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt; is an open source proxy server developed for launching
man-in-the-middle attacks against network communications (primarily HTTP(S). mitmproxy
enables passive sniffing, active modification and replaying of HTTP messages. It is meant
to be used for troubleshooting, reverse engineering and penetration testing of networked
software.&lt;/p&gt;
&lt;p&gt;We will be setting up mitmproxy with iOS 15 device for the scraping and gray hat automation
purposes. One use case of mitmproxy-iPhone setup is discussed in my previous post about
&lt;a href=&#34;https://www.trickster.dev/post/using-python-and-mitmproxy-to-scrape-private-api-of-mobile-app/&#34;&gt;scraping private API of mobile app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this point, the only prerequisite for the following procedure is that iOS device and your
computer is connected to the same IP network (it&amp;rsquo;s okay if computer is using Ethernet connection
and iPhone is using WiFi, but the IP subnet has to match). You should be good to go if both are
connected to the same WiFi network.&lt;/p&gt;
&lt;p&gt;First we need to install mitmproxy on our local machine. Simple and safe way to do this is to
follow what the official site tells you to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On Linux, download the binary tarball and install it. Also some Linux distribution provide a
mitmproxy package in the package managers.&lt;/li&gt;
&lt;li&gt;On macOS, use Homebrew command &lt;code&gt;brew install mitmproxy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;On Windows, download the installer executable and run it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use the official &lt;a href=&#34;https://hub.docker.com/r/mitmproxy/mitmproxy/&#34;&gt;Docker image&lt;/a&gt; that
is available on Docker Hub.&lt;/p&gt;
&lt;p&gt;mitmproxy installation can be checked as follows by running &lt;code&gt;mitmproxy --version&lt;/code&gt;. Now you
can use mitmproxy TUI by running &lt;code&gt;mitmproxy&lt;/code&gt; command or web interface by running &lt;code&gt;mitmweb&lt;/code&gt;
command. There is also &lt;code&gt;mitmdump&lt;/code&gt; command that works bit like tcpdump and provides no interactivity.
Have mitmproxy running for the next steps.&lt;/p&gt;
&lt;p&gt;Find out your IP address of your local computer &lt;em&gt;within the local subnet&lt;/em&gt;. Depending on your
operating system, there will be a platform-specific way to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On macOS and some Linux systems, run &lt;code&gt;ifconfig&lt;/code&gt; and note the IP address for your primary
network interface that you are using (something like &lt;code&gt;eth0&lt;/code&gt; or &lt;code&gt;en0&lt;/code&gt;). You can also check GUI
settings app (on macOS, IP address is available at Settings -&amp;gt; Network -&amp;gt; Your primary network).&lt;/li&gt;
&lt;li&gt;On Windows, it will be available in the output of &lt;code&gt;ipconfig&lt;/code&gt; command and also in the network
settings GUI.&lt;/li&gt;
&lt;li&gt;On some Linux systems, it will be available in the output of &lt;code&gt;ip addr&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now open the Settings app of your iOS device and go to Wi-Fi part. Press the (i) button next
to the name of your WiFi network. In next page, scroll down to bottom and tap on Configure
Proxy cell. Next, choose Manual. Three new cells will appear. In the Server field, enter
IP address of your computer. In the port field, enter 8080.&lt;/p&gt;
&lt;p&gt;Now we need to install the root X.509 certificate to intercept HTTP traffic within TLS
connections. Open Safari on your iPhone and go to &lt;a href=&#34;http://mitm.it&#34;&gt;mitm.it&lt;/a&gt;. Press the green
button below &amp;ldquo;iOS&amp;rdquo; label. You will be asked if you want to download configuration profile -
press Allow button. Now go back to Settings app. You will see &amp;ldquo;Profile Downloaded&amp;rdquo; cell on
the top. Pressing it will open the &amp;ldquo;Install Profile&amp;rdquo; dialog with Install button at the top -
press it and verify the installation with your passcode.&lt;/p&gt;
&lt;p&gt;There is one step left on the iOS device side. Go back to the home screen of Settings app
and go &amp;ldquo;General&amp;rdquo; -&amp;gt; &amp;ldquo;About&amp;rdquo; -&amp;gt; &amp;ldquo;Certificate Trust Settings&amp;rdquo;. Make sure the switch next to
&amp;ldquo;mitmproxy&amp;rdquo; text is on. At this point, your mitmproxy instance should be showing HTTP
requests that iOS and various apps are making in the background.&lt;/p&gt;
&lt;p&gt;If this does not work right away - double check your settings and try switching your iOS
device to airplane mode and back. Note that some apps are using certificate pinning and
will not work if traffic is being intercepted like this - more on this later.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using Python and mitmproxy to scrape private API of mobile app</title>
      <link>https://www.trickster.dev/post/using-python-and-mitmproxy-to-scrape-private-api-of-mobile-app/</link>
      <pubDate>Wed, 19 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/using-python-and-mitmproxy-to-scrape-private-api-of-mobile-app/</guid>
      <description>&lt;p&gt;Web scraping is a widely known way to gather information from external sources. However, it is not the only way. Another way
is API scraping. We define API scraping as the activity of automatically extracting data from reverse engineered private APIs.
In this post, we will go through an example of the reverse engineering private API of a mobile app and developing a simple API
scraping script that reproduces API calls to extract the exact information that the app is showing on the mobile device.&lt;/p&gt;
&lt;p&gt;To exemplify API scraping, we will be extracting a list of ongoing anime TV series form
&lt;a href=&#34;https://apps.apple.com/us/app/myanimelist-official/id1469330778&#34;&gt;MyAnimeList Official&lt;/a&gt; app. The part of the app we are
interested in is &amp;ldquo;This Season&amp;rdquo; subtab in &amp;ldquo;Seasonal&amp;rdquo; tab.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/IMG_55B9BF6431CC-1.png&#34;&gt;App screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The tool we will be using for intercepting HTTP traffic is called &lt;a href=&#34;https://mitmproxy.org/&#34;&gt;mitmproxy&lt;/a&gt;. It is a little
HTTP proxy server that can be launched on your computer and provide a way to inspect and modify the HTTP traffic even
if TLS is used. Luckily MAL app does not perform TLS cert pinning which makes it fairly easy to intercept
private API calls with a proper setup.&lt;/p&gt;
&lt;p&gt;In this post, I won&amp;rsquo;t be covering how to set up mitmproxy with your mobile device as there are many blog posts and
Youtube videos detailing on how to do this with both iOS and Android devices. You may want to check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/testvagrant/intercept-ios-android-network-calls-using-mitmproxy-4d3c94831f62&#34;&gt;Intercept iOS/Android Network Calls using mitmproxy&lt;/a&gt; on Medium&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=xQGC-8ojYbU&#34;&gt;Reverse Engineering a Private API with mitmproxy&lt;/a&gt; on Youtube&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once mitmproxy is running in the local network and iPhone is configured to pass HTTP requests through mitmproxy instance,
we are ready to do some reverse engineering of API communications. We start by performing user actions on the app and see
requests appearing in mitmproxy flow list. In mitmproxy parlance, flow is a collection of inter-related network messages.
At this point, we are only concerned about HTTP flows that consist of HTTP requests and corresponding responses. Pay close
attention to what HTTP flows correspond to which action is performed on the app. Occasionally you may want to press Z key
to clean up the flow list so that the exact flows you want to see for specific action appear on the top of new list.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-01-18_15.56.04.png&#34;&gt;List of flows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can navigate the flow list with arrow keys and press Enter on the entry that we want to inspect closer. If we want to
export requests, responses or both to a file that can be either curl(1) snippet or raw representation of HTTP messages, we
can press the E key and mitmproxy will present a menu with several choices.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-01-18_15.57.25.png&#34;&gt;Flow details - request&lt;/a&gt;
&lt;a href=&#34;https://www.trickster.dev/2022-01-18_15.57.32.png&#34;&gt;Flow details - response&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We discover that data about currently airing anime TV series is loaded via the following API request:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET https://api.myanimelist.net/v3/anime?fields=alternative_titles%2Cmedia_type%2Cgenres%2Cnum_episodes%2Cstatus%2Cstart_date%2Cend_date%2Caverage_episode_duration%2Csynopsis%2Cmean%2Crank%2Cpopularity%2Cnum_list_users%2Cnum_favorites%2Cnum_scoring_users%2Cstart_season%2Cbroadcast%2Cmy_list_status%7Bstart_date%2Cfinish_date%7D%2Cfavorites_info%2Cnsfw%2Ccreated_at%2Cupdated_at&amp;amp;limit=50&amp;amp;media_type=tv&amp;amp;offset=0&amp;amp;sort=anime_num_list_users&amp;amp;start_season_season=winter&amp;amp;start_season_year=2022 HTTP/2.0
accept: application/json
x-mal-client-id: 6591a087c62b3e94d769cd8e35ffe909
cache-control: public, max-age=60
user-agent: MAL (ios, 139)
accept-language: en-GB,en;q=0.9
accept-encoding: gzip, deflate, br
content-length: 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let us discuss what we see here. This is a rather straightforward call to RESTful API that asks for a JSON response that can be
compressed. &lt;code&gt;User-Agent&lt;/code&gt; header is custom to the app. &lt;code&gt;x-mal-client-id&lt;/code&gt; seems to be an API key that is hardcoded into the app
and that we will be reusing in our script. The API call is to &lt;code&gt;/v3/anime&lt;/code&gt; endpoint with a bunch of URL parameters narrowing down the
exact data the app wants to receive. Notably, it has &lt;code&gt;start_season_season&lt;/code&gt; parameter with the value &lt;code&gt;winter&lt;/code&gt; and &lt;code&gt;start_season_year&lt;/code&gt;
parameter with the value &lt;code&gt;2022&lt;/code&gt;. We don&amp;rsquo;t want to hardcode these parameters and will be looking into earlier API calls to see
where the app gets these from.&lt;/p&gt;
&lt;p&gt;Luckily for us, the app makes the following request that gets a JSON response with a list of seasons, one of which is marked as
current.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-01-18_17.38.04.png&#34;&gt;Seasons API response&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET https://api.myanimelist.net/v3/anime/season HTTP/2.0
accept: */*
x-mal-client-id: 6591a087c62b3e94d769cd8e35ffe909
cache-control: public, max-age=60
user-agent: MAL (ios, 139)
accept-language: en-GB,en;q=0.9
accept-encoding: gzip, deflate, br
content-length: 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, let us export curl(1) snippets for both of these requests and use &lt;a href=&#34;https://curlconverter.com&#34;&gt;curlconverter&lt;/a&gt; to convert them
to Python snippets.&lt;/p&gt;
&lt;p&gt;mitmproxy gives us the following snippet to reproduce the early API call that gets a list of seasons:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: */*&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-mal-client-id: 6591a087c62b3e94d769cd8e35ffe909&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: public, max-age=60&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: MAL (ios, 139)&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en;q=0.9&amp;#39;&lt;/span&gt; --compressed https://api.myanimelist.net/v3/anime/season
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Curlconverter converts it to the following Python snippet;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*/*&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-mal-client-id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6591a087c62b3e94d769cd8e35ffe909&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;public, max-age=60&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MAL (ios, 139)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.myanimelist.net/v3/anime/season&amp;#39;&lt;/span&gt;, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Curl snippet to get list of ongoing anime TV shows is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept: application/json&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-mal-client-id: 6591a087c62b3e94d769cd8e35ffe909&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control: public, max-age=60&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent: MAL (ios, 139)&amp;#39;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language: en-GB,en;q=0.9&amp;#39;&lt;/span&gt; --compressed &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.myanimelist.net/v3/anime?fields=alternative_titles%2Cmedia_type%2Cgenres%2Cnum_episodes%2Cstatus%2Cstart_date%2Cend_date%2Caverage_episode_duration%2Csynopsis%2Cmean%2Crank%2Cpopularity%2Cnum_list_users%2Cnum_favorites%2Cnum_scoring_users%2Cstart_season%2Cbroadcast%2Cmy_list_status%7Bstart_date%2Cfinish_date%7D%2Cfavorites_info%2Cnsfw%2Ccreated_at%2Cupdated_at&amp;amp;limit=50&amp;amp;media_type=tv&amp;amp;offset=0&amp;amp;sort=anime_num_list_users&amp;amp;start_season_season=winter&amp;amp;start_season_year=2022&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Corresponding Python snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;x-mal-client-id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6591a087c62b3e94d769cd8e35ffe909&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;public, max-age=60&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MAL (ios, 139)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;alternative_titles,media_type,genres,num_episodes,status,start_date,end_date,average_episode_duration,synopsis,mean,rank,popularity,num_list_users,num_favorites,num_scoring_users,start_season,broadcast,my_list_status{start_date,finish_date},favorites_info,nsfw,created_at,updated_at&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;limit&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;50&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;media_type&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;tv&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;offset&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sort&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;anime_num_list_users&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;start_season_season&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;winter&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;start_season_year&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2022&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://api.myanimelist.net/v3/anime&amp;#39;&lt;/span&gt;, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#NB. Original query string below. It seems impossible to parse and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#reproduce query strings 100% accurately so the one below is given&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#in case the reproduced version is not &amp;#34;correct&amp;#34;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# response = requests.get(&amp;#39;https://api.myanimelist.net/v3/anime?fields=alternative_titles%2Cmedia_type%2Cgenres%2Cnum_episodes%2Cstatus%2Cstart_date%2Cend_date%2Caverage_episode_duration%2Csynopsis%2Cmean%2Crank%2Cpopularity%2Cnum_list_users%2Cnum_favorites%2Cnum_scoring_users%2Cstart_season%2Cbroadcast%2Cmy_list_status%7Bstart_date%2Cfinish_date%7D%2Cfavorites_info%2Cnsfw%2Ccreated_at%2Cupdated_at&amp;amp;limit=50&amp;amp;media_type=tv&amp;amp;offset=0&amp;amp;sort=anime_num_list_users&amp;amp;start_season_season=winter&amp;amp;start_season_year=2022&amp;#39;, headers=headers)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have the building blocks ready to write a Python script that reproduces both API calls, parses JSON responses, extracts
the data fields we are interested in, and saves them into  CSV file. Take a look at the following code.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;synopsis&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;genres&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cur_season.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*/*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x-mal-client-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;6591a087c62b3e94d769cd8e35ffe909&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public, max-age=60&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user-agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MAL (ios, 139)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept-language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en;q=0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://api.myanimelist.net/v3/anime/season&amp;#34;&lt;/span&gt;, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp1&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    season &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; season_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        node_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; season_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;is_current&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            season &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;season&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            year &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;year&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;alternative_titles,media_type,genres,num_episodes,status,start_date,end_date,average_episode_duration,synopsis,mean,rank,popularity,num_list_users,num_favorites,num_scoring_users,start_season,broadcast,my_list_status{start_date,finish_date},favorites_info,nsfw,created_at,updated_at&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;50&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;media_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tv&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;offset&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sort&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;anime_num_list_users&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start_season_season&amp;#34;&lt;/span&gt;: season,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start_season_year&amp;#34;&lt;/span&gt;: str(year),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://api.myanimelist.net/v3/anime&amp;#34;&lt;/span&gt;, headers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;headers, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; show_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        node_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; show_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node&amp;#34;&lt;/span&gt;, dict())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;genres&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            genres &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(map(&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; g: g&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;), node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;genres&amp;#34;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            genres &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;synopsis&amp;#34;&lt;/span&gt;: node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;synopsis&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;genres&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(genres),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;: node_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2022-01-18_17.41.49.png&#34;&gt;Resulting CSV file opened in LibreOffice&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Could the very same information be scraped from the website? Of course. However, we were able to get it from
mobile app backend in a pre-structured way which is not always easy when scraping the website. Furthermore,
the approach we are using in this example is widely applicable to many mobile apps even if they don&amp;rsquo;t have
a corresponding website and can be further extended into automating actions that the user would otherwise
be doing manually. Some applications attempt to prevent API scraping by implementing X.509 certificate
pinning, but there are ways to defeat it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>You don&#39;t typically need Selenium for scraping and automation</title>
      <link>https://www.trickster.dev/post/you-dont-typically-need-selenium/</link>
      <pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/you-dont-typically-need-selenium/</guid>
      <description>&lt;p&gt;First, let us presume that we want to develop code to extract structured information from web pages that
may or may not be doing a lot of API calls from client-side JavaScript. We are not trying to develop a
general crawler like Googlebot. We don&amp;rsquo;t mind writing some code that would be specific for each site we
are scraping (e.g. some Scrapy spiders or Python scripts).&lt;/p&gt;
&lt;p&gt;When coming across discussions about web scraper development on various forums online, it is common to hear
people saying that they need JavaScript rendering to scrape websites. Most commonly this is achieved by using
something like &lt;a href=&#34;https://www.selenium.dev&#34;&gt;Selenium&lt;/a&gt; - a technology to programmatically control a web browser.
However, introducing Selenium into your web scraping systems brings the following problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance penalty due to having to run an entire browser that downloads all website assets even if you don&amp;rsquo;t
need them. This also has scalability implications for your web scraping activities.&lt;/li&gt;
&lt;li&gt;Stability issues due to race conditions between your code and client-side JavaScript code that runs in the
browser. Developers who have used Selenium are painfully familiar with &lt;code&gt;WebDriverException&lt;/code&gt;,
&lt;code&gt;StaleElementReferenceException&lt;/code&gt; errors, timeouts, and difficulties getting simulated user events working reliably.&lt;/li&gt;
&lt;li&gt;Blocking evasion difficulties. Since you are running an entire web browser in a way a regular user would
not (possibly in a headless mode) there are many ways that automation is being performed. For example,
the site could check &lt;code&gt;window.navigator.webdriver&lt;/code&gt; boolean value and so on. To some extent, this can
be mitigated by &lt;a href=&#34;https://stackoverflow.com/questions/33225947/can-a-website-detect-when-you-are-using-selenium-with-chromedriver/41220267&#34;&gt;patching the web driver binary&lt;/a&gt;
or using projects like &lt;a href=&#34;https://github.com/ultrafunkamsterdam/undetected-chromedriver&#34;&gt;undetected-chromedriver&lt;/a&gt;
and &lt;a href=&#34;https://github.com/diprajpatra/selenium-stealth&#34;&gt;selenium-stealth&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How can one ditch Selenium and avoid these problems? For almost all sites, the answer is rather simple.
You open the DevTools and do a little bit of reverse engineering, besides viewing a web page source before
it gets processed by client-side JS code. When you click a link or button on the page, what requests are
seen in the Network tab? Is it a plain old HTTP GET request that simply fetches the next page? Is it some
nasty form submission via HTTP POST request that legacy ASP.NET sites tend to implement? Is it a REST API
request that fetches new data so that it could be rendered on existing DOM? Is it a GraphQL request?
Perhaps there is a more complex API flow consisting of more back-and-forth between client and server?
If there is a CSRF token somewhere in request, where does the browser take it from? Is there hidden
&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; with that token or is it hardcoded somewhere in JS snippet in the page? If specific cookies
are necessary for API calls to succeed, look earlier in the communication history to see where they
come from. If you submit a login form, what does the server return to the browser? Is it some HTTP
session cookie or API authorization token?&lt;/p&gt;
&lt;p&gt;To experiment with reproducing the request for scraping and automation purposes, it is highly useful
to use &amp;ldquo;Copy as curl&amp;rdquo; feature in Network tab of DevTools. This copies a curl(1) command that you can
paste into small shell script or into tool like &lt;a href=&#34;https://curlconverter.com&#34;&gt;curlconverter&lt;/a&gt; to convert
it to code snippet. Next, you may want to experiment with removing various cookies, headers and parameters
to establish which components of request are necessary to have it successfully handled by the server.
Do this kind of analysis and experimentation across the entire flow you are interested in and you will
be able to reproduce it in your own code without involving JS execution.&lt;/p&gt;
&lt;p&gt;Does this take some time and effort? Well, of course it does. However, tediously fighting crash bugs
in your Selenium-based code that result from race conditions also takes time and effort. I would
argue that it is more worthwhile to spend some time and effort reverse engineering the site even
if it is heavily based on client side JS rendering, as you gain performance and stability in the
code that you write. Furthermore, you may even save some time by being able to shift from scraping
HTML pages to scraping private APIs that provide JSON responses with pre-structured data that you
can conveniently parse without having to come up with multiple XPath queries or CSS selectors.&lt;/p&gt;
&lt;p&gt;You may say that modern websites may have complex API flows that makes reverse engineering
difficult. Typically that is not the case. Most of the time you don&amp;rsquo;t even need to do much
reading of JS code that does the client side rendering, as inspecting DOM tree and request-response
flows is sufficient. Remember: client side JS code is written by frontend developers who have
to manage complexity on their side. If they are making it overly complex for you, they are likely
shooting themselves in the foot as well.&lt;/p&gt;
&lt;p&gt;Primary exception to this is when site is deliberately engineered to make it difficult to automate against.
This is mostly applicable to big social media portals that are proactively fighting scraping and
automation. Another example is Amazon login page (tinker with it with NoScript being off).
However, in that case they likely have countermeasures against Selenium as well.&lt;/p&gt;
&lt;p&gt;Do I think that Selenium has no place in the toolkit of developer working on scrapers and automation?
No, I would not say that. In my opinion, the applicability of Selenium is for the following two cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Development of generalised code that crawls many sites and is not meant to be optimised
for any of them. An example of this could be a script that takes a list of URLs and tries to harvest
email addresses or other contact information.&lt;/li&gt;
&lt;li&gt;As a fallback/plan B for sites that are very difficult to reverse engineer (e.g. Google Maps,
some legacy ASP.NET web apps).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These two cases aside, I encourage everyone working on scraping and automation to take a slightly
deeper look into the inner workings of websites you are automating against. This will prove to be
worth your while.&lt;/p&gt;
&lt;p&gt;Lastly, if you really need client-side rendering, you may want to try &lt;a href=&#34;https://www.zyte.com/splash/&#34;&gt;Splash&lt;/a&gt; -
an open source program from creators of Scrapy framework that is specifically developed for
web scraping purposes, whereas Selenium was originally designed for automated testing.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Easier JSON wrangling with jq, JSONPath and JSONPointer</title>
      <link>https://www.trickster.dev/post/easier-json-wrangling-with-jq-jsonpath-and-jsonpointer/</link>
      <pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/easier-json-wrangling-with-jq-jsonpath-and-jsonpointer/</guid>
      <description>&lt;p&gt;In this day and age, JSON is the dominating textual format for information exchange between software systems.
JSON is based on key-value pairs. Key is always a string, but values can be objects (dictionaries), arrays,
numbers, booleans, strings or nulls. Common problem that software developers are running into is that
JSON tree structures can be deeply nested with parts that may or may not be present. This can lead to
tedious, awkward code. Let us get familiar with three ways to make JSON wrangling easier.&lt;/p&gt;
&lt;p&gt;Let us download a JSON document for experimentation.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl -s https://www.redbullshopus.com/products.json &amp;gt; products.json
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;jq---cli-tool-and-domain-specific-language-for-json-processing&#34;&gt;jq - CLI tool and Domain Specific Language for JSON processing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://stedolan.github.io/jq/&#34;&gt;jq&lt;/a&gt; is a command line tool that is meant to be sed(1) for JSON processing.
Think of it as a filter that reads JSON string through standard input and outputs processed results through
standard output.&lt;/p&gt;
&lt;p&gt;A simple trick to pretty-print JSON string in Unix/Linux command line is a follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | python3 -m json.tool
{
  &amp;#34;products&amp;#34;: [
    {
      &amp;#34;id&amp;#34;: 6619238891591,
      &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;,
      &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-colorswitch-hat&amp;#34;,
      &amp;#34;body_html&amp;#34;: &amp;#34;&amp;lt;p&amp;gt;Red Bull KTM Racing Team Colorswitch Hat for Men &amp;amp;amp; Women.&amp;lt;/p&amp;gt;\n&amp;lt;ul&amp;gt;\n&amp;lt;li&amp;gt;One Size Fits Most&amp;lt;/li&amp;gt;\n&amp;lt;li&amp;gt;Unisex&amp;lt;/li&amp;gt;\n&amp;lt;/ul&amp;gt;&amp;#34;,
      &amp;#34;published_at&amp;#34;: &amp;#34;2022-01-06T07:33:16-08:00&amp;#34;,
      &amp;#34;created_at&amp;#34;: &amp;#34;2022-01-06T07:33:15-08:00&amp;#34;,
      &amp;#34;updated_at&amp;#34;: &amp;#34;2022-01-06T07:33:44-08:00&amp;#34;,
      &amp;#34;vendor&amp;#34;: &amp;#34;Red Bull KTM Racing Team&amp;#34;,
      &amp;#34;product_type&amp;#34;: &amp;#34;Hats&amp;#34;,
      &amp;#34;tags&amp;#34;: [
        &amp;#34;Accessories&amp;#34;,
        &amp;#34;Cap&amp;#34;,
        &amp;#34;Hat&amp;#34;,
        &amp;#34;Head wear&amp;#34;,
        &amp;#34;Headwear&amp;#34;,
        &amp;#34;KTM&amp;#34;,
        &amp;#34;Ladies&amp;#34;,
        &amp;#34;Men&amp;#34;,
        &amp;#34;Mens&amp;#34;,
        &amp;#34;Red Bull KTM Racing Team&amp;#34;,
        &amp;#34;Unisex&amp;#34;,
        &amp;#34;Women&amp;#34;,
        &amp;#34;Womens&amp;#34;
      ],
      &amp;#34;variants&amp;#34;: [
        {
          &amp;#34;id&amp;#34;: 39565425770567,
          &amp;#34;title&amp;#34;: &amp;#34;One Size / Navy&amp;#34;,
          &amp;#34;option1&amp;#34;: &amp;#34;One Size&amp;#34;,
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be reproduced with jq identity operator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq &amp;#39;.&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One can chain JSON keys and use indices to extract parts of JSON document:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq &amp;#39;.products[0].title&amp;#39;
&amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the document we are processing, we have an array of product dictionaries at key &lt;code&gt;products&lt;/code&gt; in the root dictionary.
What if we want to extract all titles? This can be done with jq iterator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq &amp;#39;.products[].title&amp;#39;
&amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Stone Flat Hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Twist Flat Hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Women&amp;#39;s Official Teamline T-Shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline T-Shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Polo Shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Hoodie&amp;#34;
&amp;#34;Red Bull KTM Racing Team Half Zip Sweater&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Softshell Jacket&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Windbreaker&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Winter Jacket&amp;#34;
&amp;#34;Red Bull KTM Racing Team Athletic Hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team New Era 9FIFTY Stretch Hat&amp;#34;
&amp;#34;RB Leipzig Arrow Drink Bottle&amp;#34;
&amp;#34;Wings for Life Lokai Bracelet&amp;#34;
&amp;#34;Red Bull KTM Racing Team Pop Socket&amp;#34;
&amp;#34;Red Bull KTM Racing Team Sticker Pack&amp;#34;
&amp;#34;Red Bull KTM Racing Team Musquin No Tee&amp;#34;
&amp;#34;New York Red Bulls Mitchell &amp;amp; Ness Classic Snapback&amp;#34;
&amp;#34;Red Bull Rampage Camo Women&amp;#39;s Tank&amp;#34;
&amp;#34;Red Bull KTM Factory Racing Knit Runner&amp;#34;
&amp;#34;Red Bull KTM Factory Racing Logo Tee&amp;#34;
&amp;#34;Red Bull KTM Racing Team Sticker&amp;#34;
&amp;#34;Red Bull Spect Solo Goggles&amp;#34;
&amp;#34;Red Bull Soar Goggles&amp;#34;
&amp;#34;Red Bull Spect Sight Goggles&amp;#34;
&amp;#34;Red Bull Spect Rush Goggles&amp;#34;
&amp;#34;Red Bull Spect Magnetron Slick Goggles&amp;#34;
&amp;#34;Red Bull Spect Magnetron-019 Goggles&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Right now we&amp;rsquo;re extracting just the product title, but in practical projects we will need to extract more fields.
For the sake of example, let us extract the following fields and group them together for each product: &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt; and
&lt;code&gt;handle&lt;/code&gt;. We will be using pipe (&lt;code&gt;|&lt;/code&gt;) and object construction (&lt;code&gt;{}&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq &amp;#39;.products[] | {&amp;#34;title&amp;#34;: .title, &amp;#34;id&amp;#34;: .id, &amp;#34;handle&amp;#34;: .handle}&amp;#39;
{
  &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;,
  &amp;#34;id&amp;#34;: 6619238891591,
  &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-colorswitch-hat&amp;#34;
}
{
  &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Stone Flat Hat&amp;#34;,
  &amp;#34;id&amp;#34;: 6619238498375,
  &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-stone-flat-hat&amp;#34;
}
{
  &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Twist Flat Hat&amp;#34;,
  &amp;#34;id&amp;#34;: 6619238367303,
  &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-twist-flat-hat&amp;#34;
}
{
  &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Women&amp;#39;s Official Teamline T-Shirt&amp;#34;,
  &amp;#34;id&amp;#34;: 6619238105159,
  &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-womens-official-teamline-t-shirt&amp;#34;
}
{
  &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Official Teamline T-Shirt&amp;#34;,
  &amp;#34;id&amp;#34;: 6619237941319,
  &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-official-teamline-t-shirt-1&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We notice that the output isn&amp;rsquo;t quite right. This is not a valid JSON, as JSON objects are not contained in the array.
This can be fixed by using array construction (&lt;code&gt;[]&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq &amp;#39;[.products[] | {&amp;#34;title&amp;#34;: .title, &amp;#34;id&amp;#34;: .id, &amp;#34;handle&amp;#34;: .handle}]&amp;#39;
[
  {
    &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;,
    &amp;#34;id&amp;#34;: 6619238891591,
    &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-colorswitch-hat&amp;#34;
  },
  {
    &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Stone Flat Hat&amp;#34;,
    &amp;#34;id&amp;#34;: 6619238498375,
    &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-stone-flat-hat&amp;#34;
  },
  {
    &amp;#34;title&amp;#34;: &amp;#34;Red Bull KTM Racing Team Twist Flat Hat&amp;#34;,
    &amp;#34;id&amp;#34;: 6619238367303,
    &amp;#34;handle&amp;#34;: &amp;#34;red-bull-ktm-racing-team-twist-flat-hat&amp;#34;
  },
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we have a proper JSON document with JSON array as topmost element. What if we want CSV instead? This can be
achieved by forming arrays with all values lined up and piping them into &lt;code&gt;@csv&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat products.json | jq --raw-output &amp;#39;.products[] | [.title,.id,.handle] | @csv&amp;#39;
&amp;#34;Red Bull KTM Racing Team Colorswitch Hat&amp;#34;,6619238891591,&amp;#34;red-bull-ktm-racing-team-colorswitch-hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Stone Flat Hat&amp;#34;,6619238498375,&amp;#34;red-bull-ktm-racing-team-stone-flat-hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Twist Flat Hat&amp;#34;,6619238367303,&amp;#34;red-bull-ktm-racing-team-twist-flat-hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team Women&amp;#39;s Official Teamline T-Shirt&amp;#34;,6619238105159,&amp;#34;red-bull-ktm-racing-team-womens-official-teamline-t-shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline T-Shirt&amp;#34;,6619237941319,&amp;#34;red-bull-ktm-racing-team-official-teamline-t-shirt-1&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Polo Shirt&amp;#34;,6619237515335,&amp;#34;red-bull-ktm-racing-team-official-teamline-polo-shirt&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Shirt&amp;#34;,6619237220423,&amp;#34;red-bull-ktm-racing-team-official-teamline-shirt-1&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Hoodie&amp;#34;,6619236892743,&amp;#34;red-bull-ktm-racing-team-official-teamline-hoodie-1&amp;#34;
&amp;#34;Red Bull KTM Racing Team Half Zip Sweater&amp;#34;,6619236401223,&amp;#34;red-bull-ktm-racing-team-half-zip-sweater&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Softshell Jacket&amp;#34;,6619235942471,&amp;#34;red-bull-ktm-racing-team-official-teamline-softshell-jacket-1&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Windbreaker&amp;#34;,6619235582023,&amp;#34;red-bull-ktm-racing-team-official-teamline-windbreaker&amp;#34;
&amp;#34;Red Bull KTM Racing Team Official Teamline Winter Jacket&amp;#34;,6619234861127,&amp;#34;red-bull-ktm-racing-team-official-teamline-winter-jacket&amp;#34;
&amp;#34;Red Bull KTM Racing Team Athletic Hat&amp;#34;,69601558540,&amp;#34;red-bull-ktm-racing-team-athletic-hat&amp;#34;
&amp;#34;Red Bull KTM Racing Team New Era 9FIFTY Stretch Hat&amp;#34;,4430841446471,&amp;#34;red-bull-ktm-racing-team-new-era-9fifty-stretch-hat&amp;#34;
&amp;#34;RB Leipzig Arrow Drink Bottle&amp;#34;,6604396527687,&amp;#34;rb-leipzig-arrow-drink-bottle&amp;#34;
&amp;#34;Wings for Life Lokai Bracelet&amp;#34;,1482846011463,&amp;#34;wings-for-life-lokai-bracelet&amp;#34;
&amp;#34;Red Bull KTM Racing Team Pop Socket&amp;#34;,1305585745991,&amp;#34;red-bull-ktm-racing-team-pop-socket&amp;#34;
&amp;#34;Red Bull KTM Racing Team Sticker Pack&amp;#34;,69870256140,&amp;#34;red-bull-ktm-racing-team-sticker-pack&amp;#34;
&amp;#34;Red Bull KTM Racing Team Musquin No Tee&amp;#34;,69600313356,&amp;#34;red-bull-ktm-racing-team-musquin-no-tee&amp;#34;
&amp;#34;New York Red Bulls Mitchell &amp;amp; Ness Classic Snapback&amp;#34;,11735324044,&amp;#34;new-york-red-bulls-fleece-clear-script&amp;#34;
&amp;#34;Red Bull Rampage Camo Women&amp;#39;s Tank&amp;#34;,11710054476,&amp;#34;red-bull-rampage-camo-womens-tank&amp;#34;
&amp;#34;Red Bull KTM Factory Racing Knit Runner&amp;#34;,9000667276,&amp;#34;red-bull-ktm-factory-racing-knit-runner&amp;#34;
&amp;#34;Red Bull KTM Factory Racing Logo Tee&amp;#34;,3250440771,&amp;#34;red-bull-ktm-factory-racing-logo-tee&amp;#34;
&amp;#34;Red Bull KTM Racing Team Sticker&amp;#34;,1588450197575,&amp;#34;red-bull-ktm-racing-team-sticker&amp;#34;
&amp;#34;Red Bull Spect Solo Goggles&amp;#34;,6610181226567,&amp;#34;red-bull-spect-solo-goggles&amp;#34;
&amp;#34;Red Bull Soar Goggles&amp;#34;,6610180767815,&amp;#34;red-bull-soar-goggles&amp;#34;
&amp;#34;Red Bull Spect Sight Goggles&amp;#34;,6610180309063,&amp;#34;red-bull-spect-sight-goggles&amp;#34;
&amp;#34;Red Bull Spect Rush Goggles&amp;#34;,6610179850311,&amp;#34;red-bull-spect-rush-goggles&amp;#34;
&amp;#34;Red Bull Spect Magnetron Slick Goggles&amp;#34;,6610178932807,&amp;#34;red-bull-spect-magnetron-slick-goggles&amp;#34;
&amp;#34;Red Bull Spect Magnetron-019 Goggles&amp;#34;,6610178474055,&amp;#34;red-bull-spect-magnetron-019-goggles&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we also used &lt;code&gt;--raw-output&lt;/code&gt; CLI argument to get an output that is not formatted as JSON strings (i.e. without
surrounding double quotes).&lt;/p&gt;
&lt;p&gt;To learn more about jq, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Official jq tutorial at &lt;a href=&#34;https://stedolan.github.io/jq/tutorial/&#34;&gt;https://stedolan.github.io/jq/tutorial/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;jq manual at &lt;a href=&#34;https://stedolan.github.io/jq/manual/&#34;&gt;https://stedolan.github.io/jq/manual/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;jqplay - a jq playground &lt;a href=&#34;https://jqplay.org/&#34;&gt;https://jqplay.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;jsonpath---equivalent-of-xpath-for-json&#34;&gt;JSONPath - equivalent of XPath for JSON&lt;/h2&gt;
&lt;p&gt;For extracting fields from XML document there is XPath query language that enables developers to narrow down the
exact parts of XML tree they need extracted. Proposal
&lt;a href=&#34;https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-02.txt&#34;&gt;draft-ietf-jsonpath-base-02&lt;/a&gt; proposes
JSONPath - the XPath equivalent for JSON.&lt;/p&gt;
&lt;p&gt;Let us experiment with &lt;a href=&#34;https://github.com/h2non/jsonpath-ng&#34;&gt;jsonpath-ng&lt;/a&gt; - a Python module that implements JSONPath.&lt;/p&gt;
&lt;p&gt;First, let us launch a Python REPL and load the JSON file we have downloaded:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ python3
Python 3.9.9 (main, Nov 21 2021, 03:22:47) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&amp;gt;&amp;gt;&amp;gt; import json
&amp;gt;&amp;gt;&amp;gt; in_f = open(&amp;#34;products.json&amp;#34;, &amp;#34;r&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; json_dict = json.load(in_f)
&amp;gt;&amp;gt;&amp;gt; in_f.close()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also, let us import some stuff from jsonpath-ng module:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from jsonpath_ng import jsonpath, parse
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, let&amp;rsquo;s find product titles with JSONPath:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; jsonpath_expr = parse(&amp;#34;products[*].title&amp;#34;)
&amp;gt;&amp;gt;&amp;gt; matches = jsonpath_expr.find(json_dict)
&amp;gt;&amp;gt;&amp;gt; for m in matches:
...     print(m.value)
...
Red Bull KTM Racing Team Colorswitch Hat
Red Bull KTM Racing Team Stone Flat Hat
Red Bull KTM Racing Team Twist Flat Hat
Red Bull KTM Racing Team Women&amp;#39;s Official Teamline T-Shirt
Red Bull KTM Racing Team Official Teamline T-Shirt
Red Bull KTM Racing Team Official Teamline Polo Shirt
Red Bull KTM Racing Team Official Teamline Shirt
Red Bull KTM Racing Team Official Teamline Hoodie
Red Bull KTM Racing Team Half Zip Sweater
Red Bull KTM Racing Team Official Teamline Softshell Jacket
Red Bull KTM Racing Team Official Teamline Windbreaker
Red Bull KTM Racing Team Official Teamline Winter Jacket
Red Bull KTM Racing Team Athletic Hat
Red Bull KTM Racing Team New Era 9FIFTY Stretch Hat
RB Leipzig Arrow Drink Bottle
Wings for Life Lokai Bracelet
Red Bull KTM Racing Team Pop Socket
Red Bull KTM Racing Team Sticker Pack
Red Bull KTM Racing Team Musquin No Tee
New York Red Bulls Mitchell &amp;amp; Ness Classic Snapback
Red Bull Rampage Camo Women&amp;#39;s Tank
Red Bull KTM Factory Racing Knit Runner
Red Bull KTM Factory Racing Logo Tee
Red Bull KTM Racing Team Sticker
Red Bull Spect Solo Goggles
Red Bull Soar Goggles
Red Bull Spect Sight Goggles
Red Bull Spect Rush Goggles
Red Bull Spect Magnetron Slick Goggles
Red Bull Spect Magnetron-019 Goggles
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like with XPath, JSONPath query always yield an array of results even if there is only one match.&lt;/p&gt;
&lt;p&gt;Since JSONPath standard is still being developed and does not have a proper standard we will not discuss it further.
However, I would advice to keep it in mind for future use when it is more established.&lt;/p&gt;
&lt;h2 id=&#34;jsonpointer---format-for-querying-json-documents&#34;&gt;JSONPointer - format for querying JSON documents&lt;/h2&gt;
&lt;p&gt;JSONPointer, described in &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6901&#34;&gt;RFC6901&lt;/a&gt; is a rather simple a way to
query JSON documents, similar to very limited subset of XPath. It is implemented by
&lt;a href=&#34;https://github.com/stefankoegl/python-json-pointer&#34;&gt;jsonpointer&lt;/a&gt; module that is available from PIP.&lt;/p&gt;
&lt;p&gt;JSON pointer can be used in Python as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from jsonpointer import resolve_pointer
&amp;gt;&amp;gt;&amp;gt; resolve_pointer(json_dict, &amp;#39;/products/0/title&amp;#39;)
&amp;#39;Red Bull KTM Racing Team Colorswitch Hat&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately, it is very limited and does not provide support for things like recursive search, iteration and wildcards.
As such, JSONPointer is applicable only to fairly basic use cases. Think of it as a path within JSON tree structure.&lt;/p&gt;
&lt;h2 id=&#34;honorary-mention-json-patch&#34;&gt;Honorary mention: JSON Patch&lt;/h2&gt;
&lt;p&gt;JSON Patch (&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6902&#34;&gt;RFC6902&lt;/a&gt;) is a way to express modifications you need to get done
in JSON document (adding, removing, replacing and moving parts of the tree). For Python programmers, there is &lt;code&gt;jsonpatch&lt;/code&gt;
module available on PIP.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Python Web Scraping: tips and tricks</title>
      <link>https://www.trickster.dev/post/python-web-scraping-tips-and-tricks/</link>
      <pubDate>Tue, 04 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/python-web-scraping-tips-and-tricks/</guid>
      <description>&lt;h2 id=&#34;use-pandas-read_html-to-parse-html-tables-when-possible&#34;&gt;Use pandas &lt;code&gt;read_html()&lt;/code&gt; to parse HTML tables (when possible)&lt;/h2&gt;
&lt;p&gt;Some pages you might be scraping might include old-school HTML &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements with tabular data.
There&amp;rsquo;s an easy way to extract data from pages like this with into Pandas dataframes (although you may need to
clean up it afterward):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pandas &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; pd&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_html(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://en.wikipedia.org/wiki/Yakutsk&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                      Yakutsk  Якутск                                  Yakutsk  Якутск&lt;span style=&#34;color:#ae81ff&#34;&gt;.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;                 City under republic jurisdiction[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]                City under republic jurisdiction[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;                              Other transcription(s)                             Other transcription(s)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;                                             &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Yakut                                         Дьокуускай
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;                        Central Yakutsk &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; the air                       Central Yakutsk &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; the air
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ib&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;settlement&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;cols{text&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;ali&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ib&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;settlement&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;cols{text&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;ali&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;                                 Location of Yakutsk                                Location of Yakutsk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locmap &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;od{position:absolut&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;locmap &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;od{position:absolut&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;   Coordinates: &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;geo&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;default,&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;m&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;  Coordinates: &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mw&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;parser&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;output &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;geo&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;default,&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;m&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;                                             Country                                             Russia
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;                                     Federal subject                                  Sakha Republic[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;                                            Founded                                               &lt;span style=&#34;color:#ae81ff&#34;&gt;1632&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;                                  City status since                                               &lt;span style=&#34;color:#ae81ff&#34;&gt;1643&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;                                         Government                                         Government
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;                                             &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Body                                      Okrug Council
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;                                             &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Head                                   Evgeny Grigoriev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;                                               Area                                               Area
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;                                            &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Total                                 &lt;span style=&#34;color:#ae81ff&#34;&gt;122&lt;/span&gt; km2 (&lt;span style=&#34;color:#ae81ff&#34;&gt;47&lt;/span&gt; sq mi)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;17&lt;/span&gt;                                          Elevation                                      &lt;span style=&#34;color:#ae81ff&#34;&gt;95&lt;/span&gt; m (&lt;span style=&#34;color:#ae81ff&#34;&gt;312&lt;/span&gt; ft)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;18&lt;/span&gt;                                         Population                                         Population
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;19&lt;/span&gt;                               &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Estimate (&lt;span style=&#34;color:#ae81ff&#34;&gt;2018&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]                                             &lt;span style=&#34;color:#ae81ff&#34;&gt;311760&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;                                             &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Rank                                       &lt;span style=&#34;color:#ae81ff&#34;&gt;68&lt;/span&gt;th &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2010&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;                              Administrative status                              Administrative status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;                                  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Subordinated to        city of republic significance of Yakutsk[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;23&lt;/span&gt;                                       &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Capital of                                  Sakha Republic[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;                                       &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Capital of        city of republic significance of Yakutsk[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;                                   Municipal status                                   Municipal status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;26&lt;/span&gt;                                      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Urban okrug                             Yakutsk Urban Okrug[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;27&lt;/span&gt;                                       &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;•&lt;/span&gt; Capital of                             Yakutsk Urban Okrug[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt;                                          Time zone                                  UTC&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt; (MSK&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;29&lt;/span&gt;                                  Postal code(s)[&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;]                                             &lt;span style=&#34;color:#ae81ff&#34;&gt;677&lt;/span&gt;xxx
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;                                    Dialing code(s)                                         &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4112&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;31&lt;/span&gt;                                           OKTMO ID                                        &lt;span style=&#34;color:#ae81ff&#34;&gt;98701000001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;                                           City Day                         Second Sunday of September
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt;                                            Website                                          якутск&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;рф
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;look-for-json-snippets-inside-html-and-extract-them&#34;&gt;Look for JSON snippets inside HTML and extract them&lt;/h2&gt;
&lt;p&gt;Some sites have pages with JSON snippets that are meant to be parsed by search engines. They provide pre-structured data
and can save you some hassle with trying to get your XPath queries or CSS selectors right.&lt;/p&gt;
&lt;p&gt;See schema.org specification for examples of what you might find on sites that want to make search engine crawling easier.&lt;/p&gt;
&lt;h2 id=&#34;use-js2xml-to-extract-data-from-javascript-snippets&#34;&gt;Use js2xml to extract data from JavaScript snippets&lt;/h2&gt;
&lt;p&gt;On the other hand, some sites will give you pages with JavaScript snippets containing data you want to scrape.
It is a bit of a hassle to use regular expressions or basic string processing techniques to extract the data.
Luckily, there is a Python module called &lt;a href=&#34;https://github.com/scrapinghub/js2xml&#34;&gt;js2xml&lt;/a&gt; developed by Zyte, the
creators of Scrapy framework. This module converts JS code into XML trees that you can run XPath queries against.&lt;/p&gt;
&lt;p&gt;The official README file provides the following example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; js2xml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; jscode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;function factorial(n) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;...     if (n === 0) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;...         return 1;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;...     }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;...     return n * factorial(n - 1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;... }&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; parsed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; js2xml&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse(jscode)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; parsed&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//funcdecl/@name&amp;#34;&lt;/span&gt;)  &lt;span style=&#34;color:#75715e&#34;&gt;# extracts function name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;factorial&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; print(js2xml&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pretty_print(parsed))  &lt;span style=&#34;color:#75715e&#34;&gt;# pretty-print generated XML&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;program&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;funcdecl name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;factorial&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;parameters&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;identifier name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;parameters&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;predicate&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;binaryoperation operation&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;===&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;identifier name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;number value&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;binaryoperation&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;predicate&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;then&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;block&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;number value&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;block&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;then&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;binaryoperation operation&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;identifier name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;functioncall&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;function&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;identifier name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;factorial&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;function&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;arguments&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;binaryoperation operation&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;identifier name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;number value&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;binaryoperation&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;arguments&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;functioncall&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;right&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;binaryoperation&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;body&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;funcdecl&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/&lt;/span&gt;program&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;launch-lynx---dump-to-quickly-extract-text&#34;&gt;Launch &lt;code&gt;lynx --dump&lt;/code&gt; to quickly extract text&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://lynx.invisible-island.net/&#34;&gt;Lynx&lt;/a&gt; is text-only web browser dating back to the early days of World Wide Web. It has a CLI option
to quickly extract text from simple web pages:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ lynx --dump https://quotes.toscrape.com/
[1]Quotes to Scrape

   [2]Login
   &amp;#34;The world as we have created it is a process of our thinking. It
   cannot be changed without changing our thinking.&amp;#34; by Albert Einstein
   [3](about)
   Tags:
   [4]change [5]deep-thoughts [6]thinking [7]world
   &amp;#34;It is our choices, Harry, that show what we truly are, far more than
   our abilities.&amp;#34; by J.K. Rowling [8](about)
   Tags:
   [9]abilities [10]choices
   &amp;#34;There are only two ways to live your life. One is as though nothing is
   a miracle. The other is as though everything is a miracle.&amp;#34; by Albert
   Einstein [11](about)
   Tags:
   [12]inspirational [13]life [14]live [15]miracle [16]miracles

   &amp;#34;The person, be it gentleman or lady, who has not pleasure in a good
   novel, must be intolerably stupid.&amp;#34; by Jane Austen [17](about)
   Tags:
   [18]aliteracy [19]books [20]classic [21]humor

   &amp;#34;Imperfection is beauty, madness is genius and it&amp;#39;s better to be
   absolutely ridiculous than absolutely boring.&amp;#34; by Marilyn Monroe
   [22](about)
   Tags:
   [23]be-yourself [24]inspirational

   &amp;#34;Try not to become a man of success. Rather become a man of value.&amp;#34; by
   Albert Einstein [25](about)
   Tags:
   [26]adulthood [27]success [28]value

   &amp;#34;It is better to be hated for what you are than to be loved for what
   you are not.&amp;#34; by Andr? Gide [29](about)
   Tags:
   [30]life [31]love

   &amp;#34;I have not failed. I&amp;#39;ve just found 10,000 ways that won&amp;#39;t work.&amp;#34; by
   Thomas A. Edison [32](about)
   Tags:
   [33]edison [34]failure [35]inspirational [36]paraphrased

   &amp;#34;A woman is like a tea bag; you never know how strong it is until it&amp;#39;s
   in hot water.&amp;#34; by Eleanor Roosevelt [37](about)
   Tags:
   [38]misattributed-eleanor-roosevelt

   &amp;#34;A day without sunshine is like, you know, night.&amp;#34; by Steve Martin
   [39](about)
   Tags:
   [40]humor [41]obvious [42]simile

     * [43]Next -&amp;gt;

Top Ten tags

   [44]love [45]inspirational [46]life [47]humor [48]books [49]reading
   [50]friendship [51]friends [52]truth [53]simile

   Quotes by: [54]GoodReads.com

   Made with ? by [55]Scrapinghub

References

   1. https://quotes.toscrape.com/
   2. https://quotes.toscrape.com/login
   3. https://quotes.toscrape.com/author/Albert-Einstein
   4. https://quotes.toscrape.com/tag/change/page/1/
   5. https://quotes.toscrape.com/tag/deep-thoughts/page/1/
   6. https://quotes.toscrape.com/tag/thinking/page/1/
   7. https://quotes.toscrape.com/tag/world/page/1/
   8. https://quotes.toscrape.com/author/J-K-Rowling
   9. https://quotes.toscrape.com/tag/abilities/page/1/
  10. https://quotes.toscrape.com/tag/choices/page/1/
  11. https://quotes.toscrape.com/author/Albert-Einstein
  12. https://quotes.toscrape.com/tag/inspirational/page/1/
  13. https://quotes.toscrape.com/tag/life/page/1/
  14. https://quotes.toscrape.com/tag/live/page/1/
  15. https://quotes.toscrape.com/tag/miracle/page/1/
  16. https://quotes.toscrape.com/tag/miracles/page/1/
  17. https://quotes.toscrape.com/author/Jane-Austen
  18. https://quotes.toscrape.com/tag/aliteracy/page/1/
  19. https://quotes.toscrape.com/tag/books/page/1/
  20. https://quotes.toscrape.com/tag/classic/page/1/
  21. https://quotes.toscrape.com/tag/humor/page/1/
  22. https://quotes.toscrape.com/author/Marilyn-Monroe
  23. https://quotes.toscrape.com/tag/be-yourself/page/1/
  24. https://quotes.toscrape.com/tag/inspirational/page/1/
  25. https://quotes.toscrape.com/author/Albert-Einstein
  26. https://quotes.toscrape.com/tag/adulthood/page/1/
  27. https://quotes.toscrape.com/tag/success/page/1/
  28. https://quotes.toscrape.com/tag/value/page/1/
  29. https://quotes.toscrape.com/author/Andre-Gide
  30. https://quotes.toscrape.com/tag/life/page/1/
  31. https://quotes.toscrape.com/tag/love/page/1/
  32. https://quotes.toscrape.com/author/Thomas-A-Edison
  33. https://quotes.toscrape.com/tag/edison/page/1/
  34. https://quotes.toscrape.com/tag/failure/page/1/
  35. https://quotes.toscrape.com/tag/inspirational/page/1/
  36. https://quotes.toscrape.com/tag/paraphrased/page/1/
  37. https://quotes.toscrape.com/author/Eleanor-Roosevelt
  38. https://quotes.toscrape.com/tag/misattributed-eleanor-roosevelt/page/1/
  39. https://quotes.toscrape.com/author/Steve-Martin
  40. https://quotes.toscrape.com/tag/humor/page/1/
  41. https://quotes.toscrape.com/tag/obvious/page/1/
  42. https://quotes.toscrape.com/tag/simile/page/1/
  43. https://quotes.toscrape.com/page/2/
  44. https://quotes.toscrape.com/tag/love/
  45. https://quotes.toscrape.com/tag/inspirational/
  46. https://quotes.toscrape.com/tag/life/
  47. https://quotes.toscrape.com/tag/humor/
  48. https://quotes.toscrape.com/tag/books/
  49. https://quotes.toscrape.com/tag/reading/
  50. https://quotes.toscrape.com/tag/friendship/
  51. https://quotes.toscrape.com/tag/friends/
  52. https://quotes.toscrape.com/tag/truth/
  53. https://quotes.toscrape.com/tag/simile/
  54. https://www.goodreads.com/quotes
  55. https://scrapinghub.com/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you are doing some Natural Language Processing and do not want to spend too much time on web scraping it might
be a helpful shortcut to call a command like this through Python &lt;code&gt;subprocess&lt;/code&gt; module.&lt;/p&gt;
&lt;h2 id=&#34;use-multiprocessingdummy-for-easy-multithreading-of-highly-parallel-tasks&#34;&gt;Use &lt;code&gt;multiprocessing.dummy&lt;/code&gt; for easy multithreading of highly parallel tasks&lt;/h2&gt;
&lt;p&gt;For spreading the workload across a pool of child worker processes, Python provides us with &lt;code&gt;multiprocessing&lt;/code&gt;
module. However, web scraping scripts are typically I/O bound and not CPU bound. It makes more sense to
distribute workload across threads, not processes. Luckily, Python developers were kind enough to provide us
with &lt;a href=&#34;https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.dummy&#34;&gt;&lt;code&gt;multiprocessing.dummy&lt;/code&gt;&lt;/a&gt;
module that has the same API as &lt;code&gt;multiprocessing&lt;/code&gt;, but will create and use a pool of worker threads instead.
For example, one could initialize a thread pool object and call the &lt;code&gt;map()&lt;/code&gt; method with two arguments: 1) function
to scrape single page and 2) list of page URLs. The thread pool will automatically run the requests concurrently and
return a list of results. You may not want to wait for all results to come at once and may prefer to incrementally receive
them as they become ready. This can be achieved with &lt;code&gt;imap()&lt;/code&gt; method of &lt;code&gt;ThreadPool&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Grayhat Twitch Chatbots</title>
      <link>https://www.trickster.dev/post/grayhat-twitch-chatbots/</link>
      <pubDate>Mon, 27 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/grayhat-twitch-chatbots/</guid>
      <description>&lt;p&gt;Twitch is a popular video streaming platform originally developed for gamers, but now expanding beyond gaming community.
It enables viewers to use chat rooms associated with video streams to communicate amongst each other and with the streamer. Chatbots for these
chatrooms can be used for legitimate uses cases such as moderations, polls, ranking boards, and so on. Twitch is allowing such usage but requires
passing a &lt;a href=&#34;https://dev.twitch.tv/docs/irc/guide#verified-bots&#34;&gt;verification process&lt;/a&gt; for chatbot software to be used in production at scale.
On a more black hat side of things, bots have been used to launch harassment campaigns (hate raids) against streamers and content creators.
We are interested in gray hat automation here and will be looking into developing a chat bot that connects to one or more channels as a regular
user. This can be used for growth hacking purposes if one is trying to promote an offer that is of interest to gaming community.&lt;/p&gt;
&lt;p&gt;Little known fact is that Twitch uses the good old Internet Relay Chat protocol for the chat functionality, but wraps the IRC connections in WebSockets to
make it compatible with web browser. Twitch implementation of IRC is mostly compliant to &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1459.html&#34;&gt;RFC1459&lt;/a&gt;
with some differences regarding authentication - see &lt;a href=&#34;https://dev.twitch.tv/docs/irc/guide&#34;&gt;Chatbots and IRC Guide&lt;/a&gt; at Twitch developer portal.
In fact, it is possible to use Irssi or other IRC client with Twitch.&lt;/p&gt;
&lt;p&gt;To see IRC messages being sent and received on Twitch chat connection, one can simply check the Network tab via Chrome DevTools and filter by
WebSocket (choose WS from the options at the top).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-12-26_16.12.44.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The white hat approach would be to register an app at Twitch and implement OAuth or OpenID Connect flow as described in
&lt;a href=&#34;https://dev.twitch.tv/docs/authentication&#34;&gt;Authentication section&lt;/a&gt; of Twitch developer documentation. We are going to skip this
as we want our bots to appear as regular users.&lt;/p&gt;
&lt;p&gt;To login to Twitch as a regular user would, be need to use Chrome DevTools to explore the login flow so that we could reproduce it
programmatically. When trying to login to Twitch, we find that POST request is being made to &lt;code&gt;https://passport.twitch.tv/login&lt;/code&gt; with
JSON payload containing single dictionary with following key-value pairs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;client_id&lt;/code&gt; seems to be an internal ID of API client. Reuse original value.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;password&lt;/code&gt; - plaintext password to the account&lt;/li&gt;
&lt;li&gt;&lt;code&gt;undelete_user&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt; - user name of the account.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are some cookies that we have to set as well. We can get most of them by simply loading some regular page and saving the cookies
we receive with the response. However, we have to set &lt;code&gt;api_token&lt;/code&gt; cookie to the value that we see in the API request from the frontend.&lt;/p&gt;
&lt;p&gt;However this request may fail with response code 400. If this happens, we either need to solve a Captcha or send another request
to submit verification code that was emailed to the user. The former case is relatively rare and will not be covered by this post.
The latter is relatively common and can be addressed as follows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-12-26_17.36.06.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If Twitch asks for verification code, we need to send another request to the same API endpoint. JSON dictionary in the payload
has to be updated with the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;captcha&lt;/code&gt; key points to dictionary with single key-value pair:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;proof&lt;/code&gt; is value for &lt;code&gt;captcha_proof&lt;/code&gt; key from response to previous request.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;twitchguard_code&lt;/code&gt; is the code that was sent through email.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we submit this payload quickly enough (there&amp;rsquo;s some time out on Twitch side that will cause it to ask for another confirmation
if we&amp;rsquo;re being too slow), we receive a response with JSON dictionary containing the authentication token at key &lt;code&gt;access_token&lt;/code&gt;.
Furthermore, we also receive some new cookies that we have too keep for further communications with Twitch.&lt;/p&gt;
&lt;p&gt;Now we need to join a channel. To explore the API dance, I encourage you to join a live stream on Twitch and see what API messages
are appearing in Network tab of Chrome DevTools (switch to &amp;ldquo;Fetch/XHR&amp;rdquo; to see only the stuff we&amp;rsquo;re interested in here).
You will see a lot of API calls to &lt;code&gt;https://gql.twitch.tv/gql&lt;/code&gt;, most of which are related to things other than chat (video stream
playback, etc.). To be able to post chat messages to most channels, one has to follow a channel first, which is accomplished by
API call with the following payload (reformatted for readability):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FollowButton_FollowUser&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;disableNotifications&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;targetID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;24070690&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;persistedQuery&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sha256Hash&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;800e7346bdf7e5278a3c1d3f21b2b56e2639928f86815677a7126b093b2fdd08&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AvailableEmotesForChannel&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;channelID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;24070690&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;withOwner&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;persistedQuery&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;sha256Hash&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;b9ce64d02e26c6fe9adbfb3991284224498b295542f9c5a51eacd3610e659cfb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example, number &lt;code&gt;24070690&lt;/code&gt; is unique numeric ID for the channel we want to join. Yet it does not appear anywhere in
Twitch page URL, which in this case is &lt;a href=&#34;https://www.twitch.tv/pedguin&#34;&gt;https://www.twitch.tv/pedguin&lt;/a&gt;. How does the frontend code get this number?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go back to the first API call that is made when stream page is being loaded. It has the following payload:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PlaybackAccessToken_Template&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query PlaybackAccessToken_Template($login: String!, $isLive: Boolean!, $vodID: ID!, $isVod: Boolean!, $playerType: String!) {  streamPlaybackAccessToken(channelName: $login, params: {platform: \&amp;#34;web\&amp;#34;, playerBackend: \&amp;#34;mediaplayer\&amp;#34;, playerType: $playerType}) @include(if: $isLive) {    value    signature    __typename  }  videoPlaybackAccessToken(id: $vodID, params: {platform: \&amp;#34;web\&amp;#34;, playerBackend: \&amp;#34;mediaplayer\&amp;#34;, playerType: $playerType}) @include(if: $isVod) {    value    signature    __typename  }}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;isLive&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pedguin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;isVod&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;vodID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;playerType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;site&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The response to this GraphQL request has the following payload:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;streamPlaybackAccessToken&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{\&amp;#34;adblock\&amp;#34;:false,\&amp;#34;authorization\&amp;#34;:{\&amp;#34;forbidden\&amp;#34;:false,\&amp;#34;reason\&amp;#34;:\&amp;#34;\&amp;#34;},\&amp;#34;blackout_enabled\&amp;#34;:false,\&amp;#34;channel\&amp;#34;:\&amp;#34;pedguin\&amp;#34;,\&amp;#34;channel_id\&amp;#34;:24070690,\&amp;#34;chansub\&amp;#34;:{\&amp;#34;restricted_bitrates\&amp;#34;:[],\&amp;#34;view_until\&amp;#34;:1924905600},\&amp;#34;ci_gb\&amp;#34;:false,\&amp;#34;geoblock_reason\&amp;#34;:\&amp;#34;\&amp;#34;,\&amp;#34;device_id\&amp;#34;:\&amp;#34;twitch-web-wall-mason\&amp;#34;,\&amp;#34;expires\&amp;#34;:1640538149,\&amp;#34;extended_history_allowed\&amp;#34;:false,\&amp;#34;game\&amp;#34;:\&amp;#34;\&amp;#34;,\&amp;#34;hide_ads\&amp;#34;:false,\&amp;#34;https_required\&amp;#34;:true,\&amp;#34;mature\&amp;#34;:false,\&amp;#34;partner\&amp;#34;:false,\&amp;#34;platform\&amp;#34;:\&amp;#34;web\&amp;#34;,\&amp;#34;player_type\&amp;#34;:\&amp;#34;site\&amp;#34;,\&amp;#34;private\&amp;#34;:{\&amp;#34;allowed_to_view\&amp;#34;:true},\&amp;#34;privileged\&amp;#34;:false,\&amp;#34;role\&amp;#34;:\&amp;#34;\&amp;#34;,\&amp;#34;server_ads\&amp;#34;:true,\&amp;#34;show_ads\&amp;#34;:true,\&amp;#34;subscriber\&amp;#34;:false,\&amp;#34;turbo\&amp;#34;:false,\&amp;#34;user_id\&amp;#34;:[REDACTED],\&amp;#34;user_ip\&amp;#34;:\&amp;#34;[REDACTED]\&amp;#34;,\&amp;#34;version\&amp;#34;:2}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;signature&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4992225a5ea06184d4c6c6c1187a77de55f1b7e8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;__typename&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PlaybackAccessToken&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;durationMilliseconds&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;68&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PlaybackAccessToken_Template&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;requestID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;01FQVSYG7KR0K7850XKBV27C1A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We find the numeric channel ID in the nested JSON string and can extract it.&lt;/p&gt;
&lt;p&gt;Once we have followed a channel, we need to make a WebSocket connection to &lt;code&gt;wss://irc-ws.chat.twitch.tv/&lt;/code&gt; and send the following
IRC messages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;CAP REQ :twitch.tv/tags twitch.tv/commands&lt;/code&gt; - this enables Twitch specific IRC commands.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PASS&lt;/code&gt; message with &lt;code&gt;oauth:&lt;/code&gt; prepended to auth token we received when creating a session with username and password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NICK&lt;/code&gt; message with your username.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;USER&lt;/code&gt; message with your username. Does not seem to be strictly necessary, but Twitch frontend sends this and we will send it as well.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JOIN&lt;/code&gt; message with channel name that you get from stream URL. For example if stream URL is &lt;code&gt;https://twitch.tv/foo&lt;/code&gt;, then this would be &lt;code&gt;JOIN #foo&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After sending all these messages, we will be receiving and sending messages in IRC wire format. See RFC1459 and related RFCs for
more details.&lt;/p&gt;
&lt;p&gt;You may notice that JSON payload contain SHA256 hash values. It is fine to reuse intercepted hash values in your code, as they don&amp;rsquo;t
seem to depend on any other parts of payload.&lt;/p&gt;
&lt;p&gt;The following code implement all the API flows we have discussed so far and provides a simple chat bot functionality: whenever it
detects a string &lt;code&gt;!test&lt;/code&gt;, it responds with hardcoded message. For the WebSocket part, we use &lt;code&gt;websocket_client&lt;/code&gt; module from PIP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; uuid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; websocket
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MATCH_STR &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;!test&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RESPONSE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hey there!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TWITCH_USERNAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TWITCH_PASSWORD &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHANNEL_NAME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GQL_API_URL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://gql.twitch.tv/gql&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;login_to_twitch&lt;/span&gt;(username, password):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Session()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connection&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;keep-alive&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Pragma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Cache-Control&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no-cache&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Upgrade-Insecure-Requests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User-Agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sec-Fetch-Site&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sec-Fetch-Mode&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;navigate&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sec-Fetch-User&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Sec-Fetch-Dest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;document&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept-Language&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-GB,en;q=0.9,en-US;q=0.8,lt;q=0.7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.twitch.tv/directory&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_token&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;twilight.46cc59eee010864b5ddfa5bc17f457b8&amp;#34;&lt;/span&gt;, domain&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;twitch.tv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cookies)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: username,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;: password,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kimne78kx3ncx6brgo4mv6wki5h1ko&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undelete_user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://passport.twitch.tv/login&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp2&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;access_token&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error_code&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3022&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        err_msg &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error(err_msg)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        captcha_proof &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;captcha_proof&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        code &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Code from email (&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;): &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;obscured_email&amp;#34;&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        code &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; code&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        payload[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;captcha&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;proof&amp;#34;&lt;/span&gt;: captcha_proof}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        payload[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;twitchguard_code&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp3 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://passport.twitch.tv/login&amp;#34;&lt;/span&gt;, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(resp3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;access_token&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Client-Id&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kimne78kx3ncx6brgo4mv6wki5h1ko&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; token &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OAuth &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; session, token
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_channel_id&lt;/span&gt;(session, channel):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Retrieving ID for channel &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(channel))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PlaybackAccessToken&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;isLive&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;: channel&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;isVod&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vodID&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;playerType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;site&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;persistedQuery&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sha256Hash&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0828119ded1c13477966434e15800ff57ddacf13ba1911c129dc2200705b0712&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(GQL_API_URL, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;streamPlaybackAccessToken&amp;#34;&lt;/span&gt;, dict())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; value_str &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(value_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    channel_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; value_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;channel_id&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; channel_id &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Got channel ID for &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; : &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(channel, channel_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; channel_id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;follow_channel&lt;/span&gt;(session, channel_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payload &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;operationName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FollowButton_FollowUser&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;disableNotifications&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;targetID&amp;#34;&lt;/span&gt;: str(channel_id),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;persistedQuery&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sha256Hash&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;800e7346bdf7e5278a3c1d3f21b2b56e2639928f86815677a7126b093b2fdd08&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;post(GQL_API_URL, json&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;connect_to_websocket&lt;/span&gt;(username, channel, token):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; websocket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;WebSocket()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wss://irc-ws.chat.twitch.tv/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CAP REQ :twitch.tv/tags twitch.tv/commands&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PASS oauth:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(token))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;NICK &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(username))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;USER &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; 8 * :&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(username, username))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;JOIN &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(channel))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; ws
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;send_message&lt;/span&gt;(ws, channel, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    nonce &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4())&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@client-nonce=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; PRIVMSG &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; :&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(nonce, channel, message))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run_loop&lt;/span&gt;(ws, channel, match_str, response):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            message &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;recv()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;warning(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;debug(message)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;startswith(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PING&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ws&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PONG&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PRIVMSG &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; :&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(channel) &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; message:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            message &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PRIVMSG &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; :&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(channel))[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; match_str &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; message:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                send_message(ws, channel, response)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;basicConfig(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%(asctime)s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; [&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%(levelname)-5.5s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;]  &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%(message)s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DEBUG,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    websocket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;enableTrace(&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session, token &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; login_to_twitch(TWITCH_USERNAME, TWITCH_PASSWORD)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    channel_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_channel_id(session, CHANNEL_NAME)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    follow_channel(session, channel_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; connect_to_websocket(TWITCH_USERNAME, CHANNEL_NAME, token)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_loop(ws, CHANNEL_NAME, MATCH_STR, RESPONSE)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a proof of concept chatbot that replicates Discord self-botting concept on Twitch.
The following improvements could made for this code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multithreading to support multiple channels and/or accounts - one thread for WS connection.&lt;/li&gt;
&lt;li&gt;Proxy support.&lt;/li&gt;
&lt;li&gt;Captcha solver integration for the cases when Twitch does ask for captcha.&lt;/li&gt;
&lt;li&gt;Email auto-verification.&lt;/li&gt;
&lt;li&gt;Randomization/spinning of response message so that Twitch does not discard it if multiple responses are sent in short timeframe.&lt;/li&gt;
&lt;li&gt;Storing the authentication token in some local file so that the script would not need to perform login step every time it starts.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Automating Google Dorking</title>
      <link>https://www.trickster.dev/post/automating-google-dorking/</link>
      <pubDate>Sun, 19 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/automating-google-dorking/</guid>
      <description>&lt;p&gt;There is more to using Google than searching by keywords, phrases and natural language questions. Google
also has advanced features that can empower users to be extra specific with their searches.&lt;/p&gt;
&lt;p&gt;To search for an exact phrase, wrap it in double quotes, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;top secret&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To search for documents with a specific file extension, use &lt;code&gt;filetype:&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;filetype:pdf &amp;#34;top secret&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To search for results in a specific domain, use &lt;code&gt;site:&lt;/code&gt; operator. This can also be used with top-level
domain, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;site:gov filetype:pdf &amp;#34;top secret&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may also watch to narrow down results based on part of URL that is not a domain name. This
can be achieved with &lt;code&gt;inurl:&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;site:gov inurl:declassified filetype:pdf &amp;#34;top secret&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Google also supports the usage of Boolean operators in search queries, for example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ethics AND (OSINT OR cybersecurity)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Google dorking is using advanced search queries to find things that are not necessarily meant to be exposed
to the public internet. For example, to find some webcams, one might search:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;intitle:&amp;#34;Live View/ — AXIS&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.exploit-db.com/google-hacking-database&#34;&gt;Google Hacking Database&lt;/a&gt; is a section of ExploitDB portal
that provides pre-written search queries that are meant to find things relevant for security. OSINT investigators
can use them to discover various pieces of information. Bounty hunters and penetration testers can use these
queries as part of the target recon phase. Likewise, security professionals working on defensive side can also benefit
when evaluating the security of systems they are protecting. Some of these queries reveal plaintext passwords or
private keys.  I don&amp;rsquo;t recommend using these for any malicious or questionable purposes, but if a site has a
bug bounty program it might be worthwhile to report it.&lt;/p&gt;
&lt;p&gt;How can one automate Google dorking? One way is to simply scrape Google search engine results pages. Google
does not exactly like that and will throw a captcha at you if they detect too many requests too quickly from a
single IP address. However, as long as one is using a proper proxy pool Google SERP scraping is not difficult
and can be done with not too much effort by knowing a little XPath/CSS and using something like Python with
&lt;a href=&#34;https://lxml.de/&#34;&gt;lxml&lt;/a&gt; module.&lt;/p&gt;
&lt;p&gt;Another way is to use one of the many SERP scraping SaaS solutions, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://zenserp.com/&#34;&gt;ZenSerp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://serpapi.com/&#34;&gt;SerpAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dataforseo.com/&#34;&gt;DataForSEO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://brightdata.com/products/search-engine-crawler&#34;&gt;Bright Data Search Engine Crawler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These options are fairly obvious to developers working with scraping and automation projects. However, there is
a third, relatively obscure option. The little known fact is that Google search has an API, kind of. They call this
&lt;a href=&#34;https://developers.google.com/custom-search&#34;&gt;Programmable Search&lt;/a&gt;. It&amp;rsquo;s meant as a way to let people integrate
the Google search engine into their own sites. If you have seen sites having a widget to search content with Google
then this is how these sites are integrating with Google. Note that only the first 100 search requests per day are free,
but after that Google will bill you $5 per 1000 queries.&lt;/p&gt;
&lt;p&gt;To set up your access to this API you need to follow the following steps.&lt;/p&gt;
&lt;p&gt;First, you need to set up a Custom Search Engine at
&lt;a href=&#34;https://programmablesearchengine.google.com/create/new&#34;&gt;Programmable Search control panel&lt;/a&gt;.
Don&amp;rsquo;t worry too much about what to put into &amp;ldquo;Sites to search&amp;rdquo; textfield, as you will be able to edit them later.
If you have a particular domain for a site you want to search (e.g. from a bug bounty platform) it would be useful
to limit results to that. Alternatively, you can put in bunch of wildcard entries for multiple popular TLDs:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;*.gov
*.edu
*.com
*.net
*.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Type in some name into &amp;ldquo;Name of the search engine&amp;rdquo; text field. Solve the captcha and press CREATE button.
Now choose the search engine you just created under &amp;ldquo;Edit search engine&amp;rdquo; text on the left hand side of
the page.  If needed, flip a &amp;ldquo;Search the entire web&amp;rdquo; switch in the settings. Note the search engine
ID as we will need it for API requests.&lt;/p&gt;
&lt;p&gt;Press &amp;ldquo;Get Started&amp;rdquo; button on &amp;ldquo;Custom Search JSON API&amp;rdquo; row. This will take you to page called
&lt;a href=&#34;https://developers.google.com/custom-search/v1/introduction&#34;&gt;Custom Search JSON API: Introduction&lt;/a&gt;.
Press &amp;ldquo;Get a key&amp;rdquo; button that will let you choose a Google Cloud project. Create a new one if needed.
Once the project is selected, you will be given the API key.&lt;/p&gt;
&lt;p&gt;Google provides an &lt;a href=&#34;https://developers.google.com/custom-search/v1/reference/rest/v1/cse/list&#34;&gt;API reference&lt;/a&gt;
with all the parameters, but for the simplest API call we only need the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cx&lt;/code&gt; - search engine ID.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt; - API key.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; - search query.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following curl snippet exemplifies API call that we are interested in:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ curl &amp;#34;https://www.googleapis.com/customsearch/v1?key=[REDACTED]&amp;amp;cx=[REDACTED]&amp;amp;q=secret&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The following simple Python script provides an example of using this API for traversing
across multiple search queries and saving the results into CSV file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SEARCH_ENGINE_ID &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;API_KEY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_snippet&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;queries.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;results.csv&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; query &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; in_f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; query&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        params &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cx&amp;#34;&lt;/span&gt;: SEARCH_ENGINE_ID,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;: API_KEY,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: query,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://www.googleapis.com/customsearch/v1&amp;#34;&lt;/span&gt;, params&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;params)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;url)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; item_dict &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; json_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;, []):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: query,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_url&amp;#34;&lt;/span&gt;: item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;link&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_title&amp;#34;&lt;/span&gt;: item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result_snippet&amp;#34;&lt;/span&gt;: item_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;snippet&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If needed, you can use &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;num&lt;/code&gt; parameters to get more pages
from API. However, you are allowed to get only up to 100 results per query
even if there are more available.&lt;/p&gt;
&lt;p&gt;You may also want to check out Google&amp;rsquo;s
&lt;a href=&#34;https://developers.google.com/knowledge-graph&#34;&gt;Knowledge Graph Search API&lt;/a&gt;
that allows one to search for entities like people, companies, places, and so on.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using ephemeral Onion Services for quick NAT traversal</title>
      <link>https://www.trickster.dev/post/using-ephemeral-onion-services-for-quick-nat-traversal/</link>
      <pubDate>Sun, 12 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/using-ephemeral-onion-services-for-quick-nat-traversal/</guid>
      <description>&lt;p&gt;Sometimes, when developing server-side software it is desirable to make it
accessible for access outside the local network, which might be shielded from
incoming TCP connections by a router that performs Network Address
Translation. One option to work around this is to use &lt;a href=&#34;https://ngrok.com/&#34;&gt;Ngrok&lt;/a&gt; -
a SaaS app that lets you tunnel out a connection from your network and
exposes it to external traffic through a cloud endpoint. However, it is
primarily designed for web apps and it would be nice if we didn&amp;rsquo;t need to rely
on a third-party SaaS vendor to make our server software accessible outside
our local network.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.torproject.org/&#34;&gt;Tor&lt;/a&gt; is an overlay network for anonymous
communications and censorship circumvention. The goal of Tor is to prevent the
gathering and analysis of traffic metadata (also known as traffic analysis).
A detailed explanation of how Tor works
is outside the scope of this text, but the key principle is that hiding of routing
information is achieved by creating tunnel connection through 3 (or more, if
both parties are within Tor network) intermediate servers with telescoping
encryption layers - 3 layers of encryption to the first node, 2 layers of encryption
to middle node and 1 layer towards the exit node. The exit node removes the final layer
of encryption and works as a proxy server towards the ultimate destination of TCP
connection. Thus no single server is aware of both initiator and destination of
TCP connection.&lt;/p&gt;
&lt;p&gt;Through a Tor feature called Onion Services, hiding a server is possible as well.
Turns out, setting up an Onion Service gives us NAT traversal for free, provided
that people accessing our server are okay to do it through Tor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gitlab.torproject.org/tpo/core/tor&#34;&gt;Tor daemon&lt;/a&gt; is a piece of software that
implements all parts of the Tor network. For configuration during runtime, it can expose
a control port that we can connect to via TCP transport and run various requests
against a running Tor instance. &lt;a href=&#34;https://gitweb.torproject.org/torspec.git/tree/control-spec.txt&#34;&gt;Tor Control Protocol&lt;/a&gt;
is a relatively simple text-based network protocol that defines requests and responses
that Tor controllers (i.e. programs interacting with Tor instance via control port)
must be able to parse and generate. One of the commands it defines is &lt;code&gt;ADD_ONION&lt;/code&gt;
at &lt;a href=&#34;https://gitweb.torproject.org/torspec.git/tree/control-spec.txt#n1748&#34;&gt;Section 3.27&lt;/a&gt;
that enables quick creation of Onion Services. We can provide cryptographic keys to be
used for this, but primarily we are concerned with port mapping between the local port
that our server is bound to (it&amp;rsquo;s fine if it&amp;rsquo;s binding the listener socket to the local host
address) and the port that Onion Service will be exposing. These can be either equal or
different, but it is an important detail to get right. Furthermore, ephemeral Onion
Services are bound to the lifecycle of control port connection - no need to worry about
removing them.&lt;/p&gt;
&lt;p&gt;We could be doing a little socket programming, but we don&amp;rsquo;t have to, as the Tor project
is also developing &lt;a href=&#34;https://stem.torproject.org/&#34;&gt;Stem&lt;/a&gt; - a Python module that implements
a Tor Control Protocol and enables us to quickly launch an ephemeral Onion Service.&lt;/p&gt;
&lt;p&gt;I wrote the following quick script that connects to a local Tor instance
(it could be a Tor browser bundle running locally) through the control port,
creates ephemeral Onion Service, uses its address to build this very blog through
&lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt;, and runs a local web server (&lt;code&gt;http.server&lt;/code&gt; from vanilla Python
installation) on local port 8080.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; stem.control &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Controller
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; http.server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; subprocess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_port()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;authenticate()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_ephemeral_hidden_service({&lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;}, await_publication&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt;(len(response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;service_id) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    onion_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; response&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;service_id &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.onion&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(onion_url, file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stderr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chdir(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tricksterblog/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subprocess&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hugo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--baseURL&amp;#34;&lt;/span&gt;, onion_url])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;chdir(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;public/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subprocess&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;python3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-m&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http.server&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8080&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To encapsulate everything within a reproducible environment, the following Docker configuration
could be used:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; debian:11&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; apt-get update &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get install -y python3 python3-pip hugo tor&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RUN&lt;/span&gt; pip3 install stem&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ADD&lt;/span&gt; . /trickster.dev&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;WORKDIR&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; /trickster.dev&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENTRYPOINT&lt;/span&gt; bash -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tor --RunAsDaemon 1 --ControlPort 9051 &amp;amp;&amp;amp; python3 run_onion_service.py&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Admittedly there are some opportunities for optimization here, as the size of resulting Docker
image is over 700 MB. However the code in this post is meant to be only a demonstration for
using Tor to expose a server in a NATted network.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-12-12_14.27.47.png&#34;&gt;Screenshot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lastly, I would not recommend doing this in corporate networks, as your IT department and
security people might disapprove. Not only this solution can be considered to be Shadow IT, but
using Tor in the corporate environment might raise suspicions of data exfiltration. Proceed with
caution.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sending mass DMs on Reddit through API: a small experiment</title>
      <link>https://www.trickster.dev/post/sending-mass-dms-on-reddit-through-api/</link>
      <pubDate>Sat, 04 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/sending-mass-dms-on-reddit-through-api/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://howitzer.co/&#34;&gt;Howitzer&lt;/a&gt; is a SaaS tool that scrapes subreddits for users mentioning given keywords and automates mass
direct message sending for growth hacking purposes.&lt;/p&gt;
&lt;p&gt;Generally speaking, there are significant difficulties when automating against major social media platforms. However, Reddit
is not as hostile towards automation as other platforms and even provides a relatively unrestricted
&lt;a href=&#34;https://www.reddit.com/dev/api/&#34;&gt;official API&lt;/a&gt; for building bots and integrations.&lt;/p&gt;
&lt;p&gt;Let us try automating against Reddit API with Python to build a poor mans Howitzer with Python.&lt;/p&gt;
&lt;p&gt;Before we start, it is important to have a pre-warmed/pre-used account that we&amp;rsquo;re willing to burn. The one I got from
&lt;a href=&#34;https://accfarm.com/buy-reddit-accounts/softreg-reddit-accounts&#34;&gt;Accfarm&lt;/a&gt; has over 1000 karma (I paid close to
$40 for it). Reddit will not allow DM&amp;rsquo;ing users if the account is freshly created.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://praw.readthedocs.io/en/stable/index.html&#34;&gt;PRAW&lt;/a&gt; is a Python module that wraps Reddit API calls into Pythonic
interface and takes care of OAuth flow for us. We will be using it for automation. It is available on PIP.&lt;/p&gt;
&lt;p&gt;To set up the account for automation, we visit &lt;a href=&#34;https://old.reddit.com/prefs/apps/&#34;&gt;apps section&lt;/a&gt; of user preferences and press
&amp;ldquo;create another app&amp;hellip;&amp;rdquo; button. Then we choose &amp;ldquo;script&amp;rdquo; option, and enter some name and redirect URI into respective fields.
Don&amp;rsquo;t worry too much about the redirect - I entered http://localhost and got no errors about it. Once you submit this form,
you will get your API credentials. Client ID is the string under app name and Client Secret is available as well.&lt;/p&gt;
&lt;p&gt;To access Reddit API through PRAW, one has to initialize a &lt;code&gt;Reddit&lt;/code&gt; object first. The simplest way to do it is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reddit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; praw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Reddit(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my client id&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client_secret&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my client secret&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_agent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my user agent&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    username&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my username&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    password&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my password&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, this entails hardcoding credentials into the Python script, which is generally a bad practice. We want to have a separate
configuration file for credentials that the code would read. PRAW can read a praw.ini file with contents like the
following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[bot]
client_id=[REDACTED]
client_secret=[REDACTED]
username=[REDACTED]
password=[REDACTED]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once this file is available in the same directory as our code, we can initialize the &lt;code&gt;Reddit&lt;/code&gt; object like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reddit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; praw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Reddit(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bot&amp;#34;&lt;/span&gt;, user_agent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;some user agent&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We only have to pass in the praw.ini file section name and PRAW will read credentials from that section.&lt;/p&gt;
&lt;p&gt;To message some users, we first have to have a list of users to message. Thus we write a quick script that searches given
subreddits by a given search query and saves the results into a CSV file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; datetime &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pprint &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pprint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; praw
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USER_AGENT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;automations&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FIELDNAMES &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subreddit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;selftext&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;datetime&amp;#34;&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OUTPUT_CSV_PATH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;posts.csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iso_timestamp_from_unix_time&lt;/span&gt;(unix_time):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; datetime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromtimestamp(unix_time)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dt&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;isoformat()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scrape_subreddit&lt;/span&gt;(reddit, query, subreddit_name):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; reddit&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;subreddit(display_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;subreddit_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; s &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; sr&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;search(query, limit&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        row &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subreddit&amp;#34;&lt;/span&gt;: subreddit_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt; : s&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;author&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: s&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;title,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;selftext&amp;#34;&lt;/span&gt;: s&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;selftext&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;datetime&amp;#34;&lt;/span&gt;: iso_timestamp_from_unix_time(s&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;created_utc),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; row
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &amp;lt;query&amp;gt; &amp;lt;subreddit1,subreddit2,...&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Use subreddit names with /r/ - e.g. &amp;#39;webscraping&amp;#39;, not &amp;#39;/r/webscraping&amp;#39;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    query &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subreddit_names &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;argv[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reddit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; praw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Reddit(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bot&amp;#34;&lt;/span&gt;, user_agent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;USER_AGENT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(OUTPUT_CSV_PATH, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; csv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DictWriter(out_f, fieldnames&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;FIELDNAMES, lineterminator&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writeheader()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; subreddit_name &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; subreddit_names:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scrape_subreddit(reddit, query, subreddit_name):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pprint(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            csv_writer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;writerow(row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, PRAW module nicely wraps Reddit API into object-oriented Python and makes things fairly easy for us.&lt;/p&gt;
&lt;p&gt;When we run this, we get a CSV file with rows similar to the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;subreddit,username,title,selftext,datetime
webscraping,Raghu1990,Can I scrape price target history from marketbeat.com ? Preferably using python.,,2021-11-27T00:50:17
webscraping,omarsika,How to access all data from api jQuery using python?,&amp;#34;Help please, I have this url here that I need to scrape for an assignment [https://www.lesmiraculeux.com/pages/store-locator](https://www.lesmiraculeux.com/pages/store-locator)  I was able to get this get request from Postman, but no matter how much I play around with the parameters or headers I can only get 100 results.   Here is the callback function settings - [https://stockist.co/api/v1/u8403/widget.js?callback=\_stockistConfigCallback](https://stockist.co/api/v1/u8403/widget.js?callback=_stockistConfigCallback)  Best I was able to do was input a list of coordinates and dedupe, is there a way to get the jQuery to return all the results instead of just 100?  Thank you!    &amp;gt; url = &amp;#34;&amp;#34;https://stockist.co/api/v1/u8403/locations/search?callback=jQuery&amp;amp;tag=u8403&amp;amp;latitude=48.88936919246074&amp;amp;longitude=2.3697855000000168&amp;#34;&amp;#34;            payload={}      headers = {            &amp;#39;authority&amp;#39;: &amp;#39;stockist.co&amp;#39;,            &amp;#39;sec-ch-ua&amp;#39;: &amp;#39;&amp;#34;&amp;#34;Google Chrome&amp;#34;&amp;#34;;v=&amp;#34;&amp;#34;95&amp;#34;&amp;#34;, &amp;#34;&amp;#34;Chromium&amp;#34;&amp;#34;;v=&amp;#34;&amp;#34;95&amp;#34;&amp;#34;, &amp;#34;&amp;#34;;Not A Brand&amp;#34;&amp;#34;;v=&amp;#34;&amp;#34;99&amp;#34;&amp;#34;&amp;#39;,            &amp;#39;sec-ch-ua-mobile&amp;#39;: &amp;#39;?0&amp;#39;,            &amp;#39;user-agent&amp;#39;: &amp;#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36&amp;#39;,            &amp;#39;sec-ch-ua-platform&amp;#39;: &amp;#39;&amp;#34;&amp;#34;Windows&amp;#34;&amp;#34;&amp;#39;,            &amp;#39;accept&amp;#39;: &amp;#39;*/*&amp;#39;,            &amp;#39;sec-fetch-site&amp;#39;: &amp;#39;cross-site&amp;#39;,            &amp;#39;sec-fetch-mode&amp;#39;: &amp;#39;no-cors&amp;#39;,            &amp;#39;sec-fetch-dest&amp;#39;: &amp;#39;script&amp;#39;,            &amp;#39;referer&amp;#39;: &amp;#39;https://www.lesmiraculeux.com/&amp;#39;,            &amp;#39;accept-language&amp;#39;: &amp;#39;en-US,en;q=0.9&amp;#39;}          response = requests.request(&amp;#34;&amp;#34;GET&amp;#34;&amp;#34;, url, headers=headers, data=payload)       data = response.content[11:-2]      stores = {&amp;#39;name&amp;#39;:[], &amp;#39;address&amp;#39;:[],&amp;#39;zipcode&amp;#39;:[],&amp;#39;city&amp;#39;:[], &amp;#39;lat&amp;#39;:[],&amp;#39;long&amp;#39;:[]}      for i in json.loads(data)[&amp;#39;locations&amp;#39;]:              stores[&amp;#39;name&amp;#39;].append(i[&amp;#39;name&amp;#39;])              stores[&amp;#39;address&amp;#39;].append(i[&amp;#39;address_line_1&amp;#39;])              stores[&amp;#39;zipcode&amp;#39;].append(i[&amp;#39;postal_code&amp;#39;])              stores[&amp;#39;city&amp;#39;].append(i[&amp;#39;city&amp;#39;])              stores[&amp;#39;lat&amp;#39;].append(i[&amp;#39;latitude&amp;#39;])              stores[&amp;#39;long&amp;#39;].append(i[&amp;#39;longitude&amp;#39;])            df = pd.DataFrame(stores)      df.head()&amp;#34;,2021-11-27T21:48:42
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We purposely include data about the post as we want to be able to evaluate data quality for growth hacking purposes. However, to
send direct messages only the second column is needed. Thus we run the following command on Unix (macOS in this case) to extract
it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cat posts.csv | tail -n +2 | awk -F &amp;#34;,&amp;#34; &amp;#39;{ print $2 }&amp;#39; | sort | uniq &amp;gt; users.txt 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that sort(1) has to be just before uniq(1) in the pipeline. Duplicate username filtering will not work otherwise.&lt;/p&gt;
&lt;p&gt;The code to mass DM the Reddit users is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; praw
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USER_AGENT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;automations&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;try_posting&lt;/span&gt;(reddit, username, subject, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        reddit&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;redditor(username)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;message(subject, message)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; praw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exceptions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;RedditAPIException &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; subexception &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; e&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; subexception&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error_type &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RATELIMIT&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                error_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str(subexception)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                print(error_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;minute&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; error_str:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; error_str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;for &amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; minute&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(delay) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; error_str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;for &amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; second&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; int(delay)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(delay)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; subexception&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;error_type &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;INVALID_USER&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;except&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reddit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; praw&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Reddit(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bot&amp;#34;&lt;/span&gt;, user_agent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;USER_AGENT)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    subject &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; input(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Subject: &amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    templ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;users.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;, encoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; username &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; in_f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        username &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; username&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(username)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; templ&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{username}}&amp;#34;&lt;/span&gt;, username)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; try_posting(reddit, username, subject, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(reddit&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;auth&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;limits)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; reddit&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;auth&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;limits[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;remaining&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; reddit&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;auth&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;limits[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;reset_timestamp&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;time()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Used up requests in current time window - sleeping for &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; seconds&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;format(timeout))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            time&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(timeout)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    in_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We instantiate a &lt;code&gt;Reddit&lt;/code&gt; object and ask the user to input DM subject via standard input.
Then we read a template of the message from m message.txt and usernames (one per line) from users.txt.
We replace &lt;code&gt;{{username}}&lt;/code&gt; in a template with an actual user name to personalize a message. I could have used jinja2 for this,
but this is just a simple experimental script that does not warrant bringing a full-powered templating engine into the
picture.&lt;/p&gt;
&lt;p&gt;Now we try posting the message by calling &lt;code&gt;try_posting&lt;/code&gt; function. This function returns a boolean value:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;True if posting succeeded or if the target user is invalid (no need to retry)&lt;/li&gt;
&lt;li&gt;False if posting failed for some reason other than target user being invalid.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all goes well, the API call in &lt;code&gt;try&lt;/code&gt; block does not throw any exceptions and we can proceed to next user.
But it turns out that Reddit API has a hidden rate limit on sending direct messages. When we hit that rate limit, we
are getting an exception of class
&lt;a href=&#34;https://praw.readthedocs.io/en/latest/code_overview/exceptions.html#praw.exceptions.RedditAPIException&#34;&gt;&lt;code&gt;praw.exceptions.RedditAPIException&lt;/code&gt;&lt;/a&gt;
that will have one or more sub-exceptions available in the list at &lt;code&gt;items&lt;/code&gt; property. To detect rate limiting, we
iterate across this list and check if there&amp;rsquo;s a subexception with &lt;code&gt;error_type&lt;/code&gt; equal to &lt;code&gt;RATELIMIT&lt;/code&gt; string.&lt;/p&gt;
&lt;p&gt;When converted to string, it will read something like the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;praw.exceptions.RedditAPIException: RATELIMIT: &amp;#34;Looks like you&amp;#39;ve been doing that a lot. Take a break for 3 minutes before trying again.&amp;#34; on field &amp;#39;ratelimit&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives us delay duration in minutes or seconds. We do a little bit of string processing to dig out the delay value and
call &lt;code&gt;time.sleep()&lt;/code&gt; with it before trying to post again.&lt;/p&gt;
&lt;p&gt;If something is wrong with the target user we will get subexception with &lt;code&gt;error_type&lt;/code&gt; being equal to &lt;code&gt;INVALID_USER&lt;/code&gt;. We
make sure to skip this user and refrain from further attempts to message them.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;main()&lt;/code&gt; function we also check &lt;code&gt;limits&lt;/code&gt; dictionary in &lt;a href=&#34;https://praw.readthedocs.io/en/stable/code_overview/other/auth.html&#34;&gt;&lt;code&gt;Auth&lt;/code&gt;&lt;/a&gt;
object that PRAW fills from the HTTP headers. This turned out to be unnecessary for our use case, as we have much tighter rate
limits for DM&amp;rsquo;ing people than the &amp;ldquo;official&amp;rdquo; Reddit rate limit of 30 requests per minute.&lt;/p&gt;
&lt;p&gt;I managed to get this stuff working but wasn&amp;rsquo;t able to growth hack my way to wealth as Reddit suspended the account
for 3 days. However, it does seem to work more or less fine on a sufficiently small scale, as long as the account is pre-warmed
and has some karma. API requests to send messages were instantly rejected on the freshly created account.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Introducing GPT-3: Playground and API</title>
      <link>https://www.trickster.dev/post/introducing-gpt3/</link>
      <pubDate>Fri, 26 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/introducing-gpt3/</guid>
      <description>&lt;p&gt;GPT-3 is a large scale natural language processing system based on deep learning and developer by
OpenAI. It is now generally available for developers and curious people.&lt;/p&gt;
&lt;p&gt;GPT-3 is the kind of AI that works exclusively on text. Text is both input and output for GPT-3.
One can provide questions and instructions in plain English and receive fairly coherent responses. However, GPT-3 is not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HAL from &lt;em&gt;2001: Space Odyssey&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Project 2501 from &lt;em&gt;Ghost In the Shell&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;T-800 from &lt;em&gt;The Terminator&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;AM from &lt;em&gt;I Have No Mouth And I Must Scream&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What GPT-3 is a tool AI (as opposed to agent AI). Given textual input (prompt) it gives an output based on both the input
and all the datasets it was trained with. The quality of output is strongly dependant on the quality of input. Prompt Engineering
(also known as Prompt Design or Prompt Programming) is the practice of crafting inputs for GPT-3 to acquire an output of
sufficiently high quality. Some say that this will be a new kind of computer programming and that crafting prompts for systems like
GPT-3 will become an established occupation in the future.&lt;/p&gt;
&lt;p&gt;The simplest way to experiment with GPT-3 is to use the &lt;a href=&#34;https://beta.openai.com/playground&#34;&gt;Playground&lt;/a&gt;. You enter your
prompt into the big text field and press the green button to generate an output. Let us go through various
configuration options on the right-hand side.&lt;/p&gt;
&lt;p&gt;The engine is the sub-model of GPT-3. There are 4 base engines available (ordered from most to least powerful):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;davinci&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curie&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;babbage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ada&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Using a more powerful engine cost more time and credits than using a less powerful one. Base engines are meant for general-purpose
tasks.&lt;/p&gt;
&lt;p&gt;Instruct engines are made to understand and follow instructions. At this point, there are 3 of them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;davinci-instruct-beta-v3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;davinci-instruct-beta&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curie-instruct-beta&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The temperature slider controls how random and creative the responses will be. For tasks and questions that have very clear, definite
answers we want to set temperature to 0. For maximum creativity, we want it to be 1.&lt;/p&gt;
&lt;p&gt;Response length is measured in tokens (one token is approx. 4 English letters).&lt;/p&gt;
&lt;p&gt;Stop sequences are up to 4 strings that will terminate the generation of output.&lt;/p&gt;
&lt;p&gt;Top P is a lot like temperature but defined in terms of token probabilities. If we set it to 0.1 it will consider only tokens
that are within 10% of probability mass.&lt;/p&gt;
&lt;p&gt;Frequency penalty let us make repetitions less like by penalizing tokens based on their frequency in the already generated text.&lt;/p&gt;
&lt;p&gt;Presence penalty is similar to frequencey penalty, but it penalizes tokens based on their presence (not frequency) in the text
generated so far.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Best of&amp;rdquo; option will make the model generates a given amount of outputs and will choose the best one out of them.&lt;/p&gt;
&lt;p&gt;If we wanted, we can enable Playground to visualize probabilities of tokens in the generated text.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Inject start text&amp;rdquo; and &amp;ldquo;Inject restart text&amp;rdquo; deal with prompts that will appear when GPT-3 is used as chatbot - see &lt;a href=&#34;https://beta.openai.com/playground/p/default-chat&#34;&gt;Chat&lt;/a&gt; example.&lt;/p&gt;
&lt;p&gt;Let us use default settings to play with the model a little.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who is Mark Zuckerberg?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mark Elliot Zuckerberg is an American computer programmer and Internet entrepreneur. He is best known as one of five co-founders of the social networking site Facebook.&lt;/p&gt;
&lt;p&gt;Zuckerberg launched Facebook from his Harvard University dormitory room on February 4, 2004. He was assisted by his college roommates and fellow Harvard students Eduardo Saverin, Andrew McCollum, Dustin Moskovitz, and Chris Hughes. The group then introduced Facebook to other college campuses. Facebook expanded rapidly, reaching one billion users by 2012.&lt;/p&gt;
&lt;p&gt;Mark Zuckerberg is also the founder of fwd.us, a political action committee which he launched in 2013. He was ranked number one on the Forbes list of ‘The World’s Most Powerful People’ in the same year.&lt;/p&gt;
&lt;p&gt;Mark Zuckerberg has won numerous awards for his work at Facebook, including Time Person of the Year in 2010, and the Marconi Prize in 2014. He has been included in the Time 100 most influential people in the world list from 2010 to 2015.&lt;/p&gt;
&lt;p&gt;In 2012, Zuckerberg was ranked number one on the Forbes list of ‘The World’s Billionaires’, after his estimated net worth rose to US$17.5 billion.&lt;/p&gt;
&lt;p&gt;In 2014, Zuckerberg was ranked the 5th richest person in the world, with an estimated net worth of US$35.3 billion.&lt;/p&gt;
&lt;p&gt;Mark Zuckerberg is married to Priscilla Chan, who is a dentist. They met at a frat party at Harvard, and married in 2012. They have a daughter named Maxima Chan Zuckerberg&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;That not too bad, huh?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try to check some logical reasoning (or lack thereof).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If Epimenides is from Crete and all Cretans are liars, is Epimenides a liar?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Answer: Yes.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Answer: Because all Cretans are liars (Epimenides is from Crete)&lt;/p&gt;
&lt;p&gt;This is why the argument is valid. The conclusion follows logically from the premises.&lt;/p&gt;
&lt;p&gt;The conclusion follows logically from the premises.&lt;/p&gt;
&lt;p&gt;This is why the&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It generated this much before warning me about unsafe content, but essentially gave a correct answer. I guess asking the AI about liars can lead to some controversial answers.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s push this angle of questioning a bit further and introduce what Douglas Hofstadter calls a Strange Loop.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If Epimenides is from Crete says that all Cretans are liars, is Epimenides lying?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If Epimenides is from Crete, is it false that Epimenides is from Crete?&lt;/p&gt;
&lt;p&gt;If Epimenides is from Crete, is Epimenides from Crete?&lt;/p&gt;
&lt;p&gt;If Epimenides is from Crete, is it true that Epimenides is from Crete?&lt;/p&gt;
&lt;p&gt;For example, the following arguments are valid, where “Epimenides is from Crete” is a sentence that is true if and only if Epimenides is from Crete:&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;
&lt;p&gt;Epimenides is from Crete.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Well, that went nowhere real quick.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try doing something a bit more practical by switching to &lt;code&gt;davinci-instruct-beta-v3&lt;/code&gt; engine and asking it the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write a Facebook ad copy for handheld holographic projector&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bring your favorite movies and games to life with the world&amp;rsquo;s first handheld holographic projector! The perfect entertainment for any occasion, the projector features advanced optics that allow you to view holograms in stunning detail.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m no copywriter and this is at the very least superior to the ad copy that I could write.&lt;/p&gt;
&lt;p&gt;What about something more technical? Can it generate some code?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write a Python script that takes a list of integers via standard input and prints a sum of them&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;def sum(nums):&lt;/p&gt;
&lt;p&gt;total = 0&lt;/p&gt;
&lt;p&gt;for i in nums:&lt;/p&gt;
&lt;p&gt;total = total + i&lt;/p&gt;
&lt;p&gt;return total&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Well, it seems it won&amp;rsquo;t be replacing software developers any time soon. Not only it&amp;rsquo;s formatted all wrong, but this is merely a Python function, not the entire program.&lt;/p&gt;
&lt;p&gt;Speaking of Python programming, GPT-3 provides a REST API with several client libraries, Python being one of them. In fact, if we press View Code button in the playground, we
can get a Python snippet that reproduces API call with the settings we have in the Playground:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; openai
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openai&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;api_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getenv(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; openai&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Completion&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  engine&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;davinci-instruct-beta-v3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  prompt&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Write a Python script that takes a list of integers via standard input and prints a sum of them&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  temperature&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0.7&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  max_tokens&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  top_p&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  frequency_penalty&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  presence_penalty&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To read more about GPT-3 API, take a look at official documentation at: &lt;a href=&#34;https://beta.openai.com/docs/introduction&#34;&gt;https://beta.openai.com/docs/introduction&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;openai&lt;/code&gt; Python module includes a CLI tool that we can use to interact with GPT-3, provided that we have &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; environment variable set to our API key:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ openai api engines.list
[organization=user-fpeechvtioklyst4iyrcy5c2] {
  &amp;#34;data&amp;#34;: [
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;ada&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;babbage&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;curie&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;curie-instruct-beta&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;davinci&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;davinci-instruct-beta&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    },
    {
      &amp;#34;created&amp;#34;: null,
      &amp;#34;id&amp;#34;: &amp;#34;davinci-instruct-beta-v3&amp;#34;,
      &amp;#34;max_replicas&amp;#34;: null,
      &amp;#34;object&amp;#34;: &amp;#34;engine&amp;#34;,
      &amp;#34;owner&amp;#34;: &amp;#34;openai&amp;#34;,
      &amp;#34;permissions&amp;#34;: null,
      &amp;#34;ready&amp;#34;: true,
      &amp;#34;ready_replicas&amp;#34;: null,
      &amp;#34;replicas&amp;#34;: null
    }
  ],
  &amp;#34;object&amp;#34;: &amp;#34;list&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>How does PerimeterX Bot Defender work</title>
      <link>https://www.trickster.dev/post/how-does-perimeterx-bot-defender-work/</link>
      <pubDate>Sat, 20 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-does-perimeterx-bot-defender-work/</guid>
      <description>&lt;p&gt;PerimeterX is a prominent vendor of anti-bot technology, used by portals such as Zillow, Crunchbase, StockX and many others.
Many developers working on web scraping or automation scripts have ran into PerimeterX Human Challenge - a proprietary CAPTCHA that
involves pressing and holding a HTML element and does not seem to solvable by any of the CAPTCHA solving services.&lt;/p&gt;
&lt;p&gt;PerimeterX has registered the following US patents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://patents.google.com/patent/US20190173900A1/en?assignee=perimeterx&amp;amp;oq=perimeterx&#34;&gt;US 10,708,287B2&lt;/a&gt; - ANALYZING CLIENT APPLICATION BEHAVIOR TO DETECT ANOMALIES AND PREVENT ACCESS&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://patents.google.com/patent/US20180109540A1/en?assignee=perimeterx&amp;amp;oq=perimeterx&#34;&gt;US 10,951,627B2&lt;/a&gt; - SECURING ORDERED RESOURCE ACCESS&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://patents.google.com/patent/US20210064685A1/en?assignee=perimeterx&amp;amp;oq=perimeterx&#34;&gt;US 2021/064685A1&lt;/a&gt; - IDENTIFYING A SCRIPT THAT ORIGINATES SYNCHRONOUS AND ASYNCHRONOUS ACTIONS (pending application)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let us take a look into these patents to discover key working principles of PerimeterX bot mitigation technology.&lt;/p&gt;
&lt;h2 id=&#34;us-10708287b2&#34;&gt;US 10,708,287B2&lt;/h2&gt;
&lt;p&gt;The following diagram visualises the overall flow of PerimeterX bot detection system from the client perspective:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://patentimages.storage.googleapis.com/a1/62/94/7dc9c2325f3243/US20190173900A1-20190606-D00005.png&#34;&gt;https://patentimages.storage.googleapis.com/a1/62/94/7dc9c2325f3243/US20190173900A1-20190606-D00005.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this context, &amp;ldquo;Application Server&amp;rdquo; is a web server hosting web application. &amp;ldquo;Client Device&amp;rdquo; is computer or
smartphone accessing the site being protected by PerimeterX. &amp;ldquo;Security Verification System&amp;rdquo; is PerimeterX
backend. Note that Application Server is allowed to be multi-server system if CDN is used or if web application
is consisting of multiple subsystems. There&amp;rsquo;s another patent that extends the technique discussed herein to enable
it to be used across multiple servers.&lt;/p&gt;
&lt;p&gt;First step of the flow is end user trying to load a page. When PerimeterX Bot Defender is deployed, a JavaScript code
from PerimeterX is being included in the HTML document being downloaded. This code is called Security Loader. As
the browser is rendering the page, Security Loader downloads and executes further JavaScript code called Security Module.
It is retrieved from PerimeterX backend with some parameters on what tests to run in the browser. The purpose of
Security Module is to look for things that may be useful as signal for automation and to check if browsers JavaScript
environment isn&amp;rsquo;t anomalous in some ways. Things that Security Module may check include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript engine&lt;/li&gt;
&lt;li&gt;HTML elements&lt;/li&gt;
&lt;li&gt;Outbound links&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;li&gt;User input behaviour and timing (mouse movement, clicks, etc.)&lt;/li&gt;
&lt;li&gt;Local storage&lt;/li&gt;
&lt;li&gt;Browser history&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When tests are finished, Security Module reports all of this back to Security Verification System, which enriches
and processes the data. Enrichment involves appending extra fields on IP address, geolocation, User-Agent header and so on.
PerimeterX gathers the data and trains machine learning models to establish baseline for
normal activity. Security score is computed based on how close test results are to the baseline. Furthermore, some anomalies
may detected at this stage, such as mismatching number of HTML element, mismatching User-Agent header to objects in JS
environment and so on. For instance, if browser claims to be Google Chrome in the User-Agent header, but does not have
&lt;code&gt;window.chrome&lt;/code&gt; object it may be the kind of anomaly that causes PerimeterX to block a browser from further interaction
with web application being protected.&lt;/p&gt;
&lt;p&gt;At this point, if security score is high enough PerimeterX may allow the browser to access the resources (with some further
monitoring during the client session). However, the analysis may not be conclusive, which may prompt further tests that may
involve actions from the use (e.g. CAPTCHA solving). Based on the results of these additional tests, PerimeterX may allow
or deny further access to the web app. The code that allows or denies access is called Security Enforcement Module and is
integrated with Application Server.&lt;/p&gt;
&lt;p&gt;When PerimeterX allows an access, it issues Security Token which is cryptographically signed object that contains security score
with information on passed and failed tests. During the client session, Security Module continues to run and provides information
on client side activity to PerimeterX. This enabled further training of ML models and also updates security score if needed.
This also enables PerimeterX to cut off the client session if anomalous activities are detected some time after the initial tests
were done. Furthermore, Security Token is provided to Application Server that might deny the access based on security score.&lt;/p&gt;
&lt;p&gt;Note that PerimeterX can also cover mobile apps. When it is integrated with mobile app systems, Security Loader is not present,
but Security Module is integrated in the form of native mobile SDK. Everything else works the same.&lt;/p&gt;
&lt;h2 id=&#34;us-10951627b2&#34;&gt;US 10,951,627B2&lt;/h2&gt;
&lt;p&gt;Web applications protected by PerimeterX may be loading some content (images, data, etc.) from servers other than the primary
web server. Entities deploying PerimeterX may want these requests to be covered by anti-botting countermeasures as well. This
patent extends the idea from previous one to address this requirement.&lt;/p&gt;
&lt;p&gt;The idea here is that primary web server issues something called Risk Token that has to be provided to secondary servers to
authorize resource access (e.g. as HTTP cookie or URL parameter value). This token is generated from parameters describing
the request with client side identifiers and expiration date. It may also include client session ID or some further information.
To prevent forging, token is cryptographically signed by using HMAC or other cryptographic technique.&lt;/p&gt;
&lt;p&gt;Resource servers are able to perform verification of the provided token and deny access to the resources if verification fails.&lt;/p&gt;
&lt;h2 id=&#34;us-2021064685a1&#34;&gt;US 2021/064685A1&lt;/h2&gt;
&lt;p&gt;As discussed before, PerimeterX Security Module perform client side activity monitoring to keep watching for anomalous behaviour
once access has been granted. This patent application is about implementing API call monitoring in frontend JavaScript
environment. PerimeterX code overwrites JS functions that it wants to monitor with wrapper code that takes note of API call
and calls the original function. In case of async actions, callbacks are overwritten in the same way. This lets identification
of the exact JS script that called a given JS function.  The patent application provides a following example code for
monitoring calls to &lt;code&gt;sync()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initiatorScript&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getCurentScript&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (document.&lt;span style=&#34;color:#a6e22e&#34;&gt;curentScript&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;curentScript&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// if there no script that is curently being processed - 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// then document.curentScript is null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initiatorScript&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// hold a reference to the original sync API function 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;realSyncAPI&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; window[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;]; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;window[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// use &amp;#39;document.curentScript&amp;#39; to record which script 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// initiated the call to sync() 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;capture&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;getCurentScript&lt;/span&gt;()); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// now cal the original sync API to perform its natural
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// behavior in the browser as it was being overwriten 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;realSyncAPI&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If extra JS is running in the browser (because of e.g. XSS attack or automation), PerimeterX is able to detect it through this
kind of monitoring.&lt;/p&gt;
&lt;p&gt;To be continued&amp;hellip;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to download embedded videos</title>
      <link>https://www.trickster.dev/post/how-to-download-embedded-videos/</link>
      <pubDate>Thu, 11 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.trickster.dev/post/how-to-download-embedded-videos/</guid>
      <description>&lt;p&gt;When wandering across the World Wide Web, many netizens have come across pages containing Youtube or Vimeo videos embedded in them. &lt;a href=&#34;https://youtube-dl.org/&#34;&gt;youtube-dl&lt;/a&gt; is a prominent tool to download online videos from many sources (not limited by Youtube - see the &lt;a href=&#34;https://ytdl-org.github.io/youtube-dl/supportedsites.html&#34;&gt;complete list of supported sites&lt;/a&gt;, but can it download the videos even if they are embedded in some third party website?&lt;/p&gt;
&lt;p&gt;Turns out, it can (with a little bit of help from the user). Let me show how.&lt;/p&gt;
&lt;p&gt;As our first example, let us explore the &lt;a href=&#34;https://funnelscripts.com/funnelscripts-webclass&#34;&gt;Funnelscripts landing page&lt;/a&gt;. No matter when you open the page, you will see the time ticking down the last few minutes to the presentation. Global market never sleeps. The same applies to Russel Brunson and his elderly friend. No matter the date and time, they are ready to give you a webinar on their SaaS product.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-11-11_17.31.39.png&#34;&gt;First image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or do they really? Let&amp;rsquo;s register for the webinar and try to see beyond the facade. Few minutes later, we are able to see what appears to be the stream of the webinar.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-11-11_17.43.12.png&#34;&gt;Second image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, opening the Chrome DevTools and exploring DOM a little quickly reveals that iframe with Vimeo player is embedded and playing a prerecorded video.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-11-11_17.43.44.png&#34;&gt;Third image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feeding the URL from &lt;code&gt;src&lt;/code&gt; attribute into youtube-dl lets us download the entire video:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ youtube-dl &amp;#34;https://player.vimeo.com/video/401113675?muted=1&amp;amp;autoplay=1&amp;amp;&amp;amp;title=0&amp;amp;byline=0&amp;amp;wmode=transparent&amp;amp;autopause=0&amp;#34;
[vimeo] 401113675: Downloading webpage
[vimeo] 401113675: Downloading akfire_interconnect_quic m3u8 information
[vimeo] 401113675: Downloading akfire_interconnect_quic m3u8 information
[vimeo] 401113675: Downloading fastly_skyfire m3u8 information
[vimeo] 401113675: Downloading fastly_skyfire m3u8 information
[vimeo] 401113675: Downloading akfire_interconnect_quic MPD information
[vimeo] 401113675: Downloading akfire_interconnect_quic MPD information
[vimeo] 401113675: Downloading fastly_skyfire MPD information
[vimeo] 401113675: Downloading fastly_skyfire MPD information
[hlsnative] Downloading m3u8 manifest
[hlsnative] Total fragments: 1275
[download] Destination: WEBINAR - NO ENCORE-401113675.fhls-fastly_skyfire_sep-2762.mp4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, you may want to download multiple embedded videos. Our second example is &lt;a href=&#34;https://dropservicemafia.com/free-course/&#34;&gt;Drop Service Mafia course page&lt;/a&gt;.
There&amp;rsquo;s multiple Youtube videos we may want downloaded to have something to watch during an inter-continental flight that people will be taking when Bali finally re-opens.&lt;/p&gt;
&lt;p&gt;The first video is MP4 file embedded via &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag and can be downloaded with curl or wget.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-11-11_18.09.37.png&#34;&gt;Fourth image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Similarly to previous example, we have Youtube videos embedded into HTML code of the page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.trickster.dev/2021-11-11_18.15.46.png&#34;&gt;Fifth image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We could download these one by one, but let&amp;rsquo;s be smarter about this. Let&amp;rsquo;s write a small Python script that will scrape the Youtube URLs of embedded videos and then let youtube-dl download them.&lt;/p&gt;
&lt;p&gt;One thing to note here is that the iframe with Youtube URL at &lt;code&gt;src&lt;/code&gt; attribute is not present in the HTML code that get downloaded from the server and is created by client-side Javascript. For each video, we get the an element like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;div class=&amp;#34;elementor-element elementor-element-f28e248 elementor-aspect-ratio-169 elementor-widget elementor-widget-video&amp;#34; data-id=&amp;#34;f28e248&amp;#34; data-element_type=&amp;#34;widget&amp;#34; data-settings=&amp;#34;{&amp;amp;quot;youtube_url&amp;amp;quot;:&amp;amp;quot;https:\/\/www.youtube.com\/watch?v=WDBlE0XeGGw&amp;amp;quot;,&amp;amp;quot;video_type&amp;amp;quot;:&amp;amp;quot;youtube&amp;amp;quot;,&amp;amp;quot;controls&amp;amp;quot;:&amp;amp;quot;yes&amp;amp;quot;,&amp;amp;quot;aspect_ratio&amp;amp;quot;:&amp;amp;quot;169&amp;amp;quot;}&amp;#34; data-widget_type=&amp;#34;video.default&amp;#34;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that Youtube URL is present in the JSON string in one of the attribute values.&lt;/p&gt;
&lt;p&gt;The code to scrape Youtube URLs and save them into the file is the following.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; lxml &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://dropservicemafia.com/free-course/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tree &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; html&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromstring(resp&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;text)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    video_divs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tree&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;xpath(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;//div[@data-widget_type=&amp;#34;video.default&amp;#34;]&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;urls.txt&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; video_div &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; video_divs:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings_json &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; video_div&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data-settings&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings_dict &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(settings_json)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        youtube_url &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; settings_dict&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;youtube_url&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; youtube_url &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;write(youtube_url &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    out_f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this scripts gives urls.txt file containing video links that we can launch youtube-dl with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ youtube-dl --batch-file=urls.txt --ignore-errors
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We are launching youtube-dl with &lt;code&gt;--ignore-errors&lt;/code&gt; since we want the tool not to stop when it runs into some of the videos that were privated.&lt;/p&gt;
&lt;p&gt;Lately youtube-dl has been rather slow, but there are new projects emerging that are building on the same concept:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/blackjack4494/yt-dlc&#34;&gt;yt-dlc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/yt-dlp/yt-dlp&#34;&gt;yt-dlp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
