הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

טיפ Web Scraping שלמדתי מטלגרם

01/01/2022

הפיצ'ר הכי מלהיב בגירסה החדשה של טלגרם נקרא תרגומים והוא מאפשר לכם לקבל תרגום של הודעה בלחיצת כפתור ובלי לצאת מהיישום. וזה מלהיב כי עכשיו כבר לא צריך יותר לעבור בין טלגרם לגוגל טרנסלייט בקריאה של ערוצים זרים.

אבל חוץ מהנוחות פיצ'ר התרגומים גם חשף טריק שלפחות אני לא חשבתי עליו קודם לגבי Web Scraping: בשביל לתרגם טלגרם הולך בעצמו לגוגל טרנסלייט עם הטקסט של ההודעה, ובשביל לא לשלם לגוגל הם משתמשים ב API פרטי שבו כרום משתמש כדי לתרגם דפי אינטרנט (במקום ב Cloud API הרגיל). הצצה זריזה בקוד של טלגרם מראה שהם משתמשים בטריק קטן כדי לא להיחסם:

private String[] userAgents = new String[] {
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", // 13.5%
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", // 6.6%
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", // 6.4%
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0", // 6.2%
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", // 5.2%
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36" // 4.8%
};

connection.setRequestProperty("User-Agent", userAgents[(int) Math.round(Math.random() * (userAgents.length - 1))]);

המשחק הוא פשוט - באותה פונקציה ששולחת בקשה לגוגל טרנסלייט טלגרם מגרילים כל פעם מחרוזת User Agent שונה, בשביל שגוגל יחשבו שמדובר במכשיר אחר כל פעם.

עד כמה זה הכרחי או עוזר? אני לא יודע. אבל תמיד נחמד להציץ בקוד של אחרים כדי ללמוד איך העולם עובד.


ותודה לדן פטרוב על המחקר והמאמר ממנו למדתי את הטריק. אם יש לכם רגע שווה לקרוא את כל הפוסט שלו.

שלושה מכשולים שבגללם אתה מסתבך עם דוקר

31/12/2021

דוקר הוא אחד מכלי הפיתוח הכי חשובים של השנים האחרונות, ובכל זאת מתכנתים לא לומדים אותו מספיק. כשאנחנו מגיעים למקום עבודה שמשתמש בדוקר אנחנו מעדיפים לעשות copy-paste לפקודות בהן כולם משתמשים וכשמשהו נשבר מקווים שאחרי איזה ריסטרט הוא יתקן את עצמו. אני יכול לחשוב על שלושה מכשולים מרכזיים בגללם אנחנו מעדיפים לשמור מרחק ביטחון מהטכנולוגיה הזו-

המשך קריאה

אצלי זה עובד (טייק 319)

30/12/2021

אחת הסיבות שדברים נשברים בלי אזהרה מוקדמת היא יצירתיות של כותבי פריימוורקס (שלרוב לא מפורסמת בדף הראשי של הפריימוורק). הדוגמה היום היא מ Jest, ושימו לב ל Issue הבא: https://github.com/facebook/jest/issues/5818

ג'סט, למי שלא מכירה, היא ספריית בדיקות JavaScript שיכולה לשמש לבדיקות קוד צד שרת וגם צד לקוח. אחד הדברים החדשניים ש Jest עושה ישר מהקופסה הוא הרצה מקבילית, כלומר אם יש לכם מספיק בדיקות - ומספיק זו מילת המפתח כאן - ג'סט ינסה לארגן חלק מהן לקבוצות ולהריץ אותן במקביל כדי לחסוך לכם זמן.

עכשיו אם יש משהו שהעולם לימד אותנו על לעשות דברים במקביל זה שהטריק הזה דורש מאמץ. רוב הסיכויים שאם כתבתם קוד "סתם כך" ואז התחלתם להריץ אותו במקביל אז משהו יישבר, ובמיוחד כשמדובר בבדיקות: מספיק ששתי בדיקות יוצרות קובץ באותו שם, או יוצרות מראש מידע זהה בבסיס הנתונים, בשביל שידרכו אחת לשניה על הרגליים. גם מתכנתים שיודעים שהבדיקות שלהם הולכות לרוץ במקביל עושים טעויות בכתיבת בדיקות כאלה.

