We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Spider public_storage is broken
During the global build at 2021-05-26-14-42-23, spider **public_storage** failed with **0 features** and **0 errors**.

Here's [the log](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/logs/public_storage.log) and [the output](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/public_storage.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/public_storage.geojson))

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `locations/spiders/public_storage.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 import scrapy
3 import json
4 
5 from locations.items import GeojsonPointItem
6 
7 
8 class PublicStorageSpider(scrapy.Spider):
9     name = "public_storage"
10     item_attributes = { 'brand': "Public Storage" }
11     allowed_domains = ["www.publicstorage.com"]
12     start_urls = (
13         'https://www.publicstorage.com/handlers/searchcoordinates.ashx?north=90.0&east=180.0&south=-90.0&west=-180.0',
14     )
15 
16     def parse(self, response):
17         data = json.loads(response.body_as_unicode())
18 
19         for store in data['response']['properties']['property']:
20             lat, lon = map(float, store['lat_long'].split(', '))
21             properties = {
22                 "ref": store.get('property_id'),
23                 "opening_hours": '; '.join(response.xpath('//time[@itemprop="openingHours"]/@datetime').extract()),
24                 "addr_full": store.get('address'),
25                 "city": store.get('city'),
26                 "state": store.get('state'),
27                 "postcode": store.get('zip'),
28                 "lat": lat,
29                 "lon": lon,
30             }
31 
32             yield GeojsonPointItem(**properties)
33
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/locations/spiders/public_storage.py b/locations/spiders/public_storage.py
--- a/locations/spiders/public_storage.py
+++ b/locations/spiders/public_storage.py
@@ -3,6 +3,7 @@
 import json
 
 from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
 
 
 class PublicStorageSpider(scrapy.Spider):
@@ -10,23 +11,45 @@
     item_attributes = { 'brand': "Public Storage" }
     allowed_domains = ["www.publicstorage.com"]
     start_urls = (
-        'https://www.publicstorage.com/handlers/searchcoordinates.ashx?north=90.0&east=180.0&south=-90.0&west=-180.0',
+        'https://www.publicstorage.com/sitemap_plp.xml',
     )
 
     def parse(self, response):
-        data = json.loads(response.body_as_unicode())
-
-        for store in data['response']['properties']['property']:
-            lat, lon = map(float, store['lat_long'].split(', '))
-            properties = {
-                "ref": store.get('property_id'),
-                "opening_hours": '; '.join(response.xpath('//time[@itemprop="openingHours"]/@datetime').extract()),
-                "addr_full": store.get('address'),
-                "city": store.get('city'),
-                "state": store.get('state'),
-                "postcode": store.get('zip'),
-                "lat": lat,
-                "lon": lon,
-            }
-
-            yield GeojsonPointItem(**properties)
+        response.selector.remove_namespaces()
+        city_urls = response.xpath('//url/loc/text()').extract()
+        for path in city_urls:
+            yield scrapy.Request(
+                path.strip(),
+                callback=self.parse_store,
+            )
+
+    def parse_hours(self, hours):
+        opening_hours = OpeningHours()
+
+        for hour in hours:
+            for day in hour['dayOfWeek']:
+                opening_hours.add_range(
+                    day=day[:2],
+                    open_time=hour["opens"],
+                    close_time=hour["closes"],
+                )
+
+        return opening_hours.as_opening_hours()
+
+    def parse_store(self, response):
+        data = json.loads(response.xpath('//script[@type="application/ld+json"]/text()').extract_first())
+        data = data['@graph'][0]
+
+        properties = {
+            "ref": data['@id'],
+            "opening_hours": self.parse_hours(data['openingHoursSpecification']),
+            "addr_full": data['address']['streetAddress'],
+            "city": data['address']['addressLocality'],
+            "state": data['address']['addressRegion'],
+            "postcode": data['address']['postalCode'],
+            "phone": data['telephone'],
+            "lat": data['geo']['latitude'],
+            "lon": data['geo']['longitude'],
+        }
+
+        yield GeojsonPointItem(**properties)

{"golden_diff": "diff --git a/locations/spiders/public_storage.py b/locations/spiders/public_storage.py\n--- a/locations/spiders/public_storage.py\n+++ b/locations/spiders/public_storage.py\n@@ -3,6 +3,7 @@\n import json\n \n from locations.items import GeojsonPointItem\n+from locations.hours import OpeningHours\n \n \n class PublicStorageSpider(scrapy.Spider):\n@@ -10,23 +11,45 @@\n     item_attributes = { 'brand': \"Public Storage\" }\n     allowed_domains = [\"www.publicstorage.com\"]\n     start_urls = (\n-        'https://www.publicstorage.com/handlers/searchcoordinates.ashx?north=90.0&east=180.0&south=-90.0&west=-180.0',\n+        'https://www.publicstorage.com/sitemap_plp.xml',\n     )\n \n     def parse(self, response):\n-        data = json.loads(response.body_as_unicode())\n-\n-        for store in data['response']['properties']['property']:\n-            lat, lon = map(float, store['lat_long'].split(', '))\n-            properties = {\n-                \"ref\": store.get('property_id'),\n-                \"opening_hours\": '; '.join(response.xpath('//time[@itemprop=\"openingHours\"]/@datetime').extract()),\n-                \"addr_full\": store.get('address'),\n-                \"city\": store.get('city'),\n-                \"state\": store.get('state'),\n-                \"postcode\": store.get('zip'),\n-                \"lat\": lat,\n-                \"lon\": lon,\n-            }\n-\n-            yield GeojsonPointItem(**properties)\n+        response.selector.remove_namespaces()\n+        city_urls = response.xpath('//url/loc/text()').extract()\n+        for path in city_urls:\n+            yield scrapy.Request(\n+                path.strip(),\n+                callback=self.parse_store,\n+            )\n+\n+    def parse_hours(self, hours):\n+        opening_hours = OpeningHours()\n+\n+        for hour in hours:\n+            for day in hour['dayOfWeek']:\n+                opening_hours.add_range(\n+                    day=day[:2],\n+                    open_time=hour[\"opens\"],\n+                    close_time=hour[\"closes\"],\n+                )\n+\n+        return opening_hours.as_opening_hours()\n+\n+    def parse_store(self, response):\n+        data = json.loads(response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first())\n+        data = data['@graph'][0]\n+\n+        properties = {\n+            \"ref\": data['@id'],\n+            \"opening_hours\": self.parse_hours(data['openingHoursSpecification']),\n+            \"addr_full\": data['address']['streetAddress'],\n+            \"city\": data['address']['addressLocality'],\n+            \"state\": data['address']['addressRegion'],\n+            \"postcode\": data['address']['postalCode'],\n+            \"phone\": data['telephone'],\n+            \"lat\": data['geo']['latitude'],\n+            \"lon\": data['geo']['longitude'],\n+        }\n+\n+        yield GeojsonPointItem(**properties)\n", "issue": "Spider public_storage is broken\nDuring the global build at 2021-05-26-14-42-23, spider **public_storage** failed with **0 features** and **0 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/logs/public_storage.log) and [the output](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/public_storage.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/public_storage.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nimport json\n\nfrom locations.items import GeojsonPointItem\n\n\nclass PublicStorageSpider(scrapy.Spider):\n    name = \"public_storage\"\n    item_attributes = { 'brand': \"Public Storage\" }\n    allowed_domains = [\"www.publicstorage.com\"]\n    start_urls = (\n        'https://www.publicstorage.com/handlers/searchcoordinates.ashx?north=90.0&east=180.0&south=-90.0&west=-180.0',\n    )\n\n    def parse(self, response):\n        data = json.loads(response.body_as_unicode())\n\n        for store in data['response']['properties']['property']:\n            lat, lon = map(float, store['lat_long'].split(', '))\n            properties = {\n                \"ref\": store.get('property_id'),\n                \"opening_hours\": '; '.join(response.xpath('//time[@itemprop=\"openingHours\"]/@datetime').extract()),\n                \"addr_full\": store.get('address'),\n                \"city\": store.get('city'),\n                \"state\": store.get('state'),\n                \"postcode\": store.get('zip'),\n                \"lat\": lat,\n                \"lon\": lon,\n            }\n\n            yield GeojsonPointItem(**properties)\n", "path": "locations/spiders/public_storage.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nimport json\n\nfrom locations.items import GeojsonPointItem\nfrom locations.hours import OpeningHours\n\n\nclass PublicStorageSpider(scrapy.Spider):\n    name = \"public_storage\"\n    item_attributes = { 'brand': \"Public Storage\" }\n    allowed_domains = [\"www.publicstorage.com\"]\n    start_urls = (\n        'https://www.publicstorage.com/sitemap_plp.xml',\n    )\n\n    def parse(self, response):\n        response.selector.remove_namespaces()\n        city_urls = response.xpath('//url/loc/text()').extract()\n        for path in city_urls:\n            yield scrapy.Request(\n                path.strip(),\n                callback=self.parse_store,\n            )\n\n    def parse_hours(self, hours):\n        opening_hours = OpeningHours()\n\n        for hour in hours:\n            for day in hour['dayOfWeek']:\n                opening_hours.add_range(\n                    day=day[:2],\n                    open_time=hour[\"opens\"],\n                    close_time=hour[\"closes\"],\n                )\n\n        return opening_hours.as_opening_hours()\n\n    def parse_store(self, response):\n        data = json.loads(response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first())\n        data = data['@graph'][0]\n\n        properties = {\n            \"ref\": data['@id'],\n            \"opening_hours\": self.parse_hours(data['openingHoursSpecification']),\n            \"addr_full\": data['address']['streetAddress'],\n            \"city\": data['address']['addressLocality'],\n            \"state\": data['address']['addressRegion'],\n            \"postcode\": data['address']['postalCode'],\n            \"phone\": data['telephone'],\n            \"lat\": data['geo']['latitude'],\n            \"lon\": data['geo']['longitude'],\n        }\n\n        yield GeojsonPointItem(**properties)\n", "path": "locations/spiders/public_storage.py"}]}