<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="131205" data-post-id="131205">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jihantoro" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jihantoro/120/16385_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jihantoro
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Your scraper application are depends on you (the logic, structure and so along) and i just want to tell ways to do scraping</p>
<p>And choose that fit with your site target</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="131205" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-131205" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="131205"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="142788" data-post-id="142788">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adammokan" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adammokan/120/26407_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adammokan
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<blockquote>
<p>Web scraping is simple</p>
</blockquote>
<p>I’d say this is a very generalized statement that should be cleared up for future readers bumping into your post. Not being disrespectful, but saying something like that without some real meat could get a new person in trouble.</p>
<p>But trust me - If you find yourself scraping 5-10 million jobs a day, it quickly becomes “<em>not simple</em>”. The premise of crawling/scraping is not complex, but I can assure you that sustaining it for 7-8 years on end and returning data in a timely fashion to paying customers is not easy at all.</p>
<p>Some more tips from my view, having done this for so long (<em>without a single legal issue</em>):</p>
<ul>
<li>Try to form a personal relationship with an IP provider - yes you can use the publicly available providers you mention above and there are tons, but none of those will scale to the numbers I needed to hit in any reasonably economic way. Easier said than done, but ask around - exhaust friends in SEO and marketing fields.</li>
<li>Start slow. If you only have like 100 IPs to work with, don’t touch a target more than 1 time per hour with the same IP to start unless you plan to treat it like a “smash and grab”.</li>
<li>Link your IPs to user agents somehow. Meaning if you pull IP <span class="hashtag-raw">#1</span> and go off to hit a site and randomly grab a UA string to roll with it - make sure the next time you show that IP - you come with the same UA string.</li>
<li>Have some controls for measuring data quality over time. This may need to be manual in your case. You’d be amazed at how many big sites now start throwing you trash data that looks correct at a glance.</li>
<li>When you think you have a crawl script ironed out - be sure you toss it at a tool like the EFF Panopticlick (and others) to look for obvious fingerprinting you may have not plugged. <a href="https://panopticlick.eff.org/" rel="noopener nofollow ugc">https://panopticlick.eff.org/</a></li>
<li>Make sure, if using a headless browser you’re plugging all the massive, truck-sized holes they all expose… Things like mocking the <code>navigator.platform</code> (and it better match the UA string) are major gaps I see all the time.</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="142788" data-batch-url="/posts/batch_likers">
                        13
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-142788" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="142788"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-most-liked cat-most-liked" title="One of the top 3 liked posts in this thread!"></div>
  </section>
</div>
    <div class="postbit" id="142797" data-post-id="142797">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mythicalprogrammer" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mythicalprogrammer/120/10430_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mythicalprogrammer
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="_russellb" data-post="1" data-topic="4823" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/_russellb/48/5399_2.png" class="avatar"> _russellb:</div>
<blockquote>
<p>I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’t hold back. Thanks.</p>
</blockquote>
</aside>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://www.scrapy.org/">
  <header class="source">
      <img src="https://www.scrapy.org/favicon/scrapy-favicon.svg" class="site-icon" alt="" width="17" height="17">

      <a href="https://www.scrapy.org/" target="_blank" rel="noopener nofollow ugc">Scrapy</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:690/361;"><img src="https://scrapy.org/og_image/og_image.png" class="thumbnail" alt="" width="690" height="690"></div>

<h3><a href="https://www.scrapy.org/" target="_blank" rel="noopener nofollow ugc">Scrapy — open source web scraping framework for Python</a></h3>

  <p>Scrapy is the leading open source Python framework for web scraping — fast, asynchronous, extensible, and BSD-licensed. Trusted by millions of developers.</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<p>They also have splash which is their own html/javascript renderer.</p>