בחזרה לג'סט ול Issue שבקישור - מסתבר שג'סט יריץ את הבדיקות שלכם במקביל רק אם הוא חושב שיש מספיק בדיקות בשביל זה ושהבדיקות מספיק איטיות בשביל שבאמת תרוויחו משהו מהרצה מקבילית. הוא גם לא יריץ במקביל בדיקות שכתובות באותו קובץ בדיקה.

אז אם אתם מתכנתים שכותבים קוד, כותבים קובץ בדיקה, מריצים את קובץ הבדיקה או כמה בדיקות קטנות ממנו ואז ממשיכים הלאה הכל יעבוד לכם כי הבדיקות רצות אחת אחרי השניה. אבל ברגע שתעבירו את הבדיקות ל CI להרצה אוטומטית, וה CI יריץ את כל סט הבדיקות תתחילו לקבל אינסוף כשלונות לא ברורים. הרצה ידנית של כל בדיקה שנכשלת או של קובץ בדיקות בודד תצליח בלי בעיה כי הכשלונות נובעים מאותה הרצה מקבילית שעובדת רק כשזה "משתלם".

ג'סט לא לבד. הרצון של כותבי כלים לכתוב כלים שירוצו מהר ויתנו הרגשה טובה למתכנתים תמיד יחזור לתקוף אותנו במעבר לפרודקשן. שמרו על ערנות והיזהרו מכלים קלים מדי.

מתי כן להשתמש ב mock

29/12/2021

ברוב הבדיקות שאני כותב אני בורח מ Mocks כמו מאש. הבעיות המרכזיות עם Mocks הן העבודה הנוספת שהוא דורש בכתיבת ה Mock ובתחזוקה, ואובדן האמינות בגלל שאני לא בודק את הדבר האמיתי. אבל Mock הוא לא לגמרי כלי מיותר ואני יכול לחשוב על שני מצבים בהם הוא אפילו הפיתרון המומלץ:

המשך קריאה

איך להשתמש ב Web Worker כדי לשפר ביצועים בקומפוננטת ריאקט

28/12/2021

רוב הזמן יישומי Front End לא עושים עבודה חישובית יותר מדי קשה, ולכן את רוב שיפורי הביצועים בריאקט אפשר לפתור עם צמצום קריאות מהשרת או צמצום render-ים. אבל מדי פעם כן יש לנו קומפוננטה שצריכה לעשות עבודה חישובית, וכשזה קורה כדאי לדעת מה לעשות - והתשובה הפשוטה היא Web Worker.

ניקח לדוגמה את הקומפוננטה הבאה שמציגה את מספר המספרים הראשוניים עד מיליון:

import { useState } from 'react';

function isPrime(n) {
  for (let i=2; i < n/2; i++) {
    if (n % i === 0) {
      return false;
    }
  }
  return true;
}

function calculatePrimesUntilMillion() {
  let count = 0;
  console.log('Start');

  for (let i=2; i < 1000000; i++) {
    if (isPrime(i)) {
      count += 1;
    }
  }
  console.log('Ready');
  return count;
}

function App() {
  const [_, forceRender] = useState(0);

  return (
    <div className="App">
      <p>There are {calculatePrimesUntilMillion()} prime numbers &lt; 1,000,000</p>
      <button onClick={() => forceRender(v => !v)}>Calculate again</button>
    </div>
  );
}

export default App;

אני כתבתי לבד את הקוד שסופר ואני אעשה לכם ספוילר - זאת ממש לא הדרך הכי יעילה לספור מספרים ראשוניים. למעשה היא מספיק לא יעילה כדי שאם תריצו את הקומפוננטה הזאת על המחשב תוכלו להרגיש כמה שניות טובות שהדפדפן ממש "תקוע", ואפילו לא מגיב כשמשנים את גודל החלון. אם יהיה לכם מזל תקבלו מכרום את ההודעה שהטאב נתקע והוא מציע לכם לסגור אותו.

