קבלת מיקוד לפי כתובת וכתובת לפי מיקוד (ובנוסף רשימת ערים ורחובות בישראל)

19 באוגוסט 2014

תגיות: ,
8 תגובות

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

לאחר חיפושים מצאתי שיש תוסף לכרום שעושה זאת שנכתב על ידי עומרי, לאחר התקנה של התוסף, הסתכלתי בקוד שלו (שנמצא C:\Users\{user}\AppData\Local\Google\Chrome\User Data\Default\Extensions\gkjdlidimdcgmohgflkklbhkphgfpkfk\1.3_0) ויש לו שם קוד מעניין שעושה את העבודה.

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

לכן כתבתי WCF Service שחושף את המידע, כאן ניתן להוריד את הפרויקט השלם. (קרדיט: הבנת המידע שמגיע מדואר ישראל העתקתי מהקוד שלו)

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

 

השירות חושף את המידע גם ב – WebGet כך שניתן לפנות אליו ב – ajax או מתוך הדפדפן.

להלן ה – Contract

Code Snippet
[ServiceContract]
public interface IMikudService
{
    [OperationContract, WebGet]
    string[] CityAutoComplete(string startsWith);

    [OperationContract, WebGet]
    IdName[] StreetAutoComplete(string startsWith, string location);

    [OperationContract, WebGet]
    ZipCode GetZipCodeByAddress(string city, string street, int house, string entrance = null);

    [OperationContract, WebGet]
    ZipCode GetZipCodeByPOB(string city, string pob);

    [OperationContract, WebGet]
    Address GetAddressByZipCode(string zipCode);
}

המתודות שהשירות חושף הם כדלהלן:

  • CityAutoComplete– מקבל התחלה של שם עיר ומחזיר השלמה של ערים.
  • StreetAutoComplete – מקבל התחלה של רחוב ושם העיר ומחזיר השלמה של רחובות.
  • GetZipCodeByAddress – מקבל עיר, רחוב, מספר, כניסה (א-י) ומחזיר את המיקוד.
  • GetZipCodeByPOB – מקבל עיר ות.ד. ומחזיר את המיקוד.
  • GetAddressByZipCode – מקבל מיקוד ומחזיר כתובת (עיר, רחוב, בית ומספר או עיר ות.ד.)

 

מתודות CityAutoComplete: (במתודה הזו נראה גם מספר מתודות המשמשות גם עבור המתודות האחרות)

Code Snippet
private const string baseUrl = "http://www.israelpost.co.il/zip_data.nsf/";
private static Encoding hebrew = Encoding.GetEncoding("Windows-1255");

public string[] CityAutoComplete(string startsWith)
{
    string data = DownloadString(MikudAction.City, "StartsWith={0}", startsWith);
    data = RemoveBrackets(data);

    var obj = JsonConvert.DeserializeObject<Cities>(data);
    return obj.Locations.Select(x => x.N).ToArray();
}

    private static string DownloadString(MikudAction action, string format, params object[] args)
{
    var parameters = args.Select(x => HttpUtility.HtmlEncode(x)).ToArray();
    WebClient wc = new WebClient();

    string actionString = GetActionString(action);

    var result = wc.DownloadData(baseUrl + actionString + string.Format(format, parameters));

    return hebrew.GetString(result);
}

private static string GetActionString(MikudAction action)
{
    string actionString = null;
    if (action == MikudAction.City)
    {
        actionString = "CreateLocationsforAutoComplete?OpenAgent&";
    }
    else if (action == MikudAction.Street)
    {
        actionString = "CreateStreetsforAutoComplete?OpenAgent&";
    }
    else if (action == MikudAction.Zip)
    {
        actionString = "SearchZip?OpenAgent&";
    }
    else if (action == MikudAction.Address)
    {
        actionString = "SearchAddress7?OpenAgent&";
    }
    return actionString;
}

private static string RemoveBrackets(string data)
{
    int indexOfArray = data.IndexOf("{");
    data = data.Substring(indexOfArray, data.Length – indexOfArray – 3);

    return data;
}

 

ראשית נגדיר שני משתנים בהם נשתמש, הראשון baseUrl המכיל את כתובת הבסיס שאליה פונים, והשני encoding לעברית כדי להמיר את התוכן שמגיע מדואר ישראל.

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

הפונקציה DownloadString, מבצעת encoding לפרמטר שהתקבל (היות שנקבל עברית) לאחר מכן נפנה למתודת GetActionString עם הערך של ה – enum שקבלנו, ונקבל בחזרה את הכתובת, בעזרת WebClient נקבל את המידע (כ – []byte) ונמיר אותו בעזרת ה – hebrew encoder לעברית.

חזרנו לפונקציה CityAutoComplete, נפעיל את פונקצית RemoveBrackets המוחקת מהערך שחוזר מספר תווים מיותרים (לזכור שהשירות הוא שירות פנימי של דואר ישראל), ולאחר מכן נשתמש ב – JsonConvert של json.net כדי להמיר לאובייקט מסוג Cities (נמצא כחלק מהפרוייקט הניתן להורדה בלינק למעלה), ובסופו של דבר נמיר את התוכן למערך של מחרוזות, שאותו נחזיר.

 

הפונקציה של הרחובות די דומה לפונקציה של הערים

Code Snippet
public IdName[] StreetAutoComplete(string startsWith, string location)
{
    string data = DownloadString(MikudAction.Street, "StartsWith={0}&Location={1}", startsWith, location);
    data = RemoveBrackets(data);

    var obj = JsonConvert.DeserializeObject<Streets>(data);
    return obj.streets.Select(x => new IdName() { Id = x.Id, Name = x.N }).ToArray();
}

 