<p>I think it’s straight up professional and industrial.</p>
<p>I’ve seen floki and I’ve done beautifulsoup. Scrapy straight up is the tool you want if you want to webscrape website you can scrape like 90% or more of the websites out there with it.</p>
<p>I do webscrapping professionally for one startup stint and I’ve been doing it on the side for the side projects. I also got paid to webscrape linkedin and it didn’t go anywhere, linkedin is pretty dang hard unless you’re willing to put in lots of man hours into it.</p>
<p>I am willing to put my reputation on that tool. It’s really really good. It’s python though.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="142797" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-142797" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="142797"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="142831" data-post-id="142831">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Extremely valuable comment, thank you for it!</p>
<aside class="quote no-group" data-username="adammokan" data-post="24" data-topic="4823">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adammokan/48/26407_2.png" class="avatar"> adammokan:</div>
<blockquote>
<ul>
<li>Have some controls for measuring data quality over time. This may need to be manual in your case. You’d be amazed at how many big sites now start throwing you trash data that looks correct at a glance.</li>
</ul>
</blockquote>
</aside>
<p>Can you clarify on that? I’ve heard sites like Amazon deliberately give you wrong prices if they detect a bot, is that true for them and others? Or what kind of trash data?</p>
<p>Additionally, how do you even test your bot against tools like Panopticlick at all? Have your bot GET their root page and click the “Go” button? Is that what you meant, or do they (or others) have a dedicated bot testing toolkit?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="142831" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-142831" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="142831"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="143026" data-post-id="143026">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adammokan" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adammokan/120/26407_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adammokan
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<blockquote>
<p>Can you clarify on that? I’ve heard sites like Amazon deliberately give you wrong prices if they detect a bot, is that true for them and others? Or what kind of trash data?</p>
</blockquote>
<p>Yes. I don’t want to speak for specific sites - but I can tell you many ‘popular’ sites will start delivering results that are either not ordered correctly (<em>if you think in terms of SERP rank data as an example, where order matters to buyers</em>), E-commerce sites will start throwing bogus prices, and so-on. The only real way to protect/test this is to do manual A/B comparisons. The other thing to consider is we’re fully immersed in a world of personalized content - so absolutely it gets difficult, even if not being thrown bogus data, is that your clients will think your results are wrong because they are viewing personalized content if they manually compare results.</p>
<blockquote>
<p>Additionally, how do you even test your bot against tools like Panopticlick at all? Have your bot GET their root page and click the “Go” button? Is that what you meant, or do they (or others) have a dedicated bot testing toolkit?</p>
</blockquote>
<p>I should have been more specific on that. I mostly reused the same boilerplate headless crawl scripts and would include the site-specific nav/logic separately. So this meant all of my ‘pre-crawl’ setup to plug gaps like setting a legit <code>navigator.platform</code> and making sure <code>navigator.webdriver</code> returns false, etc (<em>there are quite a few of these you need to cover</em>).</p>
<p>Anyhow, I would traditionally include that initial setup logic and then have a custom script that would navigate the Panopticlick site, run the full checks, screenshot the results, and study later. Just to make sure I wasn’t missing something obvious. So yes, click the go button - load the full results and screenshot the page.</p>
<p>Aside from that there are many other “how private is my browser” checks out there that test for hardware-level info that may be worth mocking on some targets.</p>
<p>This reminds me of the fun I had with geolocation/gps coords - again, depending on what you are going for. Most common case for that effort was a well-known map site. Some hints for geolocation - and this may be outdated, but it was always important to mock both <code>navigator.geolocation.getCurrentPosition</code> as well as <code>navigator.geolocation.watchPosition</code> to spoof lat/long. A little bit of noise in the <code>coords.accuracy</code> attribute went a long way here <img src="https://forum.elixirforum.com/images/emoji/apple/+1.png?v=15" title=":+1:" class="emoji" alt=":+1:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="143026" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-143026" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="143026"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="143050" data-post-id="143050">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="_rubenfa" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/_rubenfa/120/5558_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  _rubenfa
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="adammokan" data-post="28" data-topic="4823">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adammokan/48/26407_2.png" class="avatar"> adammokan:</div>
<blockquote>
<p>This reminds me of the fun I had with geolocation/gps coords - again, depending on what you are going for. Most common case for that effort was a well-known map site. Some hints for geolocation - and this may be outdated, but it was always important to mock both <code>navigator.geolocation.getCurrentPosition</code> as well as <code>navigator.geolocation.watchPosition</code> to spoof lat/long. A little bit of noise in the <code>coords.accuracy</code> attribute went a long way here</p>
</blockquote>
</aside>
<p>Geolocated results are pretty interesting too. I had a long battle with that in a previous job. You can try to use IPs from the destination country but is not always easy. I remember to obtain interesting results when the IP is geolocated in a country border.</p>
<p>Finally, we made it work by mocking geolocation properties as you said. It was especially useful for geolocated results in regions of the same country.</p>
<p>I think scraping concept is easy to understand, but there is a ton of variables to take in mind, depending on the stuff you are scraping. Some of them make the process really hard to implement.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="143050" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-143050" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="143050"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="144501" data-post-id="144501">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oltarasenko" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oltarasenko/120/15709_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oltarasenko
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hey people. Just wanted to announce that I have made an article on Erlang Solutions blog, about using Crawly: <a href="https://www.erlang-solutions.com/blog/web-scraping-with-elixir.html" class="inline-onebox" rel="noopener nofollow ugc">Erlang Solutions</a></p>
<p>Also I have made a couple of sample/tutorial projects to get started with: <a href="https://github.com/oltarasenko/crawly-spider-example" class="inline-onebox" rel="noopener nofollow ugc">GitHub - oltarasenko/crawly-spider-example · GitHub</a><br>
<a href="https://github.com/oltarasenko/products-advisor" class="inline-onebox" rel="noopener nofollow ugc">GitHub - oltarasenko/products-advisor · GitHub</a><br>
<a href="https://github.com/oltarasenko/crawly-cars" class="inline-onebox" rel="noopener nofollow ugc">GitHub - oltarasenko/crawly-cars · GitHub</a></p>
<p>Hopefully they could be interesting for people doing web scraping.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="144501" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-144501" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="144501"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="144523" data-post-id="144523">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mischov" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mischov/120/2489_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mischov
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><a class="mention" href="/u/oltarasenko" rel="nofollow">@oltarasenko</a> Couple bits of feedback that are common mistakes I see people making when using Floki.</p>
<ol>
<li>
<p>Recommend a safer HTML parser than Floki+mochiweb_html.</p>
<p>I know it’s nice to not need to start an Elixir article with “and then install Rust”, but web scraping is exactly the situation you do want an HTML5 compliant parser because you don’t know how well formed the HTML will be and mochiweb_html (Floki’s default parser) can incorrectly parse parts of the HTML if it’s malformed (and potentially just drop those parts silently).</p>
<p>At the very least use the html5ever parser with Floki, though you may have trouble getting it to compile since html5ever_elixir hasn’t been updated for nine months despite an outstanding need to upgrade Rustler so that it works with more recent versions of Erlang/OTP.</p>
<p>Better yet, use <a href="https://github.com/mischov/meeseeks" rel="noopener nofollow ugc">Meeseeks</a> instead of Floki because it will by default provide you an HTML5 compliant parser based on html5ever that <em>does</em> compile on the latest versions of Erlang/OTP.</p>
<p>Floki’s mochiweb_html parser has a place, mainly in situations where you are dealing with known, well-formed HTML and you don’t need the weight of an HTML5 compliant parser (like when you’re testing your Phoenix endpoints), but people should know the risk they’re taking if they use it for web scraping.</p>
</li>
<li>
<p>Stop parsing each page four times.</p>
<p>When you run <code>response.body |&gt; Floki.find(...)</code>, you’re really running the equivalent of <code>response.body |&gt; Floki.parse() |&gt; Floki.find(...)</code> which means your four <code>Floki.find</code>s are parsing the whole document four times.</p>
<p>Instead, try <code>parsed_body = Floki.parse(response.body)</code> then <code>parsed_body |&gt; Floki.find(...)</code>.</p>
</li>
<li>
<p>Don’t select over the whole document when you don’t need to.</p>
<p>Three of your selectors are: <code>"article.blog_post h1:first-child"</code>, <code>"article.blog_post p.subheading"</code> and <code>"article.blog_post"</code>. That means you’re selecting the same <code>article.blog_post</code> three times, then making sub-selections two of those times. Instead, try something like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">parsed_body = Floki.parse(response.body)
blog_post = Floki.find(parse_body, "article.blog_post")

