prompt: 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()
  
  ```
  
golden_diff: 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()
  
verification_info: {"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"}]}