• If you enjoy the forum please consider supporting it by signing up for a NES Membership  The benefits pay for the membership many times over.

Target Sports locusts (myself included)

skynet

NES Member
Joined
Aug 17, 2020
Messages
358
Likes
594
Location
SE Mass
Feedback: 3 / 0 / 0
This was originally going to be a message that there was tons (99+ boxes) of 45acp at TSU but it was gone in 2 minutes.

The only reason I saw it at all was I was actively writing a program that ripped their web site for any products I was interested in and notify me what was available. I saw the 45 and some 40 S&W seconds after they came out. 2 minutes later, poof, all gone. Glad I got my order in. There must be LOTS of programmers who have done the same thing. It's like ticketmaster all over again.
 
This was originally going to be a message that there was tons (99+ boxes) of 45acp at TSU but it was gone in 2 minutes.

The only reason I saw it at all was I was actively writing a program that ripped their web site for any products I was interested in and notify me what was available. I saw the 45 and some 40 S&W seconds after they came out. 2 minutes later, poof, all gone. Glad I got my order in. There must be LOTS of programmers who have done the same thing. It's like ticketmaster all over again.
4hfe14.jpg
 
TS USA website is dog slow right now. Probably too many of those software gizmos pinging it every second to check on inventory...
 
This was originally going to be a message that there was tons (99+ boxes) of 45acp at TSU but it was gone in 2 minutes.

The only reason I saw it at all was I was actively writing a program that ripped their web site for any products I was interested in and notify me what was available. I saw the 45 and some 40 S&W seconds after they came out. 2 minutes later, poof, all gone. Glad I got my order in. There must be LOTS of programmers who have done the same thing. It's like ticketmaster all over again.

Someone on a reddit gun page mentioned writing a scraper tool to get personal updates. Did you use python? I have a good bit of experience with SW dev, but I have almost no web development experience. I started to look into making a scraper to stay updated on availability of a particular firearm from some online stores, but didn’t get far in the tutorial walk throughs.

Would you mind sharing whatever script you used. I’d like to use it as a starting point to modify.
 
Someone on a reddit gun page mentioned writing a scraper tool to get personal updates. Did you use python? I have a good bit of experience with SW dev, but I have almost no web development experience. I started to look into making a scraper to stay updated on availability of a particular firearm from some online stores, but didn’t get far in the tutorial walk throughs.

Would you mind sharing whatever script you used. I’d like to use it as a starting point to modify.

This algorithm works for the big reloading vendor whose name we dare not mention because it doesn't exist: fetch the target webpage as plain text, grep the first position of "Out of stock" and "In stock", and alert you if the latter occurs before the former. The reason you need to differentiate the two strings is because the bottom of the page lists related products and their stock status. But the purchasing bots will beat you every time while you're typing in your CC number.
 
Would you mind sharing whatever script you used. I’d like to use it as a starting point to modify.

I'm not really good at computers, but this is what my hacker friend "4chan" made for me. Provided without warranty or customer support.

Stuff is trickling in once or twice a week. Please don't be a dick and hoard stuff you don't need; it's important for me to conduct prudent inventory replenishment without competing with you clowns.

Code:
(ns fed-primers.core
  (:gen-class))

