Show HN: Flyscrape – A standalone and scriptable web scraper in Go
github.com
github.com
What are some examples of sites that are not "most real world sites".
Is HN a "real world site".
What percentage of sites submitted to HN are "most real world sites". (IME, it's a minority fraction.)
Why not just delete or change the user-agent line in scrape.go before compiling.
(Personal experience: I have been successfully retrieving information from the www for decades without including a user-agent header. The number of sites I have found that require this header is relatively small. It does not rise to the level of "most".)
HN replies usually fail to include even a single example.
Similarly, do examples provided for scraper programs and libraries ever include "most real world sites".
Anyway, I’m not interested in a pedantic debate. Most (but not all) people who have attempted to scrape any popular site, or unpopular site behind Cloudflare at above-minimum protection level, in recent years should know exactly what I’m taking about.
Edit: In case you’re not aware, many of the sites I have in mind are/were capable of defeating puppeteer-extra-plugin-stealth. User agent is only the most basic thing, like level 1 in a hundred-level dungeon.
OP is making a very well known day-one point about this topic. It is somewhat surprising that the library doesn't offer a way to dynamically set it.
Edit: and this isn't even getting into stuff like Tls fingerprinting, header order, etc
Real world example, I was looking at scraping unjobs.org for a friend the other day. The need for JS rendering turned the job from 15 minutes of requests and beautifulsoup into a full-blown session with selenium, geckodriver etc.
I'm not saying the linked framework isn't nice, I've not looked at it, but tools for simple scraping are plenty and easy to use.
There's a lot more that a new framework needs to do to distinguish itself. I'd love something that makes JS rendering, proxy-rotation and catchpa solving easier, in a nice package I can deploy myself.
Below is a simple demonstration using only common UNIX utilties and minimising the number of TCP conections. No browser. No Javascript. No Selenium. No Geckodriver. No proxies.
Step 1 requires 40 TCP connections and completes in under a minute. Step 2 requires one TCP connection and completes in 10min. (NB. The connection minimisation used here, what RFCs used to call "web etiquette", is not possible using a popular headless graphical browser.) 1.htm is 1.5M, 2.htm is 40M
# step 1
x="some user-agent string"
# e.g., https://raw.githubusercontent.com/51Degrees/Device-Detection/master/data/20000%20User%20Agents.csv
n=1;while true;do
test $n -le 40||break # unjobs.org only shows listings 1-1000
case $n in 9|17|25|33)sleep 10;esac # need a delay after every 8 requests
echo "GET /new/$n HTTP/1.0@Host: unjobs.org@User-Agent: $x@" \
|tr @@ '\r\n' \
|openssl s_client -connect unjobs.org:443 -ign_eof -servername unjobs.org
n=$((n+1));
done > 1.htm
# step 2
n=1000;
grep -Eo -m$n /vacancies/1[0-9]\{12\} 1.htm \
|tail -$n \
|sed "\$!s>.*>GET & HTTP/1.1@Host: $x@Connection: keep-alive@>;
\$s>.*>GET & HTTP/1.1@Host: $x@Connection: close@>" \
|tr @@ '\r\n' \
|openssl s_client -connect unjobs.org:443 -ign_eof > 2.htm
If provided with an example of what the formatted output should loook like, I will demonstrate how to do it quickly and easily, without Python. Quite sure the text processing methods I use to extract data from HTML are faster than Python.IME, Wordpress sites do not require a user agent header. Contrast, for example, with Squarespace sites which do require a user agent header.
IME, if send a user agent header with a particular value, then some sites will block, depending on the value. Whereas if _do not send_ a user agent header, then almost all sites will accept.
The so-called "developers" who publish code and commentary about "scraping" almost always include a user agent header. Usually they use fake values. They try to guess the "correct" values to send.
Im not referring to sending fake values. Im referring to not sending the header at all. No "spoofing" is involved. This works for me, for decades, across thousands of websites.
How many new TCP connections per second.
Is this a "scraper" or a "crawler".
It appears to accept a "starting URL" and to follow links.
Opening many TCP connections is arguably still a reason why website operators try to prevent crawling (except from Googlebot IPs). As for scraping, it can be done with a single TCP connection. Perhaps "developers" instead opt to use many TCP connections and then complain when they get blocked.
Last time I looked it was the most popular choice for scraping in Go and I have some projects using it.
Is it similar? Does it have more/less features or is it more suited for a different use case? (Which one?)
Flyscrape on the other hand is a ready-made CLI tool that aims to be easy to use even for someone who is a little familiar with JavaScript. It just happens to be written in Go, but that should not matter to the end user.
It does not have full feature parity with Colly but most use cases should be covered.
What is possible though, is to use a service like ScrapingBee (not affiliated) and set it as the proxy. This would render the page on their end, in a browser.
Either way, I think you create a separate method 'find_all()' that returns a list to make the API easier to reason about.
Right now I'm just wrapping my head around how this works, and didn't try it hands-on yet, but I struggle to evaluate from the existing documentation, how useful this actually is. All examples in the repository right now are ultimately one-page scrappers, which, honestly, would be quite useless to me. Pretty much every scraper I write has at least 2-3 logical layers. Like, consider your HN-example, but you want to include top-10 comments for each post. Is it even possible? Well, I guess for HN you could just get by using allowedURLs and treating default function as a parser for the comment-page, but this isn't generic enough. Consider some internet shop. That would be (1) product category tree, sometimes much easier to hard-code, rather than scrape it every time; hard-coding often is generative (e.g. example.com/X/A-B-C, where X is a string from the list, A, B and C are padded numbers, each with a different range) (2) you go into each category, retrieve either a sub-category list (possibly, js-rendered, multiple pages) or product list (same applies) (3) open each product url, do the actual parsing (name, price, specification, etc). Each of json-object from (3) often has to include some minimal parsed data from level (2) (like category name)
More advanced, but also way to popular to imagine a generic web-scraper without it: in addition to some json-metadata you download pictures, or pdf-files, etc. (Sometimes you don't even need metadata.) Maybe just text files, but the result is several GBs, and isn't suitable to be handled as a single json-object, but rather a file/directory tree.
Is any of this possible with this tool?
Also, regardless of being it useful for my cases, some minor comments:
1. Links in docs/readme.md#configuration don't work (but the .md files for them actually exist).
2. I would suggest making "url" in the configuration either a list, or string|list. I suppose, that pretty much doesn't change the logic, but would make a lot of basic use-cases much easier to implement.
[1]: https://github.com/philippta/flyscrape/blob/master/docs/read...
Its been 8+ years since i started scraping. I even wrote a popular Go web scraping framework previously: (https://github.com/geziyor/geziyor).
My favorite stack as of 2023: TypeScript+Playwright+Crawlee(Optional) If you're serious in scraping, you should learn javascript, thus, playwright should be good.
Note: There are niche cases where lower-level language would be required (C++, Go etc), but probably only <%5
await page.getByRole('button', { name: 'Sign in' }).click();
See playwright locators: https://playwright.dev/docs/locators'get button named "sign in" and click'
Then on the back end, it generates your example code.
I love the JS flow, but I thought crul was an interesting newer tool!!
But I agree, you gotta get in there and it’s easier with JS
its show hn: https://news.ycombinator.com/item?id=34970917
I mean, at this point I am not sure Go is the right tool for the job (I am actually pretty confident that it is not).
A pretty neat stack of engineering, sure! This is cool, niely done. But I can't help but feel disturbed.
<https://github.com/PuerkitoBio/goquery>
<https://github.com/dop251/goja>
(Please do not reply to this comment of mine—if you do, I won't be able to delete it once the previous post is fixed, because the existence of the replies will prevent that.)
As a workaround one could use a service like ScrapingBee (not affiliated) as a proxy, that renders the page in a browser for you.
Surely, relying on a service for this is not always ideal. I am also working on a small wrapper that turns Chrome into an HTTPS proxy, which you could plug right into flyscrape. Unfortunately it is very experimental still and not public yet. I have not yet decided if I release it as part of flyscrape or as a separate project.
The trade-offs usually fall into:
- authing to the endpoint can sometimes be weird
- it for sure makes the traffic stand out since it isn't otherwise surrounded by those extraneous requests
- it, as with all good things scraping, carries its own maintenance and monitoring burden
However, similar to those tradeoffs, it's also been my experience that a full page load offers a ton more tracking opportunities that are not present in a direct endpoint fetch. I mean, look how many "stealth" plugins out there designed to mask the fact that a headless browser is headless
But, having said all of that: without question the biggest risk to modern day scraping is Cloudflare and Akamai gatekeeping. I do appreciate the arguments of "but ddos!11" and yet I would rather only actors that are actually exhibiting bad behavior[1] be blocked instead of everyone trying with a copy of python who have set reasonable rate limits
1 = this setting aside that "bad behavior" can be defined as "downloading data that the site makes freely available to Chrome but not freely available to python"