Initial commit

This commit is contained in:
Logan Williams
2020-12-09 12:03:20 +01:00
commit 63e2716d1d
4 changed files with 174 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store

27
README.md Normal file
View File

@@ -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 "<session-id-token>" --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 <output-location>` command line argument, the list can be saved as a GeoJSON file for other geospatial applications.
Using the `--map <output-location>` 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

BIN
docs/map-example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

145
instagram-locations.py Normal file
View File

@@ -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 = '''<html>
<head>
<title>Instagram location visualizations</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 600px;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map("map").setView([$lat, $lng], 14);
var locs = $locs;
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 18,
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
id: "mapbox/light-v9",
}).addTo(map);
function onEachFeature(feature, layer) {
layer.bindPopup(`<a href="https://www.instagram.com/explore/locations/` + feature.properties.external_id + `">` + feature.properties.name + `</a><br />` + feature.properties.address );
}
L.geoJSON(locs, {
onEachFeature: onEachFeature
}).addTo(map);
</script>
</body>
</html>'''
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()