Repository: alltheplaces/alltheplaces

Issue:
H&M
Clothing store.

They're in multiple countries, but here's the US store locator: https://www.hm.com/us/store-locator




Discussion:
Figured out how to get stores from all countries. 

Resolving patch:
diff --git a/locations/spiders/hm.py b/locations/spiders/hm.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/hm.py
@@ -0,0 +1,80 @@
+import scrapy
+from locations.items import GeojsonPointItem
+import json
+import requests
+import itertools
+
+
+def chunks(l, n):
+    for i in range(0, len(l), n):
+        yield l[i:i + n]
+
+
+def partition(l, n):
+    return list(chunks(l, n))
+
+
+def process_hours(opening_hours):
+    ret_hours = []
+    for hours_str in opening_hours:
+        split_hours = hours_str.replace(",", "").replace("AM AM","").replace("PM PM", "").split(" ")
+        if split_hours[1] == "-":
+            range_start = split_hours[0]
+            range_end = split_hours[2]
+            times = partition([x for x in split_hours[3:] if x != "-"], 2)
+        else:
+            range_start, range_end = split_hours[0], None
+            times = partition([x for x in split_hours[1:] if x != "-"], 2)
+        periods = partition(times, 2)
+        periods = [list(itertools.chain(*r)) for r in periods]
+
+        period_list = []
+
+        for start, start_period, end, end_period in periods:
+            start_hour, start_minutes = [int(x) for x in start.split(":")]
+            end_hour, end_minutes = [int(x) for x in end.split(":")]
+
+            if start_period == "PM":
+                start_hour += 12
+            end_hour += 12
+
+            hours = (start_hour, start_minutes, end_hour, end_minutes)
+            period_list.append("%02d:%02d-%02d:%02d" % hours)
+        periods_str = ", ".join(period_list)
+        if range_start and range_end:
+            ret_hours.append("{}-{} {}".format(range_start[:2], range_end[:2], periods_str))
+        elif range_start:
+            ret_hours.append("{} {}".format(range_start[:2], periods_str))
+    return "; ".join(ret_hours)
+
+
+class HMSpider(scrapy.Spider):
+    name = "hm-worldwide"
+    all_stores_uri = 'https://hm.storelocator.hm.com/rest/storelocator/stores/1.0/locale/en_US/country/{}/'
+    start_urls = ["http://www.hm.com/entrance.ahtml"]
+
+    def parse(self, response):
+        country_urls = response.css(".column li a::attr('href')").extract()
+        country_codes = {x.split("=")[1].split("&")[0].upper() for x in country_urls}
+        for country_code in country_codes:
+            yield scrapy.Request(url=self.all_stores_uri.format(country_code), callback=self.parse_country)
+
+    def parse_country(self, response):
+        stores = response.css("storeComplete")
+        for store in stores:
+            point = {
+                "lat": store.xpath("latitude/text()").extract_first(),
+                "lon": store.xpath("longitude/text()").extract_first(),
+                "name": store.xpath("name/text()").extract_first(),
+                "addr_full": store.xpath("address/addressLine/text()").extract_first(),
+                "city": store.xpath("city/text()").extract_first(),
+                "country": store.xpath("country/text()").extract_first(),
+                "phone": store.xpath("phone/text()").extract_first(),
+                "opening_hours": process_hours(store.xpath("openingHours/openingHour/text()").extract()),
+                "ref": store.xpath("storeId/text()").extract_first()
+            }
+            if "/country/US" in response.url:
+                point["state"] = store.xpath("region/name/text()").extract_first()
+                point["postcode"] = store.xpath("address/addressLine/text()").extract()[-1].split(" ")[-1]
+
+            yield GeojsonPointItem(**point)


Test patch: