<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Tracefans｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/tags/tracefans/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>Tracefans｜林鼎淵 Dean Lin</title><link>https://deanlin.net/tags/tracefans/</link><width>144</width><height>144</height></image>
    <description>林鼎淵（Dean Lin）的部落格：生成式 AI 企業內訓講師、外商資安公司軟體專家、7 本書作者。400 多篇文章用白話分享生成式 AI 實戰、Claude Code 與 Vibe Coding 教學、團隊合作與職場成長。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Sat, 26 Sep 2020 08:44:52 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/tags/tracefans/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>【Day11】舉一反三，帶你了解IG爬蟲不可忽略的細節</title>
      <link>https://deanlin.net/blog/202009/fb-ig-crawler-day-11/</link>
      <pubDate>Sat, 26 Sep 2020 08:44:52 +0800</pubDate>
      <guid>https://deanlin.net/blog/202009/fb-ig-crawler-day-11/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>有了 Facebook 爬蟲的經驗後，我相信這篇大家是有能力獨自完成的，建議大家先用自己的方式來完成今日目標，這篇文章適合實作遇到問題或是你實作完成後再來參考</description>
      <media:content url="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/cover_hu_30d62b86535ec880.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<blockquote>
<p>學習，從複製開始</p>
</blockquote>
<h2 id="筆者有話先說">筆者有話先說<a class="h-anchor" href="#%e7%ad%86%e8%80%85%e6%9c%89%e8%a9%b1%e5%85%88%e8%aa%aa" aria-label="這個段落的連結"></a></h2>
<p>有了 Facebook 爬蟲的經驗後，我相信這篇大家是有能力獨自完成的，<mark>建議大家先用自己的方式來完成今日目標</mark>，這篇文章適合實作遇到問題或是你實作完成後再來參考</p>
<hr>
<h2 id="今日目標">今日目標<a class="h-anchor" href="#%e4%bb%8a%e6%97%a5%e7%9b%ae%e6%a8%99" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="1-完成-ig-自動登入">1. 完成 IG 自動登入<a class="h-anchor" href="#1-%e5%ae%8c%e6%88%90-ig-%e8%87%aa%e5%8b%95%e7%99%bb%e5%85%a5" aria-label="這個段落的連結"></a></h3>
<p>1.1 分析 IG 登入頁面的元件</p>
<p>1.2 改用 css 抓取元件，完成 IG 自動登入</p>
<h3 class="h-nomark" id="2-從畫面判斷使用者是否登入成功">2. 從畫面判斷使用者是否登入成功<a class="h-anchor" href="#2-%e5%be%9e%e7%95%ab%e9%9d%a2%e5%88%a4%e6%96%b7%e4%bd%bf%e7%94%a8%e8%80%85%e6%98%af%e5%90%a6%e7%99%bb%e5%85%a5%e6%88%90%e5%8a%9f" aria-label="這個段落的連結"></a></h3>
<h3 class="h-nomark" id="3-前往-ig-帳號並取得追蹤人數">3. 前往 IG 帳號並取得追蹤人數<a class="h-anchor" href="#3-%e5%89%8d%e5%be%80-ig-%e5%b8%b3%e8%99%9f%e4%b8%a6%e5%8f%96%e5%be%97%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<p>3.1 了解響應式網頁對爬蟲的影響</p>
<p>3.2 設定瀏覽器開啟時的視窗大小</p>
<p>3.3 完成前往 IG 帳號並取得追蹤人數的程式</p>
<hr>
<h2 class="h-nomark" id="1-ig-自動登入">1. IG 自動登入<a class="h-anchor" href="#1-ig-%e8%87%aa%e5%8b%95%e7%99%bb%e5%85%a5" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="11-分析-ig-登入頁面的元件">1.1 分析 IG 登入頁面的元件<a class="h-anchor" href="#11-%e5%88%86%e6%9e%90-ig-%e7%99%bb%e5%85%a5%e9%a0%81%e9%9d%a2%e7%9a%84%e5%85%83%e4%bb%b6" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p>我在反覆使用 <a href="https://www.instagram.com/accounts/login">IG 登入</a> 時發現<mark>登入的畫面偶爾會長不一樣</mark>(如下圖)導致Xpath路徑錯誤</p>
<table>
  <thead>
      <tr>
          <th>版本1</th>
          <th>版本2</th>
      </tr>
  </thead>
  <tbody>
  </tbody>
