<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>headless - Hard Wired</title>
	<atom:link href="https://www.hardwired.dev/tag/headless/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.hardwired.dev</link>
	<description></description>
	<lastBuildDate>Tue, 01 Nov 2022 13:09:19 +0000</lastBuildDate>
	<language>cs</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.hardwired.dev/wp-content/uploads/2022/10/android-chrome-256x256-1-150x150.png</url>
	<title>headless - Hard Wired</title>
	<link>https://www.hardwired.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Puppeteer download BOT</title>
		<link>https://www.hardwired.dev/2022/10/31/puppeteer-download-bot/</link>
		
		<dc:creator><![CDATA[John Doe]]></dc:creator>
		<pubDate>Mon, 31 Oct 2022 13:35:28 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Různé]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[headless]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[nodejs]]></category>
		<category><![CDATA[puppeteer]]></category>
		<category><![CDATA[scraping]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[web-scraping]]></category>
		<guid isPermaLink="false">http://wordpress.hardwired.dev/?p=774</guid>

					<description><![CDATA[<p>O co jde Uděláme bota co načte webovou stránku, klikne na čudlík a stáhne soubor. Použijeme k tomu knihovnu Puppeteer &#62;&#62;&#62;</p>
<p>The post <a href="https://www.hardwired.dev/2022/10/31/puppeteer-download-bot/">Puppeteer download BOT</a> first appeared on <a href="https://www.hardwired.dev">Hard Wired</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><h1>O co jde</h1>
<p>Uděláme bota co načte webovou stránku, klikne na čudlík a stáhne soubor.</p>
<p>Použijeme k tomu knihovnu <a href="https://pptr.dev/" title="Puppeteer">Puppeteer</a> a samotný kód budeme pouštět v <a href="https://www.docker.com/" title="Dockeru">Dockeru</a>.</p>
<blockquote>
<p>Větší podrobnosti a vysvětlení zakládání NodeJs projektu v Dockeru najde v článku <a href="http://wordpress.hardwired.dev/nodejs-develompent-v-dockeru/" title="NodeJS develompent v Dockeru">NodeJS develompent v Dockeru</a>.</p>
</blockquote>
<h1>Příprava projektu</h1>
<p>Založíme nový projekt:</p>
<pre><code class="language-shell">mkdir puppet
cd puppet
npm init -y</code></pre>
<p>Upravíme <code>package.json</code> a přidáme do něj knihovnu <code>puppeteer</code>:</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;puppet&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node index.js&quot;
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;puppeteer&quot;: &quot;^19.2.0&quot;
  }
}</code></pre>
<p>Vytvoříme <code>Dockerfile</code>, který použije jako základ obraz s <code>node v16</code>, nainstaluje <code>Chrome prohlížeč</code> a nakopíruje projekt do Docker obrazu.</p>
<pre><code class="language-shell">FROM node:16