(use 'clojure.pprint)
(use 'clojure.string)

(def query-interval-seconds (* 5 60))

(def products
  [{:name "Federal small pistol"
    :url "https://www.site-that-does-not-ship-to-mass.com/product/federal-100-small-pistol-primers-1000/"
    :currently-in-stock false
    :last-time-in-stock nil}
   {:name "Federal small pistol (match)"
    :url "https://www.site-that-does-not-ship-to-mass.com/product/federal-gm100m-sm-pstl-match-1000/"
    :currently-in-stock false
    :last-time-in-stock nil}])

(defn in-stock? [url]
  (let [page (str (slurp url))
        first-in-stock-index (index-of page "In stock")
        first-out-of-stock-index (index-of page "Out of stock")]
    (cond (nil? first-in-stock-index) false
          (nil? first-out-of-stock-index) true
 
          (< first-in-stock-index first-out-of-stock-index) true
          (> first-in-stock-index first-out-of-stock-index) false)))

(defn update-products [products]
  "Returns a mutated copy of the products vector of maps,
   with most recent stock status and last date/time in-stock"
  (map (fn [p]
         (as-> p p
           (assoc p :currently-in-stock
                  (in-stock? (:url p)))
           (assoc p :last-time-in-stock
                  (if (:currently-in-stock p)
                         (str (java.time.LocalDateTime/now))
                         (:last-time-in-stock p)))))
       products))

(defn -main []
  (while true
    (let [products (update-products products)]
      (do
        (println "Query performed at: " (str (java.time.LocalDateTime/now)))
        (println "Next query in " query-interval-seconds " seconds")
        (print-table products)
        (Thread/sleep (* query-interval-seconds 1000))))))
 
Last edited:
For anyone interested, here it is in C#, the only extra library I used was HtmlAgilityPack. Presented without warranty...e.g. the second they change the web site, this will break.

C#:
using System;
using System.Collections.Generic;
using System.Net;

using HtmlAgilityPack;

namespace ConProductSearch
{
    /// <summary>
    /// Rip the web site, looking for available products.
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Program me = new Program();
            me.Run();
        }

        private void Run()
        {
            // Each product has a name (for your benefit) and a product code (to search by).  You can figure out the codes easily from each product's url
            // Change this list as you see fit
            DisplayProductAvailability("45 ACP", 70);
            DisplayProductAvailability("9 MM", 51);
            DisplayProductAvailability("40 S&W", 59);
            DisplayProductAvailability("308 Win", 101);
            DisplayProductAvailability("30-06", 105);
            DisplayProductAvailability("6.5 Creedmoor", 776);

            Console.WriteLine();
            Console.WriteLine("Hit enter to exit");
            Console.ReadLine();
        }

        private List<HtmlNode> GetAvailableProducts(int productId)
        {
            List<HtmlNode> availableProducts = new List<HtmlNode>();

            // This is an internal page used by the main product page that populates all the images, price and availability
            String url = "https://www.targetsportsusa.com/ISearch.aspx?PageNumber=0&PageSize=40&PageSort=pv.PricePerRound&ColorFilter=&SizeFilter=&PriceFilter=&TypeFilter=&ManufacturerFilter=&CategoryFilter=" + productId
                + "&GenreFilter=&DistributorFilter=&VectorFilter=&SectionFilter=&LibraryFilter=&stockFilter=false&Filter=%";

            // Load the product page and store all it's html in a dom model
            var html = new HtmlDocument();
            html.LoadHtml(new WebClient().DownloadString(url));

            HtmlNode root = html.DocumentNode;

            HtmlNode ul = root.ChildNodes.FindFirst("ul");

            HtmlNodeCollection products = ul.ChildNodes;

            foreach (HtmlNode productNode in products)
            {
                HtmlNodeCollection productParts = productNode.ChildNodes;

                foreach (HtmlNode productSubNode in productParts)
                {
                    if (productSubNode.Name.ToUpper() == "A")
                    {
                        if (productSubNode.ChildNodes.FindFirst("button").InnerText.ToUpper() == "ADD TO CART")
                        {
                            availableProducts.Add(productSubNode);
                            continue;
                        }
                    }
                }
            }

            return availableProducts;
        }

        private void DisplayProductAvailability(String productName,int productId)
        {            
            // See if is anything available
            List<HtmlNode> availableProducts = GetAvailableProducts(productId);

            if (availableProducts.Count > 0)
            {
                Console.WriteLine("--------------------------------------------------------------");
                Console.WriteLine(productName);
                Console.WriteLine();

                foreach (HtmlNode availableProduct in availableProducts)
                {
                    HtmlNodeCollection avParts = availableProduct.ChildNodes;

                    String name = avParts.FindFirst("h2").InnerText;
                    String productUrl = "";
                    String src = "";

                    HtmlAttributeCollection imageAttributes = avParts.FindFirst("span").ChildNodes.FindFirst("img").Attributes;
                    HtmlAttributeCollection anchorttributes = availableProduct.ParentNode.ChildNodes.FindFirst("a").Attributes;

                    foreach (HtmlAttribute attr in imageAttributes)
                    {
                        if (attr.Name.ToUpper() == "SRC")
                        {
                            src = attr.Value;
                            break;
                        }
                    }

                    foreach (HtmlAttribute attr in anchorttributes)
                    {
                        if (attr.Name.ToUpper() == "HREF")
                        {
                            productUrl = "https://www.targetsportsusa.com" + attr.Value;
                            break;
                        }
                    }

                    Console.WriteLine("   " + name);
                    Console.WriteLine("   " + productUrl);
                    Console.WriteLine("   " + src);
                    Console.WriteLine();
                }
            }
        }
    }
}
 
