Repository: alltheplaces/alltheplaces

Issue:
LongHorn Steakhouse
https://www.longhornsteakhouse.com/locations-sitemap.xml


Discussion:

Resolving patch:
diff --git a/locations/spiders/longhorn_steakhouse.py b/locations/spiders/longhorn_steakhouse.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/longhorn_steakhouse.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+import json
+import re
+
+import scrapy
+
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+
+class LongHornSteakhouseSpider(scrapy.Spider):
+    name = "longhorn_steakhouse"
+    item_attributes = {'brand': 'LongHorn Steakhouse', 'brand_wikidata': "Q3259007"}
+    allowed_domains = []
+    start_urls = [
+        'https://www.longhornsteakhouse.com/locations-sitemap.xml',
+    ]
+    custom_settings = {
+        'USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
+    }
+    download_delay = 5
+
+    def parse_hours(self, hours):
+        opening_hours = OpeningHours()
+
+        for hour in hours:
+            day, open_close = hour.split(' ')
+            open_time, close_time = open_close.split('-')
+            opening_hours.add_range(day=day, open_time=open_time, close_time=close_time, time_format='%H:%M')
+        return opening_hours.as_opening_hours()
+
+    def parse(self, response):
+        response.selector.remove_namespaces()
+        urls = response.xpath('//url/loc/text()').extract()
+        for url in urls:
+            yield scrapy.Request(url=url, callback=self.parse_store)
+
+    def parse_store(self, response):
+        store_data = response.xpath('//script[@type="application/ld+json" and contains(text(), "streetAddress")]/text()').extract_first()
+        if store_data:
+            data = json.loads(store_data)
+            ref = re.search(r'.+/(.+?)/?(?:\.html|$)', response.url).group(1)
+
+            # Handle store pages that are missing the application/ld+json data
+            addr, city_state_zip, phone = response.xpath('//p[@id="info-link-webhead"]/text()').extract()
+            city, state, postcode = re.search(r'(.*?),\s([A-Z]{2})\s([\d-]+)$', city_state_zip).groups()
+
+            properties = {
+                'name': data.get("name") or response.xpath('//h1[@class="style_h1"]/text()').extract_first().strip(),
+                'ref': data["branchCode"] or ref,
+                'addr_full': data["address"]["streetAddress"].strip() or addr.strip(),
+                'city': data["address"]["addressLocality"] or city,
+                'state': data["address"]["addressRegion"] or state,
+                'postcode': data["address"]["postalCode"] or postcode,
+                'country': data["address"]["addressCountry"],
+                'phone': data.get("telephone") or phone.strip(),
+                'website': data.get("url") or response.url,
+                'lat': float(data["geo"]["latitude"]),
+                'lon': float(data["geo"]["longitude"]),
+            }
+
+            hours = data.get("openingHours")
+            if hours:
+                store_hours = self.parse_hours(hours)
+                properties["opening_hours"] = store_hours
+
+            yield GeojsonPointItem(**properties)


Test patch: