<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ali</title>
    <description>The latest articles on DEV Community by Ali (@thetanweerali).</description>
    <link>https://dev.to/thetanweerali</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1167337%2F2c46b44d-b1c5-4197-83b4-014f604fd20d.PNG</url>
      <title>DEV Community: Ali</title>
      <link>https://dev.to/thetanweerali</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thetanweerali"/>
    <language>en</language>
    <item>
      <title>Bypass Bot Detection with Python Selenium. 🤖</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Thu, 21 Aug 2025 07:29:13 +0000</pubDate>
      <link>https://dev.to/thetanweerali/bypass-bot-detection-with-python-selenium-3p44</link>
      <guid>https://dev.to/thetanweerali/bypass-bot-detection-with-python-selenium-3p44</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec" class="crayons-story__hidden-navigation-link"&gt;Bypassing Bot Detection Software with Selenium in Python&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/thetanweerali" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1167337%2F2c46b44d-b1c5-4197-83b4-014f604fd20d.PNG" alt="thetanweerali profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/thetanweerali" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Ali
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Ali
                
              
              &lt;div id="story-author-preview-content-2076074" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/thetanweerali" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1167337%2F2c46b44d-b1c5-4197-83b4-014f604fd20d.PNG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Ali&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 4 '24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec" id="article-link-2076074"&gt;
          Bypassing Bot Detection Software with Selenium in Python
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/selenium"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;selenium&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;7&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              2&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>selenium</category>
      <category>python</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>ADA Title II Adopting The WCAG 2.1 AA Standard</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Thu, 14 Aug 2025 07:40:58 +0000</pubDate>
      <link>https://dev.to/thetanweerali/ada-title-ii-adopting-the-wcag-21-aa-standard-pjj</link>
      <guid>https://dev.to/thetanweerali/ada-title-ii-adopting-the-wcag-21-aa-standard-pjj</guid>
      <description>&lt;p&gt;The DOJ has updated Title II of the ADA(Americans with Disabilities Act) affecting local and state government institutions, including public universities on April 24 2024.&lt;/p&gt;

&lt;p&gt;Which basically enforces the adoption of WCAG 2.1 AA as the standard for web accessibility.&lt;/p&gt;

&lt;p&gt;Which essentially means that Web content and mobile applications provided by local and state government will have to implement extra accessibility measures.&lt;/p&gt;

&lt;p&gt;These measures aims at making content more accessible to disabled users, including those with mild disabilities such a low vision and photosensitivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key features of WCAG 2.1
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Screen Reader Compatibility
&lt;/h3&gt;