title =
  blog_post
  |&gt; Floki.find("h1:first_child")
  |&gt; Floki.text

author = 
  blog_post
  |&gt; Floki.find("p.subheading")
...
</code></pre>
<p>Doing that means that instead of walking the whole document each time you want to make a sub-selection you just walk the the portion you’re interested in. In this case when there is only one of the thing you’re making sub-selections on it’s probably not a huge difference, but in cases where you’re sub-selecting over a list of items it can add up.</p>
</li>
</ol> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="144523" data-batch-url="/posts/batch_likers">
                        6
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-144523" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="144523"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
    <div class="postbit" id="144530" data-post-id="144530">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mischov" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mischov/120/2489_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mischov
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Here is a version of your crawler that uses Meeseeks and fixes all of the above problems.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Esl do
  @behaviour Crawly.Spider

  import Meeseeks.CSS

  @impl Crawly.Spider
  def base_url() do
    "https://www.erlang-solutions.com"
  end

  @impl Crawly.Spider
  def init() do
    [
      start_urls: ["https://www.erlang-solutions.com/blog.html"]
    ]
  end

  @impl Crawly.Spider
  def parse_item(response) do
    # Parse the response body as html, though Meeseeks comes with an
    # XML parser too if you want to parse the blog posts from the 
    # RSS feed instead
    parsed_body = Meeseeks.parse(response.body, :html)

    # Get new urls to follow
    urls =
      parsed_body
      |&gt; Meeseeks.all(css("a.more"))
      |&gt; Enum.map(&amp;Meeseeks.attr(&amp;1, "href"))

    # Convert urls into requests
    requests =
      Enum.map(urls, fn url -&gt;
        url
        |&gt; build_absolute_url(response.request_url)
        |&gt; Crawly.Utils.request_from_url()
      end)

    # Extract item from a page, e.g.
    # https://www.erlang-solutions.com/blog/introducing-telemetry.html
    # 
    # Find the post using `Meeseeks.one`, which will stop after
    # the first match rather than looking through the rest of the
    # document for something it will never find more of.
    post = Meeseeks.one(parsed_body, css("article.blog_post"))

    title =
      post
      |&gt; Meeseeks.one(css("h1:first-child"))
      |&gt; Meeseeks.own_text()

    # `Meeseeks.own_text` trims automatically and only gets the text
    # from the selected element.
    author =
      post
      |&gt; Meeseeks.one(css("p.subheading"))
      |&gt; Meeseeks.own_text()

    # `Meeseeks.text` will get the combined text from the element and 
    # all of its descendants
    text = Meeseeks.text(post)

    %Crawly.ParsedItem{
      :requests =&gt; requests,
      :items =&gt; [
        %{
          title: title, 
          author: author, 
          text: text, 
          url: response.request_url
        }
      ]
    }
  end

  def build_absolute_url(url, request_url) do
    URI.merge(request_url, url) |&gt; to_string()
  end