כל זה קורה בגלל שלפונקציה calculatePrimesUntilMillion לוקח יותר מדי זמן לרוץ, ובזמן הזה היא תוקעת את כל ממשק המשתמש. ברור שהדרך הכי טובה לפתור את הבעיה במקרה הזה היא לתקן את הקוד כך שירוץ יותר מהר, אבל במקרה הכללי לפעמים אתם באמת צריכים לעשות הרבה עבודה. בשביל זה בדיוק יש לנו את ה Web Worker.

ווב וורקר הוא דרך להפעיל קוד חישובי ב Thread אחר ולקבל ממנו תשובה. בריאקט אנחנו נשתמש ב useEffect כדי לשלוח הודעה ל Worker שלנו, ה Worker יעשה את החישוב בצורה אסינכרונית ובסוף נשתמש במשתנה State כדי לשמור את התוצאה ולהציג אותה. בעצם Web Worker יהפוך את הקוד שלנו מכזה ש"תוקע" את הדפדפן לקוד אסינכרוני רגיל שמחכה למשהו שיקרה.

אני משתמש ב create-react-app בגירסה החדשה ביותר והוא משתמש ב webpack 5 ושם התמיכה מ Web Worker היא מובנית. בשביל להפוך את הפרויקט שלי להשתמש ב Web Worker אני צריך:

  1. ליצור קובץ חדש בשם primes.js עם התוכן הבא:
function isPrime(n) {
  for (let i=2; i < n/2; i++) {
    if (n % i === 0) {
      return false;
    }
  }
  return true;
}

function calculatePrimesUntilMillion() {
  let count = 0;
  console.log('Start');

  for (let i=2; i < 1000000; i++) {
    if (isPrime(i)) {
      count += 1;
      postMessage({ count });
    }
  }
  console.log('Ready');
  return count;
}

onmessage = function(_ev) {
  const count = calculatePrimesUntilMillion();
  postMessage({ count });
}

  1. לעדכן את קוד הקומפוננטה כדי לטעון את ה Worker:
const worker = new Worker(new URL('./primes.js', import.meta.url));

לעדכן את הקומפוננטה עצמה כך שתשתמש ב Worker, כלומר תשלח לו הודעות ותקבל ממנו עדכונים:

function App() {
  const [forceRender, setForceRender] = useState(0);
  const [primesCount, setPrimesCount] = useState(0);

  useEffect(() => {
    worker.onmessage = function(ev) {
      setPrimesCount(ev.data.count);
    };

    worker.postMessage({});
  }, [forceRender]);

  return (
    <div className="App">
      <p>There are {primesCount} prime numbers &lt; 1,000,000</p>
      <button onClick={() => setForceRender(v => !v)}>Calculate again</button>
    </div>
  );
}

בהפעלה בגירסה החדשה לא רק שהדפדפן ממשיך להגיב בזמן החישוב, אלא שאנחנו גם רואים את המספרים עולים - כי כל פעם שה Worker מוצא מספר ראשוני חדש הוא שולח Post Message שמגיע לקומפוננטה וגורם לעדכון המשתנה primesCount.

אם אתם עובדים בגירסאות ישנות יותר של create-react-app או וובפאק, שווה לבדוק את הספריה https://github.com/developit/workerize-loader שמאפשרת לטעון Web Worker גם בוובפאק 4.

מעלה הילוך

27/12/2021

כשאנחנו מקשיבים למה שקורה בתעשיה או מסתכלים ברשת על כל מיני Best Practices אנחנו עלולים לטעות ולחשוב ש-

  1. כולם כותבים Micro Services, מונוליט זה מיושן.

  2. אצל כולם יש בדיקות (יחידה + קצה לקצה).

  3. כולם עובדים עם הגירסאות החדשות ביותר של התלויות, מתקינים טלאי אבטחה בזמן ומבצעים Pen Testing לפי הוראות היצרן.

  4. לכולם יש גיבויים מתוקתקים ופעם בחודש הם עושים תרגיל שחזור מגיבוי.

  5. כולם משתמשים ב CI ומחזיקים מנגנון Deployment אוטומטי שיודע להריץ את הבדיקות ולהעביר גירסאות לפרודקשן ללא מגע יד.

  6. לכולם יש מערכות ניטור מתוחכמות שמזהות פירצות לשרתים בזמן אמת.

  7. כולם מחזיקים תיעוד עדכני של המערכת כך שמתכנתים חדשים יכולים בקלות להיכנס לקוד.

  8. כולם מוחקים קוד שלא משתמשים בו.

  9. כולם שולטים בטכנולוגיות בהן הם עובדים.

