commit 63e2716d1d44e921a79029b2e35487d02b376db4 Author: Logan Williams Date: Wed Dec 9 12:03:20 2020 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ca0973 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e26e84 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Instagram Location Search + +## Prerequisites + +This Python application requires `requests` and `numpy` to be properly installed. + +## Example usage + +The following command will search for Instagram locations nearby the coordinates 32.22 N, 110.97 W (downtown Tucson, Arizona.) The list of locations is saved as a JSON file at "locs.json". + +```python3 instagram-locations.py --session "" --lat 32.22 --lng -110.97 --json locs.json``` + +Note that this requires an Instagram session ID in order to work! See below for how to obtain one from your account. + +### Other output formats + +Using the `--geojson ` command line argument, the list can be saved as a GeoJSON file for other geospatial applications. + +Using the `--map ` command line argument, a simple Leaflet map is made to visualize the locations of the returned points. + +![docs/map-example.png] + +Multiple types of output can be generated. For example, the following command will search for Instagram locations, save the JSON list, a GeoJSON file, and a map for viewing the locations visually. + +```python3 instagram-locations.py --session "3888090946%3AhdKd2fA8d72dqD%3A16" --lat 32.22 --lng -110.97 --json locs.json --geojson locs.geojson --map map.html``` + +## Getting an Instagram session ID \ No newline at end of file diff --git a/docs/map-example.png b/docs/map-example.png new file mode 100644 index 0000000..6dd1646 Binary files /dev/null and b/docs/map-example.png differ diff --git a/instagram-locations.py b/instagram-locations.py new file mode 100644 index 0000000..26d452d --- /dev/null +++ b/instagram-locations.py @@ -0,0 +1,145 @@ +import requests +import numpy as np +import argparse +import json +from string import Template + +# gets instagram "locations" around a particular lat/lng using internal API +# (requires session cookie for authentication) +def get_instagram_locations(lat, lng, cookie): + locs = requests.get("https://www.instagram.com/location_search/?latitude=" + str(lat) + "&longitude=" + str(lng) + "&__a=1", headers={ + 'Cookie': cookie + }).json() + + return locs['venues'] + + +def get_instagram_locations_by_query(query): + locs = requests.get("https://www.instagram.com/web/search/topsearch/?context=place&query=" + query).json() + + return [v['place']['location'] for v in locs['places']] + +# queries the instagram location API for several points around a central lat/lng +# in order to return additional results +def get_fuzzy_locations(lat, lng, cookie, sigma=2): + locs = get_instagram_locations(lat, lng, cookie) + + std_lat = np.std([v['lat'] for v in locs if 'lat' in v]) + std_lng = np.std([v['lng'] for v in locs if 'lng' in v]) + + for delta_lat in range(-sigma, sigma+1): + for delta_lng in range(-sigma, sigma+1): + new_locs = get_instagram_locations(lat + delta_lat * std_lat, lng + delta_lng * std_lng, cookie) + loc_ids = [v['external_id'] for v in locs] + + for loc in new_locs: + if loc['external_id'] not in loc_ids: + locs.append(loc) + + return locs + +# converts list of instagram locations into valid geojson +def make_geojson(locs): + features = [] + + for l in [l for l in locs if 'lng' in l]: + feature = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [l["lng"], l["lat"]] + }, + "properties": l} + features.append(feature) + + return {"type": "FeatureCollection", "features": features} + +html_template = ''' + + Instagram location visualizations + + + + + + + + + + +
+ + + +''' + +def main(): + parser = argparse.ArgumentParser(description="Get a list of Instagram locations near a lat/lng") + parser.add_argument("--session", action="store", dest="session") + parser.add_argument("--json", action="store", dest="output") + parser.add_argument("--geojson", action="store", dest="geojson") + parser.add_argument("--map", action="store", dest="map") + parser.add_argument("--lat", action="store", dest="lat") + parser.add_argument("--lng", action="store", dest="lng") + + args = parser.parse_args() + + cookie = 'sessionid=' + args.session + + locations = get_fuzzy_locations(float(args.lat), float(args.lng), cookie) + + if (args.output): + json.dump(locations, open(args.output, 'w')) + + if (args.geojson): + json.dump(make_geojson(locations), open(args.geojson, 'w')) + + if (args.map): + s = Template(html_template) + viz = s.substitute(lat=args.lat, lng=args.lng, locs=json.dumps(make_geojson(locations))) + + f = open(args.map, 'w') + f.write(viz) + f.close() + +if __name__ == "__main__": + main() +