Last edited:
Saw some gold dot 9mm on there 3-5 days ago for $49.99 per box 50 rounds, 99+ available. I refreshed the page every 2-3 seconds and it decreased from 20+every refresh. 0 in about 2-3 minutes. I check both sites daily 5-7 times even if I don't want to buy. When you expect to see it you don't and when you don't expect to see it you do.
 
Someone on a reddit gun page mentioned writing a scraper tool to get personal updates. Did you use python? I have a good bit of experience with SW dev, but I have almost no web development experience. I started to look into making a scraper to stay updated on availability of a particular firearm from some online stores, but didn’t get far in the tutorial walk throughs.

Would you mind sharing whatever script you used. I’d like to use it as a starting point to modify.
Sorry, I am very old and use C#, I'm sure it would be even easier in Python, but not by much. The code is posted above. The tricky part is not the language, it's that TSU doesn't load the useful info on the first page, they make a secondary call using another web request that gets the part of the html with all the useful stuff, like images and availability. Then they swap out the old container's html with the new stuff. I only figured it out by using the browser's built in debugger and watching the load sequence. After you have that though, it is just slogging through html, which always looks screwy.
 

Attachments

  • Untitled.png
    Untitled.png
    89.5 KB · Views: 25
For anyone interested, here it is in C#, the only extra library I used was HtmlAgilityPack. Presented without warranty...e.g. the second they change the web site, this will break.

C#:
using System;
using System.Collections.Generic;
using System.Net;

using HtmlAgilityPack;

namespace ConProductSearch
{
    /// <summary>
    /// Rip the web site, looking for available products.
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Program me = new Program();
            me.Run();
        }

        private void Run()
        {
            // Each product has a name (for your benefit) and a product code (to search by).  You can figure out the codes easily from each product's url
            // Change this list as you see fit
            DisplayProductAvailability("45 ACP", 70);
            DisplayProductAvailability("9 MM", 51);
            DisplayProductAvailability("40 S&W", 59);
            DisplayProductAvailability("308 Win", 101);
            DisplayProductAvailability("6.5 Creedmoor", 776);
        }

        private List<HtmlNode> GetAvailableProducts(int productId)
        {
            List<HtmlNode> availableProducts = new List<HtmlNode>();

            // This is an internal page used by the main product page that populates all the images, price and availability
            String url = "https://www.targetsportsusa.com/ISearch.aspx?PageNumber=0&PageSize=40&PageSort=pv.PricePerRound&ColorFilter=&SizeFilter=&PriceFilter=&TypeFilter=&ManufacturerFilter=&CategoryFilter=" + productId
                + "&GenreFilter=&DistributorFilter=&VectorFilter=&SectionFilter=&LibraryFilter=&stockFilter=false&Filter=%";

            // Load the product page and store all it's html in a dom model
            var html = new HtmlDocument();
            html.LoadHtml(new WebClient().DownloadString(url));

            HtmlNode root = html.DocumentNode;

            HtmlNode ul = root.ChildNodes.FindFirst("ul");

            HtmlNodeCollection products = ul.ChildNodes;

            foreach (HtmlNode productNode in products)
            {
                HtmlNodeCollection productParts = productNode.ChildNodes;

                foreach (HtmlNode productSubNode in productParts)
                {
                    if (productSubNode.Name.ToUpper() == "A")
                    {
                        if (productSubNode.ChildNodes.FindFirst("button").InnerText.ToUpper() == "ADD TO CART")
                        {
                            availableProducts.Add(productSubNode);
                            continue;
                        }
                    }
                }
            }

            return availableProducts;
        }

        private void DisplayProductAvailability(String productName,int productId)
        {          
            // See if is anything available
            List<HtmlNode> availableProducts = GetAvailableProducts(productId);

            if (availableProducts.Count > 0)
            {
                Console.WriteLine();
                Console.WriteLine(productName);

                foreach (HtmlNode availableProduct in availableProducts)
                {
                    HtmlNodeCollection avParts = availableProduct.ChildNodes;

                    String name = avParts.FindFirst("h2").InnerText;
                    String productUrl = "";
                    String src = "";

                    HtmlAttributeCollection imageAttributes = avParts.FindFirst("span").ChildNodes.FindFirst("img").Attributes;
                    HtmlAttributeCollection anchorttributes = availableProduct.ParentNode.ParentNode.ChildNodes.FindFirst("a").Attributes;

                    foreach (HtmlAttribute attr in imageAttributes)
                    {
                        if (attr.Name.ToUpper() == "SRC")
                        {
                            src = attr.Value;
                            break;
                        }
                    }

                    foreach (HtmlAttribute attr in anchorttributes)
                    {
                        if (attr.Name.ToUpper() == "HREF")
                        {
                            productUrl = "https://www.targetsportsusa.com" + attr.Value;
                            break;
                        }
                    }

                    Console.WriteLine("   " + name);
                    Console.WriteLine("   " + productUrl);
                    Console.WriteLine("   " + src);
                }
            }
        }
    }
}
Guys, I'm dead serious! Don't mess with this stuff.... This is how Skynet begins!!


