├── .gitignore ├── .idea ├── .gitignore ├── 3- Scrapper.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml └── modules.xml ├── .vscode └── settings.json ├── Procfile ├── README.md ├── Variables Heroku.png ├── app.py ├── instructions.txt ├── requirements.txt ├── runtime.txt └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | venv/* 2 | parsing_analysis/* 3 | .vscode/* 4 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/3- Scrapper.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "venv\\Scripts\\python.exe" 3 | } -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -t 120 -b :$PORT app:app 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Build Your Free GPS Routing API with Python Flask 🗺️ 2 | *Build your free Distance Matrix API deployed on the cloud* 3 | 4 | ### Article 5 | In this [Article](https://s-saci95.medium.com/build-your-free-gps-routing-api-to-calculate-road-distances-143632cc4917), we will create a free, easy-to-implement and customizable (and a bit slow :D) Distance Matrix API using Flask with a Selenium Bot deployed on Heroku 6 | 7 | ### How does it work? 8 | Before starting to read this part, please forget everything you know about how to put in production a fast, efficient and stable code ensuring quick response with limited resources. 9 | This will be simple, quick and dirty, with no intention to be a scalable solution. The performance will be way lower than if you directly query the official Google API — but here it’s free :) 10 | 11 | ![This is an image](https://miro.medium.com/max/875/1*YqhaaI7ZuXfgAiGuuy166A.png) 12 | 13 | #### Let us do it in three steps 14 | 1. Build a Selenium Bot that will query the distance from City A to City B in Google Maps Website 15 | 2. Set up your Flask API that will receive the request and return a distance 16 | 3. Deploy your code on Heroku 17 | 18 | 19 | ## Code 20 | This repository code is ready to be deployed on Heroku: 21 | ##### 1. Copy Github repository in your local folder and create a local python environment 22 | ##### 2. Download libraries listed in requirements.txt 23 | ``` 24 | pip3 install -r requirements.txt 25 | ``` 26 | ##### 3. Download Buildpacks on Heroku to use Selenium + ChromeDriver 27 | Go to settings > Add Buildpack 28 | ![This is an image](https://miro.medium.com/max/875/1*mDsg_6F14SKeeds0kdHlwg.png) 29 | ``` 30 | Enter Two Links 31 | https://github.com/heroku/heroku-buildpack-google-chrome 32 | https://github.com/heroku/heroku-buildpack-chromedriver 33 | ``` 34 | ##### 4. Set up Environment Variables on Heroku 35 | ![This is an image](https://miro.medium.com/max/875/1*2ENP1_ndBVoaSamUXUtVxw.png) 36 | ``` 37 | CHROMEDRIVER_PATH: /app/.chromedriver/bin/chromedriver 38 | GOOGLE_CHROME_BIN: /app/.apt/usr/bin/google-chrome 39 | ``` 40 | ##### 5. Deploy your app on Heroku 41 | 42 | 43 | ## Test your API 44 | #### Test your API to calculate the distance 45 | ``` 46 | From: Paris, France 47 | To: Marseille, France 48 | ``` 49 | 50 | #### Request link 51 | http://xxx-xxx.herokuapp.com/distance/Paris,France/Marseille,France 52 | (replace xxx-xxx by your Heroku app name) 53 | 54 | #### Response 55 | {“distance”:”775 km”} 56 | 57 | ## About me 🤓 58 | Senior Supply Chain Engineer with an international experience working on Logistics and Transportation operations. \ 59 | Have a look at my portfolio: [Data Science for Supply Chain Portfolio](https://samirsaci.com) \ 60 | For **consulting or advising** on analytics and sustainable supply chain transformation, feel free to contact me via [Logigreen Consulting](https://www.logi-green.com/) \ 61 | Data Science for Warehousing📦, Transportation 🚚 and Demand Forecasting 📈 62 | -------------------------------------------------------------------------------- /Variables Heroku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samirsaci/geocoding-api/fbf9adcacba2ed403ff44fc7d46aaa9f34f7c228/Variables Heroku.png -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | from flask import Flask, render_template, request, redirect 4 | from selenium import webdriver 5 | from bs4 import BeautifulSoup as soup 6 | 7 | # Chromedrive setting 8 | chrome_options = webdriver.ChromeOptions() 9 | chrome_options.add_argument("--headless") 10 | chrome_options.add_argument("--disable-dev-shm-usage") 11 | chrome_options.add_argument("--no-sandbox") 12 | chrome_options.binary_location = os.environ.get("GOOGLE_CHROME_BIN") 13 | driver = webdriver.Chrome(executable_path=os.environ.get("CHROMEDRIVER_PATH"), chrome_options=chrome_options) 14 | 15 | # Link 16 | fr = 'Paris, France' 17 | to = 'Marseille, France' 18 | 19 | app = Flask(__name__) 20 | 21 | def from_to(fr, to): 22 | # url 23 | url = 'https://www.google.fr/maps/dir/{}/{}/data=!4m2!4m1!3e0'.format(fr, to) 24 | print("link: {}".format(url)) 25 | # Driver get 26 | driver.get(url) 27 | time.sleep(1) 28 | # Soupify 29 | page_soup = soup(driver.page_source, "html.parser") 30 | # Extract 31 | css_dist = "div[class^='section-directions-trip-distance'] > div" 32 | css_dura = "div[class^='section-directions-trip-duration']" 33 | 34 | try: 35 | distance = page_soup.select_one(css_dist).text 36 | duration = page_soup.select_one(css_dura).text 37 | except Exception as e: 38 | print(e) 39 | distance = 'Error' 40 | duration = 'Error' 41 | print("{} to {}: {}".format(fr, to, distance)) 42 | result = { 43 | "distance": distance 44 | # "distance": distance, 45 | # "duration": duration 46 | } 47 | 48 | return result 49 | 50 | # Routing do define url 51 | @app.route('/') 52 | def index(): 53 | return render_template('index.html') 54 | 55 | @app.route('/distance//', methods=['GET', 'POST']) 56 | def distance(fr, to): 57 | #returns the post, the post_id should be an int 58 | result = from_to(fr, to) 59 | return result 60 | 61 | 62 | if __name__ == '__main__': 63 | # Threaded option to enable multiple instances for multiple user access support 64 | app.run(debug=True, port=5000) -------------------------------------------------------------------------------- /instructions.txt: -------------------------------------------------------------------------------- 1 | # Step 1: Download the Buildpacks Necessary for the ChromeDriver 2 | # Buildpack 1: https://github.com/heroku/heroku-buildpack-google-chrome 3 | # Buildpack 2: https://github.com/heroku/heroku-buildpack-chromedriver 4 | # Step 2: Add the PATH variable to the Heroku configuration 5 | # heroku config:set GOOGLE_CHROME_BIN=/app/.apt/usr/bin/google_chrome 6 | # heroku config:set CHROMEDRIVER_PATH=/app/.chromedriver/bin/chromedriver 7 | # heroku ps:scale worker=0 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samirsaci/geocoding-api/fbf9adcacba2ed403ff44fc7d46aaa9f34f7c228/requirements.txt -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.10 -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Your Google API (Free :)) 9 | 10 | 11 | --------------------------------------------------------------------------------