RUN apt-get update &amp;&amp; apt-get install gnupg wget -y &amp;&amp; \
  wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor &gt; /etc/apt/trusted.gpg.d/google-archive.gpg &amp;&amp; \
  sh -c &#039;echo &quot;deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main&quot; &gt;&gt; /etc/apt/sources.list.d/google.list&#039; &amp;&amp; \
  apt-get update &amp;&amp; \
  apt-get install google-chrome-stable -y --no-install-recommends &amp;&amp; \
  rm -rf /var/lib/apt/lists/*

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable

WORKDIR /app
COPY package*.json .
RUN npm install
COPY . ./</code></pre>
<p>Vytvoříme <code>index.js</code> soubor a necháme ho jen vypsat zprávu do konzole.</p>
<pre><code class="language-shell">touch index.js</code></pre>
<pre><code class="language-javascript">console.log(&#039;Hello from docker&#039;)</code></pre>
<p>Není úplně špatné vytvořit i <code>.dockerignore</code> soubor.</p>
<pre><code class="language-shell">node_modules
Dockerfile
.dockerignore
.git
.gitignore
README.md
docker-compose*</code></pre>
<p>Sestavíme obraz.</p>
<pre><code class="language-shell">docker build -t puppet-app-image .</code></pre>
<p>A spustíme Docker kontejner.</p>
<pre><code class="language-bash"># %cd% je aktuální složka ve Windows, pro jiné systémy použijte adekvátní náhradu

docker run -d --tty --name puppet-app -v %cd%:/app -v /app/node_modules puppet-app-image</code></pre>
<blockquote>
<p>--tty parametr je nutný. V opačném případě se Docker kontejner spustí a pak hned úspěšně ukončí. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Za normálních okolností předpokládá, že v něm něco poběží a až se to ukončí tak skončí taky. TTY toto chování potlačí.</p>
</blockquote>
<p>Jakmile kontejner běží, můžeme se do něj přihlásit:</p>
<pre><code class="language-shell">docker exec -it puppet-app bash</code></pre>
<p>Vevnitř se ocitneme ve složce <code>/app</code>, kde je náš projekt a můžeme zkusit spustit náš program.</p>
<pre><code class="language-shell">nodejs index.js</code></pre>
<p>Měl by nám odpovědět <code>Hello from docker</code>.</p>
<h1>Bot</h1>
<p>Pro ukázku bude bot stahovat zdrojový kód puppeteeru z githubu. Upravíme <code>index.js</code> následovně.</p>
<pre><code class="language-javascript">const puppeteer = require(&#039;puppeteer&#039;)

;(async () =&gt; {
    const browser = await puppeteer.launch({
        headless: true,
        args: [&#039;--no-sandbox&#039;],
    })

    const page = await browser.newPage()
    page.setViewport({ width: 1920, height: 1080 })

    const client = await page.target().createCDPSession()
    await client.send(&quot;Page.setDownloadBehavior&quot;, {
        behavior: &quot;allow&quot;,
        downloadPath: &#039;./&#039;
    })

    await page.goto(
        &#039;https://github.com/puppeteer/puppeteer/releases/tag/puppeteer-core-v19.2.0&#039;,
        { waitUntil: &#039;networkidle2&#039; }
    )
    await page.waitForTimeout(1000)

    await page.click(&#039;#repo-content-turbo-frame div.mb-3 &gt; details &gt; div &gt; div &gt; ul &gt; li:nth-child(1) &gt; div.d-flex.flex-justify-start.col-12.col-lg-9 &gt; a&#039;)
    await page.waitForTimeout(5000)

    await page.screenshot({ path: &#039;./screen.jpg&#039;, fullPage: true })

    await browser.close()
})()</code></pre>
<p>Naimportujeme do našeho projektu knihovnu <code>puppeteer</code>.</p>
<pre><code class="language-javascript">const puppeteer = require(&#039;puppeteer&#039;)</code></pre>
<p>Budeme volat asynchronní funkce v java scriptu a budeme používat klíčové slovo <code>await</code>, které čeká na provedení asynchronní funkce. Problém je to, že <code>await</code> se může objevit jen v asynchronní funkci. Tudíž náš kód musíme do jedné takové zabalit a rovnou spustit.</p>
<pre><code class="language-javascript">;(async () =&gt; {
    // code
})()</code></pre>
<p>Vytvoříme nový <code>headless prohlížeč</code>.</p>
<pre><code class="language-javascript">const browser = await puppeteer.launch({
    headless: true,
    args: [&#039;--no-sandbox&#039;],
})</code></pre>
<p>Vytvoříme novou <code>stránku/tab</code> a nastavíme rozlišení.</p>
<pre><code class="language-javascript">const page = await browser.newPage()
page.setViewport({ width: 1920, height: 1080 })</code></pre>
<p>Pro tuto konkrétní stránku nastavíme automatické stahovaní do určitého adresáře. V našem případě adresář s projektem. Takhle nebudeme vyvolávat dialog pro umístění stahovaného souboru.</p>
<pre><code class="language-javascript">const client = await page.target().createCDPSession()
await client.send(&quot;Page.setDownloadBehavior&quot;, {
    behavior: &quot;allow&quot;,
    downloadPath: &#039;./&#039;
})</code></pre>
<p>V naší <code>stránce/tabu</code> otevřeme požadovanou github stránku.</p>
<pre><code class="language-javascript">await page.goto(
    &#039;https://github.com/puppeteer/puppeteer/releases/tag/puppeteer-core-v19.2.0&#039;,
    { waitUntil: &#039;networkidle2&#039; }
)
await page.waitForTimeout(1000)</code></pre>
<p>Klikneme na odkaz pro stažení a chvíli počkáme, než se soubor stáhne <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre><code class="language-javascript">await page.click(&#039;#repo-content-turbo-frame div.mb-3 &gt; details &gt; div &gt; div &gt; ul &gt; li:nth-child(1) &gt; div.d-flex.flex-justify-start.col-12.col-lg-9 &gt; a&#039;)
await page.waitForTimeout(5000)</code></pre>
<p>K definování toho na co se má kliknout se používá <a href="https://www.w3schools.com/CSSREF/css_selectors.php" title="CSS Selector">CSS Selector</a> (<a href="https://drafts.csswg.org/selectors/" title="draft">draft</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors" title="mdn">mdn</a>). Ten pomocí html elementů, tříd, atributů a dalších dokáže sestavit unikátní cestu k prvku na stránce. Stránky generované nějakým frameworkem umí potrápit i s takhle dlouhým selektorem, jako v tomto případě.</p>
<p><img decoding="async" src="http://wordpress.hardwired.dev/wp-content/uploads/2022/10/githubsource.jpg" alt="" /></p>
<p>Pak už si jen uděláme momentku, v jakém stavu stránku opouštíme.</p>
<pre><code class="language-javascript">await page.screenshot({ path: &#039;./screen.jpg&#039;, fullPage: true })</code></pre>
<p>A prohlížeč korektně zavřeme.</p>
<pre><code class="language-javascript">await browser.close()</code></pre>
<h1>Spuštění kódu</h1>
<p>V konzoli se připojíme k našemu kontejneru.</p>
<pre><code class="language-bash">docker exec -it puppet-app bash</code></pre>
<p>A spustíme našeho bota.</p>
<pre><code class="language-bash">node index.js</code></pre>
<p>Pokud vše proběhne, jak má, tak ve složce projektu budete mít jak stažený soubor, tak fotku stránky těsně před tím než jste ji opustili.</p>
<p><img decoding="async" src="http://wordpress.hardwired.dev/wp-content/uploads/2022/10/puppeteer_screen.jpg" alt="" /></p>
<p>Toto je velice hrubý nástřel toho, co Puppeteer dokáže na konkrétním případě. Výhodou je, že se opravdu spustí prohlížeč, ve kterém můžeme ovládat i dynamicky generované <code>single page</code> aplikace. Nevýhodou je, že pokud se struktura stránky změní moc, tak musíte upravit i váš kód.</p>
<p>Happy coding!</p>

<div class="twitter-share"><a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.hardwired.dev%2F2022%2F10%2F31%2Fpuppeteer-download-bot%2F&#038;via=hessevalentino" class="twitter-share-button">Tweet</a></div><p>The post <a href="https://www.hardwired.dev/2022/10/31/puppeteer-download-bot/">Puppeteer download BOT</a> first appeared on <a href="https://www.hardwired.dev">Hard Wired</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