end
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="144530" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-144530" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="144530"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #31"></div>
  </section>
</div>
    <div class="postbit" id="144574" data-post-id="144574">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oltarasenko" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oltarasenko/120/15709_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oltarasenko
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="mischov" data-post="31" data-topic="4823">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mischov/48/2489_2.png" class="avatar"> mischov:</div>
<blockquote>
<p>ot need to start a</p>
</blockquote>
</aside>
<p>Hey <a class="mention" href="/u/mischov" rel="nofollow">@mischov</a>!</p>
<p>Thanks for the feedback. Actually I was aware that parsing would happen 3 times, but decided not to do anything with it for now. At this point, I think that it’s better to update examples, so they are not misleading! Thanks for the hint.</p>
<p>I don’t have problems with Rust. Parsing should be done by low level and fast languages! Basically, scrapy uses C parses in order to parse pages under the hood. We should do the same.</p>
<p>Q: I am currently looking for the XPath library, to use it in Crawly. But at this point, nothing works properly when in comes to HTML pages. Are you aware of the options to try XPath selectors?</p>
<p>Again huge thanks for comments!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="144574" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/web-scraping-tools/4823/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-144574" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="144574"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #32"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/4823/load_more?page=4">Load more posts (15 remaining)</a>
</div></template></turbo-stream>