שני מתודות קבלת המיקוד נראים כך:

Code Snippet
public ZipCode GetZipCodeByAddress(string city, string street, int house, string entrance = null)
{
    string data = DownloadString(MikudAction.Zip, "Location={0}&Street={1}&House={2}&Entrance={3}",
                                    city, street, house, entrance);

    return GetZipCode(data);
}

public ZipCode GetZipCodeByPOB(string city, string pob)
{
    string data = DownloadString(MikudAction.Zip, "Location={0}&POB={1}", city, pob);

    return GetZipCode(data);
}

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

Code Snippet
private static ZipCode GetZipCode(string data)
{
    var match = Regex.Match(data, @"RES[0-9]*\d");
    var result = match.Value.Substring(4);

    ZipCode zipCode = new ZipCode()
    {
        Status = true,
        Value = result
    };

    if (result == "11")
    {
        zipCode.Status = false;
        zipCode.Value = "לא נמצא מיקוד מתאים. במידה והוזנה כניסה, יש לנסות לחפש בלעדיה…";
    }
    else if (result == "12" || match.Value == "RES2")
    {
        zipCode.Status = false;
        zipCode.Value = "לא נמצא מיקוד מתאים. יש לנסות שנית עם רחוב ו/או מספר בית…";
    }
    else if (result == "13" || match.Value == "RES013")
    {
        zipCode.Status = false;
        zipCode.Value = "לא נמצא מיקוד מתאים עם העיר ו/או הרחוב שהוזנ/ה. יש לנסות שנית…";
    }
    else if (match.Value == "RES5")
    {
        zipCode.Status = false;
        zipCode.Value = "לא נמצא מיקוד";
    }
    else if (result == "")
    {
        zipCode.Status = false;
        zipCode.Value = data;
    }
    return zipCode;
}

 

בשונה מהמתודת הקודמות שהחזירו סוג של JSON, הפנייה הזאת לדואר ישראל, מחזירה HTML, מה שאומר שיש לפרסר זאת, ולכן יש כאן הגדרה של RegularExpression שמחפש את התווים RES וספרות לאחריו,  נחתוך את ארבעת התווים הראשונים שהתקבלו (אל תשאלו למה) ומה שנשאר זה המיקוד.

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

 

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

Code Snippet
public Address GetAddressByZipCode(string zipCode)
{
    string data = DownloadString(MikudAction.Address, "Zip={0}", zipCode);
    data = data.Replace("<br>", "br");

    XDocument doc = XDocument.Parse(data);
    var innerText = doc.Descendants("body").First().Value;
    var result = innerText.Split(new string[] { "br" }, StringSplitOptions.RemoveEmptyEntries);

    if (result.Length == 1)
        return null;

    Address add = new Address();
    add.City = result[1].Replace("ישוב:", "").Trim();

    if (result.Length == 3)
    {
        add.PobRange = result[2].Replace("\n", "").Trim();
    }
    else if (result.Length == 4)
    {
        add.Street = result[2].Replace("רחוב:", "").Trim();
        add.Number = result[3].Replace("מספר:", "").Replace("\n", "").Trim();
    }

    return add;

}

כמובן שראשית נקבל את המידע (מה שחוזר הינו HTML), לאחר מכן נחליף את כל תגיות ה – BR לטקסט (מכיוון שזו הדרך שלנו בתוצאה שחוזרת להפריד בין החלקים השונים בתשובה (עיר, רחוב וכו’)

כעת נקרא את התוכן כ – XML, ונחזיק במשתנה innerText את כל התוכן של ה – body (התשובה עצמה).

בעזרת מתודת Split נקבל מערך של מחרוזות כשמה שמפריד ביניהם זה אותו BR.

במידה ואין תוצאות, נחזיר null.

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

 

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

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

8 תגובות

  1. אלון19 באוגוסט 2014 ב 21:17

    תודה רבה !!!

    הגב
  2. תותח21 באוגוסט 2014 ב 17:00

    תותח כמו תמיד
    הסברים יפים,מובנים
    בלוג התכנות האהוב עלי ברשת!!!

    תודה לך ידידי!!

    הגב
  3. משה23 באוגוסט 2014 ב 20:57

    רעיון ומימוש יפה, רק לא להגביל את המשתמש רק לתוצאות שלו.

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

    הגב
    1. נפתלי24 באוגוסט 2014 ב 20:45

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

      שבות רחל, למשל – מופיע גם מופיע.

      אבל אחיה – לפי ההגדרה הכי מרחיבה שמצאתי, מדובר על שכונה ביישוב שבות רחל. ועוד איזו שכונה… גרות שם 20 משפחות. נו, באמת. זה לא אמור להופיע בשום רשימה של יישובים.

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

      הגב
  4. יגאל17 באוקטובר 2014 ב 11:43

    שלמה השלום.
    הקוד שכתבת מעולה ומאוד מועיל.

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

    הגב
  5. עמרי אריאב9 ביוני 2016 ב 11:34

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

    הגב
    1. אבי סיבוני7 בדצמבר 2016 ב 12:39

      היי עמרי אני מנסה לגשת ה url בבקשת GET ויש CORS אז השתמשתי ב JSONP רק שהוא לא יכול לעשות לזה parse אשמח לעזרה אני כותב באנגולר 1 תודה רבה 🙂

      הגב
      1. עמרי אריאב2 במרץ 2017 ב 22:08

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

        הגב