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

Here's [the log](https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/logs/heb.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/output/heb.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/output/heb.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/heb.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 import scrapy
3 import re
4 
5 from locations.items import GeojsonPointItem
6 
7 
8 class HEBSpider(scrapy.Spider):
9     name = "heb"
10     item_attributes = { 'brand': "H-E-B", 'brand_wikidata': "Q830621" }
11     allowed_domains = ["www.heb.com"]
12     download_delay = 0.2
13     start_urls = (
14         'https://www.heb.com/sitemap/storeSitemap.xml',
15     )
16 
17     def parse(self, response):
18         xml = scrapy.selector.Selector(response)
19         xml.remove_namespaces()
20 
21         urls = xml.xpath('//loc/text()').extract()
22         for url in urls:
23             yield scrapy.Request(url=url, callback=self.parse_store, meta={"url": url})
24 
25     def parse_store(self, response):
26         ref = "_".join(re.search(r".+/(.+?)/(.+?)/(.+?)/?(?:\.html|$)", response.url).groups())
27 
28         properties = {
29             'name': response.xpath('//h1[@class="store-details__store-name"]/text()').extract_first(),
30             'ref': ref,
31             'addr_full': response.xpath('//p[@itemprop="streetAddress"]/text()').extract_first(),
32             'city': response.xpath('//div[@class="store-details__location"]/p[2]/span[1]/text()').extract_first(),
33             'state': response.xpath('//div[@class="store-details__location"]/p[2]/span[2]/text()').extract_first(),
34             'postcode': response.xpath('//div[@class="store-details__location"]/p[2]/span[3]/text()').extract_first(),
35             'phone': response.xpath('//a[@class="store-details__link store-details__link--phone"]/@content/text()').extract_first(),
36             'lat': (response.xpath('//div[@id="map-wrap"]/@data-map-lat').extract_first()),
37             'lon': (response.xpath('//div[@id="map-wrap"]/@data-map-lon').extract_first()),
38             'website': response.url
39         }
40         yield GeojsonPointItem(**properties)
41
```
--- 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/heb.py b/locations/spiders/heb.py
--- a/locations/spiders/heb.py
+++ b/locations/spiders/heb.py
@@ -1,8 +1,10 @@
 # -*- coding: utf-8 -*-
+import json
+
 import scrapy
-import re
 
 from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
 
 
 class HEBSpider(scrapy.Spider):
@@ -23,18 +25,35 @@
             yield scrapy.Request(url=url, callback=self.parse_store, meta={"url": url})
 
     def parse_store(self, response):
-        ref = "_".join(re.search(r".+/(.+?)/(.+?)/(.+?)/?(?:\.html|$)", response.url).groups())
-
-        properties = {
-            'name': response.xpath('//h1[@class="store-details__store-name"]/text()').extract_first(),
-            'ref': ref,
-            'addr_full': response.xpath('//p[@itemprop="streetAddress"]/text()').extract_first(),
-            'city': response.xpath('//div[@class="store-details__location"]/p[2]/span[1]/text()').extract_first(),
-            'state': response.xpath('//div[@class="store-details__location"]/p[2]/span[2]/text()').extract_first(),
-            'postcode': response.xpath('//div[@class="store-details__location"]/p[2]/span[3]/text()').extract_first(),
-            'phone': response.xpath('//a[@class="store-details__link store-details__link--phone"]/@content/text()').extract_first(),
-            'lat': (response.xpath('//div[@id="map-wrap"]/@data-map-lat').extract_first()),
-            'lon': (response.xpath('//div[@id="map-wrap"]/@data-map-lon').extract_first()),
-            'website': response.url
-        }
-        yield GeojsonPointItem(**properties)
+        if response.request.meta.get('redirect_urls'):
+            return
+
+        store_json = json.loads(
+            response.xpath('//script[@type="application/ld+json"]/text()').extract_first()
+        )
+        yield GeojsonPointItem(
+            ref=response.url.split('/')[-1],
+            name=store_json['name'],
+            lat=float(store_json['geo']['latitude']),
+            lon=float(store_json['geo']['longitude']),
+            addr_full=store_json['address']['streetAddress'],
+            city=store_json['address']['addressLocality'],
+            state=store_json['address']['addressRegion'],
+            postcode=store_json['address']['postalCode'],
+            country=store_json['address']['addressCountry'],
+            phone=store_json['telephone'],
+            website=response.url,
+            opening_hours=self.parse_hours(store_json['openingHoursSpecification'])
+        )
+
+    def parse_hours(self, hours):
+        opening_hours = OpeningHours()
+
+        for hour in hours:
+            opening_hours.add_range(
+                day=hour["dayOfWeek"][0:2].capitalize(),
+                open_time=hour["opens"],
+                close_time=hour["closes"]
+            )
+
+        return opening_hours.as_opening_hours()

{"golden_diff": "diff --git a/locations/spiders/heb.py b/locations/spiders/heb.py\n--- a/locations/spiders/heb.py\n+++ b/locations/spiders/heb.py\n@@ -1,8 +1,10 @@\n # -*- coding: utf-8 -*-\n+import json\n+\n import scrapy\n-import re\n \n from locations.items import GeojsonPointItem\n+from locations.hours import OpeningHours\n \n \n class HEBSpider(scrapy.Spider):\n@@ -23,18 +25,35 @@\n             yield scrapy.Request(url=url, callback=self.parse_store, meta={\"url\": url})\n \n     def parse_store(self, response):\n-        ref = \"_\".join(re.search(r\".+/(.+?)/(.+?)/(.+?)/?(?:\\.html|$)\", response.url).groups())\n-\n-        properties = {\n-            'name': response.xpath('//h1[@class=\"store-details__store-name\"]/text()').extract_first(),\n-            'ref': ref,\n-            'addr_full': response.xpath('//p[@itemprop=\"streetAddress\"]/text()').extract_first(),\n-            'city': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[1]/text()').extract_first(),\n-            'state': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[2]/text()').extract_first(),\n-            'postcode': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[3]/text()').extract_first(),\n-            'phone': response.xpath('//a[@class=\"store-details__link store-details__link--phone\"]/@content/text()').extract_first(),\n-            'lat': (response.xpath('//div[@id=\"map-wrap\"]/@data-map-lat').extract_first()),\n-            'lon': (response.xpath('//div[@id=\"map-wrap\"]/@data-map-lon').extract_first()),\n-            'website': response.url\n-        }\n-        yield GeojsonPointItem(**properties)\n+        if response.request.meta.get('redirect_urls'):\n+            return\n+\n+        store_json = json.loads(\n+            response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n+        )\n+        yield GeojsonPointItem(\n+            ref=response.url.split('/')[-1],\n+            name=store_json['name'],\n+            lat=float(store_json['geo']['latitude']),\n+            lon=float(store_json['geo']['longitude']),\n+            addr_full=store_json['address']['streetAddress'],\n+            city=store_json['address']['addressLocality'],\n+            state=store_json['address']['addressRegion'],\n+            postcode=store_json['address']['postalCode'],\n+            country=store_json['address']['addressCountry'],\n+            phone=store_json['telephone'],\n+            website=response.url,\n+            opening_hours=self.parse_hours(store_json['openingHoursSpecification'])\n+        )\n+\n+    def parse_hours(self, hours):\n+        opening_hours = OpeningHours()\n+\n+        for hour in hours:\n+            opening_hours.add_range(\n+                day=hour[\"dayOfWeek\"][0:2].capitalize(),\n+                open_time=hour[\"opens\"],\n+                close_time=hour[\"closes\"]\n+            )\n+\n+        return opening_hours.as_opening_hours()\n", "issue": "Spider heb is broken\nDuring the global build at 2021-08-18-14-42-26, spider **heb** failed with **320 features** and **1 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/logs/heb.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/output/heb.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-08-18-14-42-26/output/heb.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nimport re\n\nfrom locations.items import GeojsonPointItem\n\n\nclass HEBSpider(scrapy.Spider):\n    name = \"heb\"\n    item_attributes = { 'brand': \"H-E-B\", 'brand_wikidata': \"Q830621\" }\n    allowed_domains = [\"www.heb.com\"]\n    download_delay = 0.2\n    start_urls = (\n        'https://www.heb.com/sitemap/storeSitemap.xml',\n    )\n\n    def parse(self, response):\n        xml = scrapy.selector.Selector(response)\n        xml.remove_namespaces()\n\n        urls = xml.xpath('//loc/text()').extract()\n        for url in urls:\n            yield scrapy.Request(url=url, callback=self.parse_store, meta={\"url\": url})\n\n    def parse_store(self, response):\n        ref = \"_\".join(re.search(r\".+/(.+?)/(.+?)/(.+?)/?(?:\\.html|$)\", response.url).groups())\n\n        properties = {\n            'name': response.xpath('//h1[@class=\"store-details__store-name\"]/text()').extract_first(),\n            'ref': ref,\n            'addr_full': response.xpath('//p[@itemprop=\"streetAddress\"]/text()').extract_first(),\n            'city': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[1]/text()').extract_first(),\n            'state': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[2]/text()').extract_first(),\n            'postcode': response.xpath('//div[@class=\"store-details__location\"]/p[2]/span[3]/text()').extract_first(),\n            'phone': response.xpath('//a[@class=\"store-details__link store-details__link--phone\"]/@content/text()').extract_first(),\n            'lat': (response.xpath('//div[@id=\"map-wrap\"]/@data-map-lat').extract_first()),\n            'lon': (response.xpath('//div[@id=\"map-wrap\"]/@data-map-lon').extract_first()),\n            'website': response.url\n        }\n        yield GeojsonPointItem(**properties)\n", "path": "locations/spiders/heb.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nimport json\n\nimport scrapy\n\nfrom locations.items import GeojsonPointItem\nfrom locations.hours import OpeningHours\n\n\nclass HEBSpider(scrapy.Spider):\n    name = \"heb\"\n    item_attributes = { 'brand': \"H-E-B\", 'brand_wikidata': \"Q830621\" }\n    allowed_domains = [\"www.heb.com\"]\n    download_delay = 0.2\n    start_urls = (\n        'https://www.heb.com/sitemap/storeSitemap.xml',\n    )\n\n    def parse(self, response):\n        xml = scrapy.selector.Selector(response)\n        xml.remove_namespaces()\n\n        urls = xml.xpath('//loc/text()').extract()\n        for url in urls:\n            yield scrapy.Request(url=url, callback=self.parse_store, meta={\"url\": url})\n\n    def parse_store(self, response):\n        if response.request.meta.get('redirect_urls'):\n            return\n\n        store_json = json.loads(\n            response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n        )\n        yield GeojsonPointItem(\n            ref=response.url.split('/')[-1],\n            name=store_json['name'],\n            lat=float(store_json['geo']['latitude']),\n            lon=float(store_json['geo']['longitude']),\n            addr_full=store_json['address']['streetAddress'],\n            city=store_json['address']['addressLocality'],\n            state=store_json['address']['addressRegion'],\n            postcode=store_json['address']['postalCode'],\n            country=store_json['address']['addressCountry'],\n            phone=store_json['telephone'],\n            website=response.url,\n            opening_hours=self.parse_hours(store_json['openingHoursSpecification'])\n        )\n\n    def parse_hours(self, hours):\n        opening_hours = OpeningHours()\n\n        for hour in hours:\n            opening_hours.add_range(\n                day=hour[\"dayOfWeek\"][0:2].capitalize(),\n                open_time=hour[\"opens\"],\n                close_time=hour[\"closes\"]\n            )\n\n        return opening_hours.as_opening_hours()\n", "path": "locations/spiders/heb.py"}]}