&lt;p&gt;Requiring Web content to be readable by screen readers, which means to respect page structure for navigation, context and appropriate headings (&lt;code&gt;H1&lt;/code&gt;,&lt;code&gt;H2&lt;/code&gt;,&lt;code&gt;H3&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Clear and descriptive link texts and buttons, so instead of having a link say "Click here", it's purpose will have to be conveyed clearly and explicitly such as "Learn more about WCAG 2.1".&lt;/p&gt;

&lt;p&gt;Buttons on the other hand will be required to have an &lt;code&gt;aria-label&lt;/code&gt; if the button text isn't accessible or doesn't convey a clear purpose. That way screen readers will be able to announce it clearly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyboard Navigation
&lt;/h3&gt;

&lt;p&gt;Web content will have to be keyboard friendly, meaning you can navigate the content with a keyboard instead of mouse. Ensuring that all interactive elements can be selected with the Tab key or other keyboard shortcuts.&lt;/p&gt;

&lt;p&gt;Which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;li&gt;Inputs&lt;/li&gt;
&lt;li&gt;Dropdown menus&lt;/li&gt;
&lt;li&gt;Modal dialogs&lt;/li&gt;
&lt;li&gt;Custom Widgets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which will require websites to use HTML elements that can be navigated and selected via a keyboard. Such as &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, and avoid using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; elements for interactive components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image Alternative Text
&lt;/h3&gt;

&lt;p&gt;This will require every image to have an alternative text, with some exceptions for images that are purely decorative.&lt;/p&gt;

&lt;p&gt;This can be added by using the &lt;code&gt;alt&lt;/code&gt; attribute to an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Comply ?
&lt;/h2&gt;

&lt;p&gt;Public entities that serve a population of more than 50,000 must comply by the deadline which is April 24 2026.&lt;/p&gt;

&lt;p&gt;Public university are considered as serving the whole population of their current state, which makes all university in all 50 states obligated to comply with the new WCAG 2.1 accessibility standard.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>ada</category>
      <category>wcag</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Send Emails in Python Using Gmail’s Free SMTP Mail Server API</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Mon, 11 Nov 2024 14:22:56 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-to-send-emails-in-python-using-gmails-free-smtp-mail-server-api-26fk</link>
      <guid>https://dev.to/thetanweerali/how-to-send-emails-in-python-using-gmails-free-smtp-mail-server-api-26fk</guid>
      <description>&lt;p&gt;This is the easiest way you can start sending emails with Python using only two libraries, &lt;a href="https://docs.python.org/3/library/smtplib.html" rel="noopener noreferrer"&gt;&lt;code&gt;smtplib&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.python.org/3/library/email.examples.html" rel="noopener noreferrer"&gt;&lt;code&gt;email&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will be using &lt;a href="https://developers.google.com/gmail/api/guides" rel="noopener noreferrer"&gt;Gmail’s free RESTful API&lt;/a&gt; in this example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's the code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

message = MIMEMultipart()
message["To"] = 'To line here.'
message["From"] = 'From line here.'
message["Subject"] = 'Subject line here.'

title = '&amp;lt;b&amp;gt; Title line here. &amp;lt;/b&amp;gt;'
messageText = MIMEText('''Message body goes here.''','html')
message.attach(messageText)

email = 'Your Gmail address here.'
password = 'Your app password here.'

server = smtplib.SMTP('smtp.gmail.com:587')
server.ehlo('Gmail')
server.starttls()
server.login(email,password)
fromaddr = 'From line here.'
toaddrs  = 'Address you send to.'
server.sendmail(fromaddr,toaddrs,message.as_string())

server.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code works ?
&lt;/h2&gt;

&lt;p&gt;First, let’s import &lt;code&gt;smtplib&lt;/code&gt; then &lt;code&gt;MIMEMultipart&lt;/code&gt; and &lt;code&gt;MIMEText&lt;/code&gt; from &lt;code&gt;email.mime.multipart&lt;/code&gt; and &lt;code&gt;email.mime.text&lt;/code&gt; respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then call &lt;code&gt;MIMEMultiPart()&lt;/code&gt; and instantiate it to the variable &lt;code&gt;Message&lt;/code&gt;. We then give a string value to each variable which are &lt;code&gt;Message&lt;/code&gt;, &lt;code&gt;To&lt;/code&gt;, &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;Subject&lt;/code&gt;, it should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message = MIMEMultipart()
Message["To"] = 'To line here.'
Message["From"] = 'From line here.'
Message["Subject"] = 'Subject line here.'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also give a title to our email through the &lt;code&gt;title&lt;/code&gt; variable and add a message through &lt;code&gt;MIMEText()&lt;/code&gt; and instantiate it to the variable &lt;code&gt;messageText&lt;/code&gt; and finally attach our email message to our main variable &lt;code&gt;Message&lt;/code&gt; using the &lt;code&gt;attach()&lt;/code&gt; function, it should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;title = '&amp;lt;b&amp;gt; Title line here. &amp;lt;/b&amp;gt;'
messageText = MIMEText('''Message body goes here.''','html')
Message.attach(messageText)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add our Gmail address and our App password, &lt;a href="https://support.google.com/accounts/answer/185833?hl=en" rel="noopener noreferrer"&gt;click the link here&lt;/a&gt; if you don’t know how to get one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email = 'Your Gmail address here.'
password = 'Your App password here.'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we'll have to connect to Gmail's SMTP server, we'll do that using the &lt;code&gt;smtplib&lt;/code&gt; library and we'll need two variables which are the server we want to connect to and the port which are &lt;code&gt;smtp.gmail.com&lt;/code&gt; and &lt;code&gt;587&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;smtplib&lt;/code&gt; library we'll be calling the &lt;code&gt;SMTP&lt;/code&gt; function and add the server and port variables as its arguments, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;smtplib.SMTP('smtp.gmail.com:587')&lt;/code&gt; (&lt;em&gt;don't forget the &lt;code&gt;:&lt;/code&gt; between them&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;We'll then instantiate it to the variable &lt;code&gt;server&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server = smtplib.SMTP('smtp.gmail.com:587')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then add an &lt;code&gt;ehlo&lt;/code&gt; statement using &lt;code&gt;server.ehlo(‘Gmail’)&lt;/code&gt;, this should be your domain name, this is useful when sending en email to another mail server that supports ESMTP, let’s keep it simple and just put &lt;code&gt;Gmail&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.ehlo('Gmail')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s also start the TLS protocol with &lt;code&gt;server.starttls()&lt;/code&gt;, this tells the mail server we want to send our email through a secure connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.starttls()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll then login to the mail sever using this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.login(email,password)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add a from address and to address(es), using &lt;code&gt;fromaddr&lt;/code&gt; and &lt;code&gt;toaddrs&lt;/code&gt; respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fromaddr = 'Your Gmail address.'
toaddrs  = 'Destination address.'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we send our email using &lt;code&gt;server.sendmail(fromaddr,toaddrs,message.as_string())&lt;/code&gt; and we close our connection to the mail server using &lt;code&gt;server.quit()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.sendmail(fromaddr,toaddrs,message.as_string())
server.quit()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save that in a file called &lt;code&gt;send_email.py&lt;/code&gt;, open a Terminal if you're on Linux or a Command Prompt if you're on Windows and launch it using &lt;code&gt;python send_email.py&lt;/code&gt; and you should see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyecbzzielxzmkfyxsmo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyecbzzielxzmkfyxsmo9.png" alt="Linux Terminal launching a Python script" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If nothing happens, well good news it's working!&lt;/p&gt;

&lt;p&gt;You should have received an email to your destination email(s), here's what I got:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foreo31g3n1u0t9i69zba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foreo31g3n1u0t9i69zba.png" alt="Email received to a Gmail account after launching the Python script" width="522" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sending email using Python and Gmail's free SMTP server is the easiest way you can start sending email within your Python code, this is exactly what I've done when I built my first online busines ever. &lt;a href="https://dev.to/thetanweerali/how-i-bootstrapped-my-saas-business-with-0-38dg"&gt;You can read more about it here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>gmail</category>
      <category>server</category>
    </item>
    <item>
      <title>How to Scrape Product Pages(Etsy, Amazon, Ebay) in Python Using Scrapy and Request Callbacks</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Tue, 05 Nov 2024 14:08:29 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-to-scrape-product-pagesetsy-amazon-ebay-in-python-using-scrapy-and-request-callbacks-13ad</link>
      <guid>https://dev.to/thetanweerali/how-to-scrape-product-pagesetsy-amazon-ebay-in-python-using-scrapy-and-request-callbacks-13ad</guid>
      <description>&lt;p&gt;(&lt;em&gt;I'm not condoning anything illegal, this is for educational purposes only&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Scrapy is one of the best web scraping frameworks in Python, it's easy to use, fast and packed with features.&lt;/p&gt;

&lt;p&gt;But what if you we wanted to scrape multiple pages recursively? Such as product pages.&lt;/p&gt;

&lt;p&gt;Well the easiest way is by adding a simple callback to a &lt;code&gt;Request&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Here’s a code snippet inside a Scrapy project crawling a website with listed products such as Amazon, eBay and Etsy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def parse(self, response):
    links = response.css('a.s-item__link::attr(href)').getall()

    for link in links:
        yield Request(url=link, callback=self.parse_item)

    next_page = response.css('a.pagination___next.icon-link::attr(href)').get()
    if next_page:
        print('Next page: %s' % next_page)
        yield Request(url=next_page, callback=self.parse)

def parse_item(self, response):
    title = response.xpath('//h1[@class="x-item-title___mainTitle"]/span/text()').get()
    price = response.xpath('//span[@id="prcIsum"]/text()').get()

    yield {'title':title,
           'price':price}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How it works?
&lt;/h2&gt;

&lt;p&gt;First, it gets the links of each items listed on a products page using this line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;links = response.css('a.s-item__link::attr(href)').getall()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It then loops through each one of those links, sends a request with the &lt;code&gt;yield&lt;/code&gt; statement and does a callback to our &lt;code&gt;parse_item&lt;/code&gt; function with &lt;code&gt;self.parse_item&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for link in links:
     yield Request(url=link,callback=self.parse_item)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;parse_item&lt;/code&gt; function, it gets the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;price&lt;/code&gt; of the item and returns them with the &lt;code&gt;yield&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def parse_item(self, response):
      title = response.xpath('//h1[@class="x-item-title__mainTitle"]/span/text()').get()
      price = response.xpath('//span[@id="prcIsum"]/text()').get()

      yield {'title':title,
             'price':price}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the &lt;code&gt;parse&lt;/code&gt; function is still running, our code then gets the link to the next page, requests it with a &lt;code&gt;yield&lt;/code&gt; statement and does a callback to our &lt;code&gt;parse&lt;/code&gt; function with &lt;code&gt;self.parse&lt;/code&gt; and starts all over again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next_page = response.css('a.pagination__next.icon-link::attr(href)').get()
  if next_page:
        print('Next page:%s' % next_page)
        yield Request(url=next_page,callback=self.parse)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There you go, it's that simple!&lt;/p&gt;

&lt;p&gt;Scraping product pages recursively with Scrapy can be implemented as easily as adding a callback to a &lt;code&gt;Request&lt;/code&gt; function.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>data</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Bypassing Bot Detection Software with Selenium in Python</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Mon, 04 Nov 2024 08:10:26 +0000</pubDate>
      <link>https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec</link>
      <guid>https://dev.to/thetanweerali/bypassing-bot-detection-software-with-selenium-in-python-1mec</guid>
      <description>&lt;p&gt;&lt;em&gt;(Use at your own risk, I'm not condoning anything illegal)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Websites such as Amazon uses bot detection software such as Imperva and FingerprintJS to block automated tools from interacting with their website. And they're rightly doing so to keep web scrapers and hackers away from stealing their precious data.&lt;/p&gt;

&lt;p&gt;If they only knew... &lt;/p&gt;

&lt;p&gt;Those security controls are fairly easy to bypass if you know what you're doing.&lt;/p&gt;

&lt;p&gt;There are a few ways we can bypass those security controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rotate proxies&lt;/li&gt;
&lt;li&gt;Using cookies&lt;/li&gt;
&lt;li&gt;Rotate User Agents and HTTP Headers&lt;/li&gt;
&lt;li&gt;Remote Javascript signatures in your webdriver(&lt;em&gt;we'll explore this one soon!&lt;/em&gt; 😎)&lt;/li&gt;
&lt;li&gt;Avoiding patterns when interacting with websites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using Selenium and you're getting blocked by automation software.&lt;/p&gt;

&lt;p&gt;There's a little trick we can implement that can save us the hassle of setting up rotating proxies, headers or user agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disable Selenium Automation Flag
&lt;/h2&gt;

&lt;p&gt;But here's another simpler way that might do the trick - disabling Selenium's automation flag.&lt;/p&gt;

&lt;p&gt;Selenium by default has automation flags enabled which might block you from accessing websites using anti bot software.&lt;/p&gt;

&lt;p&gt;To bypass the block you'll have to disable those automation flags in your Selenium code, let's see how we can do that using a simple one liner.&lt;/p&gt;

&lt;p&gt;First, let's call the &lt;code&gt;webdriver.Chromeoptions()&lt;/code&gt; function and instantiate it to a variable called &lt;code&gt;options&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This enable's us to add custom options to our webdriver:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;options = webdriver.ChromeOptions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s add the option disabling the automation flag by adding the following line to our Selenium code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;options.add_argument("--disable-blink-features=AutomationControlled")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This simple one-liner disables the automation flag and tells the website that you're not a bot.&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>python</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Easiest Way to Handle Drop Down Menus in Python Using Selenium ?</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Thu, 31 Oct 2024 05:58:47 +0000</pubDate>
      <link>https://dev.to/thetanweerali/easiest-way-to-handle-drop-down-menus-in-python-using-selenium--3hph</link>
      <guid>https://dev.to/thetanweerali/easiest-way-to-handle-drop-down-menus-in-python-using-selenium--3hph</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Selenium has a neat way to handle drown down menus by using the &lt;code&gt;Select&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;For this example, we will testing it out on:&lt;br&gt;
&lt;a href="https://app.endtest.io/guides/docs/how-to-test-dropdowns/" rel="noopener noreferrer"&gt;https://app.endtest.io/guides/docs/how-to-test-dropdowns/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5y7yfw0lvxjtdzuaytlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5y7yfw0lvxjtdzuaytlf.png" alt="Drop down test" width="269" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing the Select class
&lt;/h2&gt;

&lt;p&gt;First let’s import the &lt;code&gt;Select&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;from selenium.webdriver.support.select import Select&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the Drop Down Element
&lt;/h2&gt;

&lt;p&gt;Now let's call the drop down by using its &lt;code&gt;ID&lt;/code&gt;, which is &lt;code&gt;pets&lt;/code&gt; and name its instance &lt;code&gt;drop_down&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop_down = driver.find_element_by_id('pets')&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting the drop down
&lt;/h2&gt;

&lt;p&gt;Now we've got the drop down selected and will name its instance &lt;code&gt;drop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop = Select(drop_down)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are multiple ways we can select values in a drop down menu, either by index, value or visible text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting by Index
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;drop.select_by_index(2)&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting by Value
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;drop.select_by_value('cat')&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting by Visible Text
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;drop.select_by_visible_text("Dog")&lt;/code&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>selenium</category>
      <category>webdev</category>
      <category>automation</category>
    </item>
    <item>
      <title>How I Bootstrapped My SaaS Business With $0</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Wed, 30 Oct 2024 03:41:10 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-i-bootstrapped-my-saas-business-with-0-38dg</link>
      <guid>https://dev.to/thetanweerali/how-i-bootstrapped-my-saas-business-with-0-38dg</guid>
      <description>&lt;p&gt;Let’s go back in time...&lt;/p&gt;

&lt;p&gt;Date: November 21th, 2018&lt;/p&gt;

&lt;p&gt;Location: Montreal, Canada&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwrnj6hae09s8211vdfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwrnj6hae09s8211vdfy.png" alt="Screenshot of Montreal on Google Maps" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;I’m thinking about startup ideas, looking at my 13-inch Thinkpad screen with a medium sized coffee in a Tim Horton’s coffee shop at around 10:00 PM. It’s a cold snowy day, snow flakes are slowly falling to the ground, as I’m watching outside through a window facing the street, a beautiful sight. Being a night owl, I always loved working at night, there’s something about night time work that helps me focus. Probably because everyone is sleeping and there’s less distraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So here I am thinking about starting a business without spending a dime, I challenged myself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftngvh760sry6lr2k37le.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftngvh760sry6lr2k37le.jpg" alt="Me Holding a Tim Horton's cup of coffee" width="800" height="1071"&gt;&lt;/a&gt; Tim Horton’s coffee, the best (I'm joking)&lt;/p&gt;

&lt;p&gt;At that time, I knew enough of Python to scrape websites, from data extraction to cleaning and loading to a database, that’s the easy part. If something doesn’t work, Google it. Fixing issues in the business world is the hardest part. You can’t Google that and find a straigth forward solution. That’s what business is all about, fixing problems and getting paid for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I knew that presenting the right data to the right person would be profitable, that was my vantage point.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We all consume data, like when we look at weather forecasts before going outside, if it’s going to rain, we bring an umbrella. That’s actionable data, information that changes our behavior, it helps us act the right way at the right time. I was looking for a continuous source of data that was monetizable and actionable. I wasn’t interested in building a machine learning model, just a simple data reporting service. I believe there’s plenty of untapped opportunities in the AaaS(Analytics as a Service) domain, going straigth to automated models is a bit too far fetched for what I was looking for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keeping it simple, not everyone needs machine learning models and it doesn’t solve everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At that time, I was also looking for new ways to make money, and I found out about phone flipping. You’re basically buying used phones and reselling them for profit on sites like eBay. I knew a couple guys in the US that were doing some good money doing that. I joined a couple phone reselling groups on Facebook, and I noticed that many were struggling when estimating phone resale prices. One way that some did it was by going on eBay, type the phone model and estimate the price from the first couple listings. That solved the problem, but still tiresome if you got a lot of phones to sell.&lt;/p&gt;

&lt;p&gt;So I thought to myself, why not automate that ?&lt;/p&gt;

&lt;p&gt;That’s what I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding it
&lt;/h2&gt;

&lt;p&gt;Bots running on my laptop turned Linux server… At a coffee shop. lol&lt;br&gt;
Everything was done in Python using Scrapy and running automatically on my laptop with Debian Linux, Thanks to crontab. Sleepless nights and a visit to the hospital due to a panic attack right in the middle of the night. Sacrifices, definitely not a walk in the park, man.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0qwqwvpbzbactn5bhlo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0qwqwvpbzbactn5bhlo.png" alt="Terminal screen on a Thinkpad X230 laptop" width="756" height="1030"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At one point, I was thinking about giving up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can’t do that, I’ve put in so many hours in that project&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I told myself.&lt;/p&gt;

&lt;p&gt;The hardest part by far, was trying to get quality data. Separating each phones (i.e cleaning and separating the iPhone X 64G from iPhone XS 256G), that took me weeks to get it right. I ended up many times with false positives/negatives.&lt;/p&gt;

&lt;p&gt;One of the techniques I used to remove bad data, was to look at the title used for the listing. Listings with the keyword “fake”, “not working”, “bad esn” or “iCloud” were separated from the rest.&lt;/p&gt;

&lt;p&gt;The naming of my business was the least of my issues at that point, but I ended going with &lt;strong&gt;Phone Flipping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54xi1kpzj4flt95t7568.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54xi1kpzj4flt95t7568.jpg" alt="Phone Flipping Ad of my own hand holding an iPod" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MVP Release
&lt;/h2&gt;

&lt;p&gt;Finally, after months of hard work, I’ve finally got something out and running without issues. I had around 70 bots pulling data from listing websites, then transforming, cleaning and storing it into a database. A fully automated data pipeline.&lt;/p&gt;

&lt;p&gt;My bots were pulling data everyday at 8:00 AM from those listing websites. I was rotating my proxies and user-agents for each request. I also added random intervals between requests, to avoid getting detected by anti-bot systems.&lt;/p&gt;

&lt;p&gt;I was sending the pricing reports every Friday morning at 8:00 AM by email.&lt;/p&gt;

&lt;p&gt;I was using &lt;a href="https://developers.google.com/gmail/imap/imap-smtp" rel="noopener noreferrer"&gt;Gmail’s free SMTP server&lt;/a&gt;, and some python code to automate the whole thing.&lt;/p&gt;

&lt;p&gt;That was my mail server! I really wanted to spend 0$.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgv632gshas2qxbk59a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgv632gshas2qxbk59a.png" alt="Screenshot free Gmail account" width="576" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the marketing side, I did everything organically, no paid ads. All I did is post content in Facebook Groups and have an opt-in form on a website to get emails. Why emails ? Because they’re super easy to get, and it’s one of the best marketing channel in terms of ROI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You want free eBay phone pricing updates ?&lt;/p&gt;

&lt;p&gt;We need an email&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As simple as that.&lt;/p&gt;

&lt;p&gt;There’s one YouTuber that helped me a lot, &lt;a href="https://www.youtube.com/@KishVlogs" rel="noopener noreferrer"&gt;Kish Israni&lt;/a&gt;. He let me post my charts every week in his Facebook Group. A lot of my users were from his following. I leveraged his distribution channel. So shoutout to him !&lt;/p&gt;

&lt;p&gt;If you have no idea what’s a distribution channel, it just means a way to distribute your content to a wider audience.&lt;/p&gt;

&lt;p&gt;In those Facebook groups, I was helping phone resellers with price estimation and I was answering their questions. Having a close relationship with potential customers is crucial for growth when you’re bootstrapped. Showing authenticity, empathy and understanding is key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I focused on giving value first, I was not trying to sell anything!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;2–3 days later, after sharing free pricing sheets on multiple phone reselling Groups on Facebook.&lt;/p&gt;

&lt;p&gt;I got dozens of subscribers messaging me. My inbox was full of interested users wondering what the heck I was doing and where I got the data. In a couple months, it grew to hundreds of users on the free and paid plan. I was charging between 20$–40$ per month.&lt;/p&gt;

&lt;p&gt;I still remember the first person wanting to join the paid plan, a store manager. He payed me 40$ a month. A surreal moment for me, seeing that 40$ in my PayPal account.&lt;/p&gt;

&lt;p&gt;Here’s the email I sent inviting free user to join the Pro plan:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydtk8imz5aelbz62y3df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydtk8imz5aelbz62y3df.png" alt="Phone Flipping email" width="455" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I played a lot with the pricing to get it right. Should I go with a trial or not?, should I have a single plan or not? A lot of testing, trials and errors.&lt;/p&gt;

&lt;p&gt;Before monetizing, I sent out a Google survey to get an idea of how many were interested in a paid plan. Around 20% were interested in giving money to me.&lt;/p&gt;

&lt;p&gt;It was weird, because I felt like an imposter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do they really like my service ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I kept thinking.&lt;/p&gt;

&lt;p&gt;I couldn’t believe it, people were really giving me money for a PDF file I was sending every Friday morning.&lt;/p&gt;

&lt;p&gt;Everything was automated, the only thing that needed some maintenance was the crawling bots that were pulling data from eBay listings. Because as soon eBay was updating their DOM structure, 90% of the time my bots would crash and require me to extract the data again using either CSS or XPATH. That was a real pain in the ass, at one point, I was staying up the whole night debugging my bots and going to sleep when the sun was rising at 5 AM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hcs38npb55i1qpci3qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hcs38npb55i1qpci3qi.png" alt="Phone Flipping pricing sheet" width="753" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjeomkqjrbfpajthj1wgm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjeomkqjrbfpajthj1wgm.png" alt="Phone Flipping Probability Density Function chart" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft63kk9bkqzzzyegve214.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft63kk9bkqzzzyegve214.jpg" alt="Client testimonial" width="515" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21ikcfnxr5t6cwcttt1q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21ikcfnxr5t6cwcttt1q.jpg" alt="Client testimonial" width="545" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbh7gl2gyyapv0x8wfde.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbh7gl2gyyapv0x8wfde.jpg" alt="Client testimonial" width="496" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how it worked:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull pricing data from eBay using Scrapy&lt;/li&gt;
&lt;li&gt;Store data in text files&lt;/li&gt;
&lt;li&gt;Clean data and analyze&lt;/li&gt;
&lt;li&gt;Plot data in graphs inside Jupyter Notebook&lt;/li&gt;
&lt;li&gt;Generate PDF reports from Jupyter Notebook&lt;/li&gt;
&lt;li&gt;Send PDF reports using Gmail's SMTP Mail server&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So this is how I launched my first online business, my first 1$ online. The things I’ve learned in the process of building my business is worth more than the money I’ve made. Especially in marketing and sales, I’ve learned a ton !&lt;/p&gt;

&lt;p&gt;I ended taking down Phone Flipping, because during the pandemic my paid users stopped paying. I was also dealing with some serious mental health issues. The pandemic had a huge impact on me, which led me to stop everything.&lt;/p&gt;

&lt;p&gt;It was unsustainable.&lt;/p&gt;

&lt;p&gt;Overall this is how it worked:&lt;/p&gt;

&lt;p&gt;There's a couple things I’ve learned in the journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bringing the right information to the right person is monetizable.&lt;/li&gt;
&lt;li&gt;People will pay you for automating small parts of their business.&lt;/li&gt;
&lt;li&gt;Keep it simple, find solutions to simple problems.&lt;/li&gt;
&lt;li&gt;Getting quality data is the hardest part, quality over quantity.&lt;/li&gt;
&lt;li&gt;Network, network, network&lt;/li&gt;
&lt;li&gt;Have a close relationship with your clients and have a feedback loop.&lt;/li&gt;
&lt;li&gt;Just do it ! Like Nike.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>saas</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How To Find Zombie Hosts With Nmap?</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Tue, 29 Oct 2024 15:52:59 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-to-find-zombie-hosts-with-nmap-1h1a</link>
      <guid>https://dev.to/thetanweerali/how-to-find-zombie-hosts-with-nmap-1h1a</guid>
      <description>&lt;p&gt;For educational purposes only&lt;/p&gt;

&lt;p&gt;If you are familiar with Nmap, you probably already know that scanning networks can easily be detected by firewals and IDS (Intrusion Detection Systems). Which can blow your cover and get your IP or proxy blacklisted.&lt;/p&gt;

&lt;p&gt;There are multiple ways to avoid that to happen. Here we’ll explore a method called idle scan, also known as a ‘zombie scan’. Which is an advanced port scanning method used to avoid detection by having a ‘zombie’ between you and the machine you’re scanning.&lt;/p&gt;

&lt;p&gt;This is a blind scanning method used to scan for open ports on the target machine without ever sending a single packet from the attackers IP address.&lt;/p&gt;

&lt;p&gt;The scanning works in five stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works ?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The attacker sends an unrequested SYN/ACK to the zombie machine. Which the zombie didn't expect, so it responds by sending a RST to the attacker and by doing so reveals its IP ID.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker sends a forged SYN to the target making it look like it comes from the zombie.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The target then sends a unrequested SYN/ACK to the zombie.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The zombie then responds with a RST which increments its IP ID. Indicating that the port is open, if the port is closed, the target sends a RST to the zombie and the IP ID will not be incremented.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker repeats step 1 and verifies if the IP ID has been incremented, if so, the port scanned is open, if not the port is closed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How do you find a zombie ?
&lt;/h2&gt;

&lt;p&gt;Nmap NSE (Nmap Scripting Engine) comes with a handy script called IPIDSEQ. Which we can use to scan for random zombies on the internet. Using the following script, Nmap will scan port 80 of 100 random hosts across the internet.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nmap -p 80 -script ipidseq -iR 100&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The hardest part in a zombie scan is to find a suitable machine to act as a zombie as sometimes you can end up with false positives. The candidate host shouldn’t be getting too much traffic because its IP ID won’t be accurate and predictable. Printers, Windows and old Linux servers work fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to launch a idle scan with Nmap ?
&lt;/h2&gt;

&lt;p&gt;Here is a bare bones zombie scan using Nmap:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nmap -sI &amp;lt;zombie host&amp;gt; -p &amp;lt;ports to scan&amp;gt; &amp;lt;target&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We’ve covered the infamous idle scan, incredibly useful for host discovery. It is one of the best ways to scan hosts without getting detected. But keep in mind that some modern IDS’s can detect when a idle scan is happening on their network. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be careful guys!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is for educational purposes only. Scanning network without explicit permission can land you in jail. I am not responsible for any damages incurred!&lt;/p&gt;

</description>
      <category>nmap</category>
      <category>pentest</category>
      <category>security</category>
      <category>network</category>
    </item>
    <item>
      <title>How to Encrypt a USB drive Using Cryptsetup with LUKS on Linux</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Tue, 29 Oct 2024 14:54:57 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-to-encrypt-a-usb-drive-using-cryptsetup-with-luks-on-linux-4blf</link>
      <guid>https://dev.to/thetanweerali/how-to-encrypt-a-usb-drive-using-cryptsetup-with-luks-on-linux-4blf</guid>
      <description>&lt;p&gt;Do you even encrypt ?&lt;/p&gt;

&lt;p&gt;Bitlocker is Windows default disk encryption software.&lt;/p&gt;

&lt;p&gt;Cryptsetup on the other is Linux go-to for full disk encryption. It comes preinstalled on the major Linux distributions out there. It supports multiple encryption formats, including Bitlocker’s.&lt;/p&gt;

&lt;p&gt;But our focus here will be on the LUKS(Linux Unified Key Setup) format, which is the standard in terms of Linux disk encryption.&lt;/p&gt;

&lt;p&gt;Cryptsetup offers plenty of options when encrypting drives.&lt;/p&gt;

&lt;p&gt;But before doing anything, let’s run a benchmark to test our computer’s encryption/decryption speed. This will tell us the best algorithm to use to encrypt our USB drive.&lt;/p&gt;

&lt;p&gt;Let’s run &lt;code&gt;cryptsetup benchmark&lt;/code&gt; in the terminal.&lt;/p&gt;

&lt;p&gt;Here's the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthuam80p3azefbwbzekv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthuam80p3azefbwbzekv.png" alt=" " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will ignore the first test and jump right to the second part. Here the &lt;code&gt;aes-xts&lt;/code&gt; algorithm has the fastest encryption and decryption speed overall for our machine. We’ll go with the last one in the list with a &lt;code&gt;Key&lt;/code&gt; size of &lt;code&gt;512-Bit&lt;/code&gt;. (it's highlighted in blue)&lt;/p&gt;

&lt;p&gt;Here are the settings I use when encrypting:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo luksFormat (usb drive path) -c aes-xts-plain64 — key-size 512 — hash sha512 — iter-time 50000&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Let’s explore each one of these parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;c&lt;/strong&gt;: Ciphering algorithm used for encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;key-size&lt;/strong&gt;: Key size used for encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hash&lt;/strong&gt;: Hashing method used on the passphrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iter-time&lt;/strong&gt;: Number of milliseconds to process the passphrase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that running that command will format and fully encrypt your drive so be careful and make sure to backup your files before doing anything and you’re good to go.&lt;/p&gt;

&lt;p&gt;Oh! I almost forgot, always use long pass-phrases to encrypt. This will ensure that your drive doesn’t get brute-forced easily.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>encryption</category>
      <category>ubuntu</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Built My First Online Business With Zero Dollars</title>
      <dc:creator>Ali</dc:creator>
      <pubDate>Sat, 23 Sep 2023 14:22:15 +0000</pubDate>
      <link>https://dev.to/thetanweerali/how-i-built-my-first-online-business-with-zero-dollars-40ab</link>
      <guid>https://dev.to/thetanweerali/how-i-built-my-first-online-business-with-zero-dollars-40ab</guid>
      <description>&lt;p&gt;Back in 2018, I was brainstorming ideas for a business.&lt;/p&gt;

&lt;p&gt;I already knew how to code in Python and knew the data stack pretty well.&lt;/p&gt;

&lt;p&gt;At that time I was playing around with the &lt;a href="https://www.openexoplanetcatalogue.com/" rel="noopener noreferrer"&gt;Open Exoplanet Catalog&lt;/a&gt; dataset trying to find habitable planets. (Yes, I really did lol)&lt;/p&gt;

&lt;p&gt;Then a few weeks passed and I found out about a side hustle called "Phone Flipping". Which consists of buying phones for a low price and reselling them at a higher price on sites like eBay.&lt;/p&gt;

&lt;p&gt;I then decided to dig more so I joined a couple phone resale groups on Facebook.&lt;/p&gt;

&lt;p&gt;And I noticed something, a recurring question from phone resellers.&lt;/p&gt;

&lt;p&gt;A lot of them were asking others for help when appraising and estimating phone prices.&lt;/p&gt;

&lt;p&gt;Asking questions such as: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hey! I just bought an iPhone, how much should I resell it for?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found what I was looking for.&lt;/p&gt;

&lt;p&gt;A problem, now I needed a solution.&lt;/p&gt;

&lt;p&gt;I knew that eBay was the main marketplace where iPhones were getting sold in the US.&lt;/p&gt;

&lt;p&gt;And since I was into data science and I knew web scraping, that's exactly what I did.&lt;/p&gt;

&lt;p&gt;I started pulling phone pricing data every single day of the week and running analysis on it inside a Jupyter Notebook on my Thinkpad X230.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhkzosz26704hdgldnur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhkzosz26704hdgldnur.png" alt="Photo of a Thinkpad laptop" width="709" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built an MVP in a couple weeks and released it to the world. Everything was built using Python and It was running on a local Debian Linux server in my bedroom.&lt;/p&gt;

&lt;p&gt;This is how it worked:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull pricing data from eBay using Scrapy&lt;/li&gt;
&lt;li&gt;Store data in text files&lt;/li&gt;
&lt;li&gt;Clean data and analyze&lt;/li&gt;
&lt;li&gt;Plot data in graphs inside Jupyter Notebook&lt;/li&gt;
&lt;li&gt;Generate PDF reports from Jupyter Notebook&lt;/li&gt;
&lt;li&gt;Send PDF reports using Gmail's SMTP Mail server &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had a free and paid monthly plan which I was charging $40 per month. &lt;/p&gt;

&lt;p&gt;My first client was a Sprint store manager, when I saw that $40 deposited on my Paypal account I was the happiest man alive.&lt;/p&gt;

&lt;p&gt;I then started sharing the pricing charts on multiple Facebook Groups every Friday morning.&lt;/p&gt;

&lt;p&gt;I ended up with hundreds of free and paid users.&lt;/p&gt;

&lt;p&gt;Then in 2020, the pandemic happened and I lost all my paying customers.&lt;/p&gt;

&lt;p&gt;I took down everything.&lt;/p&gt;

&lt;p&gt;I couldn't sustain it, it was too overwhelming for me.&lt;/p&gt;

&lt;p&gt;I lost my business but I gained a ton of experience.&lt;/p&gt;

&lt;p&gt;Full article here: &lt;a href="https://dev.to/thetanweerali/how-i-bootstrapped-my-saas-business-with-0-38dg"&gt;https://dev.to/thetanweerali/how-i-bootstrapped-my-saas-business-with-0-38dg&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