אני בטוח שיש מתכנתים שיכולים לסמן "וי" על כל הרשימה כאן, אבל האמת שמעולם לא פגשתי אותם. המטרה של כל הכתבות האלה באינטרנט היא לא שנרגיש רע, אלא שתמיד יהיה לנו מול העיניים רעיון לצעד הבא. אבל זה לא אומר שחייבים עכשיו לקחת אותו.

לכל סעיף כאן יש עלות. כמו שאי אפשר להתחיל נסיעה בהילוך חמישי, גם פרויקט צריך את הקצב והזמן שלו. עד שצוברים מהירות מספיק להשאיר את הרשימה מול העיניים ולהחליט עם עצמכם ומראש מה יהיה ההילוך הבא.

הכח לקום בבוקר

26/12/2021

בשביל ללמוד משהו חדש, לבנות משהו חדש או לאמץ הרגל חדש צריך אנרגיה. אנרגיה, כלומר הכח לעשות את הדבר הזה שצריך לעשות כל יום בשביל להגיע למטרה שאותה רוצים להשיג. את האנרגיה אפשר להשיג מאחד או יותר מהמקורות הבאים:

  1. חלומות - החלום על איך החיים יראו אחרי שאדע את הדבר הזה שאני לומד (חלום על העבודה שתהיה לי, על הטיול שאוכל לעשות)

  2. לחץ חברתי - כל החברים נרשמו לחוג כדורגל ומשחקים יחד בהפסקות, אז כדאי שגם אני אצטרף...

  3. הבוס אמר לי - כשיש מטרה קונקרטית שצריך לבצע, לדוגמה פרויקט בעבודה.

  4. זה מעניין אותי - כי כשאני מתעניין במשהו ההשקעה נראית הרבה יותר קלה.

  5. רצון להרשים ולהפתיע - כשגורם חיצוני שחשוב לי מאוד מעריך הישג מסוים, יש סיכוי טוב שאצליח להתמיד כדי להרשים אותו/אותה.

  6. כי אנשים כמוני עושים דברים כאלה - כשאני מצפה מעצמי להתנהגות מסוימת שמתאימה לדימוי העצמי שלי.

אם אתם מנסים לעשות שינוי ולא מצליחים להתמיד, הטריק כאן הוא לחפש מקורות אנרגיה נוספים (אפשר יותר מאחד) שיעזרו לנו לעבור את הקושי הראשוני.

אופטימיות

25/12/2021

בטעות קניתי היום את הספר הלא נכון באודיבל. נו, רציתי לקנות ספר מסוים ואיכשהו הייתי בטוח שאני בעמוד שלו אפילו שהייתי בעמוד של ספר אחר ומפה לשם שילמתי על הספר שלא רציתי.

עכשיו לא נעים להגיד אבל זאת לא הפעם הראשונה שזה קורה לי. אני מכיר את אמזון ואודיבל, וקיבלתי מהם כבר לא מעט החזרים בעבר אפילו על דברים הרבה יותר מוזרים. לכן בלי לחשוב פעמיים שלחתי אימייל לתמיכה להסביר את המצב והמשכתי לקנות את הספר שרציתי, בלי לחכות לתשובה של התמיכה או לזיכוי. הכל טוב הזיכוי יגיע.

