mirror of
https://github.com/bellingcat/instagram-location-search.git
synced 2026-06-08 02:28:29 +03:00
146 lines
4.5 KiB
Python
146 lines
4.5 KiB
Python
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:
|
|
'© <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()
|
|
|