</table>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-01.webp" width="1400" height="1267" alt="Imgur" fetchpriority="high" decoding="async"></p>
<p>|</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-02.webp" width="1400" height="1267" alt="Imgur" loading="lazy" decoding="async"></p>
</li>
<li>
<p>先<mark>進入開發者模式</mark>確認紅框內元件的特徵</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-03.webp" width="1400" height="1267" alt="Imgur" loading="lazy" decoding="async"></p>
<ul>
<li>在下圖我們觀察到 <strong>電話號碼、用戶名稱或電子郵件</strong> 以及 <strong>密碼</strong> 這兩個元件的 <strong>共通點</strong>：
<ol>
<li>
<p>都是 <code>input 的 html 標籤</code></p>
</li>
<li>
<p>用 <code>name 這個 attribute</code> 來表示他們的作用</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-04.webp" width="968" height="142" alt="1.1 分析 IG 登入頁面的元件（圖 4）" loading="lazy" decoding="async"></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-05.webp" width="946" height="142" alt="1.1 分析 IG 登入頁面的元件（圖 5）" loading="lazy" decoding="async"></p>
</li>
</ol>
</li>
<li><strong>登入按鈕</strong> 則用以下條件來取出：
<ol>
<li>
<p>使用 <code>button 的 html 標籤</code></p>
</li>
<li>
<p>用 <code>type 這個 attribute</code> 來表示他要執行的動作</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-06.webp" width="1024" height="302" alt="1.1 分析 IG 登入頁面的元件（圖 6）" loading="lazy" decoding="async"></p>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<h3 class="h-nomark" id="12-改用-css-抓取元件完成-ig-自動登入">1.2 改用 css 抓取元件，完成 IG 自動登入<a class="h-anchor" href="#12-%e6%94%b9%e7%94%a8-css-%e6%8a%93%e5%8f%96%e5%85%83%e4%bb%b6%e5%ae%8c%e6%88%90-ig-%e8%87%aa%e5%8b%95%e7%99%bb%e5%85%a5" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p>根據上面的分析後我們<mark>改用 css 抓取 IG 登入的元件</mark>來操作</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//填入ig登入資訊
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> ig_username_ele <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.css(<span style="color:#a5d6ff">&#34;input[name=&#39;username&#39;]&#34;</span>)));
</span></span><span style="display:flex;"><span>ig_username_ele.sendKeys(ig_username)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> ig_password_ele <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.css(<span style="color:#a5d6ff">&#34;input[name=&#39;password&#39;]&#34;</span>)));
</span></span><span style="display:flex;"><span>ig_password_ele.sendKeys(ig_userpass)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//抓到登入按鈕然後點擊
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> login_elem <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.css(<span style="color:#a5d6ff">&#34;button[type=&#39;submit&#39;]&#34;</span>)))
</span></span><span style="display:flex;"><span>login_elem.click()
</span></span></code></pre></div><blockquote>
<ul>
<li>
<p>別忘記在 .env檔填寫 IG登入的帳號密碼</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#填寫自己登入IG的真實資訊(建議開小帳號來實驗，因為帳號使用太頻繁會被官方鎖住)</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">IG_USERNAME</span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;ig username&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">IG_PASSWORD</span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;ig password&#39;</span>
</span></span></code></pre></div></li>
</ul>
</blockquote>
</li>
</ul>
<hr>
<h2 class="h-nomark" id="2-判斷使用者是否登入成功">2. 判斷使用者是否登入成功<a class="h-anchor" href="#2-%e5%88%a4%e6%96%b7%e4%bd%bf%e7%94%a8%e8%80%85%e6%98%af%e5%90%a6%e7%99%bb%e5%85%a5%e6%88%90%e5%8a%9f" aria-label="這個段落的連結"></a></h2>
<ul>
<li>
<p>與 FB 類似，<mark>IG 要登入之後才會有右上角功能列</mark>，這裡我使用他作為判斷登入與否</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-07.webp" width="1024" height="1013" alt="2. 判斷使用者是否登入成功（圖 7）" loading="lazy" decoding="async"></p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//登入後才會有右上角功能列，我們以這個來判斷是否登入
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.xpath(<span style="color:#a5d6ff">`//*[@id=&#34;react-root&#34;]//*[contains(@class,&#34;_47KiJ&#34;)]`</span>)))
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 class="h-nomark" id="3-抓出-ig-追蹤人數">3. 抓出 IG 追蹤人數<a class="h-anchor" href="#3-%e6%8a%93%e5%87%ba-ig-%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="31-了解響應式網頁對爬蟲的影響">3.1 了解響應式網頁對爬蟲的影響<a class="h-anchor" href="#31-%e4%ba%86%e8%a7%a3%e9%9f%bf%e6%87%89%e5%bc%8f%e7%b6%b2%e9%a0%81%e5%b0%8d%e7%88%ac%e8%9f%b2%e7%9a%84%e5%bd%b1%e9%9f%bf" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>響應式網頁設計(RWD)是什麼？</strong>：網頁會根據瀏覽器解析度的不同重新排版，同一個網頁你在手機、平板、電腦看到的畫面配置都是不一樣的</li>
<li>如果你跟我一樣充滿實驗精神，你會發現 <mark>IG 的頁面會隨著螢幕寬度更改而重新排版</mark>，Xpath 的路徑也會跟著改變
<ul>
<li>
<p><strong>寬螢幕</strong> Xpath</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-08.webp" width="640" height="94" alt="Imgur" loading="lazy" decoding="async"></p>
<pre tabindex="0"><code>//*[@id=&#34;react-root&#34;]/section/main/div/header/section/ul/li[2]/a/span
</code></pre></li>
<li>
<p><strong>窄螢幕</strong> Xpath</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-09.webp" width="640" height="71" alt="Imgur" loading="lazy" decoding="async"></p>
<pre tabindex="0"><code>//*[@id=&#34;react-root&#34;]/section/main/div/ul/li[2]/a/span
</code></pre></li>
</ul>
</li>
</ul>
<hr>
<h3 class="h-nomark" id="32-設定瀏覽器開啟時的視窗大小">3.2 設定瀏覽器開啟時的視窗大小<a class="h-anchor" href="#32-%e8%a8%ad%e5%ae%9a%e7%80%8f%e8%a6%bd%e5%99%a8%e9%96%8b%e5%95%9f%e6%99%82%e7%9a%84%e8%a6%96%e7%aa%97%e5%a4%a7%e5%b0%8f" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p>為了避免因為螢幕寬度不一樣造成 Xpath 不同，所以我們要<mark>設定瀏覽器開啟時的視窗大小</mark>(本專案以寬螢幕作為範例)，這樣才能保證你的爬蟲在不同電腦有一樣的執行結果</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// 建立browser的類型
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> driver <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> <span style="color:#ff7b72">new</span> webdriver.Builder().forBrowser(<span style="color:#a5d6ff">&#34;chrome&#34;</span>).withCapabilities(options).build();
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//考慮到ig在不同螢幕寬度時的Xpath不一樣，所以我們要在這裡設定統一的視窗大小
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.manage().window().setRect({ width<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">1280</span>, height<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">800</span>, x<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">0</span>, y<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">0</span> });
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 class="h-nomark" id="33-完成前往-ig-帳號並取得追蹤人數的程式">3.3 完成前往 IG 帳號並取得追蹤人數的程式<a class="h-anchor" href="#33-%e5%ae%8c%e6%88%90%e5%89%8d%e5%be%80-ig-%e5%b8%b3%e8%99%9f%e4%b8%a6%e5%8f%96%e5%be%97%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8%e7%9a%84%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p>在固定螢幕寬度後，使用 Xpath 就能穩定的抓出帳號追蹤人數</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//登入成功後要前往粉專頁面
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> fanpage <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#34;https://www.instagram.com/baobaonevertell/&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.get(fanpage)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.sleep(<span style="color:#a5d6ff">3000</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> ig_trace <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>;<span style="color:#8b949e;font-style:italic">//這是紀錄IG追蹤人數
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> ig_trace_xpath <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">`//*[@id=&#34;react-root&#34;]/section/main/div/header/section/ul/li[2]/a/span`</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> ig_trace_ele <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.xpath(ig_trace_xpath)))
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// ig因為當人數破萬時文字不會顯示，所以改抓title
</span></span></span><span style="display:flex;"><span>ig_trace <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> ig_trace_ele.getAttribute(<span style="color:#a5d6ff">&#39;title&#39;</span>)
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">`追蹤人數：</span><span style="color:#a5d6ff">${</span>ig_trace<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">`</span>)
</span></span><span style="display:flex;"><span>driver.quit();
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="執行程式">執行程式<a class="h-anchor" href="#%e5%9f%b7%e8%a1%8c%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h3>
<ol>
<li>
<p>在專案資料夾的終端機(Terminal)執行指令</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>yarn start
</span></span></code></pre></div></li>
<li>
<p>你會看到 Instagram 自動登入 → 跳轉到指定帳號 → 關閉</p>
</li>
<li>
<p>確認輸出正確的粉專追蹤人數你就成功嚕～</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-10.webp" width="1024" height="133" alt="執行程式（圖 10）" loading="lazy" decoding="async"></p>
</li>
</ol>
<p>到目前為止我們已經可以抓出 FB &amp; IG 粉專的追蹤人數了，充滿好奇心的讀者可以先試著看看利用爬蟲爬完 FB 粉專後繼續爬 IG</p>
<hr>
<h3 id="ℹ-專案原始碼">ℹ️ 專案原始碼<a class="h-anchor" href="#%e2%84%b9-%e5%b0%88%e6%a1%88%e5%8e%9f%e5%a7%8b%e7%a2%bc" aria-label="這個段落的連結"></a></h3>
<ul>
<li>今天的完整程式碼可以在<a href="https://github.com/dean9703111/ithelp_30days/tree/master/day11">這裡</a>找到喔</li>
<li>我也貼心地把昨天的把昨天FB爬蟲的程式碼打包成<a href="https://github.com/dean9703111/ithelp_30days/raw/master/sampleCode/day10_sample_code.zip">壓縮檔</a>，你可以用裡面乾淨的環境來實作今天的功能喔
<ul>
<li>請記得在終端機下指令 <strong>yarn</strong> 才會把之前的套件安裝</li>
<li>windows需下載與你chrome版本相同的<a href="http://chromedriver.storage.googleapis.com/index.html">chrome driver</a>放在專案根目錄</li>
<li>調整你.env檔填上IG登入資訊</li>
</ul>
</li>
</ul>
<hr>
<h3 id="參考資源">參考資源<a class="h-anchor" href="#%e5%8f%83%e8%80%83%e8%b3%87%e6%ba%90" aria-label="這個段落的連結"></a></h3>
<ol>
<li><a href="https://stackoverflow.com/a/49940401">Filling in login forms in Instagram using selenium and webdriver (chrome) python OSX</a></li>
</ol>
<blockquote>
<p><em>免責聲明:文章技術僅抓取公開數據作爲研究，任何組織和個人不得以此技術盜取他人智慧財產、造成網站損害，否則一切后果由該組織或個人承擔。作者不承擔任何法律及連帶責任！</em></p>
</blockquote>
<hr>
<blockquote>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-11/images/fb-ig-crawler-day-11-11.webp" width="1024" height="233" alt="參考資源（圖 11）" loading="lazy" decoding="async"></p>
<p><strong>在許多人的幫助下，本系列文章已出版成書，並添加了新的篇章與細節補充：</strong></p>
<ul>
<li>加入更多實務經驗，用完整的開發流程讓讀者了解專案每個階段要注意的事項</li>
<li>將爬蟲的步驟與技巧做更詳細的說明，讓讀者可以輕鬆入門</li>
<li>調整專案架構
<ul>
<li>優化爬蟲程式，以更廣的視角來擷取網頁資訊</li>
<li>增加資料驗證、錯誤通知等功能，讓爬蟲執行遇到問題時可以第一時間通知使用者</li>
<li>排程部分改用 node-schedule &amp; pm2 的組合，讓讀者可以輕鬆管理專案程序並獲得更精確的 log 資訊</li>
</ul>
</li>
</ul>
<p><strong>有興趣的朋友可以到天瓏書局選購，感謝大家的支持。</strong></p>
<p><strong>購書連結</strong>：<a href="https://www.tenlong.com.tw/products/9789864348008">https://www.tenlong.com.tw/products/9789864348008</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>【Day10】關閉干擾爬蟲的彈窗，將FB粉專追蹤數納入囊中</title>
      <link>https://deanlin.net/blog/202009/fb-ig-crawler-day-10/</link>
      <pubDate>Fri, 25 Sep 2020 09:09:47 +0800</pubDate>
      <guid>https://deanlin.net/blog/202009/fb-ig-crawler-day-10/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>我的文章是以專案的角度寫作，遇到問題才會分析解決的方案，與專門介紹套件的文章風格不同，因為這個系列文撰寫的初衷並不是教一個技術而是培養你解決問題的能力</description>
      <media:content url="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/cover_hu_4c30013eb6506aeb.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<blockquote>
<p>遇到困難挫折時，多想一下自己的初衷；如果你沒有初衷，那就想一下薪水吧</p>
</blockquote>
<h2 id="筆者有話先說">筆者有話先說<a class="h-anchor" href="#%e7%ad%86%e8%80%85%e6%9c%89%e8%a9%b1%e5%85%88%e8%aa%aa" aria-label="這個段落的連結"></a></h2>
<p>我的文章是以專案的角度寫作，遇到問題才會分析解決的方案，與專門介紹套件的文章風格不同，因為這個系列文撰寫的初衷並不是教一個技術而是培養你解決問題的能力</p>
<hr>
<h2 id="今日目標">今日目標<a class="h-anchor" href="#%e4%bb%8a%e6%97%a5%e7%9b%ae%e6%a8%99" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="1-關閉通知彈窗">1. 關閉通知彈窗<a class="h-anchor" href="#1-%e9%97%9c%e9%96%89%e9%80%9a%e7%9f%a5%e5%bd%88%e7%aa%97" aria-label="這個段落的連結"></a></h3>
<p>1.1 用 Google 關鍵字來尋找解答</p>
<p>1.2 調整 chrome 的參數來關閉通知彈窗</p>
<h3 class="h-nomark" id="2-用參數區分爬蟲要跑-fb-的經典版還是新版">2. 用參數區分爬蟲要跑 FB 的經典版還是新版<a class="h-anchor" href="#2-%e7%94%a8%e5%8f%83%e6%95%b8%e5%8d%80%e5%88%86%e7%88%ac%e8%9f%b2%e8%a6%81%e8%b7%91-fb-%e7%9a%84%e7%b6%93%e5%85%b8%e7%89%88%e9%82%84%e6%98%af%e6%96%b0%e7%89%88" aria-label="這個段落的連結"></a></h3>
<p>2.1 判斷自己的 FB 是經典版本還是新版</p>
<p>2.2 在 .env加入參數來做區別</p>
<h3 class="h-nomark" id="3-分析今日目標前往-fb-粉專取得追蹤人數資訊">3. 分析今日目標：前往 FB 粉專取得追蹤人數資訊<a class="h-anchor" href="#3-%e5%88%86%e6%9e%90%e4%bb%8a%e6%97%a5%e7%9b%ae%e6%a8%99%e5%89%8d%e5%be%80-fb-%e7%b2%89%e5%b0%88%e5%8f%96%e5%be%97%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8%e8%b3%87%e8%a8%8a" aria-label="這個段落的連結"></a></h3>
<h3 class="h-nomark" id="4-前往-fb-粉專">4. 前往 FB 粉專<a class="h-anchor" href="#4-%e5%89%8d%e5%be%80-fb-%e7%b2%89%e5%b0%88" aria-label="這個段落的連結"></a></h3>
<p>4.1 程式執行的順序跟你想的不一樣</p>
<p>4.2 從畫面判斷使用者是否登入成功，使用 Class 抓取網頁元件</p>
<p>4.3 加入登入完成才能前往粉專的邏輯</p>
<h3 class="h-nomark" id="5-取得-fb-粉專追蹤人數">5. 取得 FB 粉專追蹤人數<a class="h-anchor" href="#5-%e5%8f%96%e5%be%97-fb-%e7%b2%89%e5%b0%88%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<p>5.1 在粉專頁面找出追蹤人數的位置，並分析取出元件的方法</p>
<p>5.2 取出粉專的追蹤者人數</p>
<h3 class="h-nomark" id="6-完成爬蟲後關閉瀏覽器">6. 完成爬蟲後關閉瀏覽器<a class="h-anchor" href="#6-%e5%ae%8c%e6%88%90%e7%88%ac%e8%9f%b2%e5%be%8c%e9%97%9c%e9%96%89%e7%80%8f%e8%a6%bd%e5%99%a8" aria-label="這個段落的連結"></a></h3>
<hr>
<h2 class="h-nomark" id="1-關閉通知彈窗-1">1. 關閉通知彈窗<a class="h-anchor" href="#1-%e9%97%9c%e9%96%89%e9%80%9a%e7%9f%a5%e5%bd%88%e7%aa%97-1" aria-label="這個段落的連結"></a></h2>
<p>昨天登入FB的時候應該大家畫面都會長這樣</p>
<ul>
<li>
<p>經典版：</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-01.webp" width="1024" height="386" alt="1. 關閉通知彈窗（圖 1）" fetchpriority="high" decoding="async"></p>
</li>
<li>
<p>新版：</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-02.webp" width="1024" height="396" alt="1. 關閉通知彈窗（圖 2）" loading="lazy" decoding="async"></p>
</li>
</ul>
<p><mark>在通知彈窗存在的狀態下是無法抓取網頁元件的</mark>(充滿嘗試精神的讀者的可以嘗試看看)</p>
<h3 class="h-nomark" id="11-用-google-關鍵字來尋找解答">1.1 用 Google 關鍵字來尋找解答<a class="h-anchor" href="#11-%e7%94%a8-google-%e9%97%9c%e9%8d%b5%e5%ad%97%e4%be%86%e5%b0%8b%e6%89%be%e8%a7%a3%e7%ad%94" aria-label="這個段落的連結"></a></h3>
<p><mark>遇到你覺得很多人也會遇到的問題時</mark>用 Google 找解答會比你研究文檔快很多，像這個問題在 Google 下關鍵字：<code>nodejs selenium-webdriver notifications</code>，前幾篇一定會有文章能解決你的問題</p>
<blockquote>
<p>在這裡 <strong>通知</strong> 的英文為 <strong>notification</strong>，<mark>再次提醒用英文比較容易找到解答</mark>，如果一開始不知道哪個才是準確的關鍵字，你可以先用 <strong>Google 翻譯</strong>，然後每個單字都嘗試看看</p>
</blockquote>
<hr>
<h3 class="h-nomark" id="12-調整-chrome-的參數來關閉通知彈窗">1.2 調整 chrome 的參數來關閉通知彈窗<a class="h-anchor" href="#12-%e8%aa%bf%e6%95%b4-chrome-%e7%9a%84%e5%8f%83%e6%95%b8%e4%be%86%e9%97%9c%e9%96%89%e9%80%9a%e7%9f%a5%e5%bd%88%e7%aa%97" aria-label="這個段落的連結"></a></h3>
<ol>
<li>
<p>更改 chrome 瀏覽器 notifications 的設定</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> chrome <span style="color:#ff7b72;font-weight:bold">=</span> require(<span style="color:#a5d6ff">&#39;selenium-webdriver/chrome&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> options <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">new</span> chrome.Options();
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//因為FB會有notifications干擾到爬蟲，所以要先把它關閉
</span></span></span><span style="display:flex;"><span>options.setUserPreferences({ <span style="color:#a5d6ff">&#39;profile.default_content_setting_values.notifications&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">1</span> });
</span></span></code></pre></div></li>
<li>
<p>讓 chrome 瀏覽器依照這個設定建立</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">let</span> driver <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">new</span> webdriver.Builder().forBrowser(<span style="color:#a5d6ff">&#34;chrome&#34;</span>).
</span></span><span style="display:flex;"><span>    withCapabilities(options).build();<span style="color:#8b949e;font-style:italic">// 建立這個browser的類型
</span></span></span></code></pre></div></li>
<li>
<p><strong>index.js</strong> 加入上面的設定後在終端機(Terminal)執行指令就<strong>會發現彈窗提示不見了</strong>!</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>yarn start
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 class="h-nomark" id="2-用參數區分爬蟲要跑-fb-的經典版還是新版-1">2. 用參數區分爬蟲要跑 FB 的經典版還是新版<a class="h-anchor" href="#2-%e7%94%a8%e5%8f%83%e6%95%b8%e5%8d%80%e5%88%86%e7%88%ac%e8%9f%b2%e8%a6%81%e8%b7%91-fb-%e7%9a%84%e7%b6%93%e5%85%b8%e7%89%88%e9%82%84%e6%98%af%e6%96%b0%e7%89%88-1" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="21-判斷自己的-fb-是經典版本還是新版">2.1 判斷自己的 FB 是經典版本還是新版<a class="h-anchor" href="#21-%e5%88%a4%e6%96%b7%e8%87%aa%e5%b7%b1%e7%9a%84-fb-%e6%98%af%e7%b6%93%e5%85%b8%e7%89%88%e6%9c%ac%e9%82%84%e6%98%af%e6%96%b0%e7%89%88" aria-label="這個段落的連結"></a></h3>
<p>FB 網頁更新非常頻繁，到現在更有<mark>經典版以及新版的區別</mark>，如果不清楚自己是什麼版本可以看下面的畫面做判斷</p>
<ul>
<li>
<p><strong>經典版</strong></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-03.webp" width="1024" height="359" alt="2.1 判斷自己的 FB 是經典版本還是新版（圖 3）" loading="lazy" decoding="async"></p>
</li>
<li>
<p><strong>新版</strong></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-04.webp" width="1024" height="424" alt="2.1 判斷自己的 FB 是經典版本還是新版（圖 4）" loading="lazy" decoding="async"></p>
</li>
</ul>
<hr>
<h3 class="h-nomark" id="22-在-env加入參數來做區別">2.2 在 .env加入參數來做區別<a class="h-anchor" href="#22-%e5%9c%a8-env%e5%8a%a0%e5%85%a5%e5%8f%83%e6%95%b8%e4%be%86%e5%81%9a%e5%8d%80%e5%88%a5" aria-label="這個段落的連結"></a></h3>
<p>因為有經典版(classic)及新版(new)的區別，所以我們在 .env環境設定檔需要增加一個參數：<code>FB_VERSION</code> 來識別，檔案調整如下：</p>
<h4 id="env">.env<a class="h-anchor" href="#env" aria-label="這個段落的連結"></a></h4>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#填寫自己登入FB的真實資訊(建議開小帳號來實驗，因為帳號使用太頻繁會被官方鎖住)</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">FB_USERNAME</span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;fb username&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">FB_PASSWORD</span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;fb password&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#FB跑經典版還是新版(classic/new)</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">FB_VERSION</span><span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;new&#39;</span>
</span></span></code></pre></div><blockquote>
<p>接下來的畫面也會依據經典版以及新版做相關的說明</p>
</blockquote>
<hr>
<h2 class="h-nomark" id="3-分析今日目標前往-fb-粉專取得追蹤人數資訊-1">3. 分析今日目標：前往 FB 粉專取得追蹤人數資訊<a class="h-anchor" href="#3-%e5%88%86%e6%9e%90%e4%bb%8a%e6%97%a5%e7%9b%ae%e6%a8%99%e5%89%8d%e5%be%80-fb-%e7%b2%89%e5%b0%88%e5%8f%96%e5%be%97%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8%e8%b3%87%e8%a8%8a-1" aria-label="這個段落的連結"></a></h2>
<p>面對複雜問題時，我一律建議把<mark>大問題先拆解成小問題，小問題再細分成功能項目</mark>，這樣同一時間只需集中注意力完成一個功能，這樣的做法能帶給你階段性成就感，也讓你大腦有喘息的空間</p>
<p>像是今天要做的功能 <strong>前往 FB 粉專取得追蹤人數資訊</strong> 可以拆分成幾個步驟：</p>
<ol>
<li>前往FB粉專</li>
<li>找出追蹤者人數的元件位置</li>
<li>關閉瀏覽器</li>
</ol>
<blockquote>
<p>建議大家可以自己先按照昨天所提供的方法來實做看看會遇到什麼樣的問題，再來看下面我我所遇到的狀況及解決方式</p>
</blockquote>
<hr>
<h2 class="h-nomark" id="4-前往-fb-粉專-1">4. 前往 FB 粉專<a class="h-anchor" href="#4-%e5%89%8d%e5%be%80-fb-%e7%b2%89%e5%b0%88-1" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="41-程式執行的順序跟你想的不一樣">4.1 程式執行的順序跟你想的不一樣<a class="h-anchor" href="#41-%e7%a8%8b%e5%bc%8f%e5%9f%b7%e8%a1%8c%e7%9a%84%e9%a0%86%e5%ba%8f%e8%b7%9f%e4%bd%a0%e6%83%b3%e7%9a%84%e4%b8%8d%e4%b8%80%e6%a8%a3" aria-label="這個段落的連結"></a></h3>
<p>我一開始天真的以為在使用者登入後兩行程式碼就能解決</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//登入成功後要前往粉專頁面
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> fanpage <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#34;https://www.facebook.com/baobaonevertell/&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.get(fanpage)
</span></span></code></pre></div><p>但實際執行後你會發現很詭異的事情，<mark>在FB登入成功前網頁就直接導向到粉絲專頁了</mark></p>
<hr>
<h3 class="h-nomark" id="42-從畫面判斷使用者是否登入成功使用-class-抓取網頁元件">4.2 從畫面判斷使用者是否登入成功，使用 Class 抓取網頁元件<a class="h-anchor" href="#42-%e5%be%9e%e7%95%ab%e9%9d%a2%e5%88%a4%e6%96%b7%e4%bd%bf%e7%94%a8%e8%80%85%e6%98%af%e5%90%a6%e7%99%bb%e5%85%a5%e6%88%90%e5%8a%9f%e4%bd%bf%e7%94%a8-class-%e6%8a%93%e5%8f%96%e7%b6%b2%e9%a0%81%e5%85%83%e4%bb%b6" aria-label="這個段落的連結"></a></h3>
<p>因為 FB 在執行登入作業時需要<strong>等待 server 回應資料確認使用者身份</strong>，這塊我們可以從 <mark>FB 有什麼元件是登入後才會出現在畫面</mark>的這個方向去思考，接下來我會介紹<mark>用 Class 抓取網頁元件</mark>的方法</p>
<ul>
<li>
<p><strong>經典版</strong>：一定要在登入後 Facebook 才會有名字顯示，class 為：<code>_1vp5</code></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-05.webp" width="1024" height="301" alt="4.2 從畫面判斷使用者是否登入成功，使用 Class 抓取網頁元件（圖 5）" loading="lazy" decoding="async"></p>
</li>
<li>
<p><strong>新版</strong>：一定要在登入後 Facebook 才會有訊息及通知區塊顯示，共同的 class 為：<code>fzdkajry</code></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-06.webp" width="1024" height="208" alt="4.2 從畫面判斷使用者是否登入成功，使用 Class 抓取網頁元件（圖 6）" loading="lazy" decoding="async"></p>
</li>
</ul>
<blockquote>
<p>昨天教的用 Xpath 取出元件的方法在這裡不適用，因為他們取出的路徑太過複雜，只要 FB 的版面一改版就有很高的機率壞掉<br>* 經典版：//<em>[@id=&ldquo;u_0_e&rdquo;]/div[1]/div[1]/div/a/span/span<br>* 新版： //</em>[@id=&ldquo;mount_0_0&rdquo;]/div/div[1]/div[1]/div[2]/div[4]/div[1]/div[1]/span/div/div[1]/svg</p>
</blockquote>
<ul>
<li>
<p><strong>為了方便區分經典版/新版的 FB</strong>，我們增加一個函式：<code>getCrawlerPath</code>，來回傳各自的爬蟲路徑</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">function</span> getCrawlerPath () {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> (process.env.FB_VERSION <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">&#39;new&#39;</span>) {<span style="color:#8b949e;font-style:italic">//如果是新版FB
</span></span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_head_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[contains(@class,&#34;fzdkajry&#34;)]`</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    } <span style="color:#ff7b72">else</span> {<span style="color:#8b949e;font-style:italic">//如果為設定皆默認為舊版
</span></span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_head_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[contains(@class,&#34;_1vp5&#34;)]`</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 class="h-nomark" id="43-加入登入完成才能前往粉專的邏輯">4.3 加入登入完成才能前往粉專的邏輯<a class="h-anchor" href="#43-%e5%8a%a0%e5%85%a5%e7%99%bb%e5%85%a5%e5%ae%8c%e6%88%90%e6%89%8d%e8%83%bd%e5%89%8d%e5%be%80%e7%b2%89%e5%b0%88%e7%9a%84%e9%82%8f%e8%bc%af" aria-label="這個段落的連結"></a></h3>
<ul>
<li>我們用上一步的class：<code>fb_head_path</code>，作為判斷登入與否的依據</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// FB有經典版以及新版的區分，兩者的爬蟲路徑不同，我們藉由函式取得各自的路徑
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> { fb_head_path } <span style="color:#ff7b72;font-weight:bold">=</span> getCrawlerPath();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//因為登入這件事情要等server回應，你直接跳轉粉絲專頁會導致登入失敗
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//以登入後才會出現的元件作為判斷登入與否的依據
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.wait(until.elementLocated(By.xpath(fb_head_path)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//登入成功後要前往粉專頁面
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> fanpage <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#34;https://www.facebook.com/baobaonevertell/&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.get(fanpage)
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span></code></pre></div><hr>
<h2 class="h-nomark" id="5-取得-fb-粉專追蹤人數-1">5. 取得 FB 粉專追蹤人數<a class="h-anchor" href="#5-%e5%8f%96%e5%be%97-fb-%e7%b2%89%e5%b0%88%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8-1" aria-label="這個段落的連結"></a></h2>
<h3 class="h-nomark" id="51-在粉專頁面找出追蹤人數的位置並分析取出元件的方法">5.1 在粉專頁面找出追蹤人數的位置，並分析取出元件的方法<a class="h-anchor" href="#51-%e5%9c%a8%e7%b2%89%e5%b0%88%e9%a0%81%e9%9d%a2%e6%89%be%e5%87%ba%e8%bf%bd%e8%b9%a4%e4%ba%ba%e6%95%b8%e7%9a%84%e4%bd%8d%e7%bd%ae%e4%b8%a6%e5%88%86%e6%9e%90%e5%8f%96%e5%87%ba%e5%85%83%e4%bb%b6%e7%9a%84%e6%96%b9%e6%b3%95" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p><strong>經典版</strong></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-07.webp" width="1024" height="803" alt="5.1 在粉專頁面找出追蹤人數的位置，並分析取出元件的方法（圖 7）" loading="lazy" decoding="async"></p>
<ul>
<li>
<p>紅框位置 Xpath 的路徑</p>
<pre tabindex="0"><code>//*[@id=&#34;PagesProfileHomeSecondaryColumnPagelet&#34;]/div/div[1]/div/div[1]/div[4]/div/div[2]/div
</code></pre></li>
<li>
<p>如果你只要爬這個粉絲團的話用這個 Xpath 就足夠了，但你如果常逛粉絲團，你會發現<mark>不是每個粉絲團顯示追蹤人數的 Xpath 位置都一樣</mark>，下面的粉絲團網址你可以點進去試試看：</p>
<ul>
<li><a href="https://www.facebook.com/missunexpected2015/">小姐非常有事</a></li>
</ul>
<pre tabindex="0"><code>//*[@id=&#34;PagesProfileHomeSecondaryColumnPagelet&#34;]/div/div[1]/div/div[2]/div[4]/div/div[2]/div
</code></pre><ul>
<li><a href="https://www.facebook.com/HumanDesignAu/">人類圖澳洲</a></li>
</ul>
<pre tabindex="0"><code>//*[@id=&#34;PagesProfileHomeSecondaryColumnPagelet&#34;]/div/div[3]/div/div[2]/div[4]/div/div[2]/div
</code></pre><p>仔細一看就會發現 <strong>每個 Xpath 都會有細微的不同</strong> ，所以昨天教的 Xpath 在這裡就失靈了，我們需要換一個方法來判斷，也就是該元件的 class 結構</p>
</li>
<li>
<p><strong>使用 class 找出網頁元件</strong></p>
<ul>
<li>
<p>下面的幾張圖你可以觀察到這個追蹤者的資訊都在相同的 <strong>class=&quot;_4bl9&quot;</strong> 之下</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-08.webp" width="230" height="149" alt="Imgur" loading="lazy" decoding="async"></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-09.webp" width="230" height="149" alt="Imgur" loading="lazy" decoding="async"></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-10.webp" width="230" height="149" alt="Imgur" loading="lazy" decoding="async"></p>
</li>
<li>
<p>但是 Facebook 有很多的元件都使用到這個 class 所以我們需要<strong>把所有符合的 class 都抓下來，透過分析字串(xxx人在追蹤)來得到正確的資訊</strong></p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>新版</strong></p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-11.webp" width="1024" height="670" alt="5.1 在粉專頁面找出追蹤人數的位置，並分析取出元件的方法（圖 11）" loading="lazy" decoding="async"></p>
<ul>
<li>
<p>紅框位置Xpath的路徑</p>
<pre tabindex="0"><code>//*[@id=&#34;mount_0_0&#34;]/div/div[1]/div[1]/div[3]/div/div/div[1]/div[1]/div[4]/div[2]/div/div[1]/div[2]/div[1]/div/div/div/div[2]/div[4]/div/div/div/div[2]/div/div/span/span
</code></pre></li>
<li>
<p><strong>與經典版相同的地方</strong>：不是每個粉絲團的 Xpath 都一樣，所以我們需要改用 class 結構來分析</p>
</li>
<li>
<p><strong>class 結構分析</strong>：你會很崩潰的發現他的 class 有夠多&hellip;</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-12.webp" width="950" height="132" alt="5.1 在粉專頁面找出追蹤人數的位置，並分析取出元件的方法（圖 12）" loading="lazy" decoding="async"></p>
<ul>
<li><mark>在發現這些 class 都與很多 facebook 元件共用的狀態下，你要思考的是哪個元件被共用的次數是最少的</mark>，經由這樣的邏輯思考後，發現 <code>class=&quot;knvmm38d&quot;</code> 是裡面共用次數最少的</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>為了方便區分經典版/新版的FB</strong>，我們將上面的路徑新增到 <code>getCrawlerPath</code> 回傳參數中</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">function</span> getCrawlerPath () {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> (process.env.FB_VERSION <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">&#39;new&#39;</span>) {<span style="color:#8b949e;font-style:italic">//如果是新版FB
</span></span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_head_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[contains(@class,&#34;fzdkajry&#34;)]`</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_trace_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[contains(@class,&#34;knvmm38d&#34;)]`</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    } <span style="color:#ff7b72">else</span> {<span style="color:#8b949e;font-style:italic">//如果為設定皆默認為舊版
</span></span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_head_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[contains(@class,&#34;_1vp5&#34;)]`</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">&#34;fb_trace_path&#34;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">`//*[@id=&#34;PagesProfileHomeSecondaryColumnPagelet&#34;]//*[contains(@class,&#34;_4bl9&#34;)]`</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 class="h-nomark" id="52-取出粉專的追蹤者人數">5.2 取出粉專的追蹤者人數<a class="h-anchor" href="#52-%e5%8f%96%e5%87%ba%e7%b2%89%e5%b0%88%e7%9a%84%e8%bf%bd%e8%b9%a4%e8%80%85%e4%ba%ba%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<ul>
<li>
<p>因為這次要<mark>抓取的資料要從複數個 class 裡面篩選出來</mark>，但在我們無法決定 class 載入的順序的情況下，偶而會出現抓不到追蹤人數的元件，為了保證元件載入的穩定性，讓<mark>瀏覽器前往粉專頁面3秒後再進行爬蟲</mark></p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.sleep(<span style="color:#a5d6ff">3000</span>)
</span></span></code></pre></div></li>
<li>
<p>在確認需要分析的 class 之後，只要把對應的 class 全部取出來轉換成文字，<strong>用 for/of迴圈做字串分析</strong>就能找出需要的資訊</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">await</span> driver.sleep(<span style="color:#a5d6ff">3000</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> fb_trace <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>;<span style="color:#8b949e;font-style:italic">//這是紀錄FB追蹤人數
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">//因為考慮到登入之後每個粉專顯示追蹤人數的位置都不一樣，所以就採用全抓在分析
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> fb_trace_eles <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> driver.wait(until.elementsLocated(By.xpath(fb_trace_path)))
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> (<span style="color:#ff7b72">const</span> fb_trace_ele <span style="color:#ff7b72">of</span> fb_trace_eles) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">const</span> fb_text <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> fb_trace_ele.getText()
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> (fb_text.includes(<span style="color:#a5d6ff">&#39;人在追蹤&#39;</span>)) {
</span></span><span style="display:flex;"><span>        fb_trace <span style="color:#ff7b72;font-weight:bold">=</span> fb_text
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">`追蹤人數：</span><span style="color:#a5d6ff">${</span>fb_trace<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">`</span>)
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>.
</span></span></code></pre></div><blockquote>
<p>儘量不要在 forEach 中使用 aysnc/await，因為他還需要透過一個 callback 函式才能使用，邏輯表現不如 for/of 來的直觀</p>
</blockquote>
<hr>
<h2 class="h-nomark" id="6-完成爬蟲後關閉瀏覽器-1">6. 完成爬蟲後關閉瀏覽器<a class="h-anchor" href="#6-%e5%ae%8c%e6%88%90%e7%88%ac%e8%9f%b2%e5%be%8c%e9%97%9c%e9%96%89%e7%80%8f%e8%a6%bd%e5%99%a8-1" aria-label="這個段落的連結"></a></h2>
<p>確認爬蟲都執行完畢後，用下面這行程式即可關閉瀏覽器</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>driver.quit();
</span></span></code></pre></div><hr>
<h3 id="執行程式">執行程式<a class="h-anchor" href="#%e5%9f%b7%e8%a1%8c%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h3>
<ol>
<li>
<p>在專案資料夾的終端機(Terminal)執行指令</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>yarn start
</span></span></code></pre></div></li>
<li>
<p>你會看到 Facebook 自動登入 → 跳轉到粉絲頁 → 關閉</p>
</li>
<li>
<p>如果能正確輸出該粉專的追蹤人數你就成功嚕～</p>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-13.webp" width="1024" height="134" alt="執行程式（圖 13）" loading="lazy" decoding="async"></p>
</li>
</ol>
<p>相信到這裡大家都能成功地抓出粉專的追蹤者人數了，對這個爬蟲專案應該也充滿了信心了吧！</p>
<hr>
<h3 id="ℹ-專案原始碼">ℹ️ 專案原始碼<a class="h-anchor" href="#%e2%84%b9-%e5%b0%88%e6%a1%88%e5%8e%9f%e5%a7%8b%e7%a2%bc" aria-label="這個段落的連結"></a></h3>
<ul>
<li>今天的完整程式碼可以在<a href="https://github.com/dean9703111/ithelp_30days/tree/master/day10">這裡</a>找到喔</li>
<li>我也貼心地把昨天的把昨天的程式碼打包成<a href="https://github.com/dean9703111/ithelp_30days/raw/master/sampleCode/day9_sample_code.zip">壓縮檔</a>，你可以用裡面乾淨的環境來實作今天的功能喔
<ul>
<li>請記得在終端機下指令 <strong>yarn</strong> 才會把之前的套件安裝</li>
<li>windows需下載與你chrome版本相同的<a href="http://chromedriver.storage.googleapis.com/index.html">chrome driver</a>放在專案根目錄</li>
<li>調整.env檔
<ul>
<li>填上FB登入資訊</li>
<li>填上FB版本(classic/new)</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="參考資源">參考資源<a class="h-anchor" href="#%e5%8f%83%e8%80%83%e8%b3%87%e6%ba%90" aria-label="這個段落的連結"></a></h3>
<ol>
<li><a href="https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop">Using async/await with a forEach loop</a></li>
<li><a href="https://www.jishuwen.com/d/2M0c/zh-tw">JavaScript 的 4 種陣列遍歷方法： for VS forEach() VS for/in VS for/of</a></li>
</ol>
<blockquote>
<p><em>免責聲明:文章技術僅抓取公開數據作爲研究，任何組織和個人不得以此技術盜取他人智慧財產、造成網站損害，否則一切后果由該組織或個人承擔。作者不承擔任何法律及連帶責任！</em></p>
</blockquote>
<hr>
<blockquote>
<p><img src="https://deanlin.net/blog/202009/fb-ig-crawler-day-10/images/fb-ig-crawler-day-10-14.webp" width="1024" height="233" alt="參考資源（圖 14）" loading="lazy" decoding="async"></p>
<p><strong>在許多人的幫助下，本系列文章已出版成書，並添加了新的篇章與細節補充：</strong></p>
<ul>
<li>加入更多實務經驗，用完整的開發流程讓讀者了解專案每個階段要注意的事項</li>
<li>將爬蟲的步驟與技巧做更詳細的說明，讓讀者可以輕鬆入門</li>
<li>調整專案架構
<ul>
<li>優化爬蟲程式，以更廣的視角來擷取網頁資訊</li>
<li>增加資料驗證、錯誤通知等功能，讓爬蟲執行遇到問題時可以第一時間通知使用者</li>
<li>排程部分改用 node-schedule &amp; pm2 的組合，讓讀者可以輕鬆管理專案程序並獲得更精確的 log 資訊</li>
</ul>
</li>
</ul>
<p><strong>有興趣的朋友可以到天瓏書局選購，感謝大家的支持。</strong></p>
<p><strong>購書連結</strong>：<a href="https://www.tenlong.com.tw/products/9789864348008">https://www.tenlong.com.tw/products/9789864348008</a></p>
</blockquote>
]]></content:encoded>
    </item>
  </channel>
</rss>