היכולת להתקדם בלי לחכות לראות שהצעד הקודם הצליח היא חלק ממה שאנחנו קוראים אופטימיות. ככל שיש לנו פחות נסיון בנושא מסוים כך נדרשת מנה גדולה יותר של אופטימיות כדי להתקדם, והיא רק הופכת יותר חשובה כשאנחנו מסתכלים על דברים גדולים יותר מספרים קוליים:

  1. אופטימיות מאפשרת לי להמשיך להשקיע בפיתוח הפרויקט שלי, גם אם הוא עדיין לא הכניס אף לקוח. אני יודע שבסוף אמצא את הדרך למכירות, וכדאי שהפרויקט יהיה מוכן כשזה יקרה.

  2. אופטימיות מאפשרת לי להמשיך ללמוד ולתרגל JavaScript, גם אם הקורות חיים שלי עדיין לא מספיק טובים כדי להגיע לראיון עבודה. אני יודע שבסוף אצליח למצוא את הדרך לראיונות וכדאי שאגיע מוכן כשזה יקרה.

  3. אופטימיות מאפשרת לי לחקור לעומק כיוון מסוים לפיתרון בלי לדאוג שאולי זה לא הכיוון הנכון. אני יודע שבסוף אמצא את הבאג והדרך להגיע אליו היא להיכנס לעובי הקורה.

בקצרה אופטימיות מאפשרת לנו להתקדם עם התהליך שעכשיו דורש את תשומת הלב שלנו (קניית הספר הנכון), ולהניח שתהליכים אחרים יסתדרו (אפילו שאולי לא לגמרי ברור עכשיו איך).

ומרגע שהבנו איך האופטימיות משפיעה על ההתנהגות ביום יום אפשר להתחיל לתרגל אותה: גם אם אתה לא בטוח בכלל שתקבל זיכוי, בכל זאת לקנות את הספר הנכון. גם אם את מרגישה שלעולם לא יזמינו אותך לראיון עבודה, בכל זאת להמשיך ללמוד כל יום לפי שעון ולא לפי מוטיבציה.

כי ההתנהגות משפיעה על הגישה, ולאורך זמן ההתנהגות האופטימית והגישה האופטימית עוזרות לנו להשקיע בתהליכים הנכונים ובכמות הנדרשת בשביל להתקדם.

הזמן הכי טוב לתקן בדיקה

24/12/2021

הזמן הכי טוב לתקן בדיקה הוא בדיוק ברגע שכתבת את הקוד ששבר אותה. ככל שמתרחקים מאותו הרגע קורים שני דברים רעים:

  1. אתה שוכח מה עשית ולמה זה שבר את הבדיקה.

  2. בדיקות נוספות נשברות בגלל שינויים חדשים.

כמעט בכל פרויקט, אם אני מעביר חודשיים בלי להריץ את הבדיקות - אפילו אם באמת לא שברתי כלום מבחינת הלוגיקה של המערכת - עדיין יהיו לי לא מעט בדיקות שבורות לתקן.

בעבודה עם גיט קל לייצר תהליך עבודה אוטומטי שמריץ את הבדיקות לפני הקומיט. בהנחה שאתם בפרויקט node ומריצים את הבדיקות עם npm test כל מה שצריך לעשות זה ליצור סקריפט חדש בתיקיית .git/hooks ולקרוא לו בשם pre-commit, בתוכו לכתוב את התוכן:

#!/bin/sh

echo "*****Running unit tests******"

git stash -q --keep-index

npm test

status=$?

git stash pop -q

exit $status

ולתת לו הרשאות הרצה:

$ chmod +x .git/hooks/pre-commit

בפעם הבאה שתנסו לעשות commit לקוד גיט קודם כל יריץ את הבדיקות ויצעק עליכם אם משהו נכשל.

נ.ב. בפרויקט שנוצר עם create-react-app צריך להפעיל את שורת הרצת הבדיקות ובמקום לכתוב npm test נכתוב שם npm test -- --watchAll=false כדי להריץ את הבדיקות פעם אחת ולא לעקוב אחרי שינויים בקבצים.

איך לבדוק Custom Hook בריאקט

23/12/2021

ספריית react-testing-library מספקת כלים מצוינים לבדיקת קומפוננטות - אבל מה עושים עם Custom Hooks? האם עלינו להשאיר אותם לגורלם להישבר ללא בדיקות?

מסתבר שיש פיתרון ואפילו פשוט - במקום לכתוב קוד שבודק את ה Hook, אנחנו כותבים קומפוננטה שמשתמשת ב Hook ובודקים את הקומפוננטה. בואו נראה דוגמה.

המשך קריאה