View: https://www.youtube.com/watch?v=_Wlsd9mljiU
 
Saw some gold dot 9mm on there 3-5 days ago for $49.99 per box 50 rounds, 99+ available. I refreshed the page every 2-3 seconds and it decreased from 20+every refresh. 0 in about 2-3 minutes. I check both sites daily 5-7 times even if I don't want to buy. When you expect to see it you don't and when you don't expect to see it you do.
Yep, I started out writing a program that would email me when ammo was available. I now realize that is not going to work because by the time I was notified, it would be gone.
 
Clearly it is screen scrapers which makes the TS Email today about being ready for text alert when it come laughable. Blazer Brass 115gr 9MM came and went today at 1:20PM in under 2 minutes at $29.99 a box. I'm sure it was gone before any text alerts were delivered.
 
Sorry, I am very old and use C#, I'm sure it would be even easier in Python, but not by much. ...

Thanks for the info. Also, heh, after basic the first language I learned was FORTRAN. Though, most of my current work is C++ and Java.
 
So you programmers are the reason things were selling out in under 5 minutes. Let’s hope Soros doesn’t get his soy army’s nerd division to do the same and dry us out.
 
To clarify, I'm not necessarily accusing you guys of being greedy, just more resourceful than me. And I hope the opposing side in our civil unrest doesn't realize the same opportunity.

Since I don't speak l33t, I was just paying a neighbor kid to camp out on TSUSA during his fall break and offering him a 10% finder's fee. But now he's been told to stand down.
 
I got 2 text message alerts today that some 9mm came back in stock. first one was sold out in 6 minutes and the second one was sold out in 3 minutes. priced at $0.55 cents per round. Insanity...
 
GREED at it's worst. Discusting. And bragging yet.

Lol, so you're pissed because some guy figured out how to (effectively) stand in line ahead of you?

*roiling fist* NO DATS CHEATING!!!! etc. [rofl]

You'd be better off yelling at TS for not limiting purchases or doing a lottery or some other scheme, etc, to more evenly distribute whatever little ammo they do get.

Also as much as I like TS, for those in the know, due to the ammo crisis they're way beyond the pale now as far as pricing goes. Generally speaking you would do better, on average, to shop local on the ground in New England. TS is easily 10-20CPR or more on a lot of stuff -OVER- local prices. The problem with local is availability, but if you made a circuit you could probably scrape up a few boxes here and there.
 
Back
